为 TheRouter 开发一个 IDEA 插件

本插件代码已全部开源,走过路过请给个star:
github.com/kymjs/TheRo...

这篇文章是假定你已经有了 idea 插件开发的入门知识,来教你如何实现一个实际项目的功能。如果你还不知道如何开发一个插件,建议先从这个链接查看官网相关文档 plugins.jetbrains.com/docs/intell...

背景介绍

TheRouter 是一个移动端动态路由框架,同时支持 Android、iOS,具有高度双端一致性,可以把原本类依赖解耦为字符串依赖。

做过 Android 开发的应该都知道,页面跳转强行跟Activity.class绑定的,所以大部分路由框架都会把class替换成一个字符串,用字符串来解耦,但是这样一来就又变成了字符串满天飞的情况。

所以我做了这个用于自动跳转的高效辅助插件,可以直接从路由的声明处查看到哪些地方跳转到本路由,再也不用怕路由字符串满天飞了。

需要哪些功能

页面跳转

先想想需要哪些功能,首先页面跳转肯定是要有个导航的。类似这张图,在路由声明的地方,只需要点一下侧面的导航箭头,就能跳转到所有使用了这个路由页面的地方,并且还能标记出有哪些类用到了他。

版本号更新

对于开源项目,最麻烦的就是什么时候有新版本,新版本修复了什么问题。类似这张图,如果能有一个提醒,每次有新版本就告诉我,新版本有什么功能,修复了什么问题,跟我当前使用的版本有哪些变化,直接升级需不需要改代码,那就方便很多了。

一键迁移工具

TheRouter 官网本身提供了一个图形化界面的迁移工具,可以直接从其他路由迁移到TheRouter。但是使用的时候还得要额外下载一个APP,既然代码都是开源的,如果这个功能直接放在开发工具里面,那就方便多了。

导航跳转是如何实现的

在 idea 插件的开发中,有个很重要的类就是 LineMarkerProvider ,他是 Jetbrains 提供的 SDK 中的类。他是代码中每一行的标记提供者,也就对应了上面图中代码侧面的标记,是如何创建的。

比如在给 TheRouter 设计的这个插件里,就用这样的代码创建了一个侧边栏行标记:

erlang 复制代码
if (allMarkerStatus[key] != STATUS_SHOWN) {
    allMarkerStatus[key] = STATUS_SHOWN
    val builder = NavigationGutterIconBuilder.create(getIcon(targetContent.type))
    builder.setAlignment(GutterIconRenderer.Alignment.CENTER)
    builder.setTargets(all)
    if (targetContent.type == TYPE_ANNOTATION || targetContent.type == TYPE_ACTION) {
        builder.setTooltipTitle("TheRouter:跳转到使用处")
    } else {
        builder.setTooltipTitle("TheRouter:跳转到声明处")
    }
    result.add(builder.createLineMarkerInfo(psiElement))
}

其中的 NavigationGutterIconBuilder.create()就是创建一个侧边栏行标记,入参是一个图片资源,这里我根据要展示的类型返回了不同的资源图片。

kotlin 复制代码
fun getIcon(type: Int): Icon {
    return when (type) {
        TYPE_ANNOTATION -> IconLoader.getIcon("/icon/icon_from.png")
        TYPE_NAVIGATION -> IconLoader.getIcon("/icon/icon_to.png")
        TYPE_ACTION -> IconLoader.getIcon("/icon/icon_from.png")
        else -> IconLoader.getIcon("/icon/icon_warn.png")
    }
}

找到要跳转的psi

解决了显示的标记,剩下的就是找到点击标记后要跳到哪里了。

在 idea 插件的 SDK 中,还有个很重要的类就是 PsiElement,PSI (Program Structure Interface),指程序结构接口,主要负责解析文件、创建语法、语义代码。一个 PsiElement 就表示了一个我们要处理的代码语句。

在上面代码的builder里面有一个重要的方法是builder.setTargets(),就表示点击这个图标后要跳到哪里,上面的代码中传入的all 是一个PsiElement集合,如果只有一个PsiElement,点击以后就会直接跳转,如果集合有多个元素,则会先展示一个选择框,由插件的使用方自行选择跳到哪个目标PsiElement

而这个all集合的获取,也是来自另一个SDK中的方法PsiManager.getInstance(project).findFile(virtualFile),他可以将整个项目中的所有代码以 psi 的方式返回给你,这样你就可以根据实际需要,过滤找到跳转的目标位置了。具体使用可以看下面这段节选代码:

