如何自己开发一个IDEA插件

如何开发一款IDEA插件?如题所见,我们在使用IDEA的时候避免不了使用一些插件,包括作者之前也推荐过一些好用的IDEA插件:

还没用过Idea的这几款插件?那你也太out了

那么当一些插件不满足自己的要求的时候,大家有没有想过自己去开发一款idea插件?那么今天富贵同学就带大家自己动手去开发一款idea插件。

首先我们要开发的准备条件肯定是有一个IDEA了,然后我们使用Java去开发IDEA插件,IDEA与Java对应的版本可以去这里参考:

plugins.jetbrains.com/docs/intell...

作者的IDEA版本是2025+,所以选择的JDK版本是21。然后使用gradle去构建我们的项目(插件),gradle是类似maven一样的构建工具,如果你还不知道gradle是什么,可以去阅读一下作者之前的文章让你对gradle有一个简单的认识:Gradle的安装和配置

第一步,安装Plugin DevKit插件

这个插件的目的是让我们能够在新建项目的时候选择IDE插件 这个选项。如果没有这个插件,那么我们在新建项目的时候就选择不了IDE插件

我们直接去插件市场搜索Plugin DevKit安装即可

第二步,打开内部模式

主菜单栏-->帮助(Help)-->编辑自定义属性。输入idea.is.internal=true开启内部模式,重启后生效。

这一步的目的是:如果你想开发一款插件,那你是不是得要知道把他放在哪里,是放在工具栏里面,还是左边栏里面,是放在菜单栏里面?菜单栏的哪一个里面?而这些在IDEA中都有一个唯一的key,例如你要放在Tools下面

那么他的id就是ToolsMenu (该位置在IDEA的group-id),如果你想放在Run下面,那么我们可以这么查看他的group-id:

我们先点开这里:

然后鼠标放在上面,同时按住ctrl+alt+鼠标左键 就会出现这样的弹框:

那么 RunMenu 就是他的group-id 了,我们在后面写代码的时候可以使用这个 group-id ,这样你的插件就可以在这里显示了。

第三步,新建一个项目

菜单-文件-新建-项目:

选择好你的信息,选择插件,IDEA对应的JDK版本,可以直接参考作者的参数:

第四步,修改构建文件 build.gradle.kts

如果前面你的插件安装好了,按照步骤创建完插件,会得到一个build.gradle.kts 文件,这个时候它里面应该是这样的:

json 复制代码
plugins {
    id("java")
    id("org.jetbrains.kotlin.jvm") version "2.1.0"
    id("org.jetbrains.intellij.platform") version "2.5.0"
}

group = "com.wangfugui"
version = "1.0-SNAPSHOT"

repositories {
    mavenCentral()
    intellijPlatform {
        defaultRepositories()
    }
}

// Configure Gradle IntelliJ Plugin
// Read more: https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin.html
dependencies {
    intellijPlatform {
        create("IC", "2025.1")
        testFramework(org.jetbrains.intellij.platform.gradle.TestFrameworkType.Platform)

        // Add necessary plugin dependencies for compilation here, example:
        // bundledPlugin("com.intellij.java")
    }
}

intellijPlatform {
    pluginConfiguration {
        ideaVersion {
            sinceBuild = "251"
        }

        changeNotes = """
      Initial version
    """.trimIndent()
    }
}

tasks {
    // Set the JVM compatibility versions
    withType<JavaCompile> {
        sourceCompatibility = "21"
        targetCompatibility = "21"
    }
    withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
        kotlinOptions.jvmTarget = "21"
    }
}

我们需要把 repositories 里面的内容替换成:

scss 复制代码
repositories {
    //    mavenCentral()
    maven {
        url = uri("https://maven.aliyun.com/repository/public")
    }
    intellijPlatform {
        defaultRepositories()
    }
}

然后点击这个图标重新加载一次(记得先把当前的构建任务取消,就是idea里面有进度条那个)

然后等就完事了,第一次构建会比较慢,作者花费了11分钟:

第五步,编写AnAction文件

AnAction是一个类,我们想要自己做一些事件则需要继承该类,然后实现对应的方法,例如作者想做一个输入一个时间戳就装换为可视化的时间格式出来,所以我们需要写一个类继承AnAction类,然后实现它的一个方法:

scala 复制代码
package com.wangfugui.timetransfe;

import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.ui.Messages;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @Author: masiyi
 * @Describe:
 */
