进入插件世界
类别
插件主要分为如下几类:
UI Themes(UI主题) Custom language support (自定义编程语言支持,如Kotlin) Framework integration (框架集成,例如Spring,T插件正属于此类) Tool integration (工具集成,如Maven、Gradle、Git) User interface add-ons (用户界面组件,例如Random Background)
技术栈
开发一个插件需要的用到的技术栈:
Java & Kotlin IntelliJ SDK Gradle:依赖管理、sandbox、打包发版 Swing:是的,整个IDEA界面组件用的都是Swing
新建插件
官方提供了 插件模板
项目基本结构:
├── build.gradle # 构建脚本文件
├── gradle # Gradle包装器
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties # 包装器配置文件
├── gradlew # shell脚本
├── gradlew.bat # 批处理脚本
├── settings.gradle # 对于单个项目,设置文件是可选的
└── src
└── main
├── java
│ └── io
│ └── org
│ └── example
│ └── plugin
│ ├── MyToolWindow.form
│ ├── MyToolWindow.java
│ └── MyToolWindowFactory.java
└── resources
├── images
│ ├── a.svg
│ └── b.svg
├── messages
│ ├── TBundle.properties #Bundle
└── TBundle_zh_CN.properties #国际化Bundle
├── META-INF
│ ├── plugin.xml # 插件配置文件
└── └── pluginIcon.svg #Plugin Logo ,插件在IDEA/Plugins中的展示图标
其中插件的配置文件至关重要:
<idea-plugin>
<!-- 插件名 -->
<name>MyPrluginName</name>
<!-- 插件的唯一标识符,应该是FQN,不设置即name(不推荐) -->
<id>io.org.example.plugin</id>
<!-- 插件描述,使用<![CDATA[ ]]> 来支持HTML格式-->
<description>Integrates Volume Snapshot Service W10</description>
<!-- 插件更新记录,使用<![CDATA[ ]]> 来支持HTML格式-->
<change-notes>Initial release of the plugin.</change-notes>
<!-- 插件版本,格式:BRANCH.BUILD.FIX (MAJOR.MINOR.FIX) -->
<version>1.1.1</version>
<!-- 插件依赖,可以依赖模块或插件 -->
<depends>com.intellij.java</depends>
<depends optional="true" config-file="mysecondplugin.xml">com.MySecondPlugin</depends>
<!-- 插件兼容构建的IDE版本, until-build可以不写,默认到最新版 -->
<idea-version since-build="201" until-build="211"/>
<!-- 资源包,kv,方便管理文本以及i18n-->
<resource-bundle>messages.TBundle</resource-bundle>
<!-- 行为 https://plugins.jetbrains.com/docs/intellij/plugin-actions.html-->
<actions>
</actions>
<!-- 声明该插件对IDEA core或其他插件的扩展,Ns是NameSpace的缩写 -->
<extensions defaultExtensionNs="com.intellij">
...
</extensions>
<!-- 插件定义的扩展点,以供其他插件扩展该插件,类似Java的抽象类的功能
如何在https://plugins.jetbrains.com/docs/intellij/plugin-extensions.html -->
<extensionPoints>
</extensionPoints>
<!-- 监听器
https://plugins.jetbrains.com/docs/intellij/plugin-listeners.html -->
<applicationListeners>
<listener class="io.org.example.plugin.projectManager.MyProjectListener"
topic="com.intellij.openapi.project.ProjectManagerListener"/>
</applicationListeners>
......
</idea-plugin>
IntelliJ SDK
PSI
PSI (Program Structure Interface),程序结构接口,主要负责解析文件、创建语法、语义代码。
IDEA (有很多内置的插件),它们把整个工程的所有元素解析成了它们设计实现的PsiElement,你可以用它们提供的API很方便的去CURD所有元素。
IDEA也支持自定义语言,比较复杂了,需要实现Parser、PsiElement、FileType、Visitor等,有兴趣的话可以看看这个插件
PsiFile
com.intellij.psi.PsiFile是文件结构的根,表示文件的内容为特定语言中元素的层次结构。
它是所有PSI文件的公共基类,而在特定的语言文件通常是由它的子类来表示。
例如,PsiJavaFile该类表示Java文件,而,XmlFile该类表示XML文件。
View PSI
一开始我也很懵,上哪去知道code对应的是什么PSI,特别是复杂的PSI,像嵌套的注解、xml、json等。
直到有一天发现了神奇宝贝 。
它直接打开了我和PSI之间的大门。
Tools | View PSI Structure of Current File...
,使用Gradle:runIde沙箱里才能看到这个工具。
So easy~ 哪里不会点
常用 API
com.intellij.psi.PsiFileFactory: 文件相关操作,e.g.创建文件等。 com.intellij.psi.PsiElementFactory: 元素相关操作,e.g.创建java方法、注解、字段、构造方法等。 com.intellij.psi.PsiManager: 项目访问PSI服务的主要入口点,e.g.查找文件、查找文件夹等。 com.intellij.psi.PsiClass: 在java类查找元素,e.g.查找方法、字段、注解。 com.intellij.psi.JavaPsiFacade: java元素查找等操作,e.g.查找类等。
…太多了,有些方法得自己猜下方法名全局去搜,或者猜类名利用IDEA的自动提示去查找。
写操作
⚠️ 特别注意,对PsiElement的写操作需要在 writeAction 上下文中
WriteCommandAction.runWriteCommandAction
Virtual Files
虚拟文件VirtualFile(VF)是在IntelliJ的虚拟文件系统(VFS)中的文件表示。虚拟文件即本地文件系统中的文件。
不过,虚拟文件也可以表示JAR文件中的文件:
Library library = LibraryUtil.findLibraryByClass(psiClass.getQualifiedName(), project);
VirtualFile YmlFile = library.getFiles(OrderRootType.CLASSES)[0].findChild(TModuleExplorer.T_YAML);
PsiFIle 和 VF 互转
PsiFile psiFile = PsiManager.getInstance(serverModule.getProject()).findFile(virtualFile);
VirtualFile virtualFile = psiFile.getVirtualFile();
Action
定义
com.intellij.openapi.actionSystem.AnAction
是所有Action的基类。
必须覆盖 AnAction.update(@NotNull AnActionEvent e)
⚠️它先于actionPerformed()被调用,用来更新动作状态(启用,可见),以确定该动作在IDE的UI中是否可见。
AnActionEvent 类型的对象将传递给此方法,并包含有关该操作的当前上下文的信息。
e.getPresentation().setVisible(false); //Action不可见
必须覆盖AnAction.actionPerformed(@NotNull AnActionEvent e):Action执行的操作,同样可以从AnActionEvent获取当前操作的上下文。
创建
-
根据上述Action定义手动创建。
-
利用Plugin DevKit
注册
在plugin.xml定义action注册:
<action id="T.RefreshProjectAction"
class="io.org.t.plugin.navigator.action.RefreshProjectAction"
icon="AllIcons.Actions.Refresh"
text="Refresh T " description="RefreshProjectAction">
<keyboard-shortcut keymap="$default" first-keystroke="shift meta T"/>
</action>
Grouping Action
操作组
创建
public class NewTActionGroup extends DefaultActionGroup {}
绑定UI
<!-- 注意这里的Bundle -->
<actions resource-bundle="messages.TBundle">
<group id="NewTGroupInProjectView"
class="io.org.t.plugin.navigator.action.NewTActionGroup"
popup="true">
<separator/>
<add-to-group group-id="ProjectViewPopupMenu" anchor="after" relative-to-action="WeighingNewGroup"/>
<separator/>
</group>
</actions>
需要在Bundle定义group的Text和description
Service
调用ServiceManager.getService() 获取服务实例。
IntelliJ平台可确保仅加载一个服务实例,即使它被多次调用也是如此。需要关闭挂钩/清除例程的服务可以在其中实现 Disposable并执行必要的工作dispose()。
种类
应用级别(plugin.xml/extensions/applicationService) 项目级别(plugin.xml/extensions/projectService) 模块级别(plugin.xml/extensions/moduleService)
创建
由于我开发的插件用的Service都是LightServices即ProjectService,下面只介绍Light Services
https://upsource.jetbrains.com/idea-ce/file/idea-ce-ba0c8fc9ab9bf23a71a6a963cd84fc89b09b9fc8/platform/core-api/src/com/intellij/openapi/components/Service.java
不需要注册plugin.xml,而是使用注释服务类@Service,参考检索服务。
Light Service(since 2019.3)的一些限制:
class需加上注释@Service class必须以final修饰。 不支持构造函数注入。
@Service
public final class MyProjectService {
private final Project myProject;
//看大部分源码,一般都会创建getInstance()来获取实例。
public static MyProjectService getInstance() {
return ServiceManager.getService(MyProjectService.class);
}
public MyProjectService(Project project) {
myProject = project;
}
public void someServiceMethod(String parameter) {
// do some more stuff
}
}
Plugin UI
界面组件
比较常用的组件:
Tool Windows:
<! -- plugin.xml -->
<toolWindow id="T" icon="/images/node/T.svg" anchor="left" factoryClass="io.org.t.plugin.navigator.TToolWindowFactory"/>
DialogWrapper:
这是IntelliJ封装好的对话框组件,其包含了OK、Cancel按钮、setTitle、验证、doOkAction等。
Swing UI Designer
利用Swing UI Designer设计界面:New —> Swing UI Designer —>Create Dialog Class
Palette可以自己自定义或添加其他库提供的组件,比如IDEA的EditorTextField(支持代码高光、代码提示等)
UI检查器
Gradle:runIde沙箱里,Tools | Internal Actions | UI | UI Inspector , 如何开启 |
Gradle插件
安装
Gradle IDEA插件:
Run or Debug
Gradle|toolWindow |intelliJ |runIde ,可以run或者debug,会生成一个sandbox(一个安装了当前插件的IDEA)
构建打包
Gradle|toolWindow |intelliJ |buildPlugin ,生成的插件zip文件在./build/distributions
PublishPlugin
官方提供了两种方式:
1)发布到 JetBrains插件存储库
a) 注册 Jetbrains 账号,官网上传插件。
我的实现
想了一下,功能实现和安全性上oss都做到了,就在build.gradle写了一个Gradle插件(publisT)和 oss文件更新脚本(updateOss.sh)
- build.gradle
class PublishT implements Plugin<Project> {
static def myPluginName = "T_Plugin"
static def myPluginGroup = "t"
@Override
void apply(Project project) {
project.task('checkVersion') {
log.severe("Start Check T Version...")
if (project.getVersion().toString() != "unspecified") {
compareVersion(project)
} else {
throw new Exception("[build.gradle]未声明version")
}
}.group(myPluginGroup)
project.task('publishT') {
def pluginZip = myPluginName + "-" + project.getVersion() + ".zip"
def ossUrl = "https://xxxx"
doFirst {
log.severe("Start Publish T...")
def xmlFile = project.rootDir.absolutePath + '/updatePlugins.xml'
def updatePlugins = new XmlSlurper().parse(xmlFile)
updatePlugins.plugin[0].@version = project.getVersion()
updatePlugins.plugin[0].@url = ossUrl + pluginZip
save(xmlFile, updatePlugins)
}.dependsOn("buildPlugin")
doLast {
project.exec {
workingDir './'
commandLine 'chmod', '+x', './updateOss.sh'
commandLine 'sh', './updateOss.sh', pluginZip
}
log.severe(String.format(" Successfully released [%s]!", pluginZip))
}
}.group(myPluginGroup)
}
}
- updatePlugins.xml
⚠️ 特别注意,这里的 plugin.id 、version要和plugin.xml里面的一致,不然用户在配置plugin repository 时会提示连接出错。
<?xml version="1.0" encoding="UTF-8"?><plugins>
<plugin id="io.org.t.plugin" version="0.3.6" url="https://xxxx/T-0.3.6.zip">
<idea-version since-build="201"/>
</plugin>
</plugins>
有用的链接
IntelliJ Plugin SDK Doc: https://plugins.jetbrains.com/docs/intellij/welcome.html
插件Demo工程: https://github.com/JetBrains/intellij-sdk-code-samples
IntelliJ-IDEA插件社区: https://intellij-support.jetbrains.com/hc/en-us/community/topics/200366979-IntelliJ-IDEA-Open-API-and-Plugin-Development
IntelliJ Platform UI Guidelines:里面定义了一些UI相关的规范以及使用范例。: https://jetbrains.design/intellij/
插件库:可以过滤是否开源~https://plugins.jetbrains.com/search
Gradle: https://docs.gradle.org/current/userguide/userguide.html
参考资料
https://zhuanlan.zhihu.com/p/400059601