Permalink
Browse files

IntelliJ IDEA plugin: project structure, action and class file location

  • Loading branch information...
ingokegel committed Oct 28, 2016
1 parent a44f87f commit 4cf6945e78fedfb5218638433cf044add27ef06d
View
@@ -23,6 +23,8 @@
<module name="browser_test" target="1.6" />
<module name="data_main" target="1.6" />
<module name="data_test" target="1.6" />
<module name="idea_main" target="1.6" />
<module name="idea_test" target="1.6" />
</bytecodeTargetLevel>
</component>
</project>
View
@@ -12,6 +12,7 @@
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/modules/browser" />
<option value="$PROJECT_DIR$/modules/data" />
<option value="$PROJECT_DIR$/modules/idea_publish" />
<option value="$PROJECT_DIR$/modules/installer" />
</set>
</option>
View
@@ -8,6 +8,7 @@
<module fileurl="file://$PROJECT_DIR$/.idea/modules/modules/data/data.iml" filepath="$PROJECT_DIR$/.idea/modules/modules/data/data.iml" group="data" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/modules/data/data_main.iml" filepath="$PROJECT_DIR$/.idea/modules/modules/data/data_main.iml" group="data" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/modules/data/data_test.iml" filepath="$PROJECT_DIR$/.idea/modules/modules/data/data_test.iml" group="data" />
<module fileurl="file://$PROJECT_DIR$/modules/idea/idea.iml" filepath="$PROJECT_DIR$/modules/idea/idea.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/modules/installer/installer.iml" filepath="$PROJECT_DIR$/.idea/modules/modules/installer/installer.iml" group="installer" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/jclasslib.iml" filepath="$PROJECT_DIR$/.idea/modules/jclasslib.iml" />
</modules>
View
@@ -20,6 +20,9 @@ buildscript {
maven {
url 'http://maven.ej-technologies.com/repository'
}
maven {
url 'http://dl.bintray.com/jetbrains/intellij-plugin-service'
}
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
Binary file not shown.
View
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PLUGIN_MODULE" version="4">
<component name="DevKit.ModuleBuildProperties" url="file://$MODULE_DIR$/src/main/resources/META-INF/plugin.xml" />
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/kotlin" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
</content>
<orderEntry type="jdk" jdkName="IJ CE 2016.2" jdkType="IDEA JDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module-library" scope="PROVIDED">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../../lib-compile/byteCodeViewer.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module" module-name="browser_main" />
<orderEntry type="module" module-name="data_main" />
</component>
</module>
@@ -0,0 +1,169 @@
/*
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the license, or (at your option) any later version.
*/
package org.gjt.jclasslib.idea
import com.intellij.byteCodeViewer.ByteCodeViewerManager
import com.intellij.ide.util.JavaAnonymousClassesHelper
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.progress.Task
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.CompilerModuleExtension
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.util.Computable
import com.intellij.openapi.util.IconLoader
import com.intellij.psi.*
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil
import com.intellij.psi.util.*
import org.gjt.jclasslib.io.ClassFileReader
import org.gjt.jclasslib.structures.ClassFile
import java.io.File
import java.io.FileNotFoundException
import java.io.IOException
import java.io.InputStream
class ShowBytecodeAction : AnAction() {
override fun update(e: AnActionEvent) {
e.presentation.apply {
isEnabled = getPsiElement(e)?.run { containingFile is PsiClassOwner && ByteCodeViewerManager.getContainingClass(this) != null } ?: false
icon = ICON
}
}
override fun actionPerformed(e: AnActionEvent) {
val psiElement = getPsiElement(e) ?: return
val project = e.project ?: return
ProgressManager.getInstance().run(object : Task.Backgroundable(project, "Locating class file ...") {
var classFile: ClassFile? = null
var errorMessage: String? = null
override fun run(indicator: ProgressIndicator) {
classFile = ApplicationManager.getApplication().runReadAction(Computable<ClassFile> {
try {
getClassFile(psiElement)
} catch(e: FileNotFoundException) {
errorMessage = "Class file could not be found" + (if (e.message.isNullOrBlank()) "" else ": " + e.message)
null
} catch(e: Exception) {
errorMessage = "Error reading class file: " + e.message
null
}
})
}
override fun onSuccess() {
if (!project.isDisposed && errorMessage != null && myTitle != null) {
Messages.showWarningDialog(project, errorMessage, "jclasslib bytecode viewer")
} else {
Messages.showMessageDialog("Got class file " + classFile, "jclasslib", Messages.getInformationIcon())
//TODO continue
}
}
})
}
private fun getPsiElement(e: AnActionEvent): PsiElement? {
return getPsiElement(e.dataContext, e.project, e.getData(CommonDataKeys.EDITOR))
}
private fun getPsiElement(dataContext: DataContext, project: Project?, editor: Editor?): PsiElement? {
if (project == null) {
return null
} else if (editor == null) {
return dataContext.getData(CommonDataKeys.PSI_ELEMENT)
} else {
val psiFile = PsiUtilBase.getPsiFileInEditor(editor, project)
val injectedEditor = InjectedLanguageUtil.getEditorForInjectedLanguageNoCommit(editor, psiFile)
return injectedEditor?.let { injectedEditor ->
findElementInFile(PsiUtilBase.getPsiFileInEditor(injectedEditor, project), injectedEditor)
} ?: findElementInFile(psiFile, editor)
}
}
private fun findElementInFile(psiFile: PsiFile?, editor: Editor): PsiElement? = psiFile?.findElementAt(editor.caretModel.offset)
private fun getClassFile(psiElement: PsiElement): ClassFile {
val containingClass = ByteCodeViewerManager.getContainingClass(psiElement) ?: throw FileNotFoundException("<containing class>")
val classVMName = getClassName(containingClass) ?: throw FileNotFoundException("<class name>")
val module = ModuleUtilCore.findModuleForPsiElement(psiElement)
return if (module == null) {
getClassFileNoModule(psiElement, containingClass, classVMName)
} else {
getClassFileModule(module, containingClass, classVMName)
}
}
private fun getClassFileNoModule(psiElement: PsiElement, containingClass: PsiClass, classVMName: String): ClassFile {
val project = containingClass.project
val qualifiedName = PsiUtil.getTopLevelClass(psiElement)?.qualifiedName ?: throw FileNotFoundException("<top level class>")
JavaPsiFacade.getInstance(project).findClass(qualifiedName, psiElement.resolveScope)?.let { psiClass ->
val virtualFile = PsiUtilCore.getVirtualFile(psiClass)
val fileIndex = ProjectRootManager.getInstance(project).fileIndex
if (virtualFile != null && fileIndex.isInLibraryClasses(virtualFile)) {
try {
val rootForFile = fileIndex.getClassRootForFile(virtualFile)
if (rootForFile != null) {
val classFile = rootForFile.findFileByRelativePath("/" + classVMName.replace('.', '/') + ".class")
if (classFile != null) {
return readClassFile(classFile.inputStream)
}
}
} catch (e: IOException) {
LOG.error(e)
}
}
}
throw FileNotFoundException()
}
private fun getClassFileModule(module: Module, containingClass: PsiClass, classVMName: String): ClassFile {
val virtualFile = containingClass.containingFile.virtualFile ?: throw FileNotFoundException("<virtual file>")
val moduleExtension = CompilerModuleExtension.getInstance(module) ?: throw FileNotFoundException("<module extension>")
val file = File(if (ProjectRootManager.getInstance(module.project).fileIndex.isInTestSourceContent(virtualFile)) {
val pathForTests = moduleExtension.compilerOutputPathForTests ?: throw FileNotFoundException("<compilerOutputPathForTests>")
pathForTests.path
} else {
val compilerOutputPath = moduleExtension.compilerOutputPath ?: throw FileNotFoundException("<compilerOutputPath>")
compilerOutputPath.path
} + "/" + classVMName.replace('.', '/') + ".class")
if (file.exists()) {
return readClassFile(file.inputStream())
} else {
throw FileNotFoundException(file.path)
}
}
private fun readClassFile(inputStream: InputStream): ClassFile = ClassFileReader.readFromInputStream(inputStream)
private fun getClassName(containingClass: PsiClass): String? {
if (containingClass is PsiAnonymousClass) {
val containingClassOfAnonymous = PsiTreeUtil.getParentOfType(containingClass, PsiClass::class.java) ?: return null
return getClassName(containingClassOfAnonymous) + JavaAnonymousClassesHelper.getName(containingClass)
} else {
return ClassUtil.getJVMClassName(containingClass)
}
}
companion object {
private val ICON = IconLoader.getIcon("/icons/jclasslib.png") // 13x13
private val LOG = Logger.getInstance("#" + ShowBytecodeAction::class.java.name)
}
}
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE idea-plugin PUBLIC "Plugin/DTD" "http://plugins.intellij.net/plugin.dtd">
<idea-plugin version="2" url="https://github.com/ingokegel/jclasslib">
<name>jclasslib</name>
<description>Bytecode viewer jclasslib</description>
<version>5.0</version>
<idea-version since-build="162"/>
<depends>ByteCodeViewer</depends>
<actions>
<action id="ShowByteCodeJclasslib" class="org.gjt.jclasslib.idea.ShowBytecodeAction"
text="Show Bytecode With jclasslib">
<add-to-group group-id="QuickActions" anchor="after" relative-to-action="QuickJavaDoc"/>
</action>
</actions>
</idea-plugin>
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,25 @@
plugins {
id 'org.jetbrains.intellij' version '0.1.10'
}
apply plugin: 'kotlin'
intellij {
version 'IC-2016.2.5'
pluginName 'jclasslib'
}
sourceSets {
main {
kotlin {
srcDirs += "../idea/src/main/kotlin"
}
resources {
srcDirs += "../idea/src/main/resources"
}
}
}
dependencies {
compile project(':browser')
compileOnly ':byteCodeViewer'
}
View
@@ -1,5 +1,5 @@
file('modules').listFiles()
.findAll {it.isDirectory() && !it.name.startsWith('.')}
.findAll {it.isDirectory() && !it.name.startsWith('.') && new File(it, it.name + '.gradle').exists()}
.each { File dir ->
include dir.name
ProjectDescriptor project = findProject(':' + dir.name)

0 comments on commit 4cf6945

Please sign in to comment.