为 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
相关推荐
计算机毕设指导63 天前
基于Springboot华强北商城二手手机管理系统【附源码】
java·开发语言·spring boot·后端·mysql·spring·intellij idea
独泪了无痕11 天前
【IntelliJ IDEA 集成工具】TalkX - AI编程助手
人工智能·个人开发·intellij idea
乄bluefox14 天前
IDEA 自动生成方法注释
java·intellij-idea·intellij idea
知北游丶17 天前
一款 IDEA 必备的 JSON 处理工具插件 — Json Assistant
java·intellij idea·jetbrains plugin·intellij plugin
我命由我1234519 天前
12.Java 泛型(自定义泛型类、自定义泛型接口、自定义泛型方法、泛型的继承与通配符)
java·开发语言·后端·java-ee·intellij-idea·intellij idea·后端开发
独泪了无痕1 个月前
探索 IntelliJ IDEA 中 Spring Boot 运行配置
spring boot·intellij idea
THRUSTER111111 个月前
Java学习笔记--继承的介绍,基本使用,成员变量和成员方法访问特点
java·开发语言·笔记·学习·学习方法·继承·intellij idea
计算机毕设指导61 个月前
基于SpringBoot共享汽车管理系统【附源码】
java·spring boot·后端·mysql·spring·汽车·intellij idea
codeMaster__hyd1 个月前
使用IDEA构建springboot项目+整合Mybatis
java·linux·centos·intellij-idea·intellij idea·1024程序员节
疯狂学习GIS1 个月前
创建第一个IDEA的Java项目的方法
java·后端·intellij idea