idea插件开发教程
主要的插件类型UI主题插件自定义语言支持插件框架集成插件工具集成插件开发插件的三种方式Using GradleUsing GitHub Template在github上选择一个模板,会自动根据模板生成一个配置好的插件项目,可以直接clone到本地开发Using DevKit使用DevKit开发插件设置开发环境准备工作确认启用Plugin DevKit插件,该插件是IDEA自带的插件为项目配置In
主要的插件类型
- UI主题插件
- 自定义语言支持插件
- 框架集成插件
- 工具集成插件
开发插件的三种方式
- Using Gradle
- Using GitHub Template
在github上选择一个模板,会自动根据模板生成一个配置好的插件项目,可以直接clone到本地开发 - Using DevKit
使用DevKit开发插件
设置开发环境
准备工作
确认启用Plugin DevKit插件,该插件是IDEA自带的插件
为项目配置IntelliJ Platform SDK
- 创建一个ntelliJ Platform SDK
- 指定sdk的位置
- 配置sdk源码路径,如果不需要debug此步可以跳过
- 设置沙箱目录, 插件项目的setting信息会保存在这个目录, 可以指定任意目录。
创建插件项目
-
创建项目
-
创建具体的插件模块, 如果该项目只开发一个插件则不用创建模块。
-
设置插件的sdk
启动配置
在Run Configuration菜单下配置启动配置, 配置jre。 点击run按钮,会启动一个ieda实例,并且启用当前开发的插件。
部署插件
- build项目或模块
- Prepare your plugin for deployment.
如果插件没有其他依赖,则会创建一个jar包,否则会创建一个zip压缩包。
发布插件
可以发布到自定义仓库或者发布到idea公共仓库
插件架构
插件jar包含的文件
- 插件配置文件plugin.xml
- 实现插件功能的类
- 插件依赖的jar包
插件ClassLoader
每个插件都使用独立的ClassLoader, 所以每个插件可以使用不同版本的依赖,不会相互影响。如果插件的classLoader没有发现某个类,则默认由IDEA的ckassLoader加载。但是可以在plugin.xml中的元素下声明依赖其它插件,那么就会使用其它插件的classLoader加载未找到的类。
Actions
action官方文档
Action是实现插件功能的类, 一个Action类需要继承AnAction并且实现actionPerformed方法。当用户点击菜单或者工具栏按钮, 按快捷键,或者通过Help | Find Action点击时, IntelliJ Platform系统会回调对应Action的actionPerformed方法。
Action会被分组, 一个组可以包含actions也可以包含子组,一个action组就是一个菜单或者工具栏,子组就是子菜单。
注册Action
通过plugin.xml注册action。
<actions>
<!--
- "id" (required) - specifies a unique identifier for the action
- "class" (required) - specifies the FQN of the class implementing
the action
- "text" (required) - specifies the default long-version text to be
displayed for the action (tooltip for toolbar button or text for
menu item)
- "use-shortcut-of" (optional) - specifies the ID of the action
whose keyboard shortcut this action will use
- "description" (optional) - specifies the text which is displayed
in the status bar when the action is focused
- "icon" (optional) - specifies the icon which is displayed on
the toolbar button or next to the menu item
-->
<action
id="VssIntegration.GarbageCollection"
class="com.example.impl.CollectGarbage"
text="Garbage Collector: Collect _Garbage"
description="Run garbage collector"
icon="icons/garbage.png">
<!--
The <override-text> element defines an alternate version of the text
for the menu action. Attributes:
- "text" (required) - defines the text to be displayed for the action
- "place" (required) - declares where the alternate text should
be used. In this example, any time the action is displayed in
the IDE main menu (and submenus), the override-text version should
be used.
The second <override-text> element uses the alternate attribute
"use-text-of-place" to define a location (EditorPopup) to use the same
text as is used in MainMenu. It is a way to specify the use of
an alternate menu text in multiple discrete menu groups.
-->
<override-text place="MainMenu" text="Collect _Garbage"/>
<override-text place="EditorPopup" use-text-of-place="MainMenu"/>
<!-- Provide alternative names for searching action by name -->
<synonym text="GC"/>
<!--
The <add-to-group> node specifies that the action should be added to an
existing group. An action can be added to several groups. Attributes:
- "group-id" (required) - specifies the ID of the group to which the
action is added. The group must be an implementation of the
DefaultActionGroup class.
- "anchor" (required) - specifies the position of the action in
the relative to other actions. Allowed values: "first", "last",
"before", and "after".
- "relative-to-action" (mandatory if "anchor" is "before" or "after") -
specifies the action before or after which the current action is
inserted.
-->
<add-to-group
group-id="ToolsMenu"
relative-to-action="GenerateJavadoc"
anchor="after"/>
<!--
The <keyboard-shortcut> node specifies the keyboard shortcut for
the action. An action can have several keyboard shortcuts. Attributes:
- "first-keystroke" (required) - specifies the first keystroke of
the action. The keystrokes are specified according to the regular
Swing rules.
- "second-keystroke" (optional) - specifies the second keystroke of
the action.
- "keymap" (required) - specifies the keymap for which the action
is active. IDs of the standard keymaps are defined as constants
in the com.intellij.openapi.keymap.KeymapManager class.
- "remove" (optional) - shortcut should be removed from the specified
action. See the second <keyboard-shortcut> example element below.
- "replace-all" (optional) - removes all keyboard and mouse shortcuts
from the specified action before adding the specified shortcut.
See the third <keyboard-shortcut> example element below.
-->
<!-- Add the first and second keystrokes to all keymaps... -->
<keyboard-shortcut
keymap="$default"
first-keystroke="control alt G"
second-keystroke="C"/>
<!-- ...except the "Mac OS X" keymap and its children. -->
<keyboard-shortcut
keymap="Mac OS X"
first-keystroke="control alt G"
second-keystroke="C"
remove="true"/>
<!-- The "Mac OS X 10.5+" keymap and its children will have only this
keyboard shortcut for this action. -->
<keyboard-shortcut
keymap="Mac OS X 10.5+"
first-keystroke="control alt G"
second-keystroke="C"
replace-all="true"/>
<!--
The <mouse-shortcut> node specifies the mouse shortcut for the action.
An action can have several mouse shortcuts. Attributes:
- "keystroke" (required) - specifies the clicks and modifiers for
the action. It is defined as a sequence of words separated by spaces:
* mouse buttons: "button1", "button2", "button3"
* modifier keys: "shift", "control", "meta", "alt", "altGraph"
* button double-click: "doubleClick"
- "keymap" (required) - specifies the keymap for which the action is
active. IDs of the standard keymaps are defined as constants in
the com.intellij.openapi.keymap.KeymapManager class.
The <mouse-shortcut> element can also specify "remove" and "replace-all"
attributes. See <keyboard-shortcut> description above for details.
-->
<mouse-shortcut
keymap="$default"
keystroke="control button3 doubleClick"/>
</action>
<!--
This action declares neither a text nor a description attribute. If it
has a resource bundle declared, the text and descriptions will be
retrieved based on the action-id incorporated in the key for a translated
string.
-->
<action
id="sdk.action.PopupDialogAction"
class="sdk.action.PopupDialogAction"
icon="SdkIcons.Sdk_default_icon"/>
<!--
The <group> element defines an action group. The <action>, <group>
and <separator> elements defined within it are automatically included
in the group. Attributes:
- "id" (required) - specifies a unique identifier for the group.
- "class" (optional) - specifies the FQN of the class implementing
the group. If not specified,
com.intellij.openapi.actionSystem.DefaultActionGroup is used.
- "text" (optional) - specifies the text of the group (text for the menu
item showing the submenu).
- "description" (optional) specifies the text which is displayed in the
status bar when the group has focus.
- "icon" (optional) - specifies the icon which is displayed on
the toolbar button or next to the menu group.
- "popup" (optional) - specifies how the group is presented in the menu.
* "true" - group actions are placed in a submenu
* "false" - actions are displayed as a section of the same menu
delimited by separators.
- "compact" (optional) - specifies whether an action within that group
is visible when disabled. Setting compact="true" specifies an action
in the group isn't visible unless the action is enabled.
-->
<group
class="com.example.impl.MyActionGroup"
id="TestActionGroup"
text="Test Group"
description="Group with test actions"
icon="icons/testgroup.png"
popup="true"
compact="true">
<action
id="VssIntegration.TestAction"
class="com.example.impl.TestAction"
text="My Test Action"
description="My test action"/>
<!-- The <separator> element defines a separator between actions.
It can also have an <add-to-group> child element. -->
<separator/>
<group id="TestActionSubGroup"/>
<!-- The <reference> element allows adding an existing action to
the group. The mandatory "ref" attribute specifies the ID of
the action to add. -->
<reference ref="EditorCopy"/>
<add-to-group
group-id="MainMenu"
relative-to-action="HelpMenu"
anchor="before"/>
</group>
</actions>
扩展(Extensions)
扩展是插件最常用的一种扩展IDEA功能的方式, 只是这种方式没有直接把Action加到菜单或者工具栏那么直接。扩展功能是通过IDEA自带的或者其它插件提供的一些扩展点实现的。
声明扩展要在plugin.xml中添加标签。
<!--
defaultExtensionNs属性只有两种值:
1. com.intellij 使用 IntelliJ Platform核心扩展点
2. {ID of a plugin} 使用其它插件的扩展点,必须要配置其它插件的依赖
子标签的名称必须和扩展点的名称相同
扩展点有两种类型
3. 扩展点使用interface 属性声明, 则设置扩展点标签的implementation属性, 属性值设置实现了interface对应接口的实现类的全限定名
4. 扩展点使用beanClass属性声明,则可以设置beanClass对应的扩展点类中@Attribute注解标注的属性。
可以使用view | quick document 查看扩展点帮助
-->
<extensions defaultExtensionNs="com.intellij">
<appStarter implementation="com.example.MyAppStarter"/>
<projectTemplatesFactory implementation="com.example.MyProjectTemplatesFactory"/>
</extensions>
<!--
Declare extensions to access extension points in a custom plugin "another.plugin".
The "myExtensionPoint" extension point has been declared using "beanClass"
and exposes custom properties "key" and "implementationClass".
-->
<extensions defaultExtensionNs="another.plugin">
<myExtensionPoint
key="keyValue"
implementationClass="com.example.MyExtensionPointImpl"/>
</extensions>
Service
Service是插件的一个组件, 是为了把公共的逻辑放到一起,Service的实例是单例的。
有三种类型的Service, 应用级别的,项目级别的,模块级别的。
声明Service有两种方式
-
类上加@Service注解,service不需要被重写的时候可以使用这种方式
-
在plugin.xml中声明,通过applicationService,projectService扩展点声明
<extensions defaultExtensionNs="com.intellij">
<!-- Declare the application-level service -->
<applicationService
serviceInterface="mypackage.MyApplicationService"
serviceImplementation="mypackage.MyApplicationServiceImpl"/>
<!-- Declare the project-level service -->
<projectService
serviceInterface="mypackage.MyProjectService"
serviceImplementation="mypackage.MyProjectServiceImpl"/>
</extensions>
获取Service
MyApplicationService applicationService = ApplicationManager.getApplication()
.getService(MyApplicationService.class);
MyProjectService projectService = project.getService(MyProjectService.class);
实现类可以封装静态方法,方便获取
MyApplicationService applicationService = MyApplicationService.getInstance();
MyProjectService projectService = MyProjectService.getInstance(project);
和其它Service交互
@Service
public final class ProjectService {
private final Project myProject;
public ProjectService(Project project) {
myProject = project;
}
public void someServiceMethod(String parameter) {
AnotherService anotherService = myProject.getService(AnotherService.class);
String result = anotherService.anotherServiceMethod(parameter, false);
// do some more stuff
}
}
Extension Points
声明扩展点
有两种类型的扩展点
- Interface 其它插件提供该接口的实现类
- Bean 其它插件提供子类, 这种方式其它插件可以设置一些扩展点类的属性值
<idea-plugin>
<id>my.plugin</id>
<extensionPoints>
<extensionPoint
name="myExtensionPoint1"
beanClass="com.example.MyBeanClass"/>
<extensionPoint
name="myExtensionPoint2"
interface="com.example.MyInterface"/>
</extensionPoints>
</idea-plugin>
扩展点类
public class MyBeanClass extends AbstractExtensionPointBean {
@Attribute("key")
public String key;
@Attribute("implementationClass")
public String implementationClass;
public String getKey() {
return key;
}
public String getClass() {
return implementationClass;
}
}
其它插件使用该扩展点
<idea-plugin>
<id>another.plugin</id>
<!-- Declare dependency on plugin defining extension point: -->
<depends>my.plugin</depends>
<!-- Use "my.plugin" namespace: -->
<extensions defaultExtensionNs="my.plugin">
<myExtensionPoint1
key="someKey"
implementationClass="another.some.implementation.class"/>
<myExtensionPoint2
implementation="another.MyInterfaceImpl"/>
</extension>
</idea-plugin>
声明扩展点的类使用扩展点
public class MyExtensionUsingService {
private static final ExtensionPointName<MyBeanClass> EP_NAME =
ExtensionPointName.create("my.plugin.myExtensionPoint1");
public void useExtensions() {
for (MyBeanClass extension : EP_NAME.getExtensionList()) {
String key = extension.getKey();
String clazz = extension.getClass();
// ...
}
}
}
plugin.xml
这个示例配置文件除了Action的配置,其余配置全部都配置了。
<!--
An optional "url" attribute specifies the link to the plugin homepage.
It is displayed on the plugin page in the Marketplace.
-->
<idea-plugin url="https://example.com/my-plugin-site">
<!--
Unique identifier of the plugin. It should be a fully qualified name
including namespace to not collide with existing plugins. It cannot be
changed between the plugin versions. If not specified, <name> will be
used (not recommended).
-->
<id>com.example.myplugin</id>
<!-- Public plugin name. It should use Title Cases. Guidelines:
https://plugins.jetbrains.com/docs/marketplace/plugin-overview-page.html#plugin-name
-->
<name>My Framework Support</name>
<!--
Plugin version. Plugins uploaded to the Marketplace must follow
the semantic versioning: https://plugins.jetbrains.com/docs/marketplace/semver.html.
It is displayed in the "Plugins" settings dialog and in the Marketplace
plugin page.
-->
<version>1.0.0</version>
<!--
Vendor name or Organization ID (if you have one created).
Attributes:
- "url" (optional) - specifies the link to the vendor's homepage
- "email" (optional) - specifies the vendor's email address
Displayed on the Plugins Page.
-->
<vendor
url="https://plugins.jetbrains.com/my-company"
email="contact@example.com">My Company</vendor>
<!--
IMPORTANT: This tag should not be used in free plugins.
If you decide to make your plugin paid, you will need to define
the parameters in the <product-descriptor> tag.
You can also enable free functionality in a paid plugin. Learn more in
a guide to selling plugin: https://plugins.jetbrains.com/build-and-market
-->
<product-descriptor
code="PMYPLUGIN"
release-date="20210901"
release-version="20211"
optional="true"/>
<!-- Minimum and maximum IDE build versions compatible with the plugin. -->
<idea-version since-build="193" until-build="193.*"/>
<!--
Plugin description displayed on the Marketplace plugin page and in
the IDE Plugin Manager.
Simple HTML elements (text formatting, paragraphs, lists, etc.) can be
added inside of <![CDATA[ ]]> tag. Guidelines:
https://plugins.jetbrains.com/docs/marketplace/plugin-overview-page.html#plugin-description
-->
<description>
<![CDATA[
Provides support for <a href="https://example.com/my-framework">My
Framework</a>.
<p>Includes support for:
<ul>
<li>code completion</li>
<li>references</li>
<li>refactoring</li>
</ul>
</p>
]]>
</description>
<!--
Short summary of new features and bugfixes in the latest plugin version.
Displayed on the Marketplace plugin page and in the IDE Plugin Manager.
Simple HTML elements can be included between <![CDATA[ ]]> tags.
-->
<change-notes>Initial release of the plugin.</change-notes>
<!--
Product and plugin compatibility requirements. Read more:
https://plugins.jetbrains.com/docs/intellij/plugin-compatibility.html
-->
<depends>com.intellij.modules.platform</depends>
<depends>com.example.third-party-plugin</depends>
<!--
Optional dependency on another plugin. If the plugin with the
"com.example.my-second-plugin" ID is installed, the contents
of "mysecondplugin.xml" (the format of this file conforms to
the format of plugin.xml) will be loaded.
-->
<depends
optional="true"
config-file="mysecondplugin.xml">com.example.my-second-plugin</depends>
<!--
Resource bundle (/messages/MyPluginBundle.properties) to be used with
"key" attributes in extension points and implicit keys like
"action.[ActionID].text|description".
-->
<resource-bundle>messages.MyPluginBundle</resource-bundle>
<!--
Extension points defined by the plugin. Extension points are registered
by a plugin so that other plugins can provide this plugin with certain
data. Read more:
https://plugins.jetbrains.com/docs/intellij/plugin-extension-points.html
-->
<extensionPoints>
<extensionPoint
name="testExtensionPoint"
beanClass="com.example.impl.MyExtensionBean"/>
<applicationService
serviceImplementation="com.example.impl.MyApplicationService"/>
<projectService
serviceImplementation="com.example.impl.MyProjectService"/>
</extensionPoints>
<!--
Application-level listeners. For more information, see:
https://plugins.jetbrains.com/docs/intellij/plugin-listeners.html#defining-application-level-listeners
-->
<applicationListeners>
<listener
class="com.example.impl.MyListener"
topic="com.intellij.openapi.vfs.newvfs.BulkFileListener"/>
</applicationListeners>
<!--
Project-level listeners. For more information, see:
https://plugins.jetbrains.com/docs/intellij/plugin-listeners.html#defining-project-level-listeners
-->
<projectListeners>
<listener
class="com.example.impl.MyToolwindowListener"
topic="com.intellij.openapi.wm.ex.ToolWindowManagerListener"/>
</projectListeners>
<!--
Actions. For more information, see:
https://plugins.jetbrains.com/docs/intellij/basic-action-system.html
-->
<actions>
<action
id="VssIntegration.GarbageCollection"
class="com.example.impl.CollectGarbage"
text="Collect _Garbage"
description="Run garbage collector">
<keyboard-shortcut
first-keystroke="control alt G"
second-keystroke="C"
keymap="$default"/>
</action>
</actions>
<!-- Custom extensions declaration. For more information, see:
https://plugins.jetbrains.com/docs/intellij/plugin-extensions.html#declaring-extensions
-->
<extensions defaultExtensionNs="VssIntegration">
<myExtensionPoint implementation="com.example.impl.MyExtensionImpl"/>
</extensions>
<!--
DEPRECATED: Do not use in new plugins!
Plugin's application components.
See https://plugins.jetbrains.com/docs/intellij/plugin-components.html
for migration steps.
-->
<application-components>
<component>
<!-- Component's interface class -->
<interface-class>com.example.Component1Interface</interface-class>
<!-- Component's implementation class -->
<implementation-class>com.example.impl.Component1Impl</implementation-class>
</component>
</application-components>
<!--
DEPRECATED: Do not use in new plugins!
Plugin's project components.
See https://plugins.jetbrains.com/docs/intellij/plugin-components.html
for migration steps.
-->
<project-components>
<component>
<!-- Interface and implementation classes are the same -->
<implementation-class>com.example.Component2</implementation-class>
<!--
If the "workspace" option is set "true", the component saves its state
to the .iws file instead of the .ipr file. Note that the <option>
element is used only if the component implements the
JDOMExternalizable interface. Otherwise, the use of the <option>
element takes no effect.
-->
<option name="workspace" value="true"/>
<!--
If the "loadForDefaultProject" tag is present, the project component
is instantiated also for the default project.
-->
<loadForDefaultProject/>
</component>
</project-components>
<!--
DEPRECATED: Do not use in new plugins!
Plugin's module components.
See https://plugins.jetbrains.com/docs/intellij/plugin-components.html
for migration steps.
-->
<module-components>
<component>
<implementation-class>com.example.Component3</implementation-class>
</component>
</module-components>
</idea-plugin>
更多推荐
所有评论(0)