Language API: (1) getVariants() never called? (2) Find Usages only partly works

I am having two more problems with Language API. I hope someone can help
me with them.

First, even when inside an element which implements PsiReference, (I can
ctrl+click on it so it must be PsiReference for sure), I press
ctrl+space anywhere in it and my breakpoint inside getVariants() is
never hit - it's never called. Is there some special setup necessary for
completion to work?

Second, I implemented a find-usages provider. Here are facts:

1. Ctrlshiftf7 on a definition works, highlighting all the right usages.
2. Ctrlshiftf7 on a usage does not work, even though it is a
PsiReference to the original element. It says "no usages found"
3. Ctrl+f7 sort of works on a reference, it correctly shows the dialog
saying "Find usasges for message xxx". However, when I click "find," it
says no usages are found.
4. Ctrl+f7 doesn't work at all on the element itself, a dialog says
"Cannot find usages."

I think am missing some piece here. I will paste my reference class and
my find usages provider:

 defs = 
protoFile.getMessageDefinitions();
         return defs.toArray();
     }

     public boolean isSoft() {
         return false;
     }
}



class ProtoFindUsagesProvider implements FindUsagesProvider {
     public boolean mayHaveReferences(IElementType token, short 
searchContext) {
         if ((searchContext & UsageSearchContext.IN_CODE) != 0 && (token
                 == ProtoElementTypes.MESSAGE_TYPE_REFERENCE)) {
             return true;
         }
         if ((searchContext & UsageSearchContext.IN_COMMENTS) != 0 && (
                 token == ProtoTokenTypes.END_OF_LINE_COMMENT || token
                         == ProtoTokenTypes.C_STYLE_COMMENT)) {
             return true;
         }
         if ((searchContext & UsageSearchContext.IN_STRINGS) != 0
                 && token == ProtoTokenTypes.STRING_LITERAL) {
             return true;
         }
         return false;
     }

     @Nullable
     public WordsScanner getWordsScanner() {
         return new ProtoWordsScanner();
     }

     public boolean canFindUsagesFor(PsiElement psiElement) {
         return psiElement instanceof ProtoMessageDefinition;
     }

     @Nullable
     public String getHelpId(PsiElement psiElement) {
         return null;
     }

     @NotNull
     public String getType(PsiElement psiElement) {
         if (psiElement instanceof ProtoMessageDefinition) return "message";
         if (psiElement instanceof ProtoServiceDefinition) return "service";
         if (psiElement instanceof ProtoProperty) return "property";
         if (psiElement instanceof ProtoRpcDefinition) return "rpc";
         return "";
     }

     @NotNull
     public String getDescriptiveName(PsiElement psiElement) {
         return getNodeText(psiElement, false);
     }

     @NotNull
     public String getNodeText(PsiElement psiElement, boolean useFullName) {
         if (psiElement instanceof ProtoNamedElement) {
             ProtoNamedElement protoNamedElement = (ProtoNamedElement) 
psiElement;

             String name = protoNamedElement.getName();
             if (name != null) return name;
         }
         return "";
     }

}
]]>

7 comments
Comment actions Permalink

Does file.findReferenceAt(offset) return your reference?


"Keith Lea" <keith@cs.oswego.edu> wrote in message
news:dbrjke$k6k$1@is.intellij.net...
>I am having two more problems with Language API. I hope someone can help me
>with them.
>

First, even when inside an element which implements PsiReference, (I can
ctrlclick on it so it must be PsiReference for sure), I press ctrlspace
anywhere in it and my breakpoint inside getVariants() is never hit - it's
never called. Is there some special setup necessary for completion to
work?

>

Second, I implemented a find-usages provider. Here are facts:

>

1. Ctrlshiftf7 on a definition works, highlighting all the right usages.
2. Ctrlshiftf7 on a usage does not work, even though it is a
PsiReference to the original element. It says "no usages found"
3. Ctrl+f7 sort of works on a reference, it correctly shows the dialog
saying "Find usasges for message xxx". However, when I click "find," it
says no usages are found.
4. Ctrl+f7 doesn't work at all on the element itself, a dialog says
"Cannot find usages."

>

I think am missing some piece here. I will paste my reference class and my
find usages provider:

>

 public class ProtoMessageTypeReference extends ASTWrapperPsiElement
