Replacing the content of a java file editor

Hi,

I'm just beginning with IntelliJ plugin developpment.
I would like to replace *automatically* (on opening) the Java editor content, when the source file is not found (ie I'd like to override the default behaviour that shows comments with "compiled code"). And of course I'd like to keep the same behaviour in order to lookup for sources, notifications, and of course display the original sources if IntelliJ knows them.

To my understanding I should register a component in the aplication scope.
I think the component class should take EditorFactory as an argument, but from there I'm a bit lost.
JavaEditorFileSwapper, AttachSourcesNotificationProvider seem to be good guesses. But I'm not sure how I can plug there.

Can I extend AttachSourcesNotificationProvider and override createNotificationPanel? Or is there a better way?
Besides if it is the best way, how would I register this "extension point" and replace the current AttachSourcesNotificationProvider ?


By the way I'm developping this with IntelliJ 10.5.2.


Guidance would be very much appreciated.

4 comments

Hello Brice,

If you want to plug in your own decompiler, the correct way to do so is to
register an extension in the ContentBasedClassFileProcessor extension point.

I'm just beginning with IntelliJ plugin developpment.

I would like to replace automatically (on opening) the Java editor
content, when the source file is not found (ie I'd like to override
the default behaviour that shows comments with "compiled code"). And
of course I'd like to keep the same behaviour in order to lookup for
sources, notifications, and of course display the original sources if
IntelliJ knows them.

To my understanding I should register a component in the aplication
scope.

I think the component class should take EditorFactory as an argument,
but from there I'm a bit lost.

JavaEditorFileSwapper, AttachSourcesNotificationProvider seem to be
good guesses. But I'm not sure how I can plug there.

Can I extend AttachSourcesNotificationProvider and override
createNotificationPanel? Or is there a better way?

Besides if it is the best way, how would I register this "extension
point" and replace the current AttachSourcesNotificationProvider ?

By the way I'm developping this with IntelliJ 10.5.2.

Guidance would be very much appreciated.


--
Dmitry Jemerov
Development Lead
JetBrains, Inc.
http://www.jetbrains.com/
"Develop with Pleasure!"


0

Hi Dmitry,

Thx for the answer, it seems it is working. The following implementation seems to do the job (don't mind yet the returned text as I'm experiencing how things work) :

public class JavaDecompilerClassFileProcessor implements ContentBasedClassFileProcessor {
    @Override
    public boolean isApplicable(Project project, VirtualFile virtualFile) {
        return virtualFile.getFileType() == StdFileTypes.CLASS;
    }


    @NotNull
    @Override
    public SyntaxHighlighter createHighlighter(Project project, VirtualFile vFile) {
        return SyntaxHighlighter.PROVIDER.create(StdFileTypes.JAVA, project, vFile);
    }


    @NotNull
    @Override
    public String obtainFileText(Project project, VirtualFile virtualFile) {
        return "/* yup */";
    }


    @Override
    public Language obtainLanguageForFile(VirtualFile virtualFile) {
        return null;
    }
}


However I experience some small issues / questions :

  • The icon of the decompiled file in the IDE shows a "binary" icon, how can I change that (note that this seem to be the default behavior) ?

icons.png

  • In order to let the attached sources be displayed, I had to make obtainLanguageForFile return null. How could I check, in a more elegant way, in the isApplicable method if the file has no source attached ? I already hove something ugly in mind that use JavaEditorFileSwapper.findSourceFile(Project project, VirtualFile eachFile), but as said I don't like this approach.

  • I'm seeing these errors logged when I open a file. It seems to be related to PsiElement child text, but I'm not sure why it happens now.
    [ 191937]  ERROR - i.impl.compiled.ClsElementImpl - getText() == null, element = PsiClass:MethodWriter, parent = PsiFile:MethodWriter.class
    java.lang.Throwable
        at com.intellij.openapi.diagnostic.Logger.error(Logger.java:52)
        at com.intellij.psi.impl.compiled.ClsElementImpl.getTextLength(ClsElementImpl.java:143)
        at com.intellij.psi.SingleRootFileViewProvider.findElementAt(SingleRootFileViewProvider.java:390)
        at com.intellij.psi.SingleRootFileViewProvider.findElementAt(SingleRootFileViewProvider.java:343)
        at com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil.findInjectedElementNoCommitWithOffset(InjectedLanguageUtil.java:246)
        at com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil.findInjectedElementNoCommit(InjectedLanguageUtil.java:257)
        at com.intellij.codeInsight.highlighting.BraceHighlightingHandler.a(BraceHighlightingHandler.java:131)
        at com.intellij.codeInsight.highlighting.BraceHighlightingHandler.access$100(BraceHighlightingHandler.java:69)
        at com.intellij.codeInsight.highlighting.BraceHighlightingHandler$1$1.compute(BraceHighlightingHandler.java:104)
        at com.intellij.codeInsight.highlighting.BraceHighlightingHandler$1$1.compute(BraceHighlightingHandler.java:100)
        at com.intellij.openapi.application.impl.ApplicationImpl$11.run(ApplicationImpl.java:818)
        at com.intellij.openapi.application.impl.ApplicationImpl.runReadAction(ApplicationImpl.java:790)
        at com.intellij.openapi.application.impl.ApplicationImpl.runReadAction(ApplicationImpl.java:816)
        at com.intellij.codeInsight.highlighting.BraceHighlightingHandler$1.run(BraceHighlightingHandler.java:100)
        at com.intellij.concurrency.JobUtil$3.call(JobUtil.java:133)
        at com.intellij.concurrency.JobUtil$3.call(JobUtil.java:130)
        at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
        at java.util.concurrent.FutureTask.run(FutureTask.java:138)
        at com.intellij.concurrency.PrioritizedFutureTask.access$101(PrioritizedFutureTask.java:31)
        at com.intellij.concurrency.PrioritizedFutureTask$1.run(PrioritizedFutureTask.java:70)
        at com.intellij.concurrency.PrioritizedFutureTask.run(PrioritizedFutureTask.java:113)
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
        at java.lang.Thread.run(Thread.java:680)
    [ 191937]  ERROR - i.impl.compiled.ClsElementImpl - IntelliJ IDEA 10.5.2  Build #IU-107.587
    [ 191937]  ERROR - i.impl.compiled.ClsElementImpl - JDK: 1.6.0_26
    [ 191937]  ERROR - i.impl.compiled.ClsElementImpl - VM: Java HotSpot(TM) 64-Bit Server VM
    [ 191937]  ERROR - i.impl.compiled.ClsElementImpl - Vendor: Apple Inc.
    [ 191937]  ERROR - i.impl.compiled.ClsElementImpl - OS: Mac OS X
    [ 191937]  ERROR - i.impl.compiled.ClsElementImpl - Last Action:


  • And at last, I saw in the MASTER community source that you extracted some methods of ContentBasedClassFileProcessor into another interface ContentBasedFileSubstitutor. As I'm using the extension point name defined in ContentBasedClassFileProcessor (as of idea/107.587). The plugin then might not work with upper version unless I part of change the code, the implemented interface, maybe even the extension point name. Can I work around this to make it work on both version ?


Thanx for your attention, it is greatly appreciated.

0

Hello brice,

You can replace the icon using the iconProvider extension point.

Returing null from obtainLanguageForFile() is not correct in this case and
shouldn't be needed for "attach sources" to work. This is also likely what
causes the exception.

The refactoring done in the master doesn't affect source compatibility, and
as far as I understand binary compatibility should have also been maintained.

Thx for the answer, it seems it is working. The following
implementation seems to do the job (don't mind yet the returned text
as I'm experiencing how things work) :

public class JavaDecompilerClassFileProcessor implements
ContentBasedClassFileProcessor {
@Override
public boolean isApplicable(Project project, VirtualFile
virtualFile) {
return virtualFile.getFileType() == StdFileTypes.CLASS;
}
@NotNull
@Override
public SyntaxHighlighter createHighlighter(Project project,
VirtualFile vFile) {
return SyntaxHighlighter.PROVIDER.create(StdFileTypes.JAVA,
project, vFile);
}
@NotNull
@Override
public String obtainFileText(Project project, VirtualFile
virtualFile) {
return "/* yup */";
}
@Override
public Language obtainLanguageForFile(VirtualFile virtualFile) {
return null;
}
}
However I experience some small issues / questions :

  • The icon of the decompiled file in the IDE shows a "binary" icon,

how can I change that (note that this seem to be the default behavior)
?

Image:icons.png

  • In order to let the attached sources be displayed, I had to make

obtainLanguageForFile return null. How could I check, in a more
elegant way, in the isApplicable method if the file has no source
attached ? I already hove something ugly in mind that use
JavaEditorFileSwapper.findSourceFile(Project project, VirtualFile
eachFile), but as said I don't like this approach.

  • I'm seeing these errors logged when I open a file. It seems to be

related to PsiElement child text, but I'm not sure why it happens now.
  ERROR - i.impl.compiled.ClsElementImpl - getText() == null,
element = PsiClass:MethodWriter, parent = PsiFile:MethodWriter.class
java.lang.Throwable
at com.intellij.openapi.diagnostic.Logger.error(Logger.java:52)
at
com.intellij.psi.impl.compiled.ClsElementImpl.getTextLength(ClsElement
Impl.java:143)
at
com.intellij.psi.SingleRootFileViewProvider.findElementAt(SingleRootFi
leViewProvider.java:390)
at
com.intellij.psi.SingleRootFileViewProvider.findElementAt(SingleRootFi
leViewProvider.java:343)
at
com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil.findIn
jectedElementNoCommitWithOffset(InjectedLanguageUtil.java:246)
at
com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil.findIn
jectedElementNoCommit(InjectedLanguageUtil.java:257)
at
com.intellij.codeInsight.highlighting.BraceHighlightingHandler.a(Brace
HighlightingHandler.java:131)
at
com.intellij.codeInsight.highlighting.BraceHighlightingHandler.access$
100(BraceHighlightingHandler.java:69)
at
com.intellij.codeInsight.highlighting.BraceHighlightingHandler$1$1.com
pute(BraceHighlightingHandler.java:104)
at
com.intellij.codeInsight.highlighting.BraceHighlightingHandler$1$1.com
pute(BraceHighlightingHandler.java:100)
at
com.intellij.openapi.application.impl.ApplicationImpl$11.run(Applicati
onImpl.java:818)
at
com.intellij.openapi.application.impl.ApplicationImpl.runReadAction(Ap
plicationImpl.java:790)
at
com.intellij.openapi.application.impl.ApplicationImpl.runReadAction(Ap
plicationImpl.java:816)
at
com.intellij.codeInsight.highlighting.BraceHighlightingHandler$1.run(B
raceHighlightingHandler.java:100)
at com.intellij.concurrency.JobUtil$3.call(JobUtil.java:133)
at com.intellij.concurrency.JobUtil$3.call(JobUtil.java:130)
at
java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at
com.intellij.concurrency.PrioritizedFutureTask.access$101(PrioritizedF
utureTask.java:31)
at
com.intellij.concurrency.PrioritizedFutureTask$1.run(PrioritizedFuture
Task.java:70)
at
com.intellij.concurrency.PrioritizedFutureTask.run(PrioritizedFutureTa
sk.java:113)
at
java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecu
tor.java:886)
at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.
java:908)
at java.lang.Thread.run(Thread.java:680)
  ERROR - i.impl.compiled.ClsElementImpl - IntelliJ IDEA
