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的源码.
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容