前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >编写一个IDEA插件之:事件监听

编写一个IDEA插件之:事件监听

作者头像
吴就业
发布2020-10-27 16:17:54
1.9K0
发布2020-10-27 16:17:54
举报
文章被收录于专栏:Java艺术Java艺术

事件监听,我们最熟悉不过的就是开发APP时,监听按钮点击事件、手指触摸及移动事件、网络状态事件等等。事件监听大多通过观察者模式实现,首先API调用者不需要知道后台是如何检测出网络状态不可用的,而只需要向系统注册一个监听器,当网络状态发生改变时,由系统回调给监听器。

本篇内容:

  • 项目或模块事件监听:在模块或者整个项目发生改变时,通过事件监听做出反应,如项目新增了一个模块或是删除了某个模块;
  • 文件编辑事件监听:在Java代码文件编辑时,通过事件监听能够知道哪个类的代码改变了,此时后台就可以刷新一些数据的缓存;

如何监听项目或模块改变事件

首先是项目级别的事件监听。添加一个项目管理事件监听器,我们需要实现ProjectManagerListener接口,该接口有四个方法,其源码如下。

代码语言:javascript
复制
public interface ProjectManagerListener extends EventListener {
  default void projectOpened(@NotNull Project project) {
  }
  default void projectClosed(@NotNull Project project) {
  }
  default void projectClosing(@NotNull Project project) {
  }
  default void projectClosingBeforeSave(@NotNull Project project) {
  }
}
  • projectOpened:该方法在项目打开时被回调;
  • projectClosingBeforeSave:在关闭项目时,开始保存项目之前被回调,或者说是在调用FileDocumentManager#saveAllDocuments方法保存所有文件之前被调用;
  • projectClosing:在projectClosingBeforeSave方法之后被回调;
  • projectClosed:与projectClosing的区别在于,projectClosed在项目已经关闭时被回调,在ProjectManagerImpl#closeProject方法执行到最后一行代码时被调用。

有了项目管理事件监听器之后,我们如何注册该监听器呢?

有两种方法,一种是代码方式注册,一种是在plugin.xml插件配置文件中注册。

代码方式注册可调用ProjectManager.getInstance().addProjectManagerListener();方法注册,但这种方式注册有一个弊端,就是无法监听到项目打开事件,projectOpened方法不会被调用,应该在我们能够调用该方法注册监听器时,项目实际已经打开了。

所以注册项目管理监听器我们只能通过修改plugin.xml配置文件方式注册,配置代码如下:

代码语言:javascript
复制
<applicationListeners>
    <listener class="com.msyc.ycpay.plugin.listener.MyProjectManagerListener"
              topic="com.intellij.openapi.project.ProjectManagerListener"/>
</applicationListeners>
  • topic:填写事件主题,类似于消息中间件中的Topic,只不过这里填写的是事件监听器的接口类名;
  • class:添加接口的实现类名;

当我们给IDEA注册自定义的项目管理事件监听器后,我们就可以通过项目管理事件监听器注册其它的事件监听器,例如注册模块监听事件,这是因为模块的事件触发在项目打开事件触发之后才会触发。因此,在projectOpened方法中可注册任何其它的事件监听器。

注册模块事件监听器代码如下:

代码语言:javascript
复制
project.getMessageBus().connect()
.subscribe(ProjectTopics.MODULES, new ModuleListener(){});

subscribe方法需要两个参数:

  • topic:主题,可选值参见ProjectTopics类的源码,有PROJECT_ROOTSMODULES
  • handler:事件处理器、监听器,当topicMODULES时,要求传递一个ModuleListener

ModuleListener接口的定义如下:

代码语言:javascript
复制
public interface ModuleListener extends EventListener {
  default void moduleAdded(@NotNull Project project, @NotNull Module module) {
  }
  default void beforeModuleRemoved(@NotNull Project project, @NotNull Module module) {
  }
  default void moduleRemoved(@NotNull Project project, @NotNull Module module) {
  }
  default void modulesRenamed(@NotNull Project project, @NotNull List<Module> modules, @NotNull Function<Module, String> oldNameProvider) {
  }
}
  • moduleAdded:添加模块完成时被调用;
  • beforeModuleRemoved:模块被移除之前被调用;
  • moduleRemoved:模块被移除时被调用;
  • modulesRenamed:模块修改名字时被调用;

