2019獨角獸企業重金招聘Python工程師標準>>>
IntelliJ IDEA 是目前最好用的 JAVA 開發 IDE,它本身的功能已經非常強大了,但是每個人的需求不一樣,有些需求 IDEA 本身無法滿足,于是我們就需要自己開發插件來解決。工欲善其事,必先利其器,想要提高開發效率,我們可以借助 IDEA 提供的插件功能來滿足我們的需求。如果沒有我需要的功能怎么辦?很簡單,我們自己造一個!
插件能做什么?
IDEA 的插件幾乎可以做任何事情,因為它把 IDE 本身的能力都封裝好開放出來了。主要的插件功能包含以下四種:
- 自定義語言支持:如果有 IDEA 暫時不支持的語言,你可以自己寫一個插件來支持,例如 Go 語言原來的支持就是通過插件做的,后來單獨做了一個 Goland。官方有自定義語言插件支持的教程。
- 框架支持:例如Struts 2?的框架支持
- 工具集成:可以給 IDEA 的自帶功能進行增強,例如對 Git 的操作增加 CodeReview 的功能。參考Gerrit
- 用戶界面:自定義的插件改變用戶界面。參考BackgroundImage
我為了減少重復代碼的編寫,寫了一個代碼生成的插件IDEA代碼生成插件CodeMaker,支持自定義代碼生成的模板。
Hello world 插件
依照慣例,我們從 Hello world 開始。
新建一個 Gradle 的插件工程
有些教程推薦用 IDEA 默認的插件工程來開始,但是我比較推薦用 Gradle 來管理整個插件工程,后面的依賴管理會很方便,否則都得靠手動管理。
點擊新建工程,選擇 Gradle
接下來填寫項目屬性
配置 Gradle,用默認配置就行
新建完工程之后,IDEA 會自動開始解析項目依賴,因為它要下載一個幾百兆的 SDK 依賴包,所以會比較久,打開科學上網能快一點。
Gradle 依賴解析完成之后,項目結構如下圖,其中 plugin.xml 是插件的配置,build.gradle 是項目依賴的配置(類比 pom.xml)。
下面就是默認生成的 plugin.xml
<idea-plugin><!--插件id--><id>com.xiaokai.test.demo</id><!--插件名稱--><name>Demo</name><!--開發者信息--><vendor email="support@yourcompany.com" url="http://www.yourcompany.com">YourCompany</vendor><!--插件說明--><description><![CDATA[Enter short description for your plugin here.<br><em>most HTML tags may be used</em>]]></description><!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/plugin_compatibility.htmlon how to target different products --><!-- uncomment to enable plugin in all products<depends>com.intellij.modules.lang</depends>--><!--依賴的其他插件能力--><extensions defaultExtensionNs="com.intellij"><!-- Add your extensions here --></extensions><!--插件動作--><actions><!-- Add your actions here --></actions>
</idea-plugin>
創建一個 Action
Action 是 IDEA 中對事件響應的處理器,它的 actionPerformed 就像是 JS 中的 onClick 方法。可以看出來,插件的開發本質上跟 web、Android 的開發沒有什么不同,因為都是事件驅動的編程。
我們可以直接使用 IDEA 提供的 Action 生成器
點擊 OK 之后會在 src 生成類文件:
package com.xiaokai.test;import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;public class HelloWorldAction extends AnAction {@Overridepublic void actionPerformed(AnActionEvent e) {// TODO: insert action logic here}
}
同時,動作的信息也會注冊到 plugin.xml 中
<!--插件動作--><actions><!-- Add your actions here --><action id="demo.hello.world" class="com.xiaokai.test.HelloWorldAction" text="HelloWorld"description="Say Hello World"><add-to-group group-id="GenerateGroup" anchor="last"/></action></actions>
彈出對話框
創建完 Action 之后我們就要開始往里面寫邏輯了,既然是 Hello World 教學,那我們就來試一下最簡單的彈出對話框。
@Overridepublic void actionPerformed(AnActionEvent e) {//獲取當前在操作的工程上下文Project project = e.getData(PlatformDataKeys.PROJECT);//獲取當前操作的類文件PsiFile psiFile = e.getData(CommonDataKeys.PSI_FILE);//獲取當前類文件的路徑String classPath = psiFile.getVirtualFile().getPath();String title = "Hello World!";//顯示對話框Messages.showMessageDialog(project, classPath, title, Messages.getInformationIcon());}
代碼寫完之后,打開 Gradle 的界面,點擊 runIde 就會啟動一個安裝了插件的 IDEA,然后就可以進行測試。你還可以右鍵啟動 Debug 模式,這樣還能進行斷點。
運行的效果如下圖:
可以看到,我們右鍵打開 Generate 菜單之后,里面最后一項就是我們添加的 Action,
進階的教程
如果想學習更多的原理和設計理念可以看IntelliJ Platform SDK的官方文檔。不過老實說,它的文檔寫的挺差的,基本上就是簡單講了一下概念和原理,沒有深入的分析。所以如果要深入研究還得靠自己。最靠譜的學習方式就是看別人寫的插件,舉個例子,你想知道怎么樣實現自動生成代碼,你就去找支持這個功能的插件,看他的源碼是怎么寫的。
我當時寫CodeMaker的時候也是靠自己啃源碼之后寫出來的。下面我簡單介紹一下我用過的一些 API,這些 API 基本都沒有文檔說明,全靠代碼相傳。
判斷當前光標選擇的元素是什么
//獲取當前事件觸發時,光標所在的元素PsiElement psiElement = anActionEvent.getData(LangDataKeys.PSI_ELEMENT);//如果光標選擇的不是類,彈出對話框提醒if (psiElement == null || !(psiElement instanceof PsiClass)) {Messages.showMessageDialog(project, "Please focus on a class", "Generate Failed", null);return;}
獲取當前類文件的所有類對象
一個類文件中可能會有內部類,所以讀取的時候返回的是一個列表
public static List<PsiClass> getClasses(PsiElement element) {List<PsiClass> elements = Lists.newArrayList();List<PsiClass> classElements = PsiTreeUtil.getChildrenOfTypeAsList(element, PsiClass.class);elements.addAll(classElements);for (PsiClass classElement : classElements) {//這里用了遞歸的方式獲取內部類elements.addAll(getClasses(classElement));}return elements;}
格式化代碼
public static void reformatJavaFile(PsiElement theElement) {CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(theElement.getProject());try {codeStyleManager.reformat(theElement);} catch (Exception e) {LOGGER.error("reformat code failed", e);}}
使用粘貼板
CopyPasteManager.getInstance().setContents(new SimpleTransferable(table.toString(), DataFlavor.allHtmlFlavor));
原文鏈接