在正式实现前,先介绍一下整体的实现思路,这里只说明如何从 Java 方法跳转到 Mapper XML 文件中的节点,反向参考代码也很好理解,思路如下:
- 左侧图标行标记符通过实现
RelatedItemLineMarkerProvider
并重写collectNavigationMarkers
方法设置。 - 判断代码行的元素类型为 PsiMethod 才进行设置,同时文件类名以 Mapper 结尾。
- 根据类名在项目查找同名的 Mapper XML 文件。
- 通过 accept 方法遍历 XML 文件所有的属性,将 id 值为对应方法名的标签所对应的元素保存到可跳转的目标。
设置行标记符号,平台给我们提供了 RelatedItemLineMarkerProvider 类进行设置,只需要自定义了自己的行标记类,然后在 plugin.xml 中添加 如下配置即可:
<codeInsight.lineMarkerProvider language="JAVA" implementationClass="cn.butterfly.psi.provider.JavaMapperLineMarkerProvider"/>
代码实现如下:
class JavaMapperLineMarkerProvider: RelatedItemLineMarkerProvider() {
override fun collectNavigationMarkers(
element: PsiElement,
result: MutableCollection<in RelatedItemLineMarkerInfo<*>>
) {
// 查找类名后缀为 Mapper 内的所有方法
if (element !is PsiMethod) {
return
}
val psiClass = PsiTreeUtil.getParentOfType(element, PsiClass::class.java) ?: return
val className = psiClass.name ?: return
if (!className.endsWith("Mapper")) {
return
}
// 查找同名 XML 文件对应的 PSI 文件对象
val virtualFile = FileTypeIndex.getFiles(XmlFileType.INSTANCE, GlobalSearchScope.allScope(element.project))
.first { it.name.startsWith(className) }
val psiFile = PsiManager.getInstance(element.project).findFile(virtualFile)
// 遍历 XML 文件中标签 id 节点值等于 Java 方法名的元素, 然后添加可跳转的行标记符
psiFile?.accept(object : XmlRecursiveElementVisitor() {
override fun visitXmlAttribute(attribute: XmlAttribute) {
if (attribute.name == "id" && attribute.value == element.name) {
// NavigationGutterIconBuilder 用于创建标识符
result.add(
NavigationGutterIconBuilder.create(PluginIcons.MAPPER_ICON)
.setTargets(setOf(attribute.navigationElement))
.setTooltipText("Navigation to target in mapper xml").createLineMarkerInfo(element)
)
}
}
})
}
}