PSI 系统
PSI
PSI(Program Structure Interface)是一个接口,项目中的一切都可以表示为 PSI 对象,例如文件可以表示为 PsiFile,类可以表示为 PsiClass 等
PSI Element
PSI Element 是 PSI 系统下的基类,PsiFile、PsiClass 等等都是一个个具体的 PsiElement 实现,插件开发的主要工作就是针对不同的 PsiElement 进行分析和处理
常用对象
PsiFile
应用中的各种文件,例如 .java、.xml 文件等
-
通过 Action 获取:
event.getData(LangDataKeys.PSI_FILE);
-
通过 VirtualFile 获取:
PsiManager.getInstance(project).findFile();
-
通过 Document 获取:
PsiDocumentManager.getInstance(project).getPsiFile();
-
通过 Element 获取:
psiElement.getContainingFile();
-
通过文件名获取:
FilenameIndex.getFilesByName(project, name, scope);
VirtualFile 与 PsiFile 的区别:
-
VirtualFile 类似于 Java 里的 File,可以做一些删减、重命名等操作
-
PsiFile 是被 PSI 封装过的,和 PSI 下的其他 Element 元素相通
PsiDirectory
应用中的目录
- 通过 PsiFile:
psiFile.getContainingDirectory();
PsiJavaFile
Java源文件,如 Test.java
- 通过 PsiFile 强转:
(PsiJavaFile) psiFile;
PsiClass
某个类,包括文件里的内部类
- 通过 PsiJavaFile 获取:
javaFile.getClasses();
PsiMethod
类中的某个方法
- 通过 PsiClass 获取:
psiClass.getMethods();
PsiField
类中的某个属性
- 通过 PsiClass 获取:
psiClass.getFields();
PsiParameterList
方法中的参数
- 通过 PsiMethod 获取:
psiMethod.getParameterList();
PsiAnnotation
各种地方的注解
- 通过能被注解标记的元素:
jvmAnnotatedElement.getAnnotations();
Document
以纯文本格式访问或者修改文件内容
- 通过 Action 获取:
event.getData(PlatformDateKeys.EDITOR).getDocument();
- 通过 PsiFile 获取:
PsiDocumentManager.getInstance().getDocument();
常用方法
创建 ElementFactory
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
编辑 PsiClass
Intellij Platform 不允许在主线程中进行实时的文件写入,而需要通过一个异步任务来进行
WriteCommandAction.runWriteCommandAction(project, () -> {
//do sth.
});
格式化代码
CodeStyleManager.getInstance(project).reformat(element);
导入类
JavaCodeStyleManager manager = JavaCodeStyleManager.getInstance(project);
manager.optimizeImports(file);
manager.shortenClassReferences(targetClass);
Find Usages
Query<PsiReference> query = ReferencesSearch.search(element);
搜索一个类的子类
Query<PsiClass> search = ClassInheritorsSearch.search(psiClass);
查询类所在的包
PsiPackage psiPackage = JavaPsiFacade.getInstance(project).findPackage(classQualifiedName);
重命名
RefactoringFactory.getInstance(Project).createRename(element, "NewName");
查找被重写的方法
Query<PsiMethod> query = OverridingMethodsSearch.search(psiMethod);
获取 Editor
DataContext context = event.getDataContext();
Editor editor = CommonDataKeys.EDITOR.getData(context);
获取 Editor 选中的文本
SelectionModel selectionModel = editor.getSelectionModel();
int start = selectionModel.getSelectionStart();
int end = selectionModel.getSelectionEnd();
TextRange range = new TextRange(start, end);
String selectTxt = document.getText(range);
Editor打开指定名称的文件
new OpenFileDescriptor(project, psiFile.getVirtualFile()).navigate(true);
// 光标移动到指定位置
int offset = psiFile.getText().indexOf("setContentView");
new OpenFileDescriptor(project, psiFile.getVirtualFile(), offset).navigate(true);
根据类全名查询 PsiClass
PsiClass psiClass = JavaPsiFacade.getInstance(project).findClass(classQualifiedName, GlobalSearchScope.projectScope(project));
根据类 SiampleName 查询 PsiClass
PsiClass[] psiClasses = PsiShortNamesCache.getInstance(project).getClassesByName(classSimpleName, GlobalSearchScope.projectScope(project));
获取鼠标选中的目录
IdeView view = anActionEvent.getRequiredData(LangDataKeys.IDE_VIEW);
PsiDirectory directory = view.getOrChooseDirectory();
插件配置参数持久化
当插件有一些用户定制的配置参数信息时,需要插件具备记忆功能,在IDEA重启后,仍然能够生效,这就需要用到插件配置信息实现接口 PersistentStateComponent
状态栏进度条
如果有耗时任务,需要增加进度条,实现 Task.Backgroundable 抽象类,重写 run 方法
使用模板
插件开发中也可以使用文件模板,将公共部分代码进行抽离,减少创建PSI对象的复杂程度
鸣谢: