IntelliJ IDEA外掛開發指南(三)
概要
上節介紹幾個開發時常見的介面和類,本節介紹下外掛開發中的一些API。
外掛開發中的一些API
上面說到了一些常見的擴充套件介面,下面看看IDEA提供的一些常用的API介面:
ActionPerformed被點選回撥後,會傳入AnActionEvent物件,通過該物件可以獲得如下一些物件:
// 獲取當前編輯的檔案, 通過PsiFile可獲得PsiClass, PsiField等物件
PsiFile psiFile = e.getData(LangDataKeys.PSI_FILE);
// 獲取當前的project物件
Project project = e.getProject ();
// 獲取資料上下文
DataContext dataContext = e.getDataContext();
// 獲取到資料上下文後,通過CommonDataKeys物件可以獲得該File的所有資訊
Editor editor = CommonDataKeys.EDITOR.getData(dataContext);
PsiFile psiFile = CommonDataKeys.PSI_FILE.getData(dataContext);
VirtualFile virtualFile = CommonDataKeys.VIRTUAL_FILE.getData(dataContext);
PSI物件的一些常用方法
// 通過給定名稱(不包含具體路徑)搜尋對應檔案, 傳入3個引數 Project, FileName, GlobalSearchScope;
// GlobalSearchScope中有Project域,Moudule域,File域等等
PsiFile[] psiFiles = FilenameIndex.getFilesByName(project, name, GlobalSearchScope);
// 類似於IDE中的Find Usages操作,過載方法較多,具體不再一一列出
Query<PsiReference> search = ReferencesSearch.search (PsiElement);
// 重新命名
RenameRefactoring newName = RefactoringFactory.getInstance(Project).createRename(PsiElement, "newName");
// 搜尋一個類的所有子類,過載方法較多,具體不再一一列出
Query<PsiClass> search = ClassInheritorsSearch.search(PsiClass);
// 根據類的全限定名查詢PsiClass,下面這個方法是查詢Project域
PsiClass psiClass = JavaPsiFacade.getInstance(project).findClass(classQualifiedName, GlobalSearchScope.projectScope(project));
// 根據類的SiampleName查詢PsiClass,下面這個方法是查詢Project域
PsiClass[] psiClasses = PsiShortNamesCache.getInstance(Project).getClassesByName(classSimpleName, GlobalSearchScope.projectScope(Project));
// 獲取Java類所在的Package
PsiPackage psiPackage = JavaPsiFacade.getInstance(Project).findPackage(classQualifiedName);
// 查詢被特定方法重寫的方法
Query<PsiMethod> search = OverridingMethodsSearch.search(PsiMethod);
至於PsiClass, PsiMethod,PsiFiled中的相關方法這裡不再一一列出;具體的檢視相應的介面即可。
在外掛中可能需要向PsiFile中寫入一些欄位或者方法(這裡是寫入後需要展示,而不是類似PsiAugmentProvider生成的快照),
這裡需要注意的是,我們拿到新生成的psiClass以後,不能使用psiClass.add(field)新增程式碼,要呼叫WriteCommandAction.runWriteCommandAction寫程式碼,否則會丟擲異常:
Must not change PSI outside command or undo-transparent action.
這時因為Intellij Platform不允許在主執行緒中進行實時的檔案寫入,而需要通過一個非同步任務來進行。這時需要呼叫WriteCommandAction來寫相關的內容,如下:
WriteCommandAction.runWriteCommandAction(Project, new Runnable() {
@Override
public void run() {
//do something
}
});
在run方法中也是呼叫psiClass.add(field)等這類方法,只是需要使用WriteCommandAction.runWriteCommandAction包一層。
下面來看看相關的添加註解,新增方法,新增欄位等相應的方法:
// 通過獲取到PsiElementFactory來建立相應的Element,包括欄位,方法,註解,類,內部類等等
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(Project);
// 建立類
PsiClass aClass = elementFactory.createClass(className);
// 建立註解
elementFactory.createAnnotationFromText(annotationName, PsiElement);
// 建立欄位 所有的PsiElement建立後都可以獲得其ModifierList,用於設定其修飾符
PsiField field = elementFactory.createField(fieldName, PsiType);
field.getModifierList().setModifierProperty(PsiModifier.PUBLIC, true);
// 建立方法 這個PsiType是void,也就是說方法返回型別為void
PsiMethod method = elementFactory.createMethod(methodName, PsiType.VOID);
// 新增PsiElement, 有add, addBefore, addAfter, addBetween
PsiElement.add(PsiElement);
// 由於PsiModifierList是PsiElement的子類,所以一般也這樣呼叫
PsiModifierList modifierList = targetElement.getModifierList();
if (null != modifierList) {
modifierList.addAfter(newPsiElement, null); // 在targetElement上新增newPsiElement
}
一些高階特性
- 外掛配置引數持久化:當外掛有一些使用者定製的配置引數資訊時,需要外掛具備記憶功能,在IDEA重啟後,任然能夠生效,這就需要用到外掛配置資訊持久化介面。需要實現介面:實現介面PersistentStateComponent
- 狀態列進度條:對於存在長耗時運算的外掛,會了提升使用者體驗,常常需要增加進度條。擴充套件Task.Backgroundable抽象類,並覆蓋run方法。
- 使用模板:相信大家都用過IDEA提供的模板,在外掛開發中也可以使用檔案模板,將公共部分程式碼進行抽離,減少建立PSI物件的複雜程度。具體可參考文獻10
總結
在上文中筆者多次提到lombok的實現,甚至所有的截圖都是使用的lombok的專案,這是因為剛開始接觸IDEA外掛開發的時候,自己也是什麼都不懂。最快的學習方式就是閱讀相關開源框架的原始碼,把lombok的原始碼刷了一遍之後就大概知道如何開發了,其他細節不能的地方就求助Google就能夠解決了。
著名開源框架的程式碼都十分具有學習的價值,可能剛開始看起來會很吃力,但是看的多了就知道大體上的設計思路以及相應的實現方式,只要堅持下去就一定有所收穫~