IDEA插件开发实践
第一步、环境准备
1、IDEA安装Plugin DevKit插件
2、开启IDEA内部模式(使用UI检查器定位代码)
输入idea.is.internal=true
开启内部模式,重启后生效。
3、gradle安装配置
gradle下载地址:https://gradle.org/releases/
注意:gradle版本需兼容IDEA的版本,否则构建会报错
版本对应查询:https://www.jetbrains.com/legal/third-party-software/?product=IIU&version=2023.1
解压gradle压缩包到指定路径,然后配置环境变量
idea中配置本地gradle
第二步、项目实战
1、背景
日常开发过程中,我们鼠标双击会选中整个单词,现在想实现按照驼峰选中,效果如图
当然,idea中有开关可以控制,但每次都进行设置不太方便,现在开发一个简单插件,实现当同时按下ctrl+alt+win键时,双击鼠标则是驼峰选中,释放时是单词选中
2、原理
开启IDEA内部模式(使用UI检查器定位代码)后,我们可以通过 ctrl+alt+鼠标左键点击对应菜单,可以获取到点击信息,然后根据关键类信息,到idea源码中查找到对应的代码,在插件开发中通过调用对应的api就可以修改配置信息。
3、新建插件项目
4、编辑build.gradle.kts配置文件内容
plugins {id("java")id("org.jetbrains.kotlin.jvm") version "1.7.20"id("org.jetbrains.intellij") version "1.13.1"
}group = "com.fufeng"
version = "1.0-SNAPSHOT"repositories {// 这里修改为阿里云的仓库地址// mavenCentral()maven {url = uri("https://maven.aliyun.com/repository/public")}
}// Configure Gradle IntelliJ Plugin
// Read more: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html
intellij {// version.set("2022.2.4")
// type.set("IC") // Target IDE Platform// 使用localPath设置本地idea的安装路径,这样在运行时会新打开一个程序用于测试插件localPath.set("D:\\develop Tools\\JetBrains\\IntelliJ IDEA 2023.1")plugins.set(listOf(/* Plugin Dependencies */))
}tasks {// Set the JVM compatibility versionswithType<JavaCompile> {sourceCompatibility = "11"targetCompatibility = "11"// 解决编译时中文报错options.encoding = "UTF-8"}// 添加以下内容,解决运行时控制台中文乱码withType<JavaExec> {jvmArgs = listOf("-Dfile.encoding=UTF-8", "-Dsun.stdout.encoding=UTF-8", "-Dsun.stderr.encoding=UTF-8")}withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {kotlinOptions.jvmTarget = "11"}patchPluginXml {sinceBuild.set("222")untilBuild.set("232.*")}signPlugin {certificateChain.set(System.getenv("CERTIFICATE_CHAIN"))privateKey.set(System.getenv("PRIVATE_KEY"))password.set(System.getenv("PRIVATE_KEY_PASSWORD"))}publishPlugin {token.set(System.getenv("PUBLISH_TOKEN"))}
}
重新构建配置文件
若出现这个报错,解决方式把项目修改为离线状态即可
打开 plugin.xml 文件发现爆红,这是因为不能使用默认的模版字符,修改一下就可以了
<!-- Plugin Configuration File. Read more: https://plugins.jetbrains.com/docs/intellij/plugin-configuration-file.html -->
<idea-plugin><!-- Unique identifier of the plugin. It should be FQN. It cannot be changed between the plugin versions. --><!-- 插件唯一id --><id>com.fufeng.SelectByCamel</id><!-- Public plugin name should be written in Title Case.Guidelines: https://plugins.jetbrains.com/docs/marketplace/plugin-overview-page.html#plugin-name --><!-- 插件名称,大驼峰格式,不能有plugin字符串 --><name>SelectByCamel</name><!-- A displayed Vendor name or Organization ID displayed on the Plugins Page. --><!-- 插件下载页面,作者相关信息 --><vendor email="123@qq.com" url="https://www.yourcompany.com">YourCompany1</vendor><!-- Description of the plugin displayed on the Plugin Page and IDE Plugin Manager.Simple HTML elements (text formatting, paragraphs, and lists) can be added inside of <![CDATA[ ]]> tag.Guidelines: https://plugins.jetbrains.com/docs/marketplace/plugin-overview-page.html#plugin-description --><description><![CDATA[插件功能描述,需要大于40个字符,支持html。。。。。。。。。。。。。。。。。。。。。。。。。。。。]]></description><!-- Product and plugin compatibility requirements.Read more: https://plugins.jetbrains.com/docs/intellij/plugin-compatibility.html --><depends>com.intellij.modules.platform</depends><!-- Extension points defined by the plugin.Read more: https://plugins.jetbrains.com/docs/intellij/plugin-extension-points.html --><extensions defaultExtensionNs="com.intellij"></extensions>
</idea-plugin>
kotlin
目录名修改为java
。选中kotlin目录,按shift+f6修改目录名为java。不修改后面可能会报错。
5、实现开关切换的两种方式
5.1、创建动作Action
- idea中的action概念可以理解为idea中的任意一个动作,点击了某个按钮,就会执行它绑定的Action类的代码。
- 使用Plugin DevKit 创建一个Action。创建成功之后就会生成一个Action类并在plugin中注册一个action
查询Tools菜单详情:
插件Action
手动切换驼峰选中开关:SelectByCamelAction:
package com.fufeng.selectcamel;import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;public class SelectCamelWordsAction extends AnAction {@Overridepublic void actionPerformed(AnActionEvent e) {System.out.println("hello plugin");// TODO: insert action logic here// 获取到编辑器设置EditorSettingsExternalizable editorSettingsExternalizable = EditorSettingsExternalizable.getInstance();// 对之前的状态取反editorSettingsExternalizable.setCamelWords(!editorSettingsExternalizable.isCamelWords());}
}
5.2、监听器实现
监听快捷键切换驼峰选中开关:MyKeyListener
在自定义的事件分发器中监听shift+ctrl+win按下,开启驼峰选中。win键抬起,关闭驼峰选中。
package com.fufeng.selectcamel;import com.intellij.openapi.application.ApplicationActivationListener;
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
import org.jetbrains.annotations.NotNull;
import com.intellij.openapi.wm.IdeFrame;import java.awt.*;
import java.awt.event.KeyEvent;/*** @ClassName: MyKeyListener* @Description:* @Author: 扶风* @Date: 2024/8/31 11:13*/
public class MyKeyListener implements ApplicationActivationListener {private final KeyEventDispatcher dispatcher = new MyKeyEventDispatcher();@Overridepublic void applicationActivated(@NotNull IdeFrame ideFrame) {KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(dispatcher);}@Overridepublic void applicationDeactivated(@NotNull IdeFrame ideFrame) {KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(dispatcher);}private static class MyKeyEventDispatcher implements KeyEventDispatcher {// 分发器逻辑private boolean shiftPressed = false;private boolean ctrlPressed = false;private boolean winPressed = false;private final EditorSettingsExternalizable editorSettingsExternalizable = EditorSettingsExternalizable.getInstance();@Overridepublic boolean dispatchKeyEvent(KeyEvent e) {int keyCode = e.getKeyCode();int id = e.getID();if (id == KeyEvent.KEY_PRESSED) {switch (keyCode) {case KeyEvent.VK_SHIFT:shiftPressed = true;break;case KeyEvent.VK_CONTROL:ctrlPressed = true;break;case KeyEvent.VK_WINDOWS:winPressed = true;break;}if (shiftPressed && ctrlPressed && winPressed) {// 开启驼峰选中System.out.println("Shift + Ctrl + Win 按下!");editorSettingsExternalizable.setCamelWords(true);}} else if (id == KeyEvent.KEY_RELEASED) {switch (keyCode) {case KeyEvent.VK_WINDOWS:if (shiftPressed && ctrlPressed) {// 关闭驼峰选中System.out.println("Win 键抬起!");editorSettingsExternalizable.setCamelWords(false);}winPressed = false;break;case KeyEvent.VK_SHIFT:shiftPressed = false;break;case KeyEvent.VK_CONTROL:ctrlPressed = false;break;}}return false; // 继续分发事件}}
}
在plugin.xml中注册应用激活时监听,并绑定到我们的监听器中。
<applicationListeners><listener class="com.fufeng.selectcamel.MyKeyListener" topic="com.intellij.openapi.application.ApplicationActivationListener"/></applicationListeners>
至此开发完成!
第三步、打包导入
构建完成后会生成一个jar包,在插件管理里面从本地导入这个jar即可使用
idea版本兼容性问题
- idea-version参考: https://plugins.jetbrains.com/docs/intellij/plugin-configuration-file.html#idea-plugin__idea-version
- 内部版本号映射参考:https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html#earlier-versions
- 修改build.gradle.kts中的patchPluginXml,它用来不是兼容的idea版本。该值会覆盖plugin.xml中的idea-versin标签,所有可以直接把兼容版本写在这里。
patchPluginXml {// 最低版本sinceBuild.set("232")// 最高版本untilBuild.set("242.*")
}
<idea-version since-build="232" until-build="242.*"/>
jetbrains.com/docs/intellij/build-number-ranges.html#earlier-versions
- 修改build.gradle.kts中的patchPluginXml,它用来不是兼容的idea版本。该值会覆盖plugin.xml中的idea-versin标签,所有可以直接把兼容版本写在这里。
patchPluginXml {// 最低版本sinceBuild.set("232")// 最高版本untilBuild.set("242.*")
}
<idea-version since-build="232" until-build="242.*"/>
- 要做到多版本兼容,就是要将最低版本设置的尽可能的小,但是同时需要你设置的版本有你插件当前使用的api。例如
applicationListeners
就在193.0之后发布,我的最低版本就只能设置193.0。最大版本不设置。