IDEA插件写的好,下班下的早

本文介绍了如何使用IDEA开发一款插件,重点是一个能自动生成枚举查找方法的插件。从创建项目、配置plugin.xml到编写代码实现功能,详细讲解了插件开发的步骤。此外,还提供了调试和运行插件的方法,以及如何处理老插件项目的识别问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

IDEA插件简介


IDEA作为一款国民级IDE,虽然收费(懂的都懂是吧),其功能之强大,其样式之美观,其拓展之强劲,使其备受追捧,IDEA拓展性通过插件平台实现,并且以JAVA作为开发语言,使用JAVA开发插件变得容易,简便。

 

如何编写一款IDEA插件


默认各位已经安装好IDEA开发环境

  1. 打开IDEA开发界面:File ->  New -> Project 
  2. 830fc8bbf5fe2b6cb9a2bc14e57ea693.png

    新建项目引导

  3. 项目类型选择Intellij Platform Plugin
  4. 187919fe6eb86a4e2ca4effe601ff4db.png

    选择Intellij Platform Plugin

  5. 8ae955ea340496c84e9f81a47358bbd0.png

    Project SDK若没有自动选中,则点击添加并选中IDEA安装目录

  6. 点击Next
  7. b0f0822da8144fd4335551164a35df28.png

    输入自定义Project name,例如code-generator

  8. 点击Finish
  9. 然后咱们就得到了插件编写的工程,如下:
  10. e178b4c00b41d6e448008c2574053e0f.png
  11. 接下来咱们首先需要关注的是resources\META-INF\plugin.xml这个配置文件
  12. <idea-plugin>
      <!--你的插件唯一ID,自定义即可-->
      <id>Your Plugin Unique Id</id>
      <!--你的插件名称,自定义即可-->
      <name>Your Plugin Name</name>
      <version>1.0.0</version>
      <!--作者信息-->
      <vendor email="Your Contact Email" url="Your Company Url">Your Company Name</vendor>
    
      <!--你的插件描述,即你这个插件是用来做什么事情的,需要按照这个格式编写,样式才会好看,这个就是安装插件时的一些展示信息-->
      <description>
        <![CDATA[
          Description.
          <p>Like These:
            <ul>
              <li>First..</li>
              <li>Second..</li>
              <li>Etc..</li>
            </ul>
          </p>
        ]]>
      </description>
    
      <!--插件的变更历史,也需要按照格式,也是在安装插件时的一些展示信息-->
      <change-notes>
        <![CDATA[
          History:
          <p>First Version:
            <ul>
              <li>time:2022-12-10</li>
              <li>Author:CoffeeEngineer</li>
            </ul>
          </p>
        ]]>
      </change-notes>
    
      <!-- please see https://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/build_number_ranges.html for description -->
      <idea-version since-build="173.0"/>
    
      <!-- please see https://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/plugin_compatibility.html
           on how to target different products -->
      <!--编写IDEA插件的依赖包-->
      <depends>com.intellij.modules.platform</depends>
      <depends>com.intellij.modules.lang</depends>
      <depends>com.intellij.modules.java</depends>
    
    
      <extensions defaultExtensionNs="com.intellij">
        <!-- Add your extensions here -->
      </extensions>
    
     <!--这里非常重要--> 
     <!--这里非常重要--> 
     <!--这里非常重要--> 
     <!--这块关乎你的插件在idea什么位置展示,如何使用快捷键触发--> 
     <actions>
        <!-- Add your actions here -->
        <!-- 一个插件可以包含多个功能,每个功能就对应一个action,id就是action的唯一标识,自定义不重复即可,同理class就是指定你的action的全例名,text就是这个action的名字,description就是这个action的描述 -->
        <action id="Your Action Id" class="Your Action Full Package Name"
                text="Your Action Name" description="Your Action Description">
          <!-- add-to-group标识这个插件添加到哪个组,即在哪里显示。
               GenerateGroup:即右键的Generate中,anchor即表示顺序,last则表示添加在最后,
               可以在添加Action时指定即可
           -->
          <add-to-group group-id="GenerateGroup" anchor="last"/>
          <!--你的这个插件由哪个快捷键触发-->
          <keyboard-shortcut keymap="$default" first-keystroke="shift ctrl alt G"/>
        </action>
    
      </actions>
    
    </idea-plugin>
  13. 到此,准备工作咱们已经做好了,现在我们开始实战。

