GrReferenceTypeEnhancer adds autocompletion, but marks field as unknown

Answered

Hi! I've added a GrReferenceTypeEnhancer to the org.intellij.groovy.referenceTypeEnhancer EP in my plugin. Reason being, that the groovy scripts are executed in an environment which injects variables and I want to add a dynamic property for every script file in every project nor do i want to add another variable just to cast the one unknown one.

I've did the following:

class GrScriptingApiReferenceTypeEnhancer : GrReferenceTypeEnhancer() {

    companion object {
        const val SCRIPTING_API_V1_FQN = "de.xxx.yyy.zzz.ScriptingApiV1"
    }

    override fun getReferenceType(ref: GrReferenceExpression?, resolved: PsiElement?): PsiType? {
        if (ref == null || ref.qualifiedReferenceName != "apiV1") {
            return null
        }
        return PsiType.getTypeByName(SCRIPTING_API_V1_FQN, ref.project, ref.resolveScope)
    }

}

 

It does work in terms of auto completion:

but is marked with “No candidates found for method call apiV1”

 

How could I implement that apiV1 is rendered just as a known variable? And is it fine to call PsiType.getTypeByName for every EP-call? Or should I rather cache that (e.g. Caffeine cache with the project as a lazy key)?

0
3 comments

Hi, if I understood your question correctly, then:
1. You may try to use `org.jetbrains.plugins.groovy.lang.resolve.NonCodeMembersContributor`. 
2. There is no need to cache it, as other implementations invoke `PsiType.getTypeByName` every time.

1

Hi Georgii, I think the contributor might be indeed the correct approach - though I don't really get it to run as I want it to. I've tried a few things and am now stuck with

class GrScriptingApiContributor : NonCodeMembersContributor() {

    override fun processDynamicElements(
        qualifierType: PsiType,
        processor: PsiScopeProcessor,
        place: PsiElement,
        state: ResolveState
    ) {
        if (!processor.shouldProcessProperties()) {
            return
        }
        val reference = place.asSafely<GrReferenceExpression>() ?: return
        val name = processor.getName(state) ?: return
        if (name != "apiV1") {
            return
        }
        PsiType.getTypeByName(SCRIPTING_API_V1_FQN, reference.project, reference.resolveScope)
            .resolve()?.processDeclarations(processor, state, null, reference)
    }

}

To be honest, the other implementations of the NonCodeMembersContributor are not helping me much either. I can tell, that the PsiType… stuff at the end is called - but it does not really work.

When it works, does that also substitute the GrReferenceTypeEnhancer - or is that still required?

0

Alright, got it - works exactly how I want it. Thank you very much!

 

Final code:

class GrScriptingApiContributor : NonCodeMembersContributor() {

    override fun processDynamicElements(
        qualifierType: PsiType,
        processor: PsiScopeProcessor,
        place: PsiElement,
        state: ResolveState
    ) {
        if (!processor.shouldProcessProperties()) {
            return
        }
        val name = processor.getName(state) ?: return
        if (name != "apiV1") {
            return
        }
        val variable = ScriptingApiImplicitProperty(place.project)
        ResolveUtil.processElement(processor, variable, state)
    }

    class ScriptingApiImplicitProperty(project: Project) : GrDynamicImplicitProperty(
        PsiManager.getInstance(project), "apiV1", SCRIPTING_API_V1_FQN, SCRIPTING_API_V1_FQN
    ) {
        override fun canNavigateToSource(): Boolean = true
    }

}
0

Please sign in to leave a comment.