我正在开发一个 IDEA 插件,它应该提供不同 XML 文件之间以及 XML 和 Java 源之间的导航。它有点类似于 Spring 支持。
待支持的 XML 标签有一个 <name> 子标签(与 Spring 中的 id 属性相反),引用就像 spring: ref="..." 导航到 Java 源代码将是稍后的步骤。xml 文件还有一个命名约定以支持新插件:*-definitions.xml
目前,我陷入了第一步:LineMarkerProvider 和 ReferenceContributor。
我使用“自定义语言支持”示例作为起点。但是因为我没有自定义语言而是想扩展 xml 功能,所以我删除了词法分析器、解析器、文件工厂等......并且只包含了 LineMarkerProvider 和 ReferenceContributor。
所以我现在所拥有的是:
- 类 DefinitionsReference 扩展 PsiReferenceBase
- 类 DefinitionsUtil(类似于 SimplePsiImplUtil)
- 类 DefinitionsLineMarkerProvider 扩展了 RelatedItemLineMarkerProvider
- 类 DefinitionsReferenceContributor 扩展 PsiReferenceContributor
我在 plugin.xml 中注册了 LineMarkerProvider (language=XML) 和 ReferenceContributor,并添加了一些日志输出来验证它们是否被调用以及结果是否为空。
尽管 ReferenceContributor.PsiReferenceProvider.getReferencesByElement() 以及 DefinitionsReference.multiResolve() 和 reslove() 确实返回了一些非空结果,并且 LineMarkerProvider.collectNavigationMarkers() 确实找到了一些要标记的地方,但我既没有得到导航栏图标也不能从 ref= 导航到相应的标签。它说“找不到要前往的声明”。
似乎我错过了一些重要的细节。
我应该首先寻找什么?
还是完全错误的方法?
编辑:
这是要支持的 XML 文件之一:
<?xml version="1.0" ?>
<event-definitions>
<data-source-definition>
<name>event.eventDataSource</name>
<class>...</class>
</data-source-definition>
<plugin-definition>
<name>event.taskPlugin</name>
<class>...</class>
<title>...</title>
<data-source ref="event.eventDataSource">
<property>eventDataSource</property>
</data-source>
<data-source ref="personDataSource"/> <!-- defined in other XML file -->
... more data-sources ...
</plugin-definition>
<editor-definition>
<name>event.admin.Editor</name>
<editor>...</editor>
<title>...</title>
<editor-view>...</editor-view>
<data-source ref="event.eventDataSource"/>
<data-source ref="personDataSource"/> <!-- defined in other XML file -->
... more data-sources ...
<plugin ref="event.taskPlugin">
...
</plugin>
<plugin ref="assignment.ratingPlugin"> <!-- defined in other XML file -->
...
</plugin>
... more plugins ...
</editor-definition>
</event-definitions>
这是我的参考贡献者:
public class DefinitionsReferenceContributor extends PsiReferenceContributor {
@Override
public void registerReferenceProviders(PsiReferenceRegistrar registrar) {
registrar.registerReferenceProvider(PlatformPatterns.psiElement(XmlTag.class),
new PsiReferenceProvider() {
@NotNull
@Override
public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) {
XmlTag tag = (XmlTag) element;
final String localName = tag.getLocalName();
if ("name".equals(localName)) {
final PsiReference[] psiReferences = DefinitionsUtil.matchTag((XmlTag) element);
return psiReferences;
} else {
return new PsiReference[0];
}
}
});
}
}
我的 LineMarkerProvider:
public class DefinitionsLineMarkerProvider extends RelatedItemLineMarkerProvider {
@Override
protected void collectNavigationMarkers(@NotNull PsiElement element, Collection<? super RelatedItemLineMarkerInfo> result) {
if (element instanceof XmlTag) {
XmlTag tag = (XmlTag) element;
final String localName = tag.getLocalName();
if ("name".equals(localName)) {
final Project project = element.getProject();
final String tagName = DefinitionsUtil.getTagName(tag);
if (tagName != null) {
final List<XmlTag> matchingTags = DefinitionsUtil.findMatchingReferences(project, tagName);
if (matchingTags.size() > 0) {
NavigationGutterIconBuilder<PsiElement> builder =
NavigationGutterIconBuilder.create(SmotiveDefinitionsIcons.FILE).
setTargets(matchingTags).
setTooltipText("Navigate to a sMOTIVE Definition");
result.add(builder.createLineMarkerInfo(element));
}
}
} else {
final String ref = DefinitionsUtil.getRefValue(tag);
if (ref != null) {
final Project project = element.getProject();
final List<XmlTag> matchingTags = DefinitionsUtil.findMatchingTags(project, ref);
if (matchingTags.size() > 0) {
NavigationGutterIconBuilder<PsiElement> builder =
NavigationGutterIconBuilder.create(SmotiveDefinitionsIcons.FILE).
setTargets(matchingTags).
setTooltipText("Navigate to a sMOTIVE Definition");
result.add(builder.createLineMarkerInfo(element));
}
}
}
}
}
}
最后,我的 plugin.xml 的扩展部分:
<extensions defaultExtensionNs="com.intellij">
<codeInsight.lineMarkerProvider language="XML" implementationClass="de.smotive.tools.navigation.definitions.DefinitionsLineMarkerProvider"/>
<psi.referenceContributor implementation="de.smotive.tools.navigation.definitions.DefinitionsReferenceContributor"/>
</extensions>
我省略了 DefinitionsUtil 因为它的大小。如果发现解决方案需要它,我稍后会添加它。