AS/IntelliJ插件开发实践

目标

输入

  1. url地址, 可能包含一级域名和二级域名
  2. get/post参数, 键值对形式, 可以动态增减
  3. 接口返回的json结构

输出

  1. 继承特定类的请求类的java文件
  2. 根据url实现相应的方法
  3. 根据get/post参数创建内部类, 并且实现toString方法, 并且创建对应的构建函数
  4. 根据json结构创建实体类, 并实现toString方法

实现

涉及的行为

  1. 从界面获取相关值
  2. 使用列表表示参数, 所以需要动态增加/删除控件
  3. 获取目录路径, 并创建java文件
  4. 往java文件写入类定义, 变量定义和方法定义
  5. 解析json

实现界面

一个界面一般包含两个文件, form fileUI class.

form file

类似Android里面的xml布局文件, 用来描述界面的布局. 使用GUI Designer(类似Android Studio的布局预览器)通过拖动控件来创建静态布局.
控件分为componentcontainer,关系和Android中的ViewViewGroup差不多, 其中container会要求指定部分仅对container生效的属性.

一般container使用默认的GridLayoutManager (IntelliJ)就可以满足要求了.

大小
  1. Horizontal/Vertical Size Policy: 决定了当该component所属的container大小发生变化时component的尺寸变化规则. 该属性只有GridLayoutManager具有, 其中可以赋值下面三个值
    a. canShrink: 实际值可以比指定值(preferred size)小.
    b. canGrow: 实际值可以比指定值大.
    c. wantGrow: 实际值会比指定值大. 优先级高于canGrow, 类似match_parent, 会填充container
  2. Preferred Size: 二维数组指定宽高, 对于GridLayoutManager可以单独指定宽或者高, -1表示该值由动态计算得出.
位置

不同的LayoutManager有不同的位置属性.
对于GridLayoutManager来说, 整个布局就是一个网格, 通过网格控制component的位置, 跟Excel有点类似.

对于对齐, 是通过HSpacer/VSpacer来占据空间来对齐的.

使用GUI Designer进行布局的时候, 如果是GridLayoutManager, 插入时会有提示插入的行号和列号, 注意网格的嵌套关系就能轻松把控件放在目标位置了, 另外通过拉伸控件可以合并网格.

UI class

布局实际使用的是Swing, 因此在UI class中涉及控件的操作都可以查阅
Swing的官方教程.

关于动态增删控件, 在Swing中, 在JLIst中的按钮是不会响应点击功能的, 不熟悉Swing浪费了很多时间…

动态增加控件

因此不能用JLIst, JTable过于复杂, 因此直接往JPanel中添加控件然后重绘来实现动态增加控件的效果.

具体的代码参考官方教程, 只记录遇到的几个坑:
注意: 下面的说法没有深究, 有可能是错误的.

布局

使用GridLayoutManager不能动态增加网格, 在这里要实现类似列表的效果, 对吼使用的是BoxLayout.

对齐

使用BoxLayout后, 增加控件会自动居中, 需要通过Box.createVerticalGlue()来创建占位控件来把增加的控件顶至顶部实现顶部对齐的效果.

重绘

增加或者删除控件之后, 需要调用revalidate()repaint()方法来重绘控件才能正常显示.

获取输入值

通过控件获取输入值非常简单, 类似Android, 控件具有各种getXXXX方法来获取输入值.

获取目录, 创建文件

PSI

插件开发中, 使用PSI Files来表示具体操作的文件.
PSI(Program Structure Interface) file是一个接口, 使用树状结构表示文件中的内容.

对于特定的文件一般有相应的子类, 例如PsiJavaFile表示一个Java文件.

如何获取PSI
  1. 在Action中通过AnActionEvent#getData(LangDataKeys.PSI_FILE)
  2. 在VirtualFile中通过PsiManager.getInstance(project).findFile()
  3. 在Document中通过PsiDocumentManager.getInstance(project).getPsiFile()
  4. 通过psiElement.getContainingFile()从element实例获取
  5. 在project中的任意位置通过文件名获取FilenameIndex.getFilesByName(project, name, scope)

这个插件的入口是Action, 所以可以通过Action就能获取到当前用户所在的Java文件的PSI实例.
PSI的操作可以参考PSI Cookbook.

获取目录

当已经有PsiFile实例时, 可以通过获取PsiFile#getParent()获取到目录实例PsiDirectory.

创建类文件

对于Java来说, 可以通过JavaDirectoryService#createClass(dir, fileName)来创建类文件, 同时会返回一个PsiClass实例表示该类.

修改类文件

对于Java, 修改类文件首先要获取想要修改的对象对应的PsiElement子类实例(下面称PsiElement元素), 例如类定义对应PsiClass实例, 方法定义对应PsiMethod等等.

在Java文件中所有东西都是用元素表示的. 整个Java文件是一棵元素树, 根节点是一个PsiClass.

通过PsiElement#add()可以给这个元素添加其他元素.
删除对象包含的元素则需要调用要删除的元素PsiElement#delete()方法.

大部分PsiElement都可以通过PsiElementFactory中的相关方法获取.

修改修饰符

只要部分元素有修饰符, 这部分元素都会继承PsiModifierListOwner.

通过PsiModifierListOwner#getModifierList()可以获取修饰符列表PsiModifierList实例.
注意修饰符列表本身也是一个元素.
通过PsiElement#add()方法就可以增加修饰符.
通过遍历PsiModifierList中包含的元素, 然后调用包含的元素的PsiElement#delete()删除修饰符.

修改继承关系

只有类元素有继承关系.

与修饰符类似, 通过PsiClass#getExtendsList()来获取继承关系列表, 然后通过add操作来增加继承关系.
删除继承关系需要PsiClass#getExtendsList().getReferenceElements()来获取元素, 然后删除这些元素.

写入变量

变量元素对应的实例为PsiField, 可以通过PsiElementFactory#createField来创建变量, 创建变量时需要通过PsiType指定变量的类型, 同样可以通过PsiElementFactory#create

写入方法

方法元素对应的实例为PsiMethod.
构造函数属于方法元素, 通过PsiElementFactory#createConstructor()来创建.

方法变量

只有方法元素有方法变量.

可以通过PsiMethod#getParameterList()获取方法变量列表, 然后修改.
方法变量也是一个元素, 实例是PsiParameter, 同样可以通过工厂方法创建, 创建时需要指定变量类型.

方法体

方法体也是一个元素, 实例是PsiCodeBlock
通过PsiMethod#getBody()来获取方法体元素, 然后通过增加statement和expression来写方法体内容.

理所当然, statement和expression也是元素, 可以通过工厂方法创建.

解析json

解析json需要引入json库, 右键项目有Open Module Settings选项, 打开面板有Dependencies面板, 可以添加依赖.

后续就是简单的创建类和变量了. 不再累述.

总结

  1. 关键是取得PsiFile类实例, 一切文件操作都是以此为根据.
  2. 理解PsiElement元素, 整个文件就是一棵元素树. 类是元素, 方法是元素, 方法体是元素, 语句是元素, 关键字也是元素.
  3. PsiElementFactory是个好东西, 能够很方便地创建元素, 其中的createXXXFromText更是个黑科技.
  4. 对于Java来说, JavaPsiFacade也是个好东西. 能够通过名称获取PsiClass
  5. 读写文件需要使用WriteCommandAction.runWriteCommandAction()来启用工作线程.
  6. 官方文档很水, 论坛还凑合.
  7. 方法的使用可以看源码的注释. 例如PsiElementFactory的源码.
    原文作者:AssIstne
    原文地址: https://www.jianshu.com/p/02a45f636043
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