kotlin 复制代码
private fun findAllTargetPsi(
    project: Project,
    filePath: String?,
    target: TargetContent
): Collection<TargetPsiElement> {
    val allTargetPsi = HashSet<TargetPsiElement>()
    val scopes = GlobalSearchScope.projectScope(project)
    val allCodeFiles = FilenameIndex.getAllFilesByExt(project, "kt", scopes)
    allCodeFiles.addAll(javaFiles)
    for (virtualFile in allCodeFiles) {
        val psiFile: PsiFile? = PsiManager.getInstance(project).findFile(virtualFile)
        psiFile ?: return HashSet()

        val properties = PsiTreeUtil.findChildrenOfType(psiFile, PsiElement::class.java)
        properties.forEach { psiElement ->
			allTargetPsi.add(TargetPsiElement(psiElement, psiFile.name))
        }
    }
    return allTargetPsi
}

迁移工具是如何实现的

有个最简单的原因,TheRouter在设计阶段就参考了大量其他路由的设计,根据官网的描述:

之所以叫TheRouter 因为 The 代表了一种唯一性,我们在设计的时候就参考了全部现有的开源方案,吸取了大量优秀实现,同时补齐了各个方案的缺点。

所以本身在 API 层面,TheRouter 跟其他的路由就没有什么区别,因此迁移工具的核心就是字符串替换,这里替换的内容没什么好讲的,重点讲讲如何在插件里面展示一个UI弹窗。

首先第一步是创建一个AnAction类,他也是 Jetbrains 提供的 SDK 中的类。用于在菜单栏中展示一个菜单项,就像这样:

同样SDK也已经提供好了弹窗的API,跟开发 Android 一样,直接调用MessageDialogBuilder就可以创建了。

kotlin 复制代码
class TransferAction : AnAction() {
    private val routerNameList = HashMap<String, ITransfer>()
    override fun actionPerformed(event: AnActionEvent) {
        val project = event.project ?: return
        val version = getVersion()
        val file = File(desktop, fileName)
        if (MessageDialogBuilder
                .okCancel(
                    "TheRouter 一键迁移工具",
                    "当前项目为:$projectPath\n\n即将迁移至 TheRouter $latestVersion。迁移完成后,会在桌面生成改动日志。请注意查看:\n\n${file.absolutePath}。"
                )
                .noText("取消")
                .yesText("开始迁移")
                .icon(Messages.getInformationIcon())
                .ask(project)
        ) {
            routerNameList["ARouter"]?.transfer(projectPath, latestVersion, file)
        }
    }
}

插件开源

本项目已基于 Apache License 2.0 协议开源,并上架插件市场,欢迎 star 与下载

GitHub:github.com/kymjs/TheRo...

插件下载: plugins.jetbrains.com/plugin/2004...

插件下载

  1. 打开 Android Studio -> Preferences -> Plugins -> Marketplace
  2. 搜索 TheRouter
  3. 点击下载
  4. 重启 Android Studio
相关推荐
Jaising6667 小时前
JetBrains AI 打零工(一)——生产力工具与程序员的驾驭之道
ai编程·intellij idea
MacroZheng3 天前
IDEA官方中文文档正式发布,太全了!
java·后端·intellij idea
ApeAssistant4 天前
Idea HttpClient
intellij idea
KK溜了溜了5 天前
JAVA-springboot整合Mybatis
spring boot·mysql·maven·mybatis·intellij idea
舒一笑5 天前
PandaCoder发布-仅以此篇记录人生第一个开源项目
intellij idea
dearxue8 天前
ApiHug 1.3.9 支持 Spring 3.5.0 + Plugin 0.7.4 内置小插件升级 & 儿童节快乐!
spring·api·intellij idea
我命由我1234517 天前
IDEA - Windows IDEA 代码块展开与折叠(基础折叠操作、高级折叠操作)
java·笔记·后端·java-ee·intellij-idea·学习方法·intellij idea
*.✧屠苏隐遥(ノ◕ヮ◕)ノ*.✧1 个月前
MyBatis快速入门——实操
java·spring boot·spring·intellij-idea·mybatis·intellij idea
chirrupy_hamal1 个月前
IntelliJ IDEA 保姆级使用教程
java·intellij idea
chirrupy_hamal1 个月前
IntelliJ IDEA
java·intellij idea