为 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
相关推荐
士伟丷3 天前
Idea 中的一些配置
intellij idea
技术无疆3 天前
Hutool:Java开发者的瑞士军刀
android·java·开发语言·ide·spring boot·gradle·intellij idea
三十一号鼓手8 天前
在对接电影票API时如何快速进行错误处理和调试
java·网络·eclipse·lisp·intellij idea
程序者王大川9 天前
【开发工具】IntelliJ IDEA插件推荐:Json Helper——让JSON处理更高效
java·开发语言·git·json·intellij-idea·intellij idea
呜呼~2251411 天前
Map集合
java·开发语言·jvm·intellij idea
呜呼~2251414 天前
List、Set、Map中的方法使用、Stream流、Collections工具类
java·开发语言·jvm·数据结构·intellij idea
秋刀鱼不做梦16 天前
Java中的经典排序算法:快速排序、归并排序和计数排序详解(如果想知道Java中有关快速排序、归并排序和计数排序的知识点,那么只看这一篇就足够了!)
java·数据结构·学习·算法·排序算法·intellij idea
cyt涛20 天前
MobaXterm连接服务器
java·运维·服务器·intellij idea
chuk.23 天前
【IDEA】一键重启多个服务
java·intellij idea
cyt涛24 天前
Maven 常用命令
java·开发语言·maven·intellij idea