插播一则通告:本人在代码一线工作近八年时间,有非常丰富的面试经验,有需要优化简历,模拟面试的同学可以联系即时沟通号:xiaolang1530368931。将简历优化成大厂面试官想看的,提前回答大厂面试官可能会问的问题,为进大厂做最后的冲刺。

IDEA插件编写实战,开发一个能够自动生成枚举查找方法的插件

  1. 上效果图
  2. c1dbd4c1eecb944060971b41fade696d.gif
  3. 跟写Java代码一样的体验,我们可以创建各种包,来对我们的类进行区分
  4. 例如我们建立包名如下:
  5. 4437cde7659835035e258f92b2f55d1c.png
  6. 然后我们在action上点击右键新建插件具体实现
  7. d40276ade4b1759d8b83b5cfd5b8f76b.png
  8. 75c77a5acd02dce7ce157b6cacdd5591.png
  9. 因为我们需要编写一个自动生成枚举查找方法的枚举,所以我们按照上面的流程创建一个action名为AutoGenEnumFindCodeAction
  10. 59fb5659049298aceab91aed82b96433.png
  11. 插件配置plugin.xml中会自动加入以下配置
  12. <action id="com.github.action.enums.AutoGenEnumFindCodeAction" class="com.github.action.enums.AutoGenEnumFindCodeAction"
                text="AutoGenEnumFindCodeAction" description="AutoGenEnumFindCodeAction">
          <!--这两项可能没有,我们可以手动加入 begin -->
          <add-to-group group-id="GenerateGroup" anchor="last"/>
          <keyboard-shortcut keymap="$default" first-keystroke="shift ctrl alt G"/>
          <!--这两项可能没有,我们可以手动加入 end-->
        </action>

  13. 编写插件框架咱们已经搭建好,那现在我们就需要编写具体的代码了,生成枚举查找方法代码如下:
  14. package com.zhuanzhuan.action.enums;
    
    import com.intellij.openapi.actionSystem.AnAction;
    import com.intellij.openapi.actionSystem.AnActionEvent;
    import com.intellij.openapi.project.Project;
    import com.intellij.openapi.ui.Messages;
    import com.intellij.psi.PsiClass;
    import com.intellij.psi.PsiElementFactory;
    import com.intellij.psi.PsiField;
    import com.intellij.psi.PsiType;
    import com.intellij.psi.impl.source.PsiFieldImpl;
    import com.zhuanzhuan.action.BaseAction;
    import com.zhuanzhuan.action.common.ImportWriteCommandAction;
    import com.zhuanzhuan.action.common.MethodWriteCommandAction;
    import com.zhuanzhuan.constants.PluginConstants;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @Author CoffeeEngineer
     * @Description 自动生成枚举查找方法
     * @Date 2022/12/3 14:40
     */
    public class AutoGenEnumFindCodeAction extends AnAction implements BaseAction {
    
        /**
         * 自动导入包 java.util.Objects;
         */
        public static final String AUTO_IMPORT_PACKAGE_JAVA_UTIL_OBJECTS = "java.util.Objects";
    
        /**
         * 一期先取第一个字段为code,第二个字段为desc,后续可以改为可选字段
         *
         * @param event
         */
        @Override
        public void actionPerformed(AnActionEvent event) {
            PsiClass psiClass = this.getCurrentClass(event);
            if (psiClass == null) {
                return;
            }
            if (!psiClass.isEnum()) {
                Messages.showWarningDialog("Please Focus On A Enum File", PluginConstants.PLUGIN_NAME);
                return;
            }
    
            PsiField[] psiFields = psiClass.getFields();
            if (psiFields == null || psiFields.length == 0) {
                Messages.showWarningDialog("Cannot Find Field From Current Enum File", PluginConstants.PLUGIN_NAME);
                return;
            }
            if (psiFields.length < 2) {
                Messages.showWarningDialog("Least Two Field To Convert", PluginConstants.PLUGIN_NAME);
                return;
            }
            //当前工程
            Project project = event.getProject();
            //文件创建工厂
            PsiElementFactory psiElementFactory = PsiElementFactory.getInstance(project);
            List<PsiField> twoFirstPsiFieldList = this.getTwoFirstPsiField(psiFields);
            if (twoFirstPsiFieldList.size() < 2) {
                Messages.showWarningDialog("Least Two Field To Convert", PluginConstants.PLUGIN_NAME);
                return;
            }
            //编码字段
            PsiField codeField = twoFirstPsiFieldList.get(0);
            //描述字段字段
            PsiField descFiled = twoFirstPsiFieldList.get(1);
            //生成根据编码查找当前枚举
            this.genFindEnumByCode(codeField, psiClass, psiElementFactory,project);
            //生成编码与描述互转
            this.genConvertCodeToDesc(codeField, descFiled, psiClass, psiElementFactory,project);
            //自动导入包
            ImportWriteCommandAction importWriteCommandAction = new ImportWriteCommandAction(project,psiClass,AUTO_IMPORT_PACKAGE_JAVA_UTIL_OBJECTS);
            importWriteCommandAction.execute();
        }
    
        /**
         * 获取前两个枚举属性字段
         *
         * @param psiFields
         * @return
         */
        private List<PsiField> getTwoFirstPsiField(PsiField[] psiFields) {
            List<PsiField> psiFieldList = new ArrayList<PsiField>(2);
            for (int i = 0; i < psiFields.length; i++) {
                PsiField psiField = psiFields[i];
                if (psiField instanceof PsiFieldImpl) {
                    psiFieldList.add(psiField);
                    if (psiFieldList.size() >= 2) {
                        break;
                    }
                    continue;
                }
            }
            return psiFieldList;
        }
    
        /**
         * 生成根据编码查找当前枚举
         *
         * @param codeField
         * @param psiClass
         * @param psiElementFactory
         */
        private void genFindEnumByCode(PsiField codeField, PsiClass psiClass, PsiElementFactory psiElementFactory, Project project) {
            //当前编码类型
            PsiType psiType = codeField.getType();
            //当前编码名称
            String codeFieldName = codeField.getName();
            //当前枚举类的名称
            String psiClassName = psiClass.getName();
            //当前枚举变量名称
            String psiVariableName = psiClassName.substring(0,1).toLowerCase() + psiClassName.substring(1);
            //生成方法模板
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("public static ").append(psiClassName);
            stringBuilder.append(" findBy").append(codeFieldName.substring(0,1).toUpperCase()).append(codeFieldName.substring(1));
            stringBuilder.append("(").append(psiType.getPresentableText()).append(" ").append(codeFieldName).append(")");
            stringBuilder.append(" {").append("\n");
            stringBuilder.append("  for (").append(psiClassName).append(" ").append(psiVariableName)
                         .append(" : values()) {").append("\n");
            stringBuilder.append("      if (Objects.equals(").append(psiVariableName).append(".get").append(codeFieldName.substring(0,1).toUpperCase()).append(codeFieldName.substring(1)).append("(),").append(codeFieldName).append(")) {").append("\n");
            stringBuilder.append("          return ").append(psiVariableName).append(";").append("\n");
            stringBuilder.append("      }");
            stringBuilder.append("  }").append("\n");
            stringBuilder.append("  return null;");
            stringBuilder.append("}");
            MethodWriteCommandAction methodWriteCommandAction = new MethodWriteCommandAction(psiClass,stringBuilder.toString(),psiElementFactory,project);
            methodWriteCommandAction.execute();
        }
    
        /**
         * 生成编码与描述互转方法
         *
         * @param codeField
         * @param descFiled
         * @param psiClass
         * @param psiElementFactory
         * @param project
         */
        private void genConvertCodeToDesc(PsiField codeField, PsiField descFiled, PsiClass psiClass, PsiElementFactory psiElementFactory,Project project) {
            //当前编码类型
            PsiType codeFieldType = codeField.getType();
            //当前编码名称
            String codeFieldName = codeField.getName();
            //当前枚举类的名称
            String psiClassName = psiClass.getName();
            //当前枚举变量名称
            String psiVariableName = psiClassName.substring(0,1).toLowerCase() + psiClassName.substring(1);
            //当前描述类型
            PsiType descFiledType = descFiled.getType();
            //当前描述名称
            String descFiledName = descFiled.getName();
            //生成方法模板
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("public static ").append(descFiledType.getPresentableText());
            stringBuilder.append(" convert").append(codeFieldName.substring(0,1).toUpperCase()).append(codeFieldName.substring(1)).append("To").append(descFiledName.substring(0,1).toUpperCase()).append(descFiledName.substring(1));
            stringBuilder.append("(").append(codeFieldType.getPresentableText()).append(" ").append(codeFieldName).append(")");
            stringBuilder.append(" {").append("\n");
            stringBuilder.append("  for (").append(psiClassName).append(" ").append(psiVariableName)
                    .append(" : values()) {").append("\n");
            stringBuilder.append("      if (Objects.equals(").append(psiVariableName).append(".get").append(codeFieldName.substring(0,1).toUpperCase()).append(codeFieldName.substring(1)).append("(),").append(codeFieldName).append(")) {").append("\n");
            stringBuilder.append("          return ").append(psiVariableName).append(".get").append(descFiledName.substring(0,1).toUpperCase()).append(descFiledName.substring(1)).append("()").append(";").append("\n");
            stringBuilder.append("      }");
            stringBuilder.append("  }").append("\n");
            stringBuilder.append("  return null;");
            stringBuilder.append("}");
            MethodWriteCommandAction methodWriteCommandAction = new MethodWriteCommandAction(psiClass,stringBuilder.toString(),psiElementFactory,project);
            methodWriteCommandAction.execute();
        }
    }
    
  15. 相关的API这里就不一一介绍了,因为这个官网都有很详细的介绍,就算没有找到相应的API,根据咱们JAVA编码的相关经验,也可以找到相应的API,这里提一个我在编写自动生成枚举查找方法相关代码遇到的一个问题,因为我们需要生成查找方法,我们就需要获取到枚举中的编码与描述这两个字段,但是IDEA官方API获取枚举字段的时候会将枚举常量和属性字段全部列出来,咱们断点看下:
  16. 我这边也先是一顿搜索操作,但是没有找到自己想要的,后面我通过断点发现,枚举常量IDEA的API给出的实现是PsiEnumConstantImpl,而属性字段是PsiFieldImpl,那么咱们就可以通过instanceof关键字进行很好的区分,就像这样:
  17. 7e01a88cb8bdbc87655cd9220e6fcc7f.png
  18. d13ccd1a210ff944e9ea82cddcf7e22a.png
  19. 因为IDEA本身就是JAVA编写,所以咱们在编写IDEA插件的时候就能得到与开发业务代码一样的体验。

 

 

 


