PsiNamedElement vs PsiNameIdentifierOwner

So, the doc says: "Every element which can be renamed or referenced needs to implement com.intellij.psi.PsiNamedElement interface." and then demonstrates by using PsiNameIdentifierOwner not PsiNamedElement.  What's the deal? Typo?

10.1. Define a base named element classhttp://www.jetbrains.org/intellij/sdk/docs/tutorials/custom_language_support/reference_contributor.html#define-a-base-named-element-class

package com.simpleplugin.psi;

import com.intellij.psi.PsiNameIdentifierOwner;

public interface SimpleNamedElement extends PsiNameIdentifierOwner {
}


My jump to declaration and find usages seems to work with plain PsiNamedElement.  What functionality relies on the PsiNameIdentifierOwner subinterface?

thanks!
Ter

5 comments
Avatar
Breandan Considine
Comment actions Permalink

We use it to differentiate between declarations and variable names. You could directly use

PsiNamedElement
, but
PsiNameIdentifierOwner
is more specific, and tells us exactly which token is the name identifier. For example, finding usages (in particular those inside
/platform/lang-impl/src/com/intellij
), reveals
RelatedLineItemProvider
or
MemberInplaceRenameHandler
, which guard additional functionality within an
instanceof
check. If you use
PsiNamedElement
for renaming it will be "clunkier" - you'll get a dialog because we're not quite sure what token we're supposed to rename:

rename.png

Where it gets tricky is when you have matching types and variables like "

x x = 1
": the whole statement could be a
PSINamedElement
, but which "x" is supposed to be the identifier? Using
PsiNameIdentifierOwner
helps clarify this ambiguity.
CtrlMouseHandler
uses it indirectly.
SafeDeleteProcessor
uses it.
NamedElementDuplicateHandler
uses it.
UnfocusedNameIdentifier
uses it. If you don't explicitly tell us what
PSIElement
the name identifier is, we might try to find it by other means, but we might not always get it right or it might not be as user-friendly for certain actions.
0
Comment actions Permalink

Hi Breandan,Thanks very much for the helpful response. I definitely get the idea that there are references and there are definitions or declarations.  I just couldn't figure out what functionality triggered the use of the PsiNameIdentifierOwner interface. Your pointers help a lot. I will dig into it. A small clarification as I'm not sure which nodes should be tagged exactly.

What I do now is tag my ID leaf nodes as PsiNamedElement but my variable definition subtree roots (nonleaf PSI nodes) as PsiNameIdentifierOwner. In other words, PsiNameIdentifierOwner points at the root of an entire definition statement rather than the individual ID node within that subtree. I then have getNameIdentifier() return that ID child. Does that sound correct?

Thanks!
Terence

0
Avatar
Breandan Considine
Comment actions Permalink
What I do now is tag my ID leaf nodes as PsiNamedElement but my variable definition subtree roots (nonleaf PSI nodes) as PsiNameIdentifierOwner. In other words, PsiNameIdentifierOwner points at the root of an entire definition statement rather than the individual ID node within that subtree. I then have getNameIdentifier() return that ID child. Does that sound correct?


Yep, that sounds right. If you look in the SDK docs under /code-samples/simple-language-plugin there is an example of this (generated by grammar-kit from the BNF). It delegates to a static method that maps to

SimpleTokenType.KEY
, but you might just as likely store a reference to it somewhere. Here's what the PSI Structure looks like, see how
SimplePropertyImpl
(the
PsiNameIdentifierOwner
) is the entire definition statement and the name identifier is the actual leaf node:

psi_structure.png
0
Comment actions Permalink

Ok, great!  And then do I ever want the PsiNamedElement for the KEY under a PsiNameIdentifierOwner to return a ref from getReference() or is that literally only for references to a definition? That could be the answer to my question I just posed in another thread where rename is happening twice, once via the reference and once via a simple setName on the definition name node that should be changed.
Ter

0
Comment actions Permalink

For my little "func f() { }" input, I get a spinning ball of death when I avoid creating a reference from the definition ID node. Just like I did in

https://devnet.jetbrains.com/message/5566463#5566463

So, I no longer create a PsiReferenceBase for the "f" node. getReference() returns null. Rename locks the GUI thread. awesome. my setName() could not be simpler:

PsiElement newNode = Trees.createLeafFromText(getProject(),
                                              SampleLanguage.INSTANCE,
                                              getContext(),
                                              name, IDTYPE);
return this.replace(newNode);


where that createLeaf... func is just:

PsiFileFactoryImpl factory = (PsiFileFactoryImpl) PsiFileFactory.getInstance(project);
PsiElement el = factory.createElementFromText(text, language, type, context);
if ( el==null ) return null;

return PsiTreeUtil.getDeepestFirst(el); // forces parsing of file!!

It seems to create a proper new node but then after the "finding usage" dialogue comes up it freezes after the progress bar goes all the way and it hits my setName method.

The spinning ball of death seems to be a recurring theme for me. Looking at the threads in the debugger shows everybody is just parked so I have no idea what is deadlocking it.

hope this extra bit of data helps. Does my setName() look ok?

Thanks,
Ter

0

Please sign in to leave a comment.