上一篇介绍怎样新建一个插件工程,本篇分析 EventBus 插件的实现.
此插件涉及较多的 Java GUI 代码,但是自己也不是很熟悉,只能做大概分析,重点在于分析扫描引用点.
扫描规则
我们项目对于 EventBus 有特殊的封装,就需要插件可以扫描出XXX.send(new NotifyEvent())
(XXX 类也可能会有子类),可以找出这个 Event 所有的接收点,并且对于继承了 Sender 的类页都应该支持.
XXXX.java1 2 3 4 5 6
| public static void notify(){ send(new NotifyEvent()); } static void send(Object eventModel) { Smart.getEventBus().post(eventModel); }
|
判断发射点
当我们打开一个 java 文件时,就会框架就会调用 getLineMarkerInfo 方法去扫描当前类的每一行代码(这么说不恰当,应该是每个字段也包括空格、空行之类,所有的信息都会转化成 PsiElement),然后我们通过 PsiElement 去判断是否时符合我们的规则.例如判断 EventBus 发射点.
PsiUtil.java1 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
| public static boolean isEventBusPost(PsiElement psiElement) { if (psiElement instanceof PsiCallExpression) { PsiCallExpression callExpression = (PsiCallExpression) psiElement; PsiMethod method = callExpression.resolveMethod(); if (method != null) { String name = method.getName(); PsiElement parent = method.getParent(); if ("post".equals(name) && parent instanceof PsiClass) { PsiClass implClass = (PsiClass) parent; return isEventBusClass(implClass) || isSuperClassEventBus(implClass); } else if ("send".equals(name) && parent instanceof PsiClass) { PsiClass implClass = (PsiClass) parent; return isEventSenderClass(implClass) || isSuperClassEventSender(implClass); } } } return false; }
private static boolean isEventBusClass(PsiClass psiClass) { return "SmartEventBus".equals(psiClass.getName()); }
private static boolean isSuperClassEventBus(PsiClass psiClass) { PsiClass[] supers = psiClass.getSupers(); if (supers.length == 0) { return false; } for (PsiClass superClass : supers) { if ("SmartEventBus".equals(superClass.getName())) { return true; } } return false; }
|
判断接收点
判断符合我们规则的接收点
1 2 3 4 5 6 7 8 9
| public static boolean isEventBusReceiver(PsiElement psiElement) { if (psiElement instanceof PsiMethod) { PsiMethod method = (PsiMethod) psiElement; String name = method.getName(); return ("onEvent".equals(name) || "onEventMainThread".equals(name)) && method.getParameterList().getParametersCount() == 1 && method.getParameterList().getParameters()[0].getType() instanceof PsiClassType; } return false; }
|
以上就是针对我们项目关于扫描的自定义代码,通过这几个简单的规则就可以做到预期的效果.
查找使用
ShowUsagesAction
继承AnAction
当我们点击 icon 后会执行ShowUsagesAction.actionPerformed()
方法,在此处执行搜索全局代码、收集使用点、显示搜索框和提示框等,需要深入了解 intellij 源码.由于能力有限不敢做过多分析,具体代码可以查看ShowUsagesAction源码.由于源码几乎没有注释,所以阅读起来有一定困难.
参考
IntelliJ Platform SDK
eventbus-intellij-plugin