查找类
获取当前项目
@Nullable
public static Project findCurrentProject() {
IdeFrame frame = IdeFocusManager.getGlobalInstance().getLastFocusedFrame();
Project project = (frame != null) ? frame.getProject() : null;
if (isValidProject(project))
return project;
for (Project p : ProjectManager.getInstance().getOpenProjects()) {
if (isValidProject(p))
return p;
}
return null;
}
@Nonnull
public static Iterable<Project> findValidProjects() {
return (Iterable<Project>)Arrays.<Project>stream(ProjectManager.getInstance().getOpenProjects())
.filter(ApplicationUtil::isValidProject)
.collect(Collectors.toList());
}
private static boolean isValidProject(@Nullable Project project) {
return (project != null && !project.isDisposed() && !project.isDefault());
}
获取当前选中的Editor
public static Editor getSelectedEditor(Project project) {
FileEditorManager editorManager = FileEditorManager.getInstance(project);
if (editorManager == null) {
return null;
}
if (editorManager instanceof FileEditorManagerImpl) {
return ((FileEditorManagerImpl)editorManager).getSelectedTextEditor(true);
}
FileEditor current = editorManager.getSelectedEditor();
if (current instanceof TextEditor) {
return ((TextEditor)current).getEditor();
}
return null;
}
获取Editor对应的PsiFile
public static PsiFile getPsiFile(@NotNull Editor editor) {
return PsiDocumentManager.getInstance(Objects.requireNonNull(editor.getProject()))
.getPsiFile(editor.getDocument());
}
Module与源文件夹
// 获取所有Module
Module[] modules = ModuleManager.getInstance(project).getModules();
// 查找某个psiFile所属的Module
Module module = ModuleUtil.findModuleForFile(psiFile);
// 查找所有的源文件夹
VirtualFile[] virtualFiles = ProjectRootManager.getInstance(project).getContentSourceRoots();
// 查找某个文件所在的源文件夹
VirtualFile sourceDirectory = ProjectRootManager.getInstance(project).getFileIndex().getSourceRootForFile(virtualFile);
PSI
PsiClass psiClass = PsiTreeUtil.getChildOfType(psiFile, PsiClass.class);
String packageName = ((PsiJavaFile) psiFile).getPackageName();
PsiMethod psiMethod = PsiTreeUtil.findElementOfClassAtOffset(psiFile, offset, PsiMethod.class, false);
格式化代码
文件类型,可以从editor获取:psiFile.getLanguage().getID()
public static String formatContent(Project project, FileType fileType, String content, int start, int end) {
String fileName = "fakeFile." + fileType.getDefaultExtension();
PsiFile psiFile = PsiFileFactory.getInstance(project).createFileFromText(fileName, fileType, content);
CodeStyleManager.getInstance(project).reformatText(psiFile, start, end);
return psiFile.getText();
}
打开diff窗口
public static void showDiff(Editor editor, String content) {
DiffManager diffManager = DiffManager.getInstance();
// 创建左侧编辑器的DiffContent
VirtualFile file = EditorUtil.getPsiFile(Objects.requireNonNull(editor)).getVirtualFile();
DiffContent leftContent = DiffContentFactory.getInstance().create(editor.getProject(), file);
// 创建右侧提供的文本的DiffContent (fileType可以根据editor中文件类型动态获取)
FileType javaFileType = FileTypeManager.getInstance().getFileTypeByExtension("java");
DiffContent rightContent = DiffContentFactory.getInstance().create(editor.getProject(), content, javaFileType);
// 打开diff窗口
SimpleDiffRequest diffRequest = new SimpleDiffRequest(file.getName(), leftContent, rightContent
, "Your version", "Recommend version");
diffManager.showDiff(editor.getProject(), diffRequest);
}
读写锁
与代码相关的数据结构(PSI, VFS, Project root model)打交道时,必须使用读写锁,如果在UI线程中进行读操作可以不用读锁,写操作必须在UI线程,且需要在write action(WA)中操作。
ApplicationManager.getApplication().runReadAction(() -> {
// do read action
});
WriteCommandAction.runWriteCommandAction(project, () -> {
// do write action
});
获取两个字符串的diff
String str1 = "log.info(\"\")";
String str2 = "log.info(\"hello: {}\", name);";
int[] str1Chars = str1.chars().toArray();
int[] str2Chars = str2.chars().toArray();
List<Pair<Integer, String>> diffResult = new ArrayList<>();
Diff.Change changes = Diff.buildChanges(str1Chars, str2Chars);
for (Diff.Change change : changes.toList()) {
if (change.inserted > 0) {
Pair<Integer, String> diffPair = Pair.create(change.line0
, new String(str2Chars, change.line1, change.inserted));
diffResult.add(diffPair);
}
}
后台线程与UI线程切换
// 切UI线程
ApplicationManager.getApplication().invokeLater(() -> {});
ApplicationManager.getApplication().invokeAndWait(() -> {});
// 切后台线程
ApplicationManager.getApplication().executeOnPooledThread(() -> {});
获取插件版本
pluginId从plugin.xml中id属性获取
String pluginId = "从plugin.xml中id属性获取";
public static String getVersion() {
IdeaPluginDescriptor pluginDescriptor = PluginManagerCore.getPlugin(pluginId);
if (pluginDescriptor == null) {
return null;
}
return pluginDescriptor.getVersion();
}
当光标在方法内时,查找离它最近的一个类
PsiMethod psiMethod = PsiTreeUtil.findElementOfClassAtOffset(psiFile, offset, PsiMethod.class, false);
if (psiMethod != null && psiMethod.getBody() != null && psiMethod.getBody().getTextOffset() < offset) {
PsiElement psiElement = psiFile.findElementAt(offset);
// 光标最近语句引用的类
PsiElement relativeElement = PsiUtil.getPrevSiblingOfType(psiElement, PsiExpressionStatement.class
, PsiDeclarationStatement.class, PsiReferenceExpression.class);
PsiClass relativePsiClass = Optional.ofNullable(relativeElement)
.map(e -> {
if (e instanceof PsiReferenceExpression) {
return (PsiReferenceExpression) e;
}
return PsiTreeUtil.findChildrenOfType(e, PsiReferenceExpression.class).stream()
.reduce((first, second) -> second).orElse(null);
})
.map(e ->{
PsiType psiType = psiExpression.getType();
if (psiType != null) {
return PsiUtil.resolveClassInType(psiType);
}
// 为null时说明这个语句没写完,只写了一个类定义,这时候需要调用advancedResolve才能获取到其对应的类型
return Optional.of(psiExpression).map(e -> e.advancedResolve(true))
.filter(r -> r instanceof ClassCandidateInfo).map(r -> ((ClassCandidateInfo) r).getElement())
.orElse(null);
})
.orElse(null);
}