Custom Plugin: Can't navigate between reference and declaration
I am currently working on a Tapestry plugin for IntelliJ. The goal is to be able to jump back and forth between the Java file containing the Tapestry events and the template file (.tml).
Current problem:
I can currently jump from the Tapestry event, “t:event=”foo” to the ‘onFoo’ method in Java. However, it tells me that the “onFoo” method has no usages, so I can't jump from the method back to the TML file.
I've been sitting here for several days now and am totally exasperated because I don't understand why this doesn't work.
Here are some Snippets from my Code:
TapestryEventReferenceContributor:
public class TapestryEventReferenceContributor extends PsiReferenceContributor {
@Override
public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) {
ElementPattern<XmlAttributeValue> pattern = XmlPatterns.xmlAttributeValue()
.withParent(XmlPatterns.xmlAttribute()
.withName(string()
.oneOf("event", "t:event"))
.inFile(psiFile()
.withName(string()
.endsWith(FileExtensions.TML.extension()))));
registrar.registerReferenceProvider(
pattern,
new TapestryEventReferenceProvider()
);
}
}
TapestryEventReferenceProvider:
public class TapestryEventReferenceProvider extends PsiReferenceProvider {
@Override
public PsiReference @NotNull [] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) {
if (!(element instanceof XmlAttributeValue attrValue)) {
return PsiReference.EMPTY_ARRAY;
}
String eventName = attrValue.getValue();
if (eventName.isEmpty()) {
return PsiReference.EMPTY_ARRAY;
}
TextRange range = new TextRange(0, attrValue.getValue().length() + 1);
return new PsiReference[]{
new TapestryEventReference(attrValue, range, eventName)
};
}
}
TapestryEventReference:
public class TapestryEventReference extends AbstractTapestryPsiReference {
private static final Logger logger = Logger.getInstance(TapestryEventReference.class);
private final String eventName;
public TapestryEventReference(
@NotNull XmlAttributeValue element,
TextRange range,
String eventName
) {
super(element, range);
this.eventName = eventName;
}
@Override
public @Nullable PsiElement resolve() {
logger.info("(resolve) => called");
PsiFile tmlFile = myElement.getContainingFile();
Optional<PsiFile> javaFile = TapestryUtil.findCorrespondingFile(myElement.getProject(), FileExtensions.JAVA, tmlFile);
if (javaFile.isEmpty() || !(javaFile.get() instanceof PsiJavaFile file)) {
logger.warn(String.format("(resolve) => can't find file '%s'", tmlFile.getName()));
return null;
}
PsiClass[] classes = file.getClasses();
for (PsiClass psiClass : classes) {
var foundOnMethod = findTapestryOnMethod(psiClass, eventName);
if (foundOnMethod.isPresent()) {
return foundOnMethod.get();
}
}
logger.warn(String.format("(resolve) => no method found for event '%s'", eventName));
return null;
}
@Override
public Object @NotNull [] getVariants() {
return PsiReference.EMPTY_ARRAY;
}
}
AbstractTapestryPsiReference:
public abstract class AbstractTapestryPsiReference extends PsiReferenceBase<PsiElement> {
private static final Logger logger = Logger.getInstance(AbstractTapestryPsiReference.class);
public AbstractTapestryPsiReference(@NotNull PsiElement element, TextRange range) {
super(element, range);
}
// [...] implemented methods for finding methods in the Tapestry components Java file
}
How can it be that it does not find the usages of a method, but jumps there from the reference? Can someone explain this to me? How do I debug such problems? I have already noticed in the PSI viewer that the reference to the PsiElement:XML_ATTRIBUTE_VALUE (i.e. the “foo” in the TML file) is defined, but is displayed in red.
请先登录再写评论。
Hi Dominique,
The method is called “onFoo”, but in the template it is “foo”. The platform is not able to understand that “foo” is usage of “onFoo”. You should extend the method search mechanism to cover this case. Implement:
Register it in plugin.xml as:
Hi Karol,
thank you for your answer, this seems to work!
Best regards,
Dominique