我很喜欢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有所了解

自动生成Java源代码

我们模仿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文件添加如下配置信息:

text="GeneratedInvokePayMethod">

效果如下图所示。

现在我们继续完成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 eventPsiElement 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:位置参照的PsiElementcodeBlock.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

原文链接:编写一个IDEA插件之:自动生成Java代码

原出处:Java艺术

侵删

java插件开发_编写一个IDEA插件之:自动生成Java代码相关推荐

  1. java 级数_编写一个Java程序实现级数运算。

    编写一个Java程序实现级数运算.其中:定义级数求和类,以及级数求积(阶乘)类,在项目主类中声明一个级数求和对象和求阶乘对象,由键盘输入级数的n值,并打印出1+2+...+n的和,以及... 编写一个 ...

  2. quicktype游戏java程序_使用QuickType工具从json自动生成类型声明代码

    一.QuickType 工具功能简介 QuickType 是一款可以根据 json 文本生成指定语言(如 Type Script,C++,,Java,C#,Go 等)类型声明代码的工具. 例如我们在写 ...

  3. start uml怎么自动生成代码_通过UML类图,自动生成相关代码

    因为去年帮我的师父开发这个小软件,所以去年整了将近一个多月的时候,把这个基本的工程弄好了.姑且就叫做 版本1.0吧. 其实,功能很好理解,就是当你把UML(这里需要注意的是,只能用在VS,所以 ros ...

  4. mysql 插件开发_编写MySQL Shell插件(REPORT)

    作者:马楚成  (Ivan Ma) 编译:徐轶韬 MySQL Shell is a powerful tool.  It allows Javascript, Python and SQL acces ...

  5. 一套完整的java程序_编写一个完整的Java Application 程序

    展开全部 /* * --测试类 (4)Test类作为主类要完成测试功能 ① 生成Rectangle对象 ② 调用对62616964757a686964616fe4b893e5b19e313333353 ...

  6. java编程用户输入两个数字_编写一个Java应用程序,要求从键盘输入两个整数,计算这两个数据...,编写一个Java应用程序,要求从键盘输入一个数,判断该数是不...

    导航:网站首页 > 编写一个Java应用程序,要求从键盘输入两个整数,计算这两个数据...,编写一个Java应用程序,要求从键盘输入一个数,判断该数是不 编写一个Java应用程序,要求从键盘输入 ...

  7. 编写一个VSCode插件

    原文链接: 编写一个VSCode插件 - 愧怍的小站 自从使用过 VSCode 后就再也离不开 VSCode,其轻量的代码编辑器与诸多插件让多数开发者爱不释手.同样我也不例外,一年前的我甚至还特意买本 ...

  8. 怎么编写一个chrome插件

    怎么编写一个chrome插件 总觉得chrome插件这种东西很高端,很遥不可及.当然复杂的chrome插件开发起来确实比较困难,但是我们可以从一个简单的chrome插件demo慢慢入手,了解一下chr ...

  9. 编写一个IDEA插件之:事件监听

    事件监听,我们最熟悉不过的就是开发APP时,监听按钮点击事件.手指触摸及移动事件.网络状态事件等等.事件监听大多通过观察者模式实现,首先API调用者不需要知道后台是如何检测出网络状态不可用的,而只需要 ...

最新文章

  1. linux 命令行 过滤,利用linux命令行工具进行文本行过滤
  2. python统计并输出文件的行数_python实用统计文件行数 类似wc命令
  3. 微软小动作气炸用户:装Win10系统,现在得被迫注册账号,脱机安装选项被悄悄删除...
  4. May 18:PHP 用到的学习工具
  5. 蓝奏云文件上传php源码_蓝奏云客户端 v0.3.1,第三方蓝奏网盘电脑版
  6. SAP CDS view 单元测试框架 Test Double 介绍
  7. 2018年单招理科考计算机吗,2018高职单招考试难吗 单招考试的内容
  8. 目标检测的图像特征提取之LBP特征
  9. c2c运营流程图_电商运营流程图模板分享,运营的核心都在这里了,快来拿走吧...
  10. 微信 华为盒子 搜索不到服务器,【沙发管家教你一招】用手机微信控制华为荣耀盒子!...
  11. jtopo连线绘制脉冲动画效果
  12. 数据仓库系列之总线架构
  13. Python实现Pearson相关系数计算及公式化简过程
  14. JDK类库创建一张简单图片
  15. 【Python】小甲鱼Python学习总结——代码版
  16. 记账软件如何查看收入支出
  17. Unet语义分割训练和TensorRT部署
  18. java设置excel密码
  19. 今天遭到鄙视了。突然感觉自己昏昏沉沉的过日子 究竟不是个事 自己得努力了!~!
  20. 模数转换,你必须知道的8个经典ADC转换电路方案

热门文章

  1. shell sort result to self
  2. 【[POI2012]PRE-Prefixuffix】
  3. Observer(观察者)模式
  4. VS2013.3 VS2014 任务资源管理器
  5. 在场景中添加光线——在反光表面添加镜面高光
  6. python 秒数转化为时分秒
  7. quick time不可用_教程|用Selenium爬资源:DIY还是花钱?
  8. 二叉树前序、中序、后序遍历(八)
  9. 夜神安卓模拟器安装xposed框架
  10. ffmpeg (二):ffmpeg结合SDL2.0解码视频流