基于IDEA2020.1的JAVA代碼提示插件開發例子
之前因為項目組有自己的代碼規範,為了限制平時的開發規範,于是基于2019.1.3版本開發了一個代碼提示的插件。但是在把IDEA切換到2020.1版本的時候,卻發現瘋狂報錯,但是網上關于IDEA插件開發的相關文章還是不夠多,隻能自己解決。于是根據官方的SDK文檔,使用Gradle重新建構了一下項目,把代碼拉了過來。下文會根據2020.1版本簡單開發一個代碼異常的提示插件,把容易踩坑的地方提示一下。
1、首先先根據IDEA插件開發官方文檔,用Gradle建立一個project
選中
file -> new -> Project...
,在彈出的視窗左側選擇
Gradle
,彈出以下界面:

預設勾選了Java,需要額外勾選
IntelliJ Platform Plugin
來表示這是一個IDEA插件項目,還需要勾選
Kotlin/JVM
這一項,為什麼要勾選這一項呢,官網是這麼介紹的:
To include support for the Kotlin language in the plugin, check the Kotlin/JVM box (circled in green below.) This option can be selected with or without the Java language.
也就是說,如果我們開發的插件需要對JAVA代碼做支援的話,是要勾選這一項的。所有如果插件是基于JAVA代碼檢查的話,需要勾選這一個選項。
勾選完之後,點選next,之後的資訊根據自己實際需要填寫即可,然後點選finish,然後默默等待Gradle建構項目,如果可以的話挂個梯子,下載下傳包什麼的還是挺慢的。
建構完後的項目的目錄結構以及每一個目錄的作用,可以直接去看官方文檔,裡面有介紹。
https://www.jetbrains.org/intellij/sdk/docs/tutorials/build_system/prerequisites.html
2、 建構完項目後,需要修改 build.gradle
部配置設定制
build.gradle
建構完後,預設會打開
build.gradle
檔案,内容如下:
plugins {
id 'java'
id 'org.jetbrains.intellij' version '0.4.19'
id 'org.jetbrains.kotlin.jvm' version '1.3.71'
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
testCompile group: 'junit', name: 'junit', version: '4.12'
}
// See https://github.com/JetBrains/gradle-intellij-plugin/
intellij {
version '2020.1'
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
patchPluginXml {
changeNotes """
Add change notes here.<br>
<em>most HTML tags may be used</em>"""
}
這裡有個坑,建構完後,我把以前的代碼複制過來,提醒我有部分類沒有找到,也就是說沒有引入對應的jar包。後來我在官網例子裡面發現,它的
build.gradle
檔案,和我的build檔案有點不一樣,具體不一樣的地方如下:
// See https://github.com/JetBrains/gradle-intellij-plugin/
intellij {
version = '2020.1'
plugins = ['java']
sameSinceUntilBuild = true
}
它在intellij裡面,多了一個
plugins = ['java']
的選項,如果缺少這個選項的話,會缺少
java-api.jar
等jar等JAVA代碼支援的jar包,導緻一些類或者方法不可用。是以如果是JAVA代碼支援的話,
build.gradle
檔案需要加上
plugins = ['java']
這一行。
3、修改 plugin.xml
檔案
plugin.xml
plugin.xml
檔案是對于本插件的作用的一些描述,以及一些依賴關系配制,建構完後的
plugin.xml
檔案内容如下:
<idea-plugin>
<id>org.example.new-plugin-for-java</id>
<name>Plugin display name here</name>
<vendor email="[email protected]" 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 https://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/plugin_compatibility.html
on how to target different products -->
<depends>com.intellij.java</depends>
<extensions defaultExtensionNs="com.intellij">
<!-- Add your extensions here -->
<localInspection
language="JAVA"
displayName="test displayer"
groupPath="Java"
groupBundle="messages.InspectionsBundle"
groupKey="group.names.probable.bugs"
enabledByDefault="true"
level="ERROR"
implementationClass="com.nw.TestInsepction"/>
</extensions>
<actions>
<!-- Add your actions here -->
</actions>
</idea-plugin>
這裡有個坑,如果是IDEA2019.2以前的版本的話,這個檔案不用其他東西,直接參考網上的插件開發教程,寫好代碼,就可以正常運作了。但是如果是IDEA2019.2的版本的話,運作的時候會瘋狂報錯,一開始不知道為什麼,隻能又去翻官方的例子,玩大家一起來找不同,結果發現官網的例子,下面的配置資訊和預設建構的不一樣:
<!-- Evaluates java PSI -->
<depends>com.intellij.modules.java</depends>
原來這裡的依賴還要改一下,改成上面這樣,那為什麼要改成這個依賴呢,這個時候,我才發現預設建構的
plugin.xml
裡面,在depends上面有一段注釋,大概意思就是,請前往注釋裡面的網站去找到如何根據産品去選擇對應的depends,那就很簡單了,直接上網頁看,裡面很多的介紹,以及各種不同的depends是幹嘛的。網頁裡面有這麼一段話:
(2) The Java language functionality was extracted as a plugin in version 2019.2 of the IntelliJ Platform. This refactoring separated the Java implementation from the other, non-language portions of the platform. Consequently, Java dependencies are expressed differently in
plugin.xml
depending on the version of the IntelliJ Platform being targeted:
- Syntax required for releases prior to 2019.2, allowable in all releases:
-
includeplugin.xml
com.intellij.modules.java
-
- Syntax for 2019.2 and later releases:
-
allowable alternative includeplugin.xml
com.intellij.java
-
required to includebuild.gradle
intellij.plugins 'java'
-
大概意思是,從2019.2版本開始後,java代碼相關的支援抽成了一個插件,不包含在預設建構的包裡面了,是以從2019.2版本開始後,
plugin.xml
和
build.gradle
需要修改成相關的配置。這也是為什麼我們第二步要改
build.gradle
的原因。
4、參考網上的例子,編寫第一個代碼提示插件
建立一個java類,内容如下,這是一個測試用的提示插件,在所有變量上面提示"this is error"
package com;
import com.intellij.codeInspection.AbstractBaseJavaLocalInspectionTool;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.psi.JavaElementVisitor;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.PsiField;
import org.jetbrains.annotations.NotNull;
/**
* 這是一個測試用的提示插件,在所有變量上面提示"this is an error"
* @author LiuYeFeng
* @date 2020/5/5
* @e-mail [email protected]
*/
public class TestInspectionTool extends AbstractBaseJavaLocalInspectionTool {
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
// 傳回一個java元素的通路器,重寫當通路變量的時候,需要做的操作
return new JavaElementVisitor() {
@Override
public void visitField(PsiField field) {
super.visitField(field);
// 注冊問題,也就是在變量上面顯示異常紅色下劃線,并提示"this is an error"
// 如果可以的話,這裡也可以附帶上快速修複問題的方法
holder.registerProblem(field, "this is an error");
}
};
}
}
然後在
plugin.xml
裡面的
extensions
添加上面編寫的插件,如下;
<extensions defaultExtensionNs="com.intellij">
<!-- Add your extensions here -->
<localInspection
language="JAVA"
displayName="Test field error"
groupPath="Java"
groupBundle="messages.InspectionsBundle"
groupKey="group.names.probable.bugs"
enabledByDefault="true"
level="ERROR"
implementationClass="com.TestInspectionTool"/>
</extensions>
以上配置大緻是注冊一個代碼提示元件,裡面的參數會決定你的提示配置在IDEA的哪個分類裡面,以及提示的等級,例子裡面用ERROR級别,可以檢視的簡單一點。
5、開始看代碼提示插件的效果
點選右上角的debug,等待編譯運作。運作後會彈出一個新的IDEA,這個IDEA會加載了我們編寫的插件,同時在我們原來編寫插件的IDEA可以看到日志輸出等東西,也可以斷點。可以簡單了解為新的IDEA是一個浏覽器,我們在用網頁調試後端接口。
因為我們編寫的插件是新寫一個變量,不管三七二十一就會報錯,提示
this is an error
,效果如下;
到此,一個完成的代碼異常提示插件,就簡單完成了。
6、代碼提示插件還可以做到的
我們編寫的這個插件,其實是比較簡單的,并沒有做邏輯分析。我們是可以根據實際需要,去編寫一些項目組本身特有的功能的,例如某些繼承自特殊類的所有字段,必須要有注釋。在Service裡面的方法,異常傳回之前必須要記錄日志或者一些其他操作等,都是可以做到的,還可以做一些快速修複(quick fix),例如字段沒有注釋可以快速生成注釋。
這就需要對IDEA文法樹相關有了解,IDEA把代碼抽象成了Psi文法樹,根據不同的代碼功能劃分為不同的
PsiElement
,例如注釋是
PsiComment
,字段是
PsiField
,方法是
PsiMethod
,類是
PsiClass
。IDEA本身也提供了相當多的工具類,例如
ReferencesSearch
可以尋找類或者方法的引用點,
PsiTreeUtil
可以處理代碼文法樹上面的一些操作,例如擷取變量所在的方法,
ControlFlowUtil
可以對代碼進行解析等,還有
PsiClassUtil
和
PsiFieldUtil
等各種相關性的工具類可以提供各種便捷的操作,這就需要插件開發者自己去摸索。遇到問題可以去IDEA官方問答論壇去找一下,說不定别人已經幫你解決了這個問題了。
附加:
IntelliJ Platform SDK官網
https://www.jetbrains.org/intellij/sdk/docs/intro/welcome.html
IntelliJ 插件開發交流社群
https://intellij-support.jetbrains.com/hc/en-us/community/topics/200366979-IntelliJ-IDEA-Open-API-and-Plugin-Development