Android Studio plugin - Groovy Psi API???

So I watched Dimitry's demo of live coding a plugin from scratch and got inspired. I am trying to feel my way around writing an Android Studio plugin using IntelliJ CE. (I noticed Studio doesn't have plugin project templates, what a shame!) My initial goal is to invoke my plugin from a "build.gradle" file and visualize/list all of the code blocks at the cursor following very close to the live coding demo. I'm currently stuck on this statement in my plugin which returns false:

PsiTreeUtil.getParentOfType(psiElement, GrCodeBlock.class

The PsiElement in question is a "whitespace element in a GrCloseableBlockImpl paranet. (psiElement.getParent() returns "Closable block" in the debugger.) I've further drilled down in the debugger to:

GrClosableBlock.class.isAssignableFrom(psiElement.getParent().getClass())

Which also returns false. Interestingly enough this returns true in the same debug session:
GrClosableBlock.class.isAssignableFrom(GrClosableBlockImpl.class)

To get my plugin to even compile I added all of the jars under the Groovy Plugin folder in my IJ 13.1 CE install location. This has me thinking the issue is most likely classloader related. I was confused why I couldn't access the Groovy Psi stuff without adding these plugin Jars. I'm seeing groovy isn't yet as fully baked into the IDE as Java though getting closer everyday. When I tried marking these jar dependencies as "provided" instead of "Compile" in the module settings I got NoClassDefFound errors so I know I have to inline them even though I feel they are already included with the product. My overall question is how does one use the Groovy Psi stuff in a plugin? Is my approach of inlining the Groovy plugin jars correct or am I missing something? Also why does my attempt at finding CodeBlocks not working as I would expect?

**Update**
I just found out that there are 2 different classloaders involved! my psiElement is loaded by:
PluginClassLoader[org.intellij.groovy, 9.0]
Where the GrCodeBlock is loaded by:
PluginClassLoader[com.craig.android.plugin.ndk, 1.0]
This probably means that I'll have to load all of the GroovyPSI stuff using reflection... (yuk!) Is there a better way?

6 comments
Comment actions Permalink

You need to add the Groovy plugin jars to the classpath of the IntelliJ IDEA SDK, not as libraries to your plugin. This will avoid the double classloading problem.

1
Comment actions Permalink

Wow, I was just thinking about doing that ! (I was also wondering what the side effects would be.) It feels sort of like a hack since I would expect the SDK to reflect all of the SDK and only the SDK stuff but I'll give it a try. Thanks Dmitry!!

0
Comment actions Permalink

Dmitry,

I'm adding gragent.jar, groovy-jps-plugin.jar, Groovy.jar, and groovy_rt.jar. Should I add all of these or only some of these or...?

0
Comment actions Permalink

I still get a ClassNotFoundException even when I add the plugin jars to my SDK classpath. (I sort of figured this would be no different than marking them as "provided" in the module classpath.) Sticking them in the SDK classpath only allos the compiler to resolve the symbols but doesn't actually distribute them when running the plugin. The problem seems to be the way my plugin is loaded. It has no visibility into the classloader that loads these Psi classes. This pushes me towards reflective calls to coerce things to work. I could also try classloader foolery and ask the other classloader to load a chunk of my plugin but I'm sure that creates other issues.

0
Comment actions Permalink

Dmitry,

I may have found my answer.I enabled:
<depends>org.intellij.groovy</depends>
In my plugin.xml along with including the libraries in my module settings with "provided" scope. That allows me to build and run without the NoClassDefFound error.
I found the info here: http://confluence.jetbrains.com/display/IDEADEV/IntelliJ+IDEA+Plugin+Structure#IntelliJIDEAPluginStructure-PluginClassLoaders

Which states:

"By default, the main IDEA class loader loads classes that were not found in the plugin class loader. However, in the plugin.xml file, one can use the <depends> element to specify that a plugin depends on one or more other plugins. In this case, the class loaders of those plugins will be used for classes not found in the current plugin. This allows a plugin to reference classes from other plugins."

1
Comment actions Permalink

Clifton/Dmitry,

I have added Groovy.jar as a Library to the project and from there I've added it to the module. Still the IDE is unable to recognize the Psi Classes. When I pressed Alt+Enter, it showed "Add Library 'Groovy' to classpath". Now, IDE recognized Psi classes. When I try to run my plugin, compiler throws "Cannot find Symbol" errors for all Psi classes I referred to.

Do you find any problem with my approach?

0

Please sign in to leave a comment.