Inline renaming: Cannot find manipulator for FooUsageImpl in FooReference

Hello,

I'm creating a custom language plugin, following the Properties tutorial. Everything is working fine so far, but now I want to extend the inline renaming so that the definition and the usage of the symbols to edit are represented by different PsiElements.

So, for example, I have a single FooDecl, representing the declaration of a symbol, and many FooUsage's, representing the usage of a symbol. Navigation like Go To Declaration is working fine.

The Refactor > Rename looks fine, but when I hit Return to make the changes permanent the following exception is thrown:

ERROR - .intellij.psi.PsiReferenceBase - Cannot find manipulator for FooUsageImpl(USAGE) in foo.FooReference@5d4776b6 class class foo.FooReference

It seems that function

public static PsiElement setName(FooUsage element, String newName)     


in class FooPsiImplUtil is never called, even though setName is generated into FooUsageImpl.

My Foo.xbnf look like

decl ::= DECLTOKEN
    {mixin="foo.psi.impl.FooNamedElementImpl"
     
implements="foo.psi.FooNamedElement"
     
methods=[getFooDecl getName setName getNameIdentifier getPresentation]}
usage ::= USAGETOKEN
    {mixin="foo.psi.impl.FooNamedElementImpl"
     
implements="foo.psi.FooNamedElement"
     
methods=[getFooUsage getName setName getNameIdentifier getReference]}


Am I missing a crucial step here?

Regards
Lars

6 comments
Comment actions Permalink

OK, after some debugging through the internal IntelliJ classes it seems that

this.myElement);


returns null for FooReference class.

I was under the impression that you don't have to define your own manipulator if the node replacement can be handled through setName calls.  The Properties example also doesn't define an explicit manipulator, so what is different here?

Lars

0
Comment actions Permalink

Reply to self again, for reference:

I was missing

@Override
public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
    FooPsiImplUtil.rename(myElement, newElementName);
    return myElement;
}


in class FooReference.  rename() then calls setName() explicitly.

0
Comment actions Permalink

hi Lars, interesting. I'm getting the same error and try the following to see if it made a difference:

 
@Override
public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
   return myElement;
}


nope.  still get the same error when I click on an identifier in the editor.  setting a breakpoint indicates it does not reach that method. grrrr!   did you figure out anything else you needed to do?

Thanks!
Terence

0
Comment actions Permalink

I didn't have to impl handleElementRename to get error to disappear. I had to pass range up to the super class:

 
public class SampleElementRef extends PsiReferenceBase<IdentifierRefNode> {
   protected String id;
   public SampleElementRef(@NotNull IdentifierRefNode element, String id) {
      /** WARNING: You must send the text range or you get this error:
       * "Cannot find manipulator for PsiElement(ID) in org.antlr.jetbrains.sample.SampleElementRef"...
       *  when you click on an identifier.
       *
       *  Wow. This also solved the rename issue that was causing GUI thread
       *  to deadlock after the rename.
       */
      super
(element, element.getTextRange());
      this.id = id;
   }
1
Comment actions Permalink

for the record, even when I have a proper set and get name, I still get the error unless I set the text range:

 
@Override
public String getName() {
   return getText();
}

@Override
public
PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException {
   System.out.println("setName("+name+")");
   /*
   From doc: "Creating a fully correct AST node from scratch is
             quite difficult. Thus, surprisingly, the easiest way to
             get the replacement node is to create a dummy file in the
             custom language so that it would contain the necessary
             node in its parse tree, build the parse tree and
             extract the necessary node from it.
    */
   
List<TokenIElementType> tokenIElementTypes =
      PSIElementTypeFactory.getTokenIElementTypes(SampleLanguage.INSTANCE);
   PsiElement newNode = Trees.createLeafFromText(getProject(),
                                                 SampleLanguage.INSTANCE,
                                                 getContext(),
                                                 name, tokenIElementTypes.get(SampleLanguageLexer.ID));
   if ( newNode!=null ) {
      this.replace(newNode);
      return newNode;
   }
   throw new IncorrectOperationException();
}
1
Comment actions Permalink

Same happens when I create an instance of com.intellij.psi.PsiReferenceBase without TextRange constructor parameter.

0

Please sign in to leave a comment.