IDEA 插件开发(一):菜单及气泡通知

开发工具

开发工具使用 Intellij IDEA,官网下载地址:https://www.jetbrains.com/idea/download/other.html

推荐使用 2020.3.4 社区版(Community),原因如下:

  1. 免费开源,在开发插件的时候,可以调试源代码
  2. 自带 Plugin DevKit 插件(IDEA 的插件开发套件);
  3. 项目默认配置 IntelliJ Platform Plugin SDK
  4. 该版本支持 M1 芯片(Apple Silicon)。

设置源码路径(可选)

  1. 查看 build 号:打开 IDEA,Help | About,查看 build 号
    查看 build 号

  2. IDEA Community 源码切换到与 build 号相同的分支,点击 Code 按钮,选择 Download ZIP

对于我们选择的 2020.3.4,源码地址为:https://github.com/JetBrains/intellij-community/tree/203.8084

File | Project Structure | Platform Settings | SDKs
设置源码路径

开发插件

创建插件工程

选择 File | New | Project,左侧栏中选择 IntelliJ Platform Plugin 工程类型:
IntelliJ Platform Plugin

点击 Next,设置工程名称位置,点击 Finish 完成创建:
设置工程

可以到 File | Project Structure 来自定义工程设置。

插件工程结构

插件工程内容:

├── .idea
│   ├── .gitignore // git 提交忽略文件
│   ├── encodings.xml // 编码配置
│   ├── misc.xml // 各种杂项
│   ├── modules.xml // 模块信息
│   ├── sonarlint // 代码扫描插件
│   ├── workspace.xml // 工作空间
├── FirstPlugin.iml  // 项目标识文件(infomation of module)
├── resources
│   └── META-INF
│      ├── plugin.xml // ★插件配置:开发描述、版本信息、Action 事件入口、扩展信息(数据存放等)
│      ├── pluginIcon.svg // ★插件图标:名称固定,不可修改,自己放入
│      ├── pluginIcon_dark.svg // ★暗色系主题下的图标
└── src               // ★具体的事件、UI 窗体、工程逻辑的 Java/Kotlin 代码

修改插件配置文件

下面示例描述了可在 plugin.xml 文件配置的主要元素:

<idea-plugin>

  <!-- 插件唯一 id,不能和其他插件项目重复,所以推荐使用 com.xxx.xxx 的格式
       插件不同版本之间不能更改,若没有指定,则与插件名称相同 -->
  <id>com.ageovb.first</id>

  <!-- 插件名称,别人在官方插件库搜索你的插件时使用的名称 -->
  <name>First</name>

  <!-- 插件版本号 -->
  <version>1.0.0</version>

  <!-- 供应商主页和email(不能使用默认值,必须修改成自己的)-->
  <vendor email="ageovb.com" url="http://www.ageovb.com">ageovb</vendor>

  <!-- 插件的描述,支持 HTML 标签;
       不能使用默认值,必须修改成自己的。并且需要大于 40 个字符 -->
  <description><![CDATA[
      基于IDEA插件模板方式创建测试工程<br>
      <em>1. 学习IDEA插件工程搭建</em>
      <em>2. 验证插件基础功能实现</em>
    ]]></description>

  <!-- 插件版本变更信息,支持 HTML 标签;
       将展示在 settings | Plugins 对话框和插件仓库的 Web 页面 -->
  <change-notes><![CDATA[
      插件开发学习功能点<br>
      <em>1. 工程搭建</em>
      <em>2. 菜单读取</em>
      <em>3. 添加菜单</em>
      <em>4. 气泡通知</em>
    ]]>
  </change-notes>
  <!-- 以上信息会被抽取至插件市场主页展示 -->

  <!-- 兼容最低版本 173,对应 2017.3 版本 -->
  <idea-version since-build="173.0"/>

  <!-- 插件所依赖的其他插件的 id -->
  <depends>com.intellij.modules.platform</depends>

  <!-- 声明该插件对 IDEA core 或其他插件的扩展 -->
  <extensions defaultExtensionNs="com.intellij">
    <!-- 2020.3 及以后的版本,注册通知服务 -->
    <!--<notificationGroup id="Custom Notification Group" displayType="BALLOON" key="notification.group.name"/>-->
  </extensions>

  <!-- 编写插件动作 -->
  <actions>
    <action id="first" class="com.ageovb.first.FirstAction" text="Notification" description="Test notification">
      <add-to-group group-id="ToolsMenu" anchor="first"/>
      <keyboard-shortcut keymap="$default" first-keystroke="meta I"/>
    </action>
  </actions>

</idea-plugin>

创建 Action

一个 Action 表示 IDEA 菜单里的一个 menu item 或工具栏上的一个按钮,通过继承 AnAction 实现,当选择一个 menu item 或点击工具栏上的按钮时,就会调用 AnAction 类的 actionPerformed() 方法。

实现自定义 Action 分两步:

  1. 定义一个或多个 action;
  2. 注册 action,将 item 添加到菜单或工具栏上。

新建 Action 文件

新建一个 Package,如 com.ageovb.first,然后右击新建 Action 文件:
新建 Action 文件

如果没有看到 Plugin DevKit 选项,说明项目没有配置 IntelliJ Platform Plugin SDK,推荐的 2020.3.4 不会出现该情况。

填写必要的信息:
菜单信息

编写 Action 代码

package com.ageovb.first;

import com.intellij.notification.Notification;
import com.intellij.notification.NotificationDisplayType;
import com.intellij.notification.NotificationGroup;
import com.intellij.notification.Notifications;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.ui.MessageType;
import org.jetbrains.annotations.NotNull;

