Skip to content

Commit

Permalink
IntelliJ IDEA plugin: project structure, action and class file location
Browse files Browse the repository at this point in the history
  • Loading branch information
ingokegel committed Oct 28, 2016
1 parent a44f87f commit 4cf6945
Show file tree
Hide file tree
Showing 12 changed files with 243 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .idea/compiler.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions build.gradle
Expand Up @@ -20,6 +20,9 @@ buildscript {
maven { maven {
url 'http://maven.ej-technologies.com/repository' url 'http://maven.ej-technologies.com/repository'
} }
maven {
url 'http://dl.bintray.com/jetbrains/intellij-plugin-service'
}
} }
dependencies { dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
Expand Down
Binary file added lib-compile/byteCodeViewer.jar
Binary file not shown.
24 changes: 24 additions & 0 deletions modules/idea/idea.iml
@@ -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>
169 changes: 169 additions & 0 deletions modules/idea/src/main/kotlin/org/gjt/jclasslib/idea/action.kt
@@ -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)
}
}
17 changes: 17 additions & 0 deletions modules/idea/src/main/resources/META-INF/plugin.xml
@@ -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 added modules/idea/src/main/resources/icons/jclasslib.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added modules/idea/src/main/resources/icons/jclasslib@2x.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions modules/idea_publish/idea_publish.gradle
@@ -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'
}
2 changes: 1 addition & 1 deletion settings.gradle
@@ -1,5 +1,5 @@
file('modules').listFiles() 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 -> .each { File dir ->
include dir.name include dir.name
ProjectDescriptor project = findProject(':' + dir.name) ProjectDescriptor project = findProject(':' + dir.name)
Expand Down

0 comments on commit 4cf6945

Please sign in to comment.