Supporting both Kotlin and Java

I wrote a plugin that currently works with Kotlin files.

However the PSI elements that the plugin looks at (specific annotations, etc) can all be met in Java files as well.

If I want to have the same inspections / quick-fixes / etc apply to both Kotlin and Java files, do I essentially need to have twice the number of classes, or is there some mechanism of easing the process? I guess I could have everything accept PsiElement but that would be pretty bad in terms of type safety.

I've not seen this documented but it would be interesting to know how JetBrains tackle this issue. Do you treat Java and Kotlin as completely separate and don't bother with any of this?

6 comments
Comment actions Permalink
Official comment

Kotlin and Java (and to some degree, Groovy) can be abstracted by using UAST-API. It is a cross-language facade that represents the common subset of the JVM languages. Central starting point is UElement, you can see a number of usages e.g. in Devkit Plugin inspections. See also https://youtrack.jetbrains.com/issue/IJSDK-540

Please feel free to ask further questions here.

Comment actions Permalink

This is great, did not know about UAST before. Thank you very much for the pointers, I'll play around with it!

1
Comment actions Permalink

Just rewrote the plugin using UAST. Everything went quite smoothly, but I have a couple of questions:

  1. Is using the InspectionManager class the only way to set up ProblemDescriptors in inspections? I'm only asking as it seems like you have to provide a specific ProblemHighlightType for each fix. Isn't that going to make choosing a highlight level in the Options menu useless as it'll be overridden by my code anyway?
  2. In some places in the code I'm getting warnings from the "UElement as PsiElement usage" inspection. Primarily because I'm accessing the nameIdentifier of several UMethods, and names of several UParameters. Overall do you think it's for the most part possible to write code that satisfies this inspection? I looked around the source and couldn't figure out how to get the nameIdentifier in another way. But it got me wondering whether the inspection should only be used as a guide, or whether it telling you strictly means it is in fact possible to get what you need in some other way.

Actually not having access to nameIdentifier on UClass prompted me to write this utility method:

fun isClassIdentifier(element: PsiElement): Boolean {
if (element !is LeafPsiElement) return false
val context = element.context
if (context is PsiClass) { // Java
return context.nameIdentifier == element
}
if (context is KtClass) { // Kotlin
return context.nameIdentifier == element
}
return false
}

It might be useful to have it on UClass to avoid this.

0
Comment actions Permalink

1. Use ProblemHighlightType.GENERIC_ERROR_OR_WARNING in such case

2. Use PsiElement identifier = UElementKt.getSourcePsiElement(method.getUastAnchor());

1
Comment actions Permalink

> whether the inspection should only be used as a guide, or whether it telling you strictly means it is in fact possible to get what you need in some other way.

The second option. It should be possible to get what you need in another way, if it is not possible then it is our (JetBrains) inadvertency or maybe what you want couldn't be safely done in a language-abstract way.

1
Comment actions Permalink

Thanks very much for the info guys. I'll keep exploring the APIs to get rid of all inspection warnings :-)

0

Please sign in to leave a comment.