>         implements ProtoElement, PsiReference {
>     public ProtoMessageTypeReference(ASTNode astNode) {super(astNode);}
>
>     public String getReferencedName() {
>         ASTNode node = getIdentifierNode();
>         return node == null ? "" : node.getText();
>     }
>
>     private ASTNode getIdentifierNode() {
>         return getNode().findChildByType(ProtoTokenTypes.IDENTIFIER);
>     }
>
>     public PsiReference getReference() {
>         return this;
>     }
>
>     public PsiElement getElement() {
>         return this;
>     }
>
>     public TextRange getRangeInElement() {
>         return new TextRange(0, getTextLength());
>     }
>
>     @Nullable
>     public PsiElement resolve() {
>         ProtoFile protoFile = PsiTreeUtil.getParentOfType(this, 
> ProtoFile.class);
>         if (protoFile == null) return null;
>         return protoFile.getMessageDefinition(getReferencedName());
>     }
>
>     public String getCanonicalText() {
>         return getReferencedName();
>     }
>
>     public PsiElement handleElementRename(String newElementName)
>             throws IncorrectOperationException {
>         return this;
>     }
>
>     public PsiElement bindToElement(PsiElement element)
>             throws IncorrectOperationException {
>         ProtoMessageDefinition def = (ProtoMessageDefinition) element;
>
>         ASTNode node = getIdentifierNode();
>         return this;
>     }
>
>     public boolean isReferenceTo(PsiElement element) {
>         if (element instanceof ProtoMessageDefinition) {
>             ProtoMessageDefinition msg = (ProtoMessageDefinition) element;
>             String name = msg.getName();
>             return name != null && name.equals(getReferencedName());
>         } else {
>             return false;
>         }
>     }
>
>     public Object[] getVariants() {
>         ProtoFile protoFile = PsiTreeUtil.getParentOfType(this, 
> ProtoFile.class);
>         if (protoFile == null) return new Object[0];
>         List defs = 
> protoFile.getMessageDefinitions();
>         return defs.toArray();
>     }
>
>     public boolean isSoft() {
>         return false;
>     }
> }
>
>
>
> class ProtoFindUsagesProvider implements FindUsagesProvider {
>     public boolean mayHaveReferences(IElementType token, short 
> searchContext) {
>         if ((searchContext & UsageSearchContext.IN_CODE) != 0 && (token
>                 == ProtoElementTypes.MESSAGE_TYPE_REFERENCE)) {
>             return true;
>         }
>         if ((searchContext & UsageSearchContext.IN_COMMENTS) != 0 && (
>                 token == ProtoTokenTypes.END_OF_LINE_COMMENT || token
>                         == ProtoTokenTypes.C_STYLE_COMMENT)) {
>             return true;
>         }
>         if ((searchContext & UsageSearchContext.IN_STRINGS) != 0
>                 && token == ProtoTokenTypes.STRING_LITERAL) {
>             return true;
>         }
>         return false;
>     }
>
>     @Nullable
>     public WordsScanner getWordsScanner() {
>         return new ProtoWordsScanner();
>     }
>
>     public boolean canFindUsagesFor(PsiElement psiElement) {
>         return psiElement instanceof ProtoMessageDefinition;
>     }
>
>     @Nullable
>     public String getHelpId(PsiElement psiElement) {
>         return null;
>     }
>
>     @NotNull
>     public String getType(PsiElement psiElement) {
>         if (psiElement instanceof ProtoMessageDefinition) return 
> "message";
>         if (psiElement instanceof ProtoServiceDefinition) return 
> "service";
>         if (psiElement instanceof ProtoProperty) return "property";
>         if (psiElement instanceof ProtoRpcDefinition) return "rpc";
>         return "";
>     }
>
>     @NotNull
>     public String getDescriptiveName(PsiElement psiElement) {
>         return getNodeText(psiElement, false);
>     }
>
>     @NotNull
>     public String getNodeText(PsiElement psiElement, boolean useFullName) 
> {
>         if (psiElement instanceof ProtoNamedElement) {
>             ProtoNamedElement protoNamedElement = (ProtoNamedElement) 
> psiElement;
>
>             String name = protoNamedElement.getName();
>             if (name != null) return name;
>         }
>         return "";
>     }
>
> }
> ]]>



0
Comment actions Permalink

Yes, it finds PsiReference instances correctly.

In the meantime I have a question about references:

Let's prtend I'm writing Java language editor with language API.

int x = 2;

should "x" be PsiReference to the PsiVariable? How does IDEA know that "x" is the name of the PsiVariable to which PsiReferences resolve?