运行&调试

  1. 如果我们是按照上面的流程创建的插件项目,那我们可以直接点击运行或debug即可调试插件,如果没有运行程序,按照以下流程创建插件运行程序即可。
  2. 304c9599ac59346658f306f29ae3e148.png
  3. 2e07a44190458e32498743a3223dd4b5.png
  4. 我们启动运行程序之后,IDEA会开启一个新的IDEA进程,我们在新进程里面就可以执行咱们的插件程序,例如我们上面编写的一个自动生成枚举查找方法的插件
  5. 605fde89ac86a9d780e3c193ee72af88.png

    启动插件程序,IDEA会新起一个进程

  6. 然后我们就可以根据plugin.xml中配置的add-to-group group-id="GenerateGroup",比如我们现在配置的GenerateGroup,我们就能在相应的位置找到,触发后就能自动执行我们编写的插件代码。

如何运行一个老插件项目

  1. 咱们拉取一个项目后,会发现IDEA并没有自动识别该项目为插件项目,我们可以按照以下流程设置,让IDEA识别出当前项目是插件项目
  2. 设置插件引用:File->Project Structure->Project Settings->Project->Project SDK->选择IDEA安装目录(如果没有则点击Add并选中IDEA安装目录)
  3. 595534e6677a425b0c9ce5a30a0cb517.png
  4. 109c00d2c562349c59f6c9f28565428c.png
  5. 将工程的iml文件中的moudle#type改成PLUGIN_MODULE
  6. 5ddf10915867f2dd5e3b60e100315c38.png
  7. 设置完这两步之后会发现工程名图片已经编程插头形式,说明IDEA已经识别了插件项目,然后我们就可以愉快按照调试的步骤调试老插件项目了。
  8. 以上完整代码已经全部上传至GITHUB:
  9. GitHub - NotExistUserName/CodeGenerator: IDEA插件-代码生成器
    IDEA插件分享就告一段落了,应该把流程都讲清楚了,如果哪里没讲清楚,可以发送邮件至504401503@qq.com,我们一起讨论,一起做出更加好用的插件,争取早日实现下班自由。如果各位同学觉得对你有所帮助,请关注、点赞、评论、收藏来支持我,手头宽裕的话也可以赞赏来表达各位的认可,各位同学的支持是对我最大的鼓励。未来为大家带来更好的创作。 

 

分享一句非常喜欢的话:把根牢牢扎深,再等春风一来,便会春暖花开。

 

版权声明:以上引用信息以及图片均来自网络公开信息,如有侵权,请留言或联系

504401503@qq.com,立马删除。

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咖啡攻城狮Alex

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值