PsiReference.resolve() correctly resolves to a PsiFile, but element is not clickable

Hello,

I'm writing a plugin that is supposed to make references to a Java class inside an XML file clickable via "Go To Declaration". The relevant part of the XML looks like this:

<action command="packagename.JavaClassName" />

 

My PsiReferenceProvider looks like this:

public PsiReference[] getReferencesByElement(@NotNull PsiElement psiElement,
@NotNull ProcessingContext processingContext) {
if (psiElement instanceof XmlAttribute) {
XmlAttribute commandAttr = (XmlAttribute) psiElement;
CmdReference cmdReference = new CmdReference(commandAttr, psiElement.getProject());

if (cmdReference.resolve() != null) {
return new PsiReference[] { cmdReference };
}

return PsiReference.EMPTY_ARRAY;
}


return new PsiReference[0];
}

and the PsiReference class:

public class CmdReference implements PsiReference {

// some fields here...

public CmdReference(XmlAttribute cmdXml, Project project) {
this.cmdElement = cmdXml;
this.project = project;
}

@Override
public PsiElement getElement() {
return cmdElement;
}

@Override
public TextRange getRangeInElement() {
// return cmdElement.getValueTextRange();
return new TextRange(0, cmdElement.getTextLength() - 1);
}

@Override
public PsiElement resolve() {

if (!"command".equals(cmdElement.getName())) {
return null;
}

project = cmdElement.getProject();

command = cmdElement.getValue();
final String[] pathElements = command.split("\\.");
String className = pathElements[pathElements.length - 1];
String baseFileName = className + ".java";

PsiFile[] filesByName =
FilenameIndex.getFilesByName(project, baseFileName, ProjectScope.getProjectScope(project));
return filesByName[0];

}

@NotNull
@Override
public String getCanonicalText() {
return command;
}

@Override
public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
throw new IncorrectOperationException();
}

@Override
public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
return resolve();
}

@Override
public boolean isReferenceTo(PsiElement element) {
return element.isEquivalentTo(resolve());
}

@NotNull
@Override
public Object[] getVariants() {
return new Object[] { resolve() };
}

@Override
public boolean isSoft() {
return true;
}
}

 

According to the debugger, the resolve() method resolves to a PsiFile with the correct file name.

However, when I click on the XML element, a little pop-up says "Cannot find declaration to go to".

I suspect it's something to with the implementation of the getRangeInElement() and/or getElement() methods.

 

Any idea what I'm missing here?

 

Cheers!

0

Hi,

is method 'isReferenceTo' called?

Anna

0
Avatar
Permanently deleted user

Hi,

thanks for the quick response!

isReferenceTo is called every time I click (with- or without Control) on the "command" part of the element. Seems to be called for all matching elements, as well.

Just noticed that my current implementation of that method never returns true. So that might be the problem?

 

-Spupys

0

Yes, it should return true to navigate.

0
Avatar
Permanently deleted user

Ok, that makes sense.

Now I just need to find a way to get some useful information out of the PsiElement argument of the isReferenceTo() method.

 

0

I am not sure that resolving to the file and not to the class is a good idea actually. Probably changing that would ease the initial problem

0
Avatar
Permanently deleted user

Actually the problem I have is that I don't understand how to use the PsiElement argument of the isReferenceTo() method. As I understand it, I need to check if that PsiElement is exactly the one element that got clicked? However, I don't see how to access any xml data (like the XmlTag's attributes) from the PsiElement structure.

0
Avatar
Permanently deleted user

Turns out that using a PsiReferenceProvider was a bit of an overkill. A more straightforward solution was to instead register and implement a GotoDeclarationHandler.

It uses PsiTreeUtil.findFirstParent(...) to find the XmlAttribute that corresponds to the clicked PsiElement, creates a PsiFile from the value using FilenameIndex.getFilesByName(...), and returns the PsiFile(s).

0

I'd rather disagree with the solution: this way you won't get find usages and refactoring functionality, which would be automatically provided when reference would correctly registered.

Anna

0

请先登录再写评论。