如何监听文件编辑事件

通过前面两篇的学习,我们已经了解什么是PSI,知道一个文件对应一个PsiFile,一个PsiFile本身也是一个PsiElement,由许多的PsiElement构成,每个PsiElement也都可以有子PsiElement

因此,监听文件改变事件其实就是监听PSI树的结构改变事件,我们需要通过PsiManager注册PsiTreeChangeListener,代码如下。

代码语言:javascript
复制
PsiManager.getInstance(project).addPsiTreeChangeListener(
                new PsiTreeChangeListener() {
                    // .....
                }, FILES::clear);

至于注册时机,视情况而定,可以在Service初始化时注册,可以在AnAction触发时注册,也可以在projectOpened事件方法中注册。

PsiTreeChangeListener接口定义的方法较多,可以分为两类事件,一类是before事件、一类是after事件,接口源码如下。

代码语言:javascript
复制
public interface PsiTreeChangeListener extends EventListener {
  void beforeChildAddition(@NotNull PsiTreeChangeEvent event);
  void beforeChildRemoval(@NotNull PsiTreeChangeEvent event);
  void beforeChildReplacement(@NotNull PsiTreeChangeEvent event);
  void beforeChildMovement(@NotNull PsiTreeChangeEvent event);
  void beforeChildrenChange(@NotNull PsiTreeChangeEvent event);
  void beforePropertyChange(@NotNull PsiTreeChangeEvent event);

  void childAdded(@NotNull PsiTreeChangeEvent event);
  void childRemoved(@NotNull PsiTreeChangeEvent event);
  void childReplaced(@NotNull PsiTreeChangeEvent event);
  void childrenChanged(@NotNull PsiTreeChangeEvent event);
  void childMoved(@NotNull PsiTreeChangeEvent event);
  void propertyChanged(@NotNull PsiTreeChangeEvent event);
}
  • childrenChanged:子元素内容改变时被调用;
  • childReplaced:子元素被替换时被调用,触发childReplaced事件也会伴随着childrenChanged事件;
  • childAdded:子元素添加时被调用,触发childAdded事件时也会伴随着childReplacedchildrenChanged或事件;
  • childRemoved:子元素移除时被调用,触发childRemoved事件也会伴随着childReplacedchildrenChanged事件;
  • propertyChanged:属性改变时被调用,例如修改文件名;

最后

“编写一个IDEA插件”系列暂时就写这些,因为对这方面感兴趣的读者可能比对汇编语言感兴趣的读者还少。其实这几篇分析的也是笔者写插件过程中用到的一些笔者认为非常重要的知识点,当然还有很多没分享,如果要继续写,估计还可以写几篇,但看到上篇的阅读量就没动力继续写下去了。

参考:

  • intellij-platform-plugin-template的项目管理监听器注册:https://sourcegraph.com/github.com/JetBrains/intellij-platform-plugin-template@main/-/blob/src/main/resources/META-INF/plugin.xml#L17:55
  • 接收有关项目结构变更的通知:https://jetbrains.org/intellij/sdk/docs/reference_guide/project_model/project.html?search=projectClosingBeforeSave

[Java艺术] 微信号:javaskill

一个只推送原创文章的技术公众号,分享Java后端相关技术。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-10-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java艺术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 如何监听项目或模块改变事件
  • 如何监听文件编辑事件
  • 最后
相关产品与服务
项目管理
CODING 项目管理(CODING Project Management,CODING-PM)工具包含迭代管理、需求管理、任务管理、缺陷管理、文件/wiki 等功能,适用于研发团队进行项目管理或敏捷开发实践。结合敏捷研发理念,帮助您对产品进行迭代规划,让每个迭代中的需求、任务、缺陷无障碍沟通流转, 让项目开发过程风险可控,达到可持续性快速迭代。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档