实战

时间:2024-03-25 18:24:07

在正式实现前,先介绍一下整体的实现思路,这里只说明如何从 Java 方法跳转到 Mapper XML 文件中的节点,反向参考代码也很好理解,思路如下:

  1. 左侧图标行标记符通过实现RelatedItemLineMarkerProvider并重写collectNavigationMarkers方法设置。
  2. 判断代码行的元素类型为 PsiMethod 才进行设置,同时文件类名以 Mapper 结尾。
  3. 根据类名在项目查找同名的 Mapper XML 文件。
  4. 通过 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)
                    )
                }
            }
        })
    }

}