public class TimeTransfe extends AnAction {


    @Override
    public void actionPerformed(AnActionEvent e) {
        
    }
}

第六步,注册TimeTransfe类

我们光继承还不够,我们还需要把这个类注册到plugin.xml文件中

我们目前的类是这样的:

至于为什么报错,因为它提示你让你把里面的内容写好就不会报错了,看注释大家应该也知道写什么,我们就不说了,我们主要需要添加下面几段配置:

xml 复制代码
<actions>
    <action id="TimeTransfe" class="com.wangfugui.timetransfe.TimeTransfe" text="TimeTransfe" description="TimeTransfe">
        <add-to-group group-id="ToolsMenu" anchor="first"/>
        <keyboard-shortcut keymap="$default" first-keystroke="ctrl alt b"/>
    </action>
</actions>

action 标签里面的内容就是让你定义名称,TimeTransfe类所在的位置,以及你的插件显示的名称以及描述

xml 复制代码
<add-to-group group-id="ToolsMenu" anchor="first"/>

这一行代码就是我们上面抓取的group-id ,例如作者想放在工具菜单里面,就写 ToolsMenu 反正最上面的位置就写 first

xml 复制代码
<keyboard-shortcut keymap="$default" first-keystroke="ctrl alt shift Z"/>

至于这一行代码就是要定义我们的快捷键,很好理解吧,这个插件的快捷键。那么写完之后完整的xml文件应该是这样的:

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>com.wangfugui.TimeTransfe</id>

    <!-- Public plugin name should be written in Title Case.
         Guidelines: https://plugins.jetbrains.com/docs/marketplace/plugin-overview-page.html#plugin-name -->
    <name>TimeTransfe</name>

    <!-- A displayed Vendor name or Organization ID displayed on the Plugins Page. -->
    <vendor url="https://blog.csdn.net/csdnerM">掉头发的王富贵</vendor>

    <!-- Description of the plugin displayed on the Plugin Page and IDE Plugin Manager.
         Guidelines: https://plugins.jetbrains.com/docs/marketplace/plugin-overview-page.html#plugin-description -->
    <description><![CDATA[
   一个时间戳转换器, 将数字时间戳转换为可视化的时间格式,可以使用快捷键ctrl alt b快速调出
  ]]></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>
    <actions>
        <action id="TimeTransfe" class="com.wangfugui.timetransfe.TimeTransfe" text="TimeTransfe" description="TimeTransfe">
            <add-to-group group-id="ToolsMenu" anchor="first"/>
            <keyboard-shortcut keymap="$default" first-keystroke="ctrl alt shift Z"/>
        </action>
    </actions>
</idea-plugin>

第七步,编写TimeTransfe类

之后因为我们需要输入一个时间戳,那么必然有一个输入框,所以我们需要当点击这个按钮的时候弹回一个输入框来:

java 复制代码
// 弹出输入框
String input = Messages.showInputDialog(
        e.getProject(),
        "请输入时间戳:",
        "时间戳转换器",
        Messages.getQuestionIcon()
);

我们点击运行按钮运行一下看看效果:

如果出现上面这种情况,只需要把这个文件夹改名为java即可:

这个时候我们点击运行,就会弹出一个新的IDEA页面出来,我们可以随便打开一个项目,就进入到我们的新窗口里面了,这个时候我们点击工具就可以看到我们的插件已经加载进来了:

我们点击该选项或者按住我们之前设置的快捷键ctrl + alt + shift + Z 就会弹出我们的弹窗:

光有输入框不够啊,我们需要获取用户输入的信息,然后把这个时间戳转换为可视化的字符串出来,那么我们一步一步来,先获取用户输入的东西: Messages.showInputDialog 方法会直接返回一个参数回来,这个参数就是用户输入的内容,接着我们就正常写java代码:

java 复制代码
// 如果用户点击了确认且输入不为空,则处理时间戳转换
if (input != null && !input.isEmpty()) {

    // 将输入字符串转换为长整型时间戳
    long timestamp = Long.parseLong(input);

    // 将时间戳转换为日期对象
    Date date = new Date(timestamp);

    // 格式化日期为中文时间格式
    SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    String formattedDate = formatter.format(date);


    System.out.println("输入时间戳: " + timestamp + " -> " + formattedDate);

} else {
    System.out.println("未输入任何内容或已取消");
}

之后我们可以随便输入一个时间戳:

