PSI Reference Help with Salesforce APEX Plugin Follow
I'm running into issues with PSI references for my Salesforce plugin. I've followed the tutorial here: http://confluence.jetbrains.com/display/IntelliJIDEA/Custom+Language+Support. And created a basic project here: https://github.com/polyglot-mark/apex-plugin.
Note: I've skipped the Syntax Highlighter and Color Settings Page and Line Marker steps for now. I just wanted to get the PSI references working.
I've gotten Annotations working. I sort of have a Completions Contributor working. But I'm stuck on Reference Contributor.
I've been through all the forum posts and discovered that I needed to add the following to my abstract "base named element" class:
public abstract class ApexNamedElementImpl extends ASTWrapperPsiElement implements ApexNamedElement {
public ApexNamedElementImpl(@NotNull ASTNode node) {
super(node);
}
@Nullable
public PsiReference getReference() {
PsiReference[] references = getReferences();
return references.length == 0 ? null : references[0];
}
@NotNull
public PsiReference[] getReferences() {
return ReferenceProvidersRegistry.getReferencesFromProviders(this);
}
}
Yes, I realise that I don't need to go through the ReferenceProvidersRegistry - I could return references directly from these methods.
I've implemented a PsiReferenceContributor (see ApexReferenceContributor) which creates PSI References (see ApexClassNameReference & ApexInterfaceNameReference).
Debugging the code, I see that ApexClassNameReference and ApexInterfaceNameReference classes are being instantiated. But non of the other methods are being called:
multiResolve(), resolve(), nor getVariants().
I'm testing it by running the plugin and creating three files:
Fred.cls with contents:
public class Fred { }
Mark.cls with contents:
public class Mark extends Fred { }
ApexClass.cls with contents:
public class ApexClass { }
While I'm editing Mark.cls, I put the cursor on Fred and press Control-Space. I would expect the ApexClass to come up in the list. But I receive "No suggestions". Is my assumption correct that ApexClass should come up?
Also renaming doesn't work - if I try and rename Fred, it only renames it in the current file.
I must be missing a key piece of the puzzle, but I can't figure it out! Could you let me know what I'm missing?
Mark
Please sign in to leave a comment.
I figured it out......I knew it had to be something simple!
The problem was with my ApexReferenceContributor. I had:
registrar.registerReferenceProvider(
psiElement(ApexExtendsClassName.class),
new PsiReferenceProvider() {
@NotNull
@Override
public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) {
return new PsiReference[] { new ApexClassNameReference((ApexExtendsClassName)element, element.getTextRange()) };
}
}
);
When I should have had:
registrar.registerReferenceProvider(
psiElement(ApexExtendsClassName.class),
new PsiReferenceProvider() {
@NotNull
@Override
public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) {
return new PsiReference[] { new ApexClassNameReference((ApexExtendsClassName)element, new TextRange(0, element.getTextLength())) };
}
}
);
The TextRange passed into the PsiReference is the range inside the Element, not the range inside the parent file!
Oops - that's a common mistake, I have made that same mistake a few times myself too.
A 'trick' I like doing for when i'm quickly testing PsiReference text ranges is to simply set soft references to false, and resolve to null - that way it's very clear to see what ranges are being contributed :)
Indeed a common mistake. You can also use Tools|View PSI Structure, click on the element and see the references. Click on the reference to inspect and it will highlight the underlying TextRange.
Thanks for the tips guys!