Intellij IDEA插件开发(三)操作PSI对象

PSI简介

PSI(Program Structure Interface)是Intellij Platform中一个非常重要的概念,在IDE所管理的Project中,每个目录,Package,源代码和资源文件都会被抽象成相应的PSI对象。本文将以PsiDirectoryPsiJavaFileXmlFile为例介绍插件对文件目录、Java类和DOM对象的操作。

一些有用的方法

通用方法
  • FilenameIndex.getFilesByName()通过给定名称(不包含具体路径)搜索对应文件
  • ReferencesSearch.search()类似于IDE中的Find Usages操作
  • RefactoringFactory.createRename()重命名
  • FileContentUtil.reparseFiles()通过VirtualFile重建PSI
Java专用方法
  • ClassInheritorsSearch.search()搜索一个类的所有子类
  • JavaPsiFacade.findClass()通过类名查找类
  • PsiShortNamesCache.getInstance().getClassesByName()通过一个短名称(例如LogUtil)查找类
  • PsiClass.getSuperClass()查找一个类的直接父类
  • JavaPsiFacade.getInstance().findPackage()获取Java类所在的Package
  • OverridingMethodsSearch.search()查找被特定方法重写的方法

实例1

下面通过一个实例,使用PSI对象创建一个目录,并在目录中创建一个Java类。

创建目录
  1. 获取Project根目录
PsiDirectory baseDir = PsiDirectoryFactory.getInstance(project).createDirectory(project.getBaseDir());
  1. 递归查找要创建的目录
PsiDirectory subDir = baseDir.findSubdirectory(moduleName);
......
  1. 判断要创建的目录是否已存在
boolean isExist = subDir == null;
  1. 如不存在,创建新目录
if(!isExist) {
subDir = moduleDir.createSubdirectory(moduleName); 
}
创建Java类
  1. 创建PsiClass
PsiClass clazz = JavaDirectoryService.getInstance().createClass(subDir, className)
  1. 添加package字段
((PsiJavaFile) clazz.getContainingFile()).setPackageName(pkgName);

package字段可通过文件路径解析或使用JavaPsiFacade获得。此处添加字段的操作必须在WriteCommandAction中异步进行。

实例2

接下来的实例演示如何操作DOM对象

操作从外部读入的DOM
  1. 获取XmlFile实例
    此处通过FileChooser引导用户从文件系统中选取一个xml文件
FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, false, false, false, false);
VirtualFile virtualFile = FileChooser.chooseFile(descriptor, project, null);
if (virtualFile != null) {
if (!virtualFile.isDirectory() && virtualFile.getName().endsWith("xml")) {
xmlFile = (XmlFile) PsiManager.getInstance(project).findFile(virtualFile);
}
}
  1. 遍历所有深度为1的Tag
XmlDocument document = xml.getDocument();
if (document != null) {
XmlTag rootTag = document.getRootTag();
if (rootTag != null) {
XmlTag[] subTags = rootTag.getSubTags();
for (XmlTag tag : subTags) {
//do something you want
}
}
}
生成XmlFile并写入文件
  1. 创建XmlFile
XmlFile xmlFile = (XmlFile) PsiFileFactory.getInstance(project).createFileFromText(xmlName, StdFileTypes.XML);
  1. 向XmlFile写入属性
XmlDocument document = xmlFile.getDocument();
if (document != null && document.getRootTag() != null) {
XmlTag rootTag = document.getRootTag();
rootTag.getAttribute(attrName).setValue(attrValue);//set value for exists attr.      
rootTag.setAttribute(name,value);//add a new attr and setting value
}
  1. 写入文件
    写入操作同样需要在WriteCommandAction中异步进行。先判断目标文件是否已存在,如果存在则执行删除
PsiFile psiFile = directory.findFile(xmlName);
if (psiFile != null) {
psiFile.delete();
}directory.add(file);

PSI对象操作就介绍到这里,下篇将介绍PSI的进阶用法

  1. 官方文档根本没有像Android开发者官网那样详细的资料,甚至连java doc都没找到。请教一下到底应该在哪里找资料呢

    • 说一下我是怎么找的吧,一部分来源是官方文档,另一部分是官方的论坛搜索,github和google也搜索过一些,反编译过一些已有的插件,不过总归还是很零碎,这也是写这一系列文章的原因,分享的同时也给自己做个备忘