就可以看到控制台打印出了一个用户输入的数字加上转换之后的可视化时间,但是这可不够啊,插件内部打印有什么用呢,我们需要返回给用户,这个时候弹框方法就出现了:

java 复制代码
// 弹出显示转换结果的对话框
Messages.showMessageDialog(
        e.getProject(),
        "转换结果: " + formattedDate,
        "时间转换结果",
        Messages.getInformationIcon()
);

那么用户输入的不是数字呢,我们可以再优化一下,整个代码就是下面的这个样子了:

java 复制代码
package com.wangfugui.timetransfe;

import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.ui.Messages;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @Author: masiyi
 * @Describe:
 */
public class TimeTransfe extends AnAction {


    @Override
    public void actionPerformed(AnActionEvent e) {
        // 弹出输入框
        String input = Messages.showInputDialog(
                e.getProject(),
                "请输入时间戳:",
                "时间戳转换器",
                Messages.getQuestionIcon()
        );
        // 如果用户点击了确认且输入不为空,则处理时间戳转换
        if (input != null && !input.isEmpty()) {
            try {
                // 将输入字符串转换为长整型时间戳
                long timestamp = Long.parseLong(input);

                // 将时间戳转换为日期对象
                Date date = new Date(timestamp);

                // 格式化日期为中文时间格式
                SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String formattedDate = formatter.format(date);

                // 弹出显示转换结果的对话框
                Messages.showMessageDialog(
                        e.getProject(),
                        "转换结果: " + formattedDate,
                        "时间转换结果",
                        Messages.getInformationIcon()
                );

                System.out.println("输入时间戳: " + timestamp + " -> " + formattedDate);
            } catch (NumberFormatException ex) {
                // 输入不是有效数字时的错误处理
                Messages.showMessageDialog(
                        e.getProject(),
                        "请输入有效的时间戳数字!",
                        "输入错误",
                        Messages.getErrorIcon()
                );
                System.out.println("无效的时间戳输入: " + input);
            }
        } else {
            System.out.println("未输入任何内容或已取消");
        }
    }
}

输入正确的时间戳:

输入非数字:

这样最基础的时间戳转换插件就做好了。

优化插件

那么我想优化一下需要怎么弄?例如我想自动获取用户输入框里面的数据,如果是非时间戳,则弹出输入框让用户自己输入,并且输入框可以支持 10 位和13 位的时间戳,那么我们优化好的代码就是下面这样的:

java 复制代码
package com.wangfugui.timetransfe;

import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.ui.Messages;

import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @Author: masiyi
 * @Describe:
 */
public class TimeTransfe extends AnAction {


    private String getClipboardContent() {
        try {
            Toolkit toolkit = Toolkit.getDefaultToolkit();
            Clipboard clipboard = toolkit.getSystemClipboard();
            Transferable transferable = clipboard.getContents(null);

            if (transferable != null && transferable.isDataFlavorSupported(DataFlavor.stringFlavor)) {
                return (String) transferable.getTransferData(DataFlavor.stringFlavor);
            }
        } catch (Exception ex) {
            // 忽略异常,返回null
        }
        return null;
    }

    @Override
    public void actionPerformed(AnActionEvent e) {
        // 获取系统剪贴板内容
        String clipboardContent = getClipboardContent();

        if (clipboardContent != null && !clipboardContent.trim().isEmpty()) {
            try {
                // 自动处理剪贴板内容
                long timestamp = parseTimestamp(clipboardContent.trim());
                Date date = new Date(timestamp);
                SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String formattedDate = formatter.format(date);

                Messages.showMessageDialog(
                        e.getProject(),
                        "转换结果: " + formattedDate,
                        "时间转换结果",
                        Messages.getInformationIcon()
                );

                System.out.println("自动转换: 输入时间戳 " + timestamp + " -> " + formattedDate);
            } catch (NumberFormatException ex) {
                // 剪贴板内容无效,弹出输入框
                handleManualInput(e);
            }
        } else {
            // 剪贴板为空,弹出输入框
            handleManualInput(e);
        }
    }

