Intellij 有很多优秀的开源插件,让我们更加高效的编程.EventBus 虽然可以很好的解耦,但是对于使用者来说不那么方便找到发射点和接收点.EventBus Plugin 帮我们愉快的解决了这个痛点.但是由于我们项目自己封装 EventBus,所以其插件无法生效,只能通过站在巨人的肩膀上,修改轮子让他重新跑起来.
配置开发环境 新建项目 开发插件可以使用收费旗舰版也可是使用免费社区版本.
File->New->Project 选择 Intellij Platform Plugin 如图 Project SDK 选择 Intellij 的安装目录,这个 SDK 版本觉得这最低兼容版本,如果要适配低版本的 Intellij 或者 Android Stuido 如果想使用 Gradle Kotlin 之类的开发方式可以选择 Gradle 然后勾选 Java、Kotlin、Intellij Platform Plugin
输入项目名称和路径,点击 Finish 即可创建插件项目
配置 SandBox IntelliJ Plugin Run/Debug模式运行在 SandBox 中进行的,和当前 Intellij 没什么关系,需要在 Project Structure 中设置 Sandbox Home 路径.
配置 plugin.xml plugin.xml 就像是我们开发 Android 时的 AndroidMainfest.xml 文件,使用到的组件都必须在这里注册,否则就不会生效.<idea-version since-build="173.0"/>
这个表示插件支持的起始版本,根据文档 可知是 2017.3 版本.<depends>com.intellij.modules.lang</depends>
是项目中用到的依赖插件
plugin.xml 1 2 3 4 5 <extensions defaultExtensionNs ="com.intellij" > <codeInsight.lineMarkerProvider language ="JAVA" implementationClass ="com.***.eventbus.EventBusLineMarkerProvider" /> </extensions >
extensions 定义插件的扩展类型,defaultExtensionNs 定义此项目是基于 Intellij Core 编写的插件,codeInsight.lineMarkerProvider 是一种扩展类型意为标记提示器,其实现的功能就是我们此次需要的,就像我们实现接口的方法后,实现类的方法前的图标,点击后会跳转到接口中. 经过这两个步骤就新建配置好了插件项目,我们就可以基于此实现我们的逻辑了.
实现 LineMarkerProvider LineMarkerProvider.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 public class EventBusLineMarkerProvider implements LineMarkerProvider { private static final Icon ICON = IconLoader.getIcon("/icons/icon.png" ); private static final int MAX_USAGES = 100 ; private static GutterIconNavigationHandler<PsiElement> SHOW_SENDERS = (e, psiElement) -> { if (psiElement instanceof PsiMethod) { Project project = psiElement.getProject(); JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(project); PsiClass eventBusClass = javaPsiFacade.findClass("com.***.event.EventBus" , GlobalSearchScope.allScope(project)); if (eventBusClass != null ) { PsiMethod postMethod = eventBusClass.findMethodsByName("post" , false )[0 ]; PsiMethod method = (PsiMethod) psiElement; PsiClass eventClass = ((PsiClassType) method.getParameterList().getParameters()[0 ].getTypeElement().getType()).resolve(); ShowUsagesAction action = new ShowUsagesAction (new SenderFilter (eventClass)); action.startFindUsages(postMethod, new RelativePoint (e), PsiUtilBase.findEditor(psiElement), MAX_USAGES); } } }; private static GutterIconNavigationHandler<PsiElement> SHOW_RECEIVERS = (e, psiElement) -> { if (psiElement instanceof PsiMethodCallExpression) { PsiMethodCallExpression expression = (PsiMethodCallExpression) psiElement; PsiType[] expressionTypes = expression.getArgumentList().getExpressionTypes(); if (expressionTypes.length > 0 ) { PsiClass eventClass = PsiUtils.getClass(expressionTypes[0 ], psiElement); if (eventClass != null ) { List<PsiElement> generatedDeclarations = new ArrayList <>(); generatedDeclarations.add(PsiUtils.getClass(expressionTypes[0 ])); for (PsiType superType : expressionTypes[0 ].getSuperTypes()) { generatedDeclarations.add(PsiUtils.getClass(superType)); } new ShowUsagesAction (new ReceiverFilter ()).startFindUsages(generatedDeclarations, eventClass, new RelativePoint (e), PsiUtilBase.findEditor(psiElement), MAX_USAGES); } } } }; @Nullable @Override public LineMarkerInfo getLineMarkerInfo (@NotNull PsiElement psiElement) { if (PsiUtils.isEventBusPost(psiElement)) { return new LineMarkerInfo <>(psiElement, psiElement.getTextRange(), ICON, Pass.UPDATE_ALL, null , SHOW_RECEIVERS, GutterIconRenderer.Alignment.LEFT); } else if (PsiUtils.isEventBusReceiver(psiElement)) { return new LineMarkerInfo <>(psiElement, psiElement.getTextRange(), ICON, Pass.UPDATE_ALL, null , SHOW_SENDERS, GutterIconRenderer.Alignment.LEFT); } return null ; } @Override public void collectSlowLineMarkers (@NotNull List<PsiElement> elements, @NotNull Collection<LineMarkerInfo> result) { } }
LineMarkerProvider 会在我们每次打开文件时执行,当 getLineMarkerInfo 执行后一行代码如果符合我们的条件时,则行号后会出现 ICON 定义的代码,这个icon 必须是 16 * 16
或者 32 * 32
尺寸的图标. PSI (Program Structure Interface)程序结构接口,可以描述文件的层次结构(所谓的PSI树),这样我们就可以针对代码,逐个进行匹配分析.这个相关文档很少,但是 Intellij 自带工具可以帮我们分析文件的 psi 树,点击 Tool->View PSI Structure of Current File 这样我们就可以直观的预览需要处理的文件.PSI 对于代码所有的关键字和标点都有定义,如下图里可以清楚的看出 通过这个工具和结合PsiElement 文档 可以学习怎么使用它.
小结 这篇只是介绍下怎么入门,简单的了解下怎么新建项目和基础的代码实现,由于相关文档比较少,如果想要编写实用的插件,就需要参考 GitHub 中大量的插件工程,通过学习可以更快入门.