发表时间:2022-03-26来源:网络
我很喜欢IDEA的一键自动生成代码功能,例如自动生成构造方法、字段的Get/Set方法、ToString方法等等,除此之外,也有一些插件提供自动生成代码的功能,例如我们所熟悉的GsonFormat插件,使用该插件可以为我们快速的解析json字符串生成一个对应的Java类,这在对接一些第三方API时很有帮助。
笔者写过一个运行时根据json自动生成Class的工具包:json-class-generator,与GsonFormat不同的是,该工具使用ASM在运行时解析json结构树生成类的字节码,而GsonFormat生成的是Java源代码。当时写json-class-generator目的是实现一个第三方API自动对接框架,由于该框架涉及到业务,所以没有开源。
虽然json-class-generator与GsonFormat实现的功能不同,但原理相似。
上一篇我们了解到,Java源代码编译后生成的Class文件有固定的结构,而在IDEA中,Java源代码也同样有固定的结构:PSI程序结构。与使用ASM操作字节码修改一个Class文件一样,我们也可以通过编辑一个Java源代码的PSI程序结构的元素修改Java代码。
读懂本篇的前提是你已经对PSI有所了解
我们模仿IDEA提供的自动生成代码功能,给右键弹出菜单的Generate...菜单添加一个子菜单:GeneratedInvokePayMethod,在插件使用者点击该菜单时自动生成一串代码,并且生成的代码插入到当前光标所在位置。
首先需要编写一个对应GeneratedInvokePayMethod菜单的Action,并实现actionPerformed方法,代码如下。
public class GeneratedInvokePayMethodAction extends AnAction { @Override public void actionPerformed(@NotNull AnActionEvent event) { } }actionPerformed方法在菜单被点击时调用,该方法只有一个参数:
event:这个参数封装了很多有用的信息,比如我们可以从该参数获取当前文件的PsiFile实例、获取当前光标落在的PsiElement等。其次,我们需要注册Action,将Action放到右键弹出菜单的GenerateGroup。需要在plugin.xml文件添加如下配置信息:
效果如下图所示。
现在我们继续完成GeneratedInvokePayMethodAction的actionPerformed方法。
由于Intellij Platform不允许插件在主线程中进行实时的文件写入,只能通过异步任务来完成写入,因此,我们需要通过WriteCommandAction.runWriteCommandAction来执行一个后台写入操作,如下代码所示。
public class GeneratedInvokePayMethodAction extends AnAction { @Override public void actionPerformed(@NotNull AnActionEvent event) { // 立即执行一个后台任务 WriteCommandAction.runWriteCommandAction(editor.getProject(), () -> { // do ... }); } }想要在当前光标所在的位置插入一行代码,那么我们需要做这些事情:
1、先判断当前文件是否是一个Java文件,借助actionPerformed的event参数可取得当前文件的PsiFile实例,判断PsiFile实例的类型是否为PsiJavaFile,如果不是,说明这不是一个Java代码文件,什么也不需要做(或者可以给出对话框提示); // AnActionEvent event PsiFile psiFile = event.getData(LangDataKeys.PSI_FILE); 2、通过第一步获取的PsiFile,查找当前光标所在位置的PsiElement实例; PsiElement element = psiFile.findElementAt(editor.getCaretModel().getOffset());其中editor.getCaretModel().getOffset()为获取当前光标位置;
另外,可以使用AnActionEvent#getData方法获取当前光标所在的PsiElement,代码如下:
// AnActionEvent event PsiElement psiElement = event.getData(LangDataKeys.PSI_ELEMENT);但这种方式不适用于当前场景,如果将光标放在一行代码的;后面,那么该方法就会返回null值。
3、根据光标所在的PsiElement,获取该PsiElement所在方法的PsiCodeBlock(一个方法只有一个PsiCodeBlock); PsiElement codeBlock = element; while (!(codeBlock instanceof PsiCodeBlock)) { codeBlock = codeBlock.getParent(); } 4、创建新的PsiElement,该PsiElement就是需要自动生成的代码;例如创建一个表达式元素(PsiExpression),可使用PsiElementFactory#createExpressionFromText方法创建,代码如下。 PsiElement newElement = PsiElementFactory.getInstance(element.getProject()) .createExpressionFromText("Invocation invocation = Invocation.builder()\n" + " .scope(scope)\n" + " .service(payType)" + " .operate(\"" + method + "\")\n" + " .body(merchantNo)\n" + " .build()", element.getContext());PsiElementFactory使用工厂模式生产PsiElement,提供了大量的API,例如创建字段的createField、创建方法的createMethod、创建类的createClass,创建关键字的createKeyword。
5、最后,将新创建的PsiElement添加到光标所在PsiElement的后面; // 参数1:新增的PsiElement // 参数2:位置参照的PsiElement codeBlock.addAfter(newElement, element);完整示例代码如下。
public class GeneratedInvokePayMethodAction extends AnAction { @Override public void actionPerformed(@NotNull AnActionEvent event) { WriteCommandAction.runWriteCommandAction(editor.getProject(), () -> { PsiFile psiFile = event.getData(LangDataKeys.PSI_FILE); // 查找当前光标停留在的元素 PsiElement element = psiFile.findElementAt(editor.getCaretModel().getOffset()); // 获取当前方法的PsiCodeBlock元素 PsiElement codeBlock = element; while (!(codeBlock instanceof PsiCodeBlock)) { codeBlock = codeBlock.getParent(); } // 使用PsiElementFactory创建表达式元素 PsiElement newElement = PsiElementFactory.getInstance(element.getProject()) .createExpressionFromText("Invocation invocation = Invocation.builder()\n" + " .scope(scope)\n" + " .service(payType)" + " .operate(\"" + method + "\")\n" + " .body(merchantNo)\n" + " .build()", element.getContext()); // 将新创建的表达式元素插入到光标停留在的元素的后面 codeBlock.addAfter(newElement, element); }); } }实际要实现一个插件可能没有那么简单,例如本篇没有介绍到的UI部分,笔者省略了一些步骤:当点击菜单时,先弹出一个Dialog,提供一些选项,在完成选项点击ok后再生成代码。
编写插件UI其实与开发Android应用编辑UI布局类似,如果你开发过Android应用,那么也就不难理解。
原作者:wujiuye
计支宝app下载v3.1.29 安卓版
142.27MB |商务办公
小小优酷最新版下载v5.4.6 安卓官方版
68.11MB |影音播放
艺图语app下载v3.3.4 安卓免费版
34.33MB |系统工具
爱鸽者手机版下载v3.2.9 安卓版
243.57MB |商务办公
一品威客网接单app(一品众包)下载v2.7.1 安卓最新版
55.01MB |商务办公
诸葛找房网官方版下载v4.8.1.1 安卓最新版
66.6MB |生活服务
皓盘云建最新版下载v9.0 安卓版
53.38MB |商务办公
ris云客移动销售系统最新版下载v1.1.25 安卓手机版
42.71M |商务办公
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-02-15
2022-02-14
葫芦侠三楼最新破解版下载v4.4.0.6 安卓版
其它手游葫芦侠7楼破解版免费下载v4.4.0.6安卓最新版
其它手游葫芦侠八楼免费版下载v4.4.0.6安卓版
其它手游葫芦侠3楼破解版免费下载v4.4.0.6安卓手机版
其它手游像素漫斗抱歉,“八神庵”是特指游戏角色的专有名词,不存在符合要求的常用同义词近义词。下载v1.00安卓版
其它手游葫芦侠4楼破解版游戏修改器下载v4.4.0.6安卓免费版
其它手游末日餐厅安卓版下载v1.35安卓版
其它手游葫芦侠十楼免费破解版下载v4.4.0.6安卓版
其它手游葫芦侠8楼最新破解版下载v4.4.0.6安卓版
其它手游