    private void handleManualInput(AnActionEvent e) {
        String input = Messages.showInputDialog(
                e.getProject(),
                "请输入时间戳(支持10位秒级或13位毫秒级):",
                "时间戳转换器",
                Messages.getQuestionIcon()
        );

        if (input != null && !input.isEmpty()) {
            try {
                long timestamp = parseTimestamp(input);
                Date date = new Date(timestamp);
                SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String formattedDate = formatter.format(date);

                Messages.showMessageDialog(
                        e.getProject(),
                        "转换结果: " + formattedDate,
                        "时间转换结果",
                        Messages.getInformationIcon()
                );

                System.out.println("手动输入: 输入时间戳 " + timestamp + " -> " + formattedDate);
            } catch (NumberFormatException ex) {
                Messages.showMessageDialog(
                        e.getProject(),
                        "请输入有效的时间戳数字(10位或13位)!",
                        "输入错误",
                        Messages.getErrorIcon()
                );
                System.out.println("无效的时间戳输入: " + input);
            }
        } else {
            System.out.println("未输入任何内容或已取消");
        }
    }


    private long parseTimestamp(String input) throws NumberFormatException {
        input = input.trim();
        if (input.length() == 10) {
            // 10位秒级时间戳,转换为毫秒
            return Long.parseLong(input) * 1000;
        } else if (input.length() == 13) {
            // 13位毫秒级时间戳
            return Long.parseLong(input);
        } else {
            throw new NumberFormatException("时间戳长度必须为10位或13位");
        }
    }
}

安装插件

写完之后我们就得到了一个完整的IDEA插件了,那么如何安装这个插件呢?总不可能每次都运行一下吧,大家在IDEA中安装插件的时候不知道大家注意这个没: 从磁盘中安装插件

那么我们现在的思路就清晰可见了:将我们的IDEA插件代码打包成一个插件安装包,然后从磁盘安装插件。

双击上面的按钮之后我们在项目当前目录的build的libs目录下面就会有一个和下面图中一样的jar包:

这个时候我们在点击插件中的 从磁盘中安装插件 之后再选中这个jar包,就可以成功安装了:

发布插件

那么就有同学问了:哎呀哎呀,那我怎么让我的小伙伴体验我自己开发的插件呢?

这位同学问得好,就像发布你自己写的jar包到maven中央仓库一样:

如何发布jar包到maven中央仓库(2024年3月最新版保姆级教程) IDEA的插件也有一个中央仓库,发布到上面之后就可以包括你的小伙伴在内全世界所有的人都可以搜索到了!那么具体的方法如下:

插件官网:plugins.jetbrains.com/developers/...

注册一个账号

点击Upload plugin

第一步,填写基础信息

填写基础信息,例如你的插件叫什么名字

第二步,上传jar包

把我上面libs文件夹里面的jar包上传上去,填写许可证,主页等信息

第三步,等待审核

注意我们的description标签要以英文开头,这就是大家为什么看到插件市场里面的都是以英文开头的插件介绍的原因了

当出现这个提示的时候,则说明你的插件已经等待审核了,等几天就可以搜索到你的插件了

到时候会有邮件发送给你,注意及时回复以及根据里面的内容进行修改,否则就会审核失败。

总体的流程大致就是作者大大这样的,大家也可以自己动手开发一款IDEA插件,如果你不知道怎么去写里面的方法,例如作者开始不知道如何读取剪切板,这个时候就直接去问大模型就好了!!

文中的代码仓库地址给到大家:gitee.com/wangfugui-m... 大家可以参考一下作者的写法,对了也可以在idea 2025版本以上的搜索到作者这个插件,欢迎大家去体验:

相关推荐
无我Code3 小时前
全套开源:一款云端服务+本地设备计算的文生图应用
前端·人工智能·后端
用户69371750013844 小时前
实测可用|小米 MiMo 百万亿 Token 免费领,开发者速冲
前端·后端·ai编程
奇逍科技圈4 小时前
掌握数字主权:中企销订货系统源码如何重构企业全渠道自主可控经营
后端
会编程的土豆4 小时前
【go】 Go语言中的 defer:从入门到理解底层机制(讲透版)
开发语言·后端·golang
白晨并不是很能熬夜4 小时前
【RPC】第 1 篇:全景篇 — 一次 RPC 调用的完整旅程
java·网络·后端·网络协议·面试·rpc·java-zookeeper
kree4 小时前
Docker 完全入门教程
后端
用户467245132234 小时前
多线程编程的噩梦:线程"挂住了"怎么办?
后端
神奇小汤圆5 小时前
Spring AI 实战:用Java搭一个Multi-Agent多智能体系统,附完整源码
后端
TE-茶叶蛋6 小时前
Spring最核心扩展点:BeanPostProcessor
java·后端·spring