Intellij IDEA插件开发(四)PSI进阶

本文是Intellij IDEA插件开发系列的最后一篇文章,将介绍PSI对象操作的进阶用法

使用模板

模板可以大大节省我们编写代码的工作量,在IDEA中,可以通过Preferences -> Editor -> File and Code Templates来查看和编辑已有的模板,或是添加新的模板。在模板中可以使用IDE提供的一些宏变量,常用的有NAMEPACKAGE_NAMEUSERDATE等,在新建模板时也可以使用自定义宏变量,自定义的宏变量将在创建文件时弹出一个输入框让用户对其赋值。

同样的,在插件开发中也可以使用文件模板,将公共部分代码进行抽离,减少创建PSI对象的复杂程度,下面将介绍模板的使用方法。

创建模板

在插件中模板文件必须存放于源文件目录的fileTemplates/internal目录内模板文件必须以${模板唯一名称}.${对应代码文件的扩展名}.ft命名,例如我们要创建一个java类的模板,命名可能为myTemplate.java.ft,模板文件内也可以使用宏变量,下面我们将创建一个java类通用的模板,包含package字段和类注释。

#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
/**
* Author: ${USER}
* Created on ${DATE}
*/
public class ${NAME} {        
}
注册模板

在使用模板前必须先向插件系统注册改模板,编辑plugin.xml文件,在extensions节点加入模板注册信息。

<extensions defaultExtensionNs="com.intellij">
<!-- Add your extensions here -->
<internalFileTemplate name="myTemplates"/>
</extensions>

此处的模板名称必须是在IDE内唯一的名称,否则调用模板时会发生冲突。

使用模板

在创建JavaClass时可以调用该模板

PsiClass clazz = JavaDirectoryService.getInstance().createClass(directory, className, templateName);

以上就是模板使用的相关方法,so easy~

为PsiClass添加类导入、接口实现、成员变量和方法

生成PsiClass后我们可以为其添加一系列Java类的内容,下面介绍各种内容的添加方式,注意,所有的添加操作都必须异步进行。

添加类导入

要为PsiClass导入一个类,那我们必须搜索到这个类在哪,怎么搜索呢,分为两部曲
1. 创建SearchScope
SearchScope是个什么玩意呢,望文生义,它就是用来描述搜索范围的,在这里我们把搜索范围定义为整个Project,IDE会搜索Project内所有的类(包括引用的类库)来寻找目标。

GlobalSearchScope searchScope = GlobalSearchScope.allScope(project);
  1. 搜它丫的
    创建好SearchScope,使用它进行搜索
PsiClass[] psiClasses = PsiShortNamesCache.getInstance(project).getClassesByName(className, searchScope);

搜索完毕之后会返回一个PsiClass数组,包含所有类名匹配的PsiClass,接下来可以过滤出目标类并生成import,同样是两步
3. 生成PsiImportStatement

PsiImportStatement importStatement = psiElementFactory.createImportStatement(psiClass);
  1. 添加到PsiClass
((PsiJavaFile) clazz.getContainingFile()).getImportList().add(importStatement)

齐活儿~

添加接口实现

添加接口实现同样需要先搜索接口类,搜索的套路还是和上面类导入的套路一样一样的,我们直接从生成接口字段PsiJavaCodeReferenceElement开始
1. 生成PsiJavaCodeReferenceElement

PsiJavaCodeReferenceElement ref = psiElementFactory.createClassReferenceElement(psiClasses);
  1. 添加到PsiClass
clazz.getImplementsList().add(ref);

需要一提的是添加接口实现声明之后接口类会被自动导入,是不是很Nice~

添加成员变量

有两个方式为PsiClass添加成员变量
1. 从字符串生成

PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
PsiField field = factory.createFieldFromText("public int a = 0;", psiClass);

使用此种方式生成成员必须在字符串中显示指定成员的类型和修饰符,并且需要自行保证无语法错误。
2. 通过Factory生成

PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
PsiField field = factory.createField("a",PsiType.INT);
field.getModifierList().setModifierProperty(PsiModifier.PUBLIC,true);

明显通过此种方式生成的Field更能保证代码质量,也更符合我们对代码进行面向对象操作的理念。

添加方法

Method同样有通过Text和通过Factory生成两种方式。
1. 通过字符串生成

PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
PsiMethod method = factory.createMethodFromText("public String toString(){}",psiClass);
  1. 通过Factory生成
PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
PsiMethod method = factory.createField("getCount",PsiType.INT);
  1. 为Method添加注解
method.getModifierList().addAnnotation("Override")

同样的,可以为PsiMethod添加修饰符和方法体,添加方式和上文添加成员变量相同。

格式化代码
CodeStyleManager.getInstance(project).reformat(psiClass);
在编辑器中打开
FileEditorManager.getInstance(project).openTextEditor(new OpenFileDescriptor(project, virtualFile), true);

注册自定义类型文件

在编写处理自定义语言或IDE未支持的文件类型时,需要自行注册文件类型到IDE,此处通过一个简单的例子来阐述。
SVG是基于XML,用于描述二维矢量图形的一种图形格式,在IDE中我们可以使用解析标准DOM的方式对其进行解析,通过两个简单步骤即可实现:
1. 添加自定义FileTypeFactory
此处需要新建一个继承了FileTypeFactory的类

public class SVGFileTypeFactory extends FileTypeFactory {
@Override
public void createFileTypes(FileTypeConsumer fileTypeConsumer) {
fileTypeConsumer.consume(XmlFileType.INSTANCE,"svg");
}
}

2.注册FileTypeFactory
编辑plugin.xml文件,在extensions节点加入

<extensions defaultExtensionNs="com.intellij">
<fileTypeFactory implementation="com.guide.plugin.SVGFileTypeFactory"/>
</extensions>

完成以上两个步骤之后就能在IDE中使用XmlFile来描述SVG文件。


Intellij IDEA 插件开发系列到这里就结束了,虽然这样属于黑科技的文章想必也不会有啥人看。

  1. 为何我的插件在AS jre 版本1.8以上才可以运行,找不到为何啊!插件的文档少之又少!😢

    • 如果开发的插件需要在AS上运行,那么需要设置插件的SDK为AS,这样插件可以保证在IDEA上也运行正确,反过来无法保证。一般开发时会把Language Level设置为Java 6来保证低版本jre的兼容性

  2. EmptyThrowable: Null child action in group New () of class class com.intellij.openapi.actionSystem.DefaultActionGroup

    • 先build,得到一个jar,把jar包给别人。然后别人选择 File -> setting -> plugin -> install plugin from disk。这个可以本地安装,不需要上传官方插件库。

    • 首先需要build出jar或zip文件,然后有几个方式进行分发:1、直接本地导入jar或zip安装2、上传到官方插件仓库3、上传到自建插件仓库,在IDEA内设置自定义仓库地址后可进行检索安装