10.5.2  Build #IU-107.587
  ERROR - i.impl.compiled.ClsElementImpl - JDK: 1.6.0_26 [
191937]  ERROR - i.impl.compiled.ClsElementImpl - VM: Java HotSpot(TM)
64-Bit Server VM   ERROR - i.impl.compiled.ClsElementImpl -
Vendor: Apple Inc.   ERROR - i.impl.compiled.ClsElementImpl -
OS: Mac OS X   ERROR - i.impl.compiled.ClsElementImpl - Last
Action:

  • And at last, I saw in the MASTER community source that you extracted

some methods of ContentBasedClassFileProcessor into another interface
ContentBasedFileSubstitutor. As I'm using the extension point name
defined in ContentBasedClassFileProcessor (as of idea/107.587). The
plugin then might not work with upper version unless I part of change
the code, the implemented interface, maybe even the extension point
name. Can I work around this to make it work on both version ?

Thanx for your attention, it is greatly appreciated.

---
Original message URL:
http://devnet.jetbrains.net/message/5364170#5364170


--
Dmitry Jemerov
Development Lead
JetBrains, Inc.
http://www.jetbrains.com/
"Develop with Pleasure!"


0

Hi Dmitry

Thx for the quick reply.
Cool for the binary compatibility.

  • Anyway regarding the issue about sources. The following snippet is the code I tried:


    @Override
    public Language obtainLanguageForFile(VirtualFile virtualFile) {
        return Language.findLanguageByID("JAVA");
    }

If I'm always returning Java language then IntelliJ always use my extension point instead of displaying the attached source.

In this case also all icons displayed are the "binary" one instead of the Java/Interface/Annotation/etc icons.

However not returning null indeed it removed the error.

So the question is : how can I check the class file has an attached source ?

  • About the IconProvider, I'm not sure how to use it to return the correct icon for the decompiled type. Maybe I could use the methods of the PsiElement.

  • By the way do IntelliJ provide any facade/utility for OS/arch information ? Or should I use the system properties os.name and os.arch ? Peter (from JetBreans told me about the SystemInfo class)


Updated the answer, corrected some typo

0

Please sign in to leave a comment.