In my classes, I've implemented getTextOffset() for things which have a name, so in that example it would return "4". This is what the JS plugin does. Should I implement getTextLength() to return 1 in this example? Or is there some special annotation or interface to implement to tell IDEA which element is the name element of a declaration?

0
Comment actions Permalink

Hello Keith,

KL> In the meantime I have a question about references:
KL>
KL> Let's prtend I'm writing Java language editor with language API.
KL>
KL> int x = 2;
KL>
KL> should "x" be PsiReference to the PsiVariable? How does IDEA know
KL> that "x" is the name of the PsiVariable to which PsiReferences
KL> resolve?

No, the "x" must not be a PsiReference in this case. Instead, it must be
a PsiNamedElement, and return "x" from its getName() method.

--
Dmitry Jemerov
Software Developer
JetBrains, Inc.
http://www.jetbrains.com
"Develop with pleasure!"


0
Comment actions Permalink

Dmitry Jemerov (JetBrains) wrote:

Hello Keith,

KL> In the meantime I have a question about references:
KL> KL> Let's prtend I'm writing Java language editor with language API.
KL> KL> int x = 2;
KL> KL> should "x" be PsiReference to the PsiVariable? How does IDEA know
KL> that "x" is the name of the PsiVariable to which PsiReferences
KL> resolve?

No, the "x" must not be a PsiReference in this case. Instead, it must be
a PsiNamedElement, and return "x" from its getName() method.


Do you mean "x" should be PsiNamedElement, or "int x = 2" should be
PsiNamedElement?

0
Comment actions Permalink

Dmitry Jemerov (JetBrains) wrote:

Hello Keith,

KL> In the meantime I have a question about references:
KL> KL> Let's prtend I'm writing Java language editor with language API.
KL> KL> int x = 2;
KL> KL> should "x" be PsiReference to the PsiVariable? How does IDEA know
KL> that "x" is the name of the PsiVariable to which PsiReferences
KL> resolve?

No, the "x" must not be a PsiReference in this case. Instead, it must be
a PsiNamedElement, and return "x" from its getName() method.


Okay, I made my definitions implement PsiNamedElement, and now some more
stuff works, like find usages on a reference. However, I still can't
Find Usages on a definition. It says "Cannot search for usages."

0
Comment actions Permalink

Definition may have reference to itself.

Keith Lea wrote:

Dmitry Jemerov (JetBrains) wrote:

>> Hello Keith,
>>
>> KL> In the meantime I have a question about references:
>> KL> KL> Let's prtend I'm writing Java language editor with language API.
>> KL> KL> int x = 2;
>> KL> KL> should "x" be PsiReference to the PsiVariable? How does IDEA know
>> KL> that "x" is the name of the PsiVariable to which PsiReferences
>> KL> resolve?
>>
>> No, the "x" must not be a PsiReference in this case. Instead, it must
>> be a PsiNamedElement, and return "x" from its getName() method.
>>


Okay, I made my definitions implement PsiNamedElement, and now some more
stuff works, like find usages on a reference. However, I still can't
Find Usages on a definition. It says "Cannot search for usages."



--
Best regards,
Maxim Mossienko
IntelliJ Labs / JetBrains Inc.
http://www.intellij.com
"Develop with pleasure!"

0
Comment actions Permalink

I don't think this is the problem. In JS plugin, JSFunction doesn't even
provide PsiElement child for name, let alone PsiReference to itself. Do
you know why this doesn't work for me? Does Find Usages only use a leaf
PsiElement, or something? When you Ctrl+F7 on my declaration, the cursor
is most definitely also inside a leaf node which is a child of the
actual declaration. Could this be the problem?

Maxim Mossienko wrote:

Definition may have reference to itself.

Keith Lea wrote:

>> Dmitry Jemerov (JetBrains) wrote:
>>
>>> Hello Keith,
>>>
>>> KL> In the meantime I have a question about references:
>>> KL> KL> Let's prtend I'm writing Java language editor with language API.
>>> KL> KL> int x = 2;
>>> KL> KL> should "x" be PsiReference to the PsiVariable? How does IDEA
>>> know
>>> KL> that "x" is the name of the PsiVariable to which PsiReferences
>>> KL> resolve?
>>>
>>> No, the "x" must not be a PsiReference in this case. Instead, it must
>>> be a PsiNamedElement, and return "x" from its getName() method.
>>>
>>
>> Okay, I made my definitions implement PsiNamedElement, and now some
>> more stuff works, like find usages on a reference. However, I still
>> can't Find Usages on a definition. It says "Cannot search for usages."


0

Please sign in to leave a comment.