IDEA插件简介
IDEA作为一款国民级IDE,虽然收费(懂的都懂是吧),其功能之强大,其样式之美观,其拓展之强劲,使其备受追捧,IDEA拓展性通过插件平台实现,并且以JAVA作为开发语言,使用JAVA开发插件变得容易,简便。
如何编写一款IDEA插件
默认各位已经安装好IDEA开发环境
- 打开IDEA开发界面:File -> New -> Project
新建项目引导
- 项目类型选择Intellij Platform Plugin
选择Intellij Platform Plugin
Project SDK若没有自动选中,则点击添加并选中IDEA安装目录
- 点击Next
输入自定义Project name,例如code-generator
- 点击Finish
- 然后咱们就得到了插件编写的工程,如下:
- 接下来咱们首先需要关注的是resources\META-INF\plugin.xml这个配置文件
-
<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>
- 到此,准备工作咱们已经做好了,现在我们开始实战。
插播一则通告:本人在代码一线工作近八年时间,有非常丰富的面试经验,有需要优化简历,模拟面试的同学可以联系即时沟通号:xiaolang1530368931。将简历优化成大厂面试官想看的,提前回答大厂面试官可能会问的问题,为进大厂做最后的冲刺。
IDEA插件编写实战,开发一个能够自动生成枚举查找方法的插件
- 上效果图
- 跟写Java代码一样的体验,我们可以创建各种包,来对我们的类进行区分
- 例如我们建立包名如下:
- 然后我们在action上点击右键新建插件具体实现
- 因为我们需要编写一个自动生成枚举查找方法的枚举,所以我们按照上面的流程创建一个action名为AutoGenEnumFindCodeAction
- 插件配置plugin.xml中会自动加入以下配置
-
<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>
-
编写插件框架咱们已经搭建好,那现在我们就需要编写具体的代码了,生成枚举查找方法代码如下: -
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(); } }
- 相关的API这里就不一一介绍了,因为这个官网都有很详细的介绍,就算没有找到相应的API,根据咱们JAVA编码的相关经验,也可以找到相应的API,这里提一个我在编写自动生成枚举查找方法相关代码遇到的一个问题,因为我们需要生成查找方法,我们就需要获取到枚举中的编码与描述这两个字段,但是IDEA官方API获取枚举字段的时候会将枚举常量和属性字段全部列出来,咱们断点看下:
- 我这边也先是一顿搜索操作,但是没有找到自己想要的,后面我通过断点发现,枚举常量IDEA的API给出的实现是PsiEnumConstantImpl,而属性字段是PsiFieldImpl,那么咱们就可以通过instanceof关键字进行很好的区分,就像这样:
- 因为IDEA本身就是JAVA编写,所以咱们在编写IDEA插件的时候就能得到与开发业务代码一样的体验。
运行&调试
- 如果我们是按照上面的流程创建的插件项目,那我们可以直接点击运行或debug即可调试插件,如果没有运行程序,按照以下流程创建插件运行程序即可。
- 我们启动运行程序之后,IDEA会开启一个新的IDEA进程,我们在新进程里面就可以执行咱们的插件程序,例如我们上面编写的一个自动生成枚举查找方法的插件
启动插件程序,IDEA会新起一个进程
- 然后我们就可以根据plugin.xml中配置的add-to-group group-id="GenerateGroup",比如我们现在配置的GenerateGroup,我们就能在相应的位置找到,触发后就能自动执行我们编写的插件代码。
如何运行一个老插件项目
- 咱们拉取一个项目后,会发现IDEA并没有自动识别该项目为插件项目,我们可以按照以下流程设置,让IDEA识别出当前项目是插件项目
- 设置插件引用:File->Project Structure->Project Settings->Project->Project SDK->选择IDEA安装目录(如果没有则点击Add并选中IDEA安装目录)
- 将工程的iml文件中的moudle#type改成PLUGIN_MODULE
- 设置完这两步之后会发现工程名图片已经编程插头形式,说明IDEA已经识别了插件项目,然后我们就可以愉快按照调试的步骤调试老插件项目了。
- 以上完整代码已经全部上传至GITHUB:
- GitHub - NotExistUserName/CodeGenerator: IDEA插件-代码生成器
IDEA插件分享就告一段落了,应该把流程都讲清楚了,如果哪里没讲清楚,可以发送邮件至504401503@qq.com,我们一起讨论,一起做出更加好用的插件,争取早日实现下班自由。如果各位同学觉得对你有所帮助,请关注、点赞、评论、收藏来支持我,手头宽裕的话也可以赞赏来表达各位的认可,各位同学的支持是对我最大的鼓励。未来为大家带来更好的创作。
分享一句非常喜欢的话:把根牢牢扎深,再等春风一来,便会春暖花开。
版权声明:以上引用信息以及图片均来自网络公开信息,如有侵权,请留言或联系
504401503@qq.com,立马删除。