当前位置: 首页 > news >正文

IDEA插件开发实践

第一步、环境准备

1、IDEA安装Plugin DevKit插件

image-20240831090042073

2、开启IDEA内部模式(使用UI检查器定位代码)

image-20240831090642654

输入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

image-20240831102225447

image-20240831102308993

解压gradle压缩包到指定路径,然后配置环境变量

image-20240831102646025

image-20240831102717383

idea中配置本地gradle

image-20240831102837756

第二步、项目实战

1、背景

日常开发过程中,我们鼠标双击会选中整个单词,现在想实现按照驼峰选中,效果如图

image-20240831120036366

当然,idea中有开关可以控制,但每次都进行设置不太方便,现在开发一个简单插件,实现当同时按下ctrl+alt+win键时,双击鼠标则是驼峰选中,释放时是单词选中

image-20240831120237778

2、原理

开启IDEA内部模式(使用UI检查器定位代码)后,我们可以通过 ctrl+alt+鼠标左键点击对应菜单,可以获取到点击信息,然后根据关键类信息,到idea源码中查找到对应的代码,在插件开发中通过调用对应的api就可以修改配置信息。

image-20240831130831811

3、新建插件项目

image-20240831121215144

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"))}
}

重新构建配置文件

image-20240831123005790

若出现这个报错,解决方式把项目修改为离线状态即可

image-20240831133929609

打开 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。不修改后面可能会报错。

image-20240831122645398

5、实现开关切换的两种方式

5.1、创建动作Action
  • idea中的action概念可以理解为idea中的任意一个动作,点击了某个按钮,就会执行它绑定的Action类的代码。
  • 使用Plugin DevKit 创建一个Action。创建成功之后就会生成一个Action类并在plugin中注册一个action

查询Tools菜单详情:

image-20240831132317527

插件Action

image-20240831123134849

image-20240831123430471

手动切换驼峰选中开关: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>

至此开发完成!

第三步、打包导入

image-20240831132643104

构建完成后会生成一个jar包,在插件管理里面从本地导入这个jar即可使用

image-20240831134550774

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。最大版本不设置。
http://www.lryc.cn/news/619004.html

相关文章:

  • 从阶段演进到智能跃迁:企业合同管理的发展与趋势
  • SynAdapt:通过合成连续思维链实现大语言模型的自适应推理
  • @Rancher简介部署使用 - Docker Compose
  • Spring MVC 处理请求的完整流程详解
  • 【Unity】Spine重新播放动画时会闪烁上次动画的残影
  • 秋招笔记-8.12
  • Tauri Qt孰优孰劣
  • 【Unity】Unity中ContentSizeFitter有时无法及时自适应大小问题解决
  • 终端安全检测和防御技术总结
  • Python初学者笔记第二十四期 -- (面向对象编程)
  • SpringBoot集成MyBatis的SQL拦截器
  • MES系统怎么实现数字化闭环与设备预测性维护?
  • SQL180 每类试卷得分前3名
  • 单例模式,动态代理,微服务原理
  • 大数据技术入门精讲(Hadoop+Spark)
  • 当机械臂装上「智能大脑」:Deepoc具身智能模型如何重构传统自动化​
  • JavaEE 初阶第十八期:叩开网络世界的大门(上)
  • 自己动手造个球平衡机器人
  • 13.深度学习——Minst手写数字识别
  • 【自动化运维神器Ansible】playbook文件内变量定义全流程解析
  • 实时域自适应检测SOTA方案RT-DATR,刷新多个跨域检测榜单!
  • wordpress数据库文件sql导入时出现#1253错误
  • Java数据结构之ArrayList
  • 嵌入式分享合集136
  • 移动端调用大模型详解
  • 关于淘宝双十一
  • 数据分析小白训练营:基于python编程语言的Numpy库介绍(第三方库)(上篇)
  • DuckDB读取xlsx格式数据的方法比较
  • 【SpringBoot】MyBatis 动态 sql
  • 如何应对CAN总线冲突和数据丢包