/**
 * 通过 Plugin Devkit 创建的 Action 继承了 AnAction
 */
public class FirstAction extends AnAction {
    /**
     * 需要实现点击事件发生之后的抽象方法
     */
    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {
        // 这是老版本的,NotificationGroup 已经过时
        NotificationGroup notificationGroup = new NotificationGroup("Notification", NotificationDisplayType.BALLOON, false);
        Notification notification = notificationGroup.createNotification("Test notification", MessageType.INFO);
        Notifications.Bus.notify(notification);

        // 2020.3 及以后的版本可以在 plugin.xml 中注册通知服务,并编写通知工具类 NotifyUtil
        //NotifyUtil.notifyInfo(e.getProject(), "Test notification");
    }
}

运行插件

与正常 Java 项目一样,直接点击右上角的 Run 按钮运行插件:
运行插件

插件以 Debug/Run 模式运行时是在 SandBox(沙箱) 中进行的,不会影响当前的 IntelliJ IDEA,启动后选择 Tools | Notification
Tools 菜单

即可看到右下角的气泡通知:
气泡通知

运行错误解决

找不到 Python

2022-10-03 14:20:52,094 [   1141]   WARN - .intellij.util.EnvironmentUtil - can't get shell environment 
java.lang.RuntimeException: command [/bin/zsh, -l, -i, -c, '/Applications/IntelliJ IDEA CE.app/Contents/bin/printenv.py' '/var/folders/k2/b_96l4cx3hdfvybcbrwjvhw80000gn/T/intellij-shell-env.17742117045732721406.tmp']
	exit code:127 text:0 out:(anon):setopt:7: can't change option: monitor

[ERROR]: gitstatus failed to initialize.


  Add the following parameter to ~/.zshrc for extra diagnostics on error:

    GITSTATUS_LOG_LEVEL=DEBUG

  Restart Zsh to retry gitstatus initialization:

    exec zsh
env: python: No such file or directory
	at com.intellij.util.EnvironmentUtil$ShellEnvReader.runProcessAndReadOutputAndEnvs(EnvironmentUtil.java:353)
	at com.intellij.util.EnvironmentUtil$ShellEnvReader.readShellEnv(EnvironmentUtil.java:254)
	at com.intellij.util.EnvironmentUtil$ShellEnvReader.readShellEnv(EnvironmentUtil.java:269)
	at com.intellij.util.EnvironmentUtil.getShellEnv(EnvironmentUtil.java:204)
	at com.intellij.util.EnvironmentUtil.lambda$loadEnvironment$0(EnvironmentUtil.java:106)
	at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1700)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.util.concurrent.Executors$PrivilegedThreadFactory$1$1.run(Executors.java:668)
	at java.base/java.util.concurrent.Executors$PrivilegedThreadFactory$1$1.run(Executors.java:665)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/java.util.concurrent.Executors$PrivilegedThreadFactory$1.run(Executors.java:665)
	at java.base/java.lang.Thread.run(Thread.java:834)

默认是使用 python 命令去获取 shell 环境变量的,即使我们使用 xcode-select --install 命令安装过开发者命令行工具,可以使用 ll /Library/Developer/CommandLineTools/usr/bin/python* 命令查看,并没有 python 命令,我们可以使用命令 sudo ln -s /Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/bin/python3 /Library/Developer/CommandLineTools/usr/bin/python 创建一个软链接,让 python 实际调用 python3

找不到类 OpenURIHandler

2022-10-03 14:20:52,631 [   1678]   WARN - i.mac.MacOSApplicationProvider - com/apple/eawt/OpenURIHandler 
java.lang.NoClassDefFoundError: com/apple/eawt/OpenURIHandler
	at com.intellij.ui.mac.MacOSApplicationProvider.initApplication(MacOSApplicationProvider.java:57)
	at com.intellij.idea.ApplicationLoader.startApp(ApplicationLoader.kt:152)
	at com.intellij.idea.ApplicationLoader.executeInitAppInEdt(ApplicationLoader.kt:68)
	at com.intellij.idea.ApplicationLoader.access$executeInitAppInEdt(ApplicationLoader.kt:1)
	at com.intellij.idea.ApplicationLoader$initApplication$1$1.run(ApplicationLoader.kt:374)
	at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:313)
	at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:770)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:715)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
	at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:740)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
	at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
Caused by: java.lang.ClassNotFoundException: com.apple.eawt.OpenURIHandler
	at com.intellij.util.lang.UrlClassLoader.findClass(UrlClassLoader.java:338)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:588)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
	... 18 more

这篇文章里指出,从 JDK 9 开始,已经不提供包 com.apple.eawtcom.apple.eio,取而代之的是 java.awt.desktop。不知道哪里的代码用的还是 JDK 8,目前不影响,先不用管。

缺少 Times 字体

Warning: the fonts "Times" and "Times" are not available for the Java logical font "Serif", which may have unexpected appearance or behavior. Re-enable the "Times" font to remove this warning.

下载并安装 Times 字体。

仓库地址

项目已上传到 Gitee

参考资料

你们要的Intellij IDEA 插件开发秘籍,来了!
Idea插件开发-开发自己的第一款idea插件
IntelliJ -IDEA插件开发教程-UI篇-编辑提示、消息通知(五)
https://plugins.jetbrains.com/docs/intellij/developing-plugins.html

posted @ 2022-10-03 16:19  ageovb  阅读(2343)  评论(0编辑  收藏  举报