Create standalone parser from plugin-code

Hi all,

I searched all over this place but I could not find a definitive answer. Is it possible to take my plugin code (which of course contains a PsiParser) and turn it into a standalone jar which can take a file, parse it and return for instance a string representation of the AST? The library can of course depend on openapi.jar or idea.jar but does something like this always needs a full Idea?

When I look at com.intellij.testFramework.ParsingTestCase, then I see that all kind of stuff is mocked up: the application is initialized, projects, PsiManagers, PsiFileFactory, etc..
Is the parser and the PsiBuilder so deep inside the Idea core, that it is impossible to simply parse a file?

Cheers
Patrick

5 comments
Comment actions Permalink

Yes, this is possible (and in fact the Kotlin compiler does roughly that). To initialize the necessary environment, use the CoreApplicationEnvironment and CoreProjectEnvironment classes.

0
Avatar
Patrick Scheibe
Comment actions Permalink

Thanks Dmitry. This seems what I was looking for. I created a very small example that I stole from here which throws an exception (I added it at the end). Can you tell me how this is supposed to work, especially how to create a project out of thing air :-) The code I used does nothing than creating the AST:

public class Main {

  private static final String baseDir = "/home/patrick/tmp";
  private static final String sourceFile = "TestClass.java";

  public static void main(String[] args) throws IOException {
    PsiFileFactory psiFileFactory = createPsiFactory();
    File file = new File(baseDir, sourceFile);
    String javaSource = FileUtil.loadFile(file);
    FileASTNode node = parseJavaSource(javaSource, psiFileFactory);

  }

  private static PsiFileFactory createPsiFactory() {
    MockProject mockProject = createProject();
    return PsiFileFactory.getInstance(mockProject);
  }

  private static FileASTNode parseJavaSource(String JAVA_SOURCE, PsiFileFactory psiFileFactory) {
    PsiFile psiFile = psiFileFactory.createFileFromText("__dummy_file__.java", JavaFileType.INSTANCE, JAVA_SOURCE);
    PsiJavaFile psiJavaFile = (PsiJavaFile)psiFile;
    return psiJavaFile.getNode();
  }

  private static MockProject createProject() {
    JavaCoreProjectEnvironment environment = new JavaCoreProjectEnvironment(new Disposable() {
      @Override
      public void dispose() {
      }
    }, new JavaCoreApplicationEnvironment(new Disposable() {
      @Override
      public void dispose() {
      }
    }));

    return environment.getProject();
  }

}


The exception thrown is

Exception in thread "main" java.lang.IllegalArgumentException: Missing extension point: com.intellij.codeInsight.containerProvider in area null
     at com.intellij.openapi.extensions.impl.ExtensionsAreaImpl.getExtensionPoint(ExtensionsAreaImpl.java:331)
     at com.intellij.openapi.extensions.impl.ExtensionsAreaImpl.getExtensionPoint(ExtensionsAreaImpl.java:340)
     at com.intellij.core.CoreApplicationEnvironment.addExtension(CoreApplicationEnvironment.java:279)
     at com.intellij.core.JavaCoreApplicationEnvironment.<init>(JavaCoreApplicationEnvironment.java:76)
     at Main.createProject(Main.java:44)
     at Main.createPsiFactory(Main.java:33)
     at Main.main(Main.java:20)
     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
     at java.lang.reflect.Method.invoke(Method.java:597)
     at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

0
Comment actions Permalink

Hello Patrick,

I was in your shoes not long ago, and you may have to register many more extension points depending on what you are doing. However, the pattern is similar. It helps to have the CE codebase close by to find the class  implementing the extension point that you need. Use IntelliJ's "Find in path" search to locate strings containing the extension point you want: com.intellij.codeInsight.containerProvider .  You will find it's located here:

ContainerProvider.png

Now to your specific question. You need to register the extension class on your "Root Area" or "null" as the exception message suggests as follows:

 
ExtensionsArea rootArea = Extensions.getRootArea();
CoreApplicationEnvironment.registerExtensionPoint(rootArea, ContainerProvider.EP_NAME, ContainerProvider.class);


This should "fix" that specific exception, but I suspect you will need to do more of this as you use more features, but the solution is similar: just register the correct EP and sometimes instances of some extensions in addition.

I hope that helps you find your way around.

Kind regards,
Dayo

0
Avatar
Patrick Scheibe
Comment actions Permalink

Hi Dayo,

thanks for your answer. I'm very very sorry that I haven't reported back earlier, because I could already fix the issue.
I wanted to report back with as much information as possible, because I think there is still an issue here (that I probably just don't understand).

Would you mind to take 5 minutes to test what I described in


I have tried to set it up as simple as possible: Clone, open, compile, see, change lib, see again.

Regarding your other comments: I'm usually not that helpless. I have of course cloned the IDEA sources locally and attached to the libs. That's almost the only way to gain insight when writing features for a plugin. Although I have to say that in recent times there is a lot more of official documentation. Still, looking through the sources is very often the only way to see how things are intended to be used. The pure size of the intellij framework is nevertheless overwhelming and I find myself often enough in situations, where I understand the ideas behind the class-layouts not before looking at it the second or third time.

Cheers
Patrick

0
Comment actions Permalink

Hello Patrick,

I cloned, built and ran the project as you describe, and it was fine: I couldn't reproduce the issue you mentioned. I suspect the only difference is that I have Java 8 as opposed to Java 6 which your screenshot suggested.

Apologies that I couldb't be of much help.

Kind regards,
Dayo

0

Please sign in to leave a comment.