写在后面

很多idea插件文档更多的是介绍如何创立一个简略的idea插件,本篇文章从开发环境、demo、生态组件、增加依赖包、源码解读、网络申请、渲染数据、页面交互等方面介绍,是一篇可能满足根本的插件开发工程要求的文章。

如有疏漏欢送斧正,如想深刻理解欢送探讨。

一、简介

IntelliJ IDEA 与 IntelliJ Platform

IntelliJ IDEA 简称 IDEA,是 Jetbrains 公司旗下的一款 JAVA 开发工具,反对 Java、Scala、Groovy 等语言的开发,同时具备反对目前支流的技术和框架,擅长于企业应用、挪动利用和 Web 利用的开发,提供了丰盛的性能,智能代码助手、代码主动提醒、重构、J2EE反对、各类版本工具(git、svn等)、JUnit、CVS整合、代码剖析、 翻新的GUI设计等。

IntelliJ Platform 是一个构建 IDE 的开源平台,基于它构建的 IDE 有 IntelliJ IDEA、WebStorm、DataGrip、以及 Android Studio 等等。IDEA 插件也是基于 IntelliJ Platform 开发的。

二、开发环境搭建

留神各软件版本要对应

1、开发工具

IDEA 2020.1 各版本下载地址:https://www.jetbrains.com/idea/download/other.html\
gradle 6.1 各版本下载地址:https://gradle.org/releases/\
org.jetbrains.intellij 0.4.22\
jdk 1.8\
首先看一下目前idea版本的变动,找到本人以后idea对应的版本须要的jdk版本\
https://plugins.jetbrains.com/docs/intellij/build-number-rang...\
接下来须要找idea对应版本的gradle版本\
https://www.jetbrains.com/legal/third-party-software/?product...\
最初就是找 gradle版本对应的 org.jetbrains.intellij 插件版本,在gradle-intellij-plugin插件的releases页面,这里会在形容中指出有各个插件版本对应最低的gradle版本。\
https://github.com/JetBrains/gradle-intellij-plugin/releases?page=1\
增加依赖到gradel配置文件,获取相干依赖配置\
https://mvnrepository.com/artifact/org.springframework/spring...

2、启用Plugin DevKit

Plugin DevKit 是 IntelliJ 的一个插件,它应用 IntelliJ IDEA 本人的构建零碎来为开发 IDEA 插件提供反对。开发 IDEA 插件之前须要装置并启用 Plugin DevKit 。\
关上 IDEA,导航到 Settings | Plugins,若插件列表中没有 Plugin DevKit,点击 Install JetBrains plugin,搜寻并装置。

3、配置IntelliJ Platform Plugin SDK

IntelliJ Platform Plugin SDK 就是开发 IntelliJ 平台插件的SDK, 是基于 JDK 之上运行的,相似于开发 Android 利用须要 Android SDK。\
3.1导航到 File | Project Structure,抉择对话框左侧栏 Platform Settings 下的 SDKs

3.2点击 + 按钮,先抉择 JDK,指定 JDK 的门路;再创立 IntelliJ Platform Plugin SDK,指定 home path 为 IDEA 的装置门路,如图

创立好 IntelliJ Platform Plugin SDK 后,抉择左侧栏 Project Settings 下的 Projects,在 Project SDK 下抉择刚创立的 IntelliJ Platform Plugin SDK。

4、设置源码门路(可选)

4.1查看 build 号:关上 IDEA,Help | About,查看版本号及 build 号

4.2IDEA Community 源码(https://github.com/JetBrains/intellij-community/):切换到与 build 号雷同的分支,点击 Clone or download 按钮,抉择 Download ZIP

4.3抉择工程构造设置后抉择SDKs->选中之前在第3步增加的sdk点击SourcePath后按如下1点击增加一个sourcePath,抉择下面下载额源码后点击OK、点击Applay

4.4未装置源码时点击某一个action(NewModuleAction)会看到如下所示浏览起来会比拟艰涩难懂。

5、Sandbox

IntelliJ IDEA 插件以 Debug/Run 模式运行时是在 SandBox 中进行的,不会影响以后的 IntelliJ IDEA;然而同一台机器同时开发多个插件时默认应用的同一个 sandbox,即在创立 IntelliJ Platform SDK 时默认指定的 Sandbox Home

如果须要每个插件的开发环境是互相独立的,能够创立多个 IntelliJ Platform SDK,为 Sandbox Home 指定不同的目录 。

三、开发一个简略插件

插件的创立、配置、运行、打包流程,以及 action

1、创立一个插件工程

抉择 File | New | Project,左侧栏中抉择 IntelliJ Platform Plugin 工程类型

点击 Next,设置工程名称及地位,点击 Finish 实现创立。能够到 File | Project Structure 来自定义工程设置。

除了在idea创立插件我的项目外,咱们还能够下载github模板代码进行批改:https://github.com/JetBrains/intellij-platform-plugin-template

2、插件工程构造

插件工程内容:

PluginDemo/    resources/      META-INF/        plugin.xml    src/      com/foo/...      ...      ...
  • src 实现插件性能的classes
  • resources/META-INF/plugin.xml 插件的配置文件,指定插件名称、形容、版本号、反对的 IntelliJ IDEA 版本、插件的 components 和 actions 以及软件商等信息。

3、plugin.xml

上面示例形容了可在 plugin.xml 文件配置的次要元素:

<idea-plugin>  <!-- 插件名称,他人在官网插件库搜寻你的插件时应用的名称 -->  <name>MyPlugin</name>  <!-- 插件惟一id,不能和其余插件我的项目反复,所以举荐应用com.xxx.xxx的格局       插件不同版本之间不能更改,若没有指定,则与插件名称雷同 -->  <id>com.example.plugin.myplugin</id>  <!-- 插件的形容 -->  <description>my plugin description</description>  <!-- 插件版本变更信息,反对HTML标签;       将展现在 settings | Plugins 对话框和插件仓库的Web页面 -->  <change-notes>Initial release of the plugin.</change-notes>  <!-- 插件版本 -->  <version>1.0</version>  <!-- 供应商主页和email-->  <vendor url="http://www.jetbrains.com" email="support@jetbrains.com" />  <!-- 插件所依赖的其余插件的id -->  <depends>MyFirstPlugin</depends>  <!-- 插件兼容IDEA的最大和最小 build 号,两个属性能够任选一个或者同时应用       官网具体介绍:http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/build_number_ranges.html-->  <idea-version since-build="3000" until-build="3999"/>  <!-- application components -->  <application-components>    <component>      <!-- 组件接口 -->      <interface-class>com.plugin.demo.Component1Interface</interface-class>      <!-- 组件的实现类 -->      <implementation-class>com.plugin.demo.impl.Component1Impl</implementation-class>    </component>  </application-components>  <!-- project components -->  <project-components>    <component>      <!-- 接口和实现类雷同 -->      <interface-class>com.plugin.demo.impl.Component2</interface-class>    </component>  </project-components>  <!-- module components -->  <module-components>    <component>      <interface-class>com.plugin.demo.impl.Component3</interface-class>    </component>  </module-components>  <!-- Actions -->  <actions>    ...  </actions>  <!-- 插件定义的扩大点,以供其余插件扩大该插件 -->  <extensionPoints>    ...  </extensionPoints>  <!-- 申明该插件对IDEA core或其余插件的扩大 -->  <extensions xmlns="com.intellij">    ...  </extensions></idea-plugin>

4、创立 Action

Action是实现插件性能的类, 一个Action类须要继承AnAction并且实现actionPerformed办法。当用户点击菜单或者工具栏按钮, 按快捷键,或者通过Help | Find Action点击时, IntelliJ Platform零碎会回调对应Action的actionPerformed办法。\
一个 Action 示意 IDEA 菜单里的一个 menu item 或工具栏上的一个按钮,通过继承 AnAction class 实现,当抉择一个 menu item 或点击工具栏上的按钮时,就会调用 AnAction 类的 actionPerformed 办法。\
实现自定义 Action 分两步:

  • 定义一个或多个 action
  • 注册 action,将 item 增加到菜单或工具栏上

4.1、定义 Action

定义一个 Java class,继承 AnAction 类,并重写 actionPerformed 办法, 如

public class ActionDemo extends AnAction {    public void actionPerformed(AnActionEvent event) {        Project project = event.getData(PlatformDataKeys.PROJECT);        Messages.showInputDialog(          project,          "What is your name?",          "Input your name",          Messages.getQuestionIcon());    }}

4.2、注册 Action

在 plugin.xml 文件的<actions>元素内注册

<actions>  <group id="MyPlugin.SampleMenu" text="Sample Menu" description="Sample menu">    <add-to-group group-id="MainMenu" anchor="last"  />       <action id="Myplugin.ActionDemo" class="Mypackage.ActionDemo" text="Text Boxes" description="A test menu item" />  </group></actions>
  • <action>元素会定义一个 action,指定 action 的 id、实现类、显示文本、形容
  • <group>元素会定义一个 action group(多个action),设置 action group 的 id、文本、形容
  • <add-to-group>元素指定其内部 action 或 action group 被增加到的地位

下面示例会定义一个被增加到 IDEA 主菜单的最初面的 “SampleMenu” 的菜单,点击该菜单将弹出一个 “Text Boxes” item,如图

4.3、疾速创立 Action

IntelliJ Platform 提供了 New Action 向导,它会帮忙咱们创立 action class 并配置 plugin.xml 文件:

在指标 package 上右键,抉择 New | Plugin DevKit | Action:

  • Action ID: action 惟一 id,举荐 format: PluginName.ID
  • Class Name: 要被创立的 action class 名称
  • Name: menu item 的文本
  • Description: action 形容,toolbar 上按钮的提醒文本,可选
  • Add to Group:抉择新 action 要被增加到的 action group(Groups, Actions)以及绝对其余 actions 的地位(Anchor)
  • Keyboard Shortcuts:指定 action 的第一和第二快捷键

留神:该向导只能向主菜单中已存在的 action group 或工具栏上增加 action,若要创立新的 action group,请参考后面的内容。

5、运行调试插件

运行/调试插件可间接在 IntelliJ IDEA 进行,抉择 Run | Edit Configurations...,若左侧栏没有 Plugin 类型的 Configuration, 点击右上角 + 按钮,抉择 Plugin 类型, 如图

Use classpath of module 抉择要调试的 module,其余配置个别默认即可;切换到 Logs 选项卡,如果勾选了 idea.log,运行插件时 idea.log 文件的内容将输入到 idea.log console。

运行插件点击工具栏上运行按钮Run

\

6、打包装置插件

6.1、打包插件

抉择 Build | Prepare Plugin Module ‘module name’ for Deployment 来打包插件:

jar类型的插件包:

    PluginDemo.jar/      com/xxx/...      ...      ...      META-INF/        plugin.xml

zip类型的插件包:

PluginDemo.zip/  lib/    libxxx.jar    libbar.jar    PluginDemo.jar/      com/xxx/...      ...      ...      META-INF/        plugin.xml

6.2、装置插件

导航到 File | Settings | Plugins 页面,点击 Install plugin from disk...

  • 抉择插件包的地位,点击 OK
  • 在插件列表中,勾选插件名字前面的 check-box 来启用插件,点击 OK
  • 重启 IDEA\
    Install JetBrains plugin... 从 JetBrains 仓库(https://plugins.jetbrains.com/)中装置插件\
    Browse repositories... 增加并治理本人的仓库

四、Action容许增加的地位

这个时候咱们理解的都比拟通俗还停留在demo层面,如何进行深刻的理解呢?

eg:咱们怎么晓得都有哪些 action 或 action group 能够被咱们增加呢?

1.增加主菜单MainMenu

1、咱们能够点击配置group-id="MainMenu"下的MainMenu

<actions>  <group id="MyPlugin.SampleMenu" text="Sample Menu" description="Sample menu">    <add-to-group group-id="MainMenu" anchor="last"  />       <action id="Myplugin.Textboxes" class="Mypackage.TextBoxes" text="Text Boxes" description="A test menu item" />  </group></actions>

2、进入PlatformActions.xml如下图,这个时候不难看出这里就是主菜单的第一列子菜单

3.这个时候如果咱们想新建个相似与File-->New和Open的菜单该怎么做呢?

3.1咱们应该先实现布局,增加主菜单MainMenu

  <!-- Actions -->    <actions>        <group id="MainMenuActionGroup" text="MainMenuActionGroup" description="MainMenuActionGroup" popup="true">            <add-to-group group-id="MainMenu" anchor="after" relative-to-action="HelpMenu"/>            <action id="OpenFile" class="com.plugin.demo.action.MainMenuOpenFileAction" text="Open"                    description="主菜单File下的Open子菜单"/>            <separator/>        </group>        <group id="JavaNewProjectOrModuleGroup" text="一级菜单" popup="true">            <add-to-group group-id="MainMenuActionGroup" anchor="before" relative-to-action="OpenFile"/>            <action id="NewProject" class="com.intellij.ide.actions.NewProjectAction"/>            <action id="ImportProject" class="com.intellij.ide.actions.ImportProjectAction"/>            <action id="ProjectFromVersionControl"class="com.intellij.openapi.wm.impl.welcomeScreen.ProjectFromVersionControlAction"/>            <separator/>            <action id="NewModule" class="com.intellij.openapi.roots.ui.configuration.actions.NewModuleAction"/>            <action id="ImportModule" class="com.intellij.ide.actions.ImportModuleAction"/>        </group>    </actions>

3.2实现自定义的关上文件

其实是通过上面的action配置的OpenFileAction找到源码

<action id="OpenFile" class="com.intellij.ide.actions.OpenFileAction" icon="AllIcons.Actions.Menu_open"/>

在将源码拷贝进去粘贴到本人的action内。这样就能够实现本人的主菜单File下的Open子菜单

3.3这个时候有人会有疑难我不晓得去哪找New对应的action呀?

这个时候咱们通过界面能够看到Project from Existing Sources...,这里咱们就能够去搜这个文本呀。既然显示在页面上。必然有中央定义了它。 ActionBundle.properties

这个时候咱们在依据对应的action定义的文本在去搜寻对应的action,com.intellij.ide.actions.ImportProjectAction

3.4这个时候咱们将对应的action拷贝到本人的插件定义的配置上也就造成了3.1的一级和二级菜单

2.增加主工具栏MainToolBar

增加主工具栏MainToolBar(如果不分明哪里是主菜单、主工具栏、导航栏、上下文菜单、弹出菜单参考https://www.w3cschool.cn/intellij6f1017d5aa6296e6c3bb8c666b1a...)

   <group>            <add-to-group group-id="MainToolBar" anchor="before" relative-to-action="SearchEverywhere"/>            <reference ref="MainMenuActionGroup"></reference>        </group>

3、增加上下文菜单ProjectViewPopupMenu

   <group>            <add-to-group group-id="ProjectViewPopupMenu" anchor="before" relative-to-action="WeighingNewGroup"/>            <reference ref="MainMenuActionGroup"></reference>        </group> 

4、增加弹出菜单EditorPopupMenu

 <!--增加到弹出框右键-->        <group>            <add-to-group group-id="EditorPopupMenu" anchor="before" relative-to-action="ShowIntentionsGroup"/>            <reference ref="MainMenuActionGroup"></reference>        </group>

5、增加打印ConsoleEditorPopupMenu

    <!--增加到控制台打印右键-->        <group>            <add-to-group group-id="ConsoleEditorPopupMenu" anchor="before" relative-to-action="CutCopyPasteGroup"/>            <reference ref="MainMenuActionGroup"></reference>        </group>

6、右键新建action时也能够间接抉择增加的地位。

1.筛选后查找要增加的group\
2.抉择对应的action\
3.抉择要增加到这个action的某个地位

五、Components(已不倡议应用)

IntelliJ IDEA 的组件模型是基于 PicoContainer 的,组件都蕴含在这些容器中,但容器有三种级别:application container,project container 以及 module container。application container 能够蕴含多个 project container,而 project container 能够蕴含多个 module container。

1、Components 类型

Components 是插件开发的根底,Components 有三种类型:

2、注册 Components

components 须要配置在 plugin.xml 中,并指定 interface 和 implementation,interface 类用于从其余组件中检索组件,implementation 类用于实例化组件。示例:

//创立一个 application level componentpublic interface Component1 extends ApplicationComponent {}public class Component1Impl implements Component1 {    @Override    public String getComponentName() {        return "PluginDemo.Component1";    }}

plugin.xml

<application-components>    <component>      <interface-class>com.example.test.Component1</interface-class>      <implementation-class>com.example.test.Component1Impl</implementation-class>    </component> </application-components>

留神:一个 interface-class 不能有多个 implementation-class,如下图:

  1. 若组件没有创立 interface 类,而是间接实现了 ApplicationComponent 等接口,interface 和 implementation 能够指定为同一个类。
  2. 每一个组件都应该有一个惟一的名字,通过 getComponentName() 返回,举荐应用 \<plugin\_name>.\<component\_name> 格局。

3、Component 周期办法

ApplicationComponent 的生命周期办法:

//构造方法public constructor(){}//初始化public void initComponent() {}public void disposeComponent() {}

ProjectComponent 的生命周期办法:

//构造方法public constructor(){}//告诉一个project曾经实现加载public void projectOpened() {}public void projectClosed() {}//执行初始化操作以及与其余 components 的通信public void initComponent() {}//开释系统资源或执行其余清理public void disposeComponent() {}

ModuleComponent 的生命周期办法:

ModuleComponent 的生命周期办法中比 ProjectComponent 多一个 moduleAdded(),用于告诉 module 曾经被增加到 project 中。

4、Component 加载

Application 级别的 components 在 IDEA 启动时加载,Project 和 Module 级别的 components 在我的项目启动时独特加载。

一个组件加载过程:

  1. 创立:调用构造方法
  2. 初始化:调用 initComponent() 办法
  3. 如果是 Project 组件,会调用 projectOpened() 办法; 如果是 Module 组件,会顺次调用 moduleAdded() 和 projectOpened() 办法

如果 component 在加载时须要用到其余 component,咱们只需在该 component 的构造方法的参数列表申明即可,在这种状况下,IntelliJ IDEA 会按正确的程序实例化所依赖的 component。

示例:

public class MyComponent implements ApplicationComponent {    private final MyOtherComponent otherComponent;    public MyComponent(MyOtherComponent otherComponent) {       this.otherComponent = otherComponent;    }    ...}

5、Component 卸载

一个组件卸载过程:

  1. 如果是 Project 或 Module 组件,调用 projectClosed()
  2. 接下来 disposeComponent() 将被调用

6、Component 容器

后面咱们提到有三种不同的容器,application container 实现 Application 接口; project container 实现 Project 接口;

module container 实现 Module 接口。每一个容器都有本人的办法去获取容器内的 component。

获取 application 容器及其外部的组件:

/获取application容器Application application = ApplicationManager.getApplication();//获取application容器中的组件MyComponent myComponent = application.getComponent(MyComponent.class);

获取 project / module 容器及其外部的组件:

在 component 构造方法的参数列表中申明:

public class MyComponent implements ProjectComponent {Project project;public MyComponent(Project project){this.project = project;}public void initComponent() {OtherComponent otherComponent = project.getComponent(OtherComponent.class);}}

在这个例子中,组件在构造方法中获取了容器对象,将其保留,而后在 component 其余中央进行援用。

7、 各组件应用机会

7.1创立一个ApplicationComponent

package com.plugin.demo.component;import com.intellij.openapi.components.ApplicationComponent;//创立一个 application level componentpublic interface ApplicationComponentDemo extends ApplicationComponent {}package com.plugin.demo.component;import com.intellij.openapi.application.Application;import com.intellij.openapi.application.ApplicationManager;public class ApplicationComponentDemoImpl implements ApplicationComponentDemo {    @Override    public String getComponentName() {        System.out.println("ApplicationComponentDemoImpl = " +this.getClass().getName());        return this.getClass().getName();    }    //初始化    public void initComponent() {        System.out.println("ApplicationComponentDemoImpl initComponent" );    }    public void disposeComponent() {        //获取application容器        Application application = ApplicationManager.getApplication();        //获取application容器中的组件        ApplicationComponentDemoImpl myComponent = application.getComponent(ApplicationComponentDemoImpl.class);        System.out.println("disposeComponent = " + myComponent.getComponentName());    }}

7.2 创立一个ProjectComponent

package com.plugin.demo.component;import com.intellij.openapi.application.Application;import com.intellij.openapi.application.ApplicationManager;import com.intellij.openapi.components.ProjectComponent;import org.jetbrains.annotations.NotNull;public class ProjectComponentDemo implements ProjectComponent {    @NotNull    @Override    public String getComponentName() {        //获取application容器        Application application = ApplicationManager.getApplication();        //获取application容器中的组件        ApplicationComponentDemoImpl myComponent = application.getComponent(ApplicationComponentDemoImpl.class);        System.out.println("ProjectComponentDemo = " + myComponent.getComponentName());        return myComponent.getComponentName();    }    @Override    public void initComponent() {//        获取application容器        Application application = ApplicationManager.getApplication();//        获取application容器中的组件        ApplicationComponentDemo component = application.getComponent(ApplicationComponentDemo.class);        System.out.println("ApplicationComponentDemoImpl initComponent = " + component.getComponentName());        System.out.println("ProjectComponentDemo initComponent");    }    @Override    public void disposeComponent() {        //获取application容器        Application application = ApplicationManager.getApplication();        //获取application容器中的组件        ApplicationComponentDemoImpl myComponent = application.getComponent(ApplicationComponentDemoImpl.class);        System.out.println("disposeComponent = " + myComponent.getComponentName());    }}

7.3创立一个ModuleComponent

package com.plugin.demo.component;import com.intellij.openapi.application.Application;import com.intellij.openapi.application.ApplicationManager;import com.intellij.openapi.components.ApplicationComponent;import com.intellij.openapi.module.ModuleComponent;import com.intellij.openapi.project.Project;import com.intellij.openapi.project.ProjectManager;import org.jetbrains.annotations.NotNull;public class ModuleComponentDemo implements ModuleComponent {    @NotNull    @Override    public String getComponentName() {        //获取application容器        Application application = ApplicationManager.getApplication();        //获取application容器中的组件        ApplicationComponentDemoImpl myComponent = application.getComponent(ApplicationComponentDemoImpl.class);        System.out.println("ApplicationComponentDemoImpl initComponent = " + myComponent.getComponentName());        ProjectManager projectManager = ProjectManager.getInstance();        Project defaultProject = projectManager.getDefaultProject();        ProjectComponentDemo component = defaultProject.getComponent(ProjectComponentDemo.class);        System.out.println("ProjectComponentDemo initComponent" + component.getComponentName());        return myComponent.getComponentName();    }    @Override    public void initComponent() {        System.out.println("ModuleComponentDemo initComponent");    }    @Override    public void disposeComponent() {        //获取application容器        Application application = ApplicationManager.getApplication();        //获取application容器中的组件        ApplicationComponentDemoImpl myComponent = application.getComponent(ApplicationComponentDemoImpl.class);        System.out.println("disposeComponent = " + myComponent.getComponentName());    }}

7.4注册配置Component

 <application-components>           <component>                <interface-class>com.plugin.demo.component.ApplicationComponentDemo</interface-class>               <implementation-class>com.plugin.demo.component.ApplicationComponentDemoImpl</implementation-class>            </component>    </application-components>    <project-components>        <component>                <interface-class>com.plugin.demo.component.ProjectComponentDemo</interface-class>               <implementation-class>com.plugin.demo.component.ProjectComponentDemo</implementation-class>            </component>    </project-components>    <module-components>        <component>                <interface-class>com.plugin.demo.component.ModuleComponentDemo</interface-class>               <implementation-class>com.plugin.demo.component.ModuleComponentDemo</implementation-class>            </component>    </module-components>

7.5运行后的预期是先执行应用层组件,在执行工程级组件,在执行模块级组件

六、Extensions and Extension Points

如果插件须要扩大 IDEA Platform 或 其余插件的性能,或为其余插件提供能够扩大本人的接口,那么就要用到 extensions 和 extension points,用于与 IDEA 和其余插件交互。

1、Extension points 扩大点

extension point 用于数据信息扩大,使其余插件能够扩大本插件的性能,可通过plugin.xml 的 元素申明,如下示例:

<extensionPoints>    <!--应用beanClass申明-->    <extensionPoint name="MyExtensionPoint1" beanClass="MyPackage.MyBeanClass" area="IDEA_APPLICATION">        <with attribute="implementationClass" implements="MyPackage.MyAbstractClass"/>    </extensionPoint>    <!--应用interface申明-->    <extensionPoint name="MyExtensionPoint2" interface="MyPlugin.MyInterface" area="IDEA_PROJECT" /></extensionPoints>
  • name 指定 extension point 的名字,当其余插件扩大该extensionPoint时,须要指定该name
  • area 有三种值,IDEAAPPLICATION,IDEAPROJECT,IDEA\_MODULE,指定extension point的级别
  • interface 指定须要扩大此 extension point 的插件必须要实现的接口
  • beanClass 指定一个类,该类有一个或多个被 @Attribute 注解的属性
  • 申明 extension point 有两种形式,指定 beanClass 或 interface
  • 如果某个属性须要是某个类的子类,或某个接口的实现类,须要通过 指明类名或接口名。

示例上述代码中的 MyExtensionPoint1 的 beanClass:

public class MyBeanClass extends AbstractExtensionPointBean {  @Attribute("key")  public String key;  @Attribute("implementationClass")  public String implementationClass;  ...}

2、Extension 扩大其余插件性能

如果插件须要扩大 IntelliJ Platform 或其余插件的性能,须要申明一个或多个 extension。

  1. 设置 的 defaultExtensionNs 属性若是扩大 IntelliJ Platform,设为 com.intellij若是扩大其余插件,则设为 pluginId
  2. 指定要扩大哪个 extension point外部的子标签的名字必须与 extension point 的 name 属性雷同
  3. 如果 extension point
  • 是通过 interface 申明的,那么应用 implementation 属性指明 interface 的实现类
  • 是通过 beanClass 申明的,那么就要为 beanClass 中被 @Attribute 注解的属性指定属性值

示例:

<!-- 扩大 interface 申明的 extensionPoint -->  <extensions defaultExtensionNs="com.intellij">    <appStarter implementation="MyPackage.MyExtension1" />    <applicationConfigurable implementation="MyPackage.MyExtension2" />  </extensions>  <!-- 扩大 beanClass 申明的 extensionPoint -->  <extensions defaultExtensionNs="pluginId">     <MyExtensionPoint1 key="keyValue" implementationClass="MyPackage.MyClassImpl"></MyExtensionPoint1>  </extensions>

插件的 service 的实现就是扩大 IDEA Platform 的 applicationService 或 projectService 两个 extension points

3、获取 extension points

IntelliJ Platform 的局部 extension points

 <extensionPoints>    <extensionPoint name="languageBundle" beanClass="com.intellij.DynamicBundle$LanguageBundleEP"/>    <!--suppress PluginXmlValidity -->    <extensionPoint name="applicationService" beanClass="com.intellij.openapi.components.ServiceDescriptor" dynamic="true"/>    <!--suppress PluginXmlValidity -->    <extensionPoint name="projectService" beanClass="com.intellij.openapi.components.ServiceDescriptor" dynamic="true"/>    <!--suppress PluginXmlValidity -->    <extensionPoint name="moduleService" beanClass="com.intellij.openapi.components.ServiceDescriptor" dynamic="true"/>    <extensionPoint name="virtualFileManagerListener" interface="com.intellij.openapi.vfs.VirtualFileManagerListener" dynamic="true"/>    <extensionPoint name="vfs.asyncListener" interface="com.intellij.openapi.vfs.AsyncFileListener" dynamic="true"/>    <!-- only bundled plugin can define startupActivity -->    <extensionPoint name="startupActivity" interface="com.intellij.openapi.startup.StartupActivity"/>    <extensionPoint name="postStartupActivity" interface="com.intellij.openapi.startup.StartupActivity" dynamic="true"/>    <extensionPoint name="backgroundPostStartupActivity" interface="com.intellij.openapi.startup.StartupActivity" dynamic="true"/>    <extensionPoint name="fileTypeDetector" interface="com.intellij.openapi.fileTypes.FileTypeRegistry$FileTypeDetector" dynamic="true"/>    <extensionPoint name="editorFactoryDocumentListener" interface="com.intellij.openapi.editor.event.DocumentListener" dynamic="true"/>    <extensionPoint name="multiHostInjector" interface="com.intellij.lang.injection.MultiHostInjector" area="IDEA_PROJECT" dynamic="true"/>    <extensionPoint name="writingAccessProvider" area="IDEA_PROJECT" interface="com.intellij.openapi.vfs.WritingAccessProvider" dynamic="true"/>    <extensionPoint name="metaLanguage" interface="com.intellij.lang.MetaLanguage"/>    <extensionPoint name="lang.parserDefinition" beanClass="com.intellij.lang.LanguageExtensionPoint" dynamic="true">      <with attribute="implementationClass" implements="com.intellij.lang.ParserDefinition"/>    </extensionPoint>    <extensionPoint name="lang.elementManipulator" beanClass="com.intellij.openapi.util.ClassExtensionPoint" dynamic="true">      <with attribute="implementationClass" implements="com.intellij.psi.ElementManipulator"/>    </extensionPoint>    <!--suppress PluginXmlValidity -->    <extensionPoint name="stubElementTypeHolder" beanClass="com.intellij.psi.stubs.StubElementTypeHolderEP" dynamic="true"/>  </extensionPoints>  <extensions defaultExtensionNs="com.intellij">    <applicationService serviceInterface="com.intellij.util.messages.MessageBusFactory"                        serviceImplementation="com.intellij.util.messages.impl.MessageBusFactoryImpl"/>  </extensions>

其余能够从被扩大插件的 plugin.xml 文件中获取\
https://plugins.jetbrains.com/intellij-platform-explorer/exte...

七、Service

参考:https://plugins.jetbrains.com/docs/intellij/plugin-services.h...

Service 也是一种按需加载的 component,在调用 ServiceManager.getService(Class)时才会加载,且程序中只有一个实例。

Service是插件的一个组件, 是为了把公共的逻辑放到一起,Service的实例是单例的。

Serivce 在 IntelliJ IDEA 中是以 extension point 模式提供的,实现本人的 service 须要扩大相应 extension point。

  • applicationService: application level service
  • projectService: project level service
  • moduleService: module level service

申明 service 时必须蕴含 serviceImplementation 属性用于实例化 service, serviceInterface 属性是可选的,可用于获取 service 实例。

1、创立 Service

在须要搁置 service 的 package 上右键, New | Plugin DevKit | xxxxService,如图

抉择相应 service,弹出如下对话框,填写 interface 类和 implementation 类,若不勾选 Separate interface from implementation,只需填写 implementation 类。

\
IntelliJ IDEA 会主动创立相应类并配置 plugin.xml 文件。\
示例:plugin.xml:

 <extensions defaultExtensionNs="com.intellij">        <applicationService serviceInterface="com.plugin.demo.service.ApplicationServiceDemo"                            serviceImplementation="com.plugin.demo.service.impl.ApplicationServiceDemoImpl"/>        <projectService serviceInterface="com.plugin.demo.service.ProjectServiceDemo"                        serviceImplementation="com.plugin.demo.service.impl.ProjectServiceDemoImpl"/>        <moduleService serviceInterface="com.plugin.demo.service.ModuleServiceDemo"                       serviceImplementation="com.plugin.demo.service.impl.ModuleServiceDemoImpl"/>    </extensions>

生成的 service 类:

public interface ApplicationServiceDemo {    static ApplicationServiceDemo getInstance() {        return ServiceManager.getService(ApplicationServiceDemo.class);    }}public interface ProjectServiceDemo {    static ProjectServiceDemo getInstance(@NotNull Project project) {        return ServiceManager.getService(project, ProjectServiceDemo.class);    }}public interface ModuleServiceDemo {    static ModuleServiceDemo getInstance(@NotNull Module module) {        return module.getService(ModuleServiceDemo.class);    }}
public class ApplicationServiceDemoImpl implements ApplicationServiceDemo {    public ApplicationServiceDemoImpl() {        System.out.println("ApplicationServiceDemoImpl = ");    }}public class ProjectServiceDemoImpl implements ProjectServiceDemo {    public ProjectServiceDemoImpl(Project project) {        System.out.println("ProjectServiceDemoImpl = " + project);    }}public class ModuleServiceDemoImpl implements ModuleServiceDemo {    public ModuleServiceDemoImpl(Module project) {        System.out.println("ModuleServiceDemoImpl = " + project);    }}

2、获取 Service

MyApplicationService applicationService = ServiceManager.getService(MyApplicationService.class);//获取 project 级别的 service,须要提供 project 对象MyProjectService projectService = ServiceManager.getService(project, MyProjectService.class);//获取 module 级别的 service,须要提供 module 对象MyModuleService moduleService = ModuleServiceManager.getService(module, MyModuleService.class);

八、长久化状态

咱们在应用 IDE 开始开发工作之前,总是要先在 settings 页面进行一些设置,且每次从新关上 IDE 后这些设置依然保留着,那么这些设置是如何保留下来的呢?

IntelliJ Platform 提供了一些 API,能够使 components 或 services 在每次关上 IDE 时依然应用之前的数据,即长久化其状态。

1、PropertiesComponent

对于一些简略大量的值,咱们能够应用 PropertiesComponent,它能够保留 application 级别和 project 级别的值。

上面办法用于获取 PropertiesComponent 对象:

//获取 application 级别的 PropertiesComponentPropertiesComponent.getInstance()//获取 project 级别的 PropertiesComponent,指定相应的 projectPropertiesComponent.getInstance(Project)propertiesComponent.setValue(name, value)propertiesComponent.getValue(name)

PropertiesComponent 保留的是键值对,因为所有插件应用的是同一个 namespace,强烈建议应用前缀来命名 name,比方应用 plugin id。

2、PersistentStateComponent

PersistentStateComponent 用于长久化比较复杂的 components 或 services,能够指定须要长久化的值、值的格局以及存储地位。

要应用 PersistentStateComponent 长久化状态:

  • 须要提供一个 PersistentStateComponent 接口的实现类(component 或 service),指定类型参数,重写 getState() 和 loadState() 办法
  • 类型参数就是要被长久化的类,它能够是一个 bean class,也能够是 PersistentStateComponent实现类自身。
  • 在 PersistentStateComponent 的实现类上,通过 @com.intellij.openapi.components.State 注解指定存储的地位

上面通过两个例子进行阐明:

class MyService implements PersistentStateComponent<MyService.State> {  //这里 state 是一个 bean class  static class State {    public String value;    ...  }  //用于保留以后的状态  State myState;  // 从以后对象里获取状态  public State getState() {    return myState;  }  // 从内部加载状态,设置给以后对象的相应字段  public void loadState(State state) {    myState = state;  }}
// 这里的 state 就是实现类自身class MyService implements PersistentStateComponent<MyService> {  public String stateValue;  ...  public MyService getState() {    return this;  }  public void loadState(MyService state) {    XmlSerializerUtil.copyBean(state, this);  }}

2.1、实现 State 类

a、字段要求

state 类中可能有多个字段,但不是所有字段都能够被长久化,能够被长久化的字段:

  • public 字段
  • bean 属性:提供 getter 和 setter 办法
  • 被注解的公有字段:应用 @Tag, @Attribute, @Property, @MapAnnotation, @AbstractCollection 等注解来自定义存储格局,个别在实现向后兼容时才思考应用这些注解

这些字段也有类型要求:

  • 数字(包含根底类型,如int,和封装类型,如Integer)
  • 布尔值
  • 字符串
  • 汇合
  • map
  • 枚举

如果不心愿某个字段被长久化,能够应用 @com.intellij.util.xmlb.annotations.Transient 注解。

b、结构器要求

state 类必须有一个默认结构器,这个结构器返回的 state 对象被认为是默认状态,只有当以后状态与默认状态不同时,状态才会被长久化。

2.2、定义存储地位

咱们能够应用 @State 注解来定义存储地位

@State(name = "PersistentDemo", storages = {@Storage(value = "PluginDemo.xml")})public class PersistentDemo implements PersistentStateComponent<PersistentDemo> {  ...}

name: 定义 xml 文件根标签的名称

storages: 一个或多个 @Storage,定义存储的地位

  • 若是 application 级别的组件运行调试时 xml 文件的地位: \~/IdeaICxxxx/system/plugins-sandbox/config/options正式环境时 xml 文件的地位: \~/IdeaICxxxx/config/options
  • 若是 project 级别的组件,默认为我的项目的 .idea/misc.xml,若指定为 StoragePathMacros.WORKSPACE\_FILE,则会被保留在 .idea/worksapce.xml

2.3、生命周期

  • loadState() 当组件被创立或 xml 文件被内部扭转(比方被版本控制系统更新)时被调用
  • getState() 当 settings 被保留(比方settings窗口失去焦点,敞开IDE)时,该办法会被调用并保留状态值。如果 getState() 返回的状态与默认状态雷同,那么什么都不会被保留。
  • noStateLoaded() 该办法不是必须实现的,当初始化组件,然而没有状态被长久化时会被调用

2.4、组件申明

长久化组件能够申明为 component,也能够申明为 service

申明为 service,plugin.xml 文件如下配置:

<extensions defaultExtensionNs="com.intellij">    <applicationService serviceImplementation="com.example.test.persisting.PersistentDemo"/>    <projectService serviceImplementation="com.example.test.persisting.PersistentDemo2"/>  </extensions>

代码中获取状态与获取 service 的形式一样:

PersistentDemo persistDemo = ServiceManager.getService(PersistentDemo.class);PersistentDemo2 persistDemo2 = ServiceManager.getService(project,PersistentDemo.class);

申明为 component,plugin.xml 文件如下配置:

<application-components>  <!--将长久化组件申明为component-->  <component>    <implementation-class>com.example.persistentdemo.PersistentComponent</implementation-class>  </component></application-components>

获取状态与获取 component 的形式一样:

public static PersistentComponent getInstance() {    return ApplicationManager.getApplication().getComponent(PersistentComponent.class);}public static PersistentComponent getInstance(Project project) {    return project.getComponent(PersistentComponent.class);}

九、插件依赖

开发插件时可能会用到其余插件,可能是 IDEA 绑定的,也可能是第三方的插件。

配置插件依赖须要将插件包增加到 SDK 的 classpath 中,并在 plugin.xml 配置。

  1. 确定插件包的地位如果插件是 IDEA 捆绑的插件,那么插件包在 IDEA 装置目录的 plugins/ 或 plugins//lib 下。如果插件是第三方或本人的,那么须要先运行一次 sandbox(其实咱们在运行调试插件的时候就是在运行sandbox)并从本地或插件仓库装置依赖插件。装置好后,插件包会放在 sandbox 目录下的 config/plugins/ 或 config/plugins//lib,查看 sandbox 目录:关上 IntelliJ Platform SDK 配置页面,其中 Sandbox Home 就是其目录。
  2. 将插件包增加到 SDK 的 classpath 中导航到 File | Project Structure | SDKs,抉择插件应用的 IntelliJ Platform SDK,点击右侧 + 号,在弹出的文件抉择框中抉择要依赖的插件包,点击 OK。

配置 plugin.xml在 plugin.xml 的 局部增加所依赖插件的id。

 org.jetbrains.kotlin

plugin id 能够从插件包的 plugin.xml 文件查看。

十、GUI 介绍

GUI 是 IntelliJ IDEA 提供的一个主动生成 java 布局代码的工具,它应用 JDK 中的 Swing 控件来实现 UI 界面。

应用步骤:

1.配置

配置 GUI首先关上 Settings 对话框,抉择 Editor | GUI Designer,如图,在 Generate GUI into: 有两个选项,生成 class 文件或 java 代码,咱们抉择生成 java 代码,因为建好布局后可能须要批改代码。其余默认即可。

2.创立 form

创立 form 文件form 文件用于记录界面布局。在相应的 package 上右键,抉择 New | GUI Form,如图,输出 form 文件名,个别与 java 文件名雷同,点击 OK 创立 form 与 java 文件。

3.面板介绍

编辑界面关上 form 文件,如图,通过拖拽控件来搭建布局。每个form文件布局的 root 控件都是一个 JPanel,可将该 root 对象传给须要该布局的类。留神:左下角的属性面板,只有当填写了 field name 属性时该控件的对象才会被当成成员变量,否则为局部变量。

4.构建

生成 java 代码搭建好布局后,点击 build

编译按钮,即可生成 java 的源码文件。

GUI 生成的办法名前后都有三个 $标识,当再次批改布局时,GUI 只会批改$ 标识的办法。

十一、源码剖析SmartConverter

SmartConverter -- POJO Object Converter

我的项目地址:https://github.com/zitiger/smartconverter

1、我的项目背景

在分层开发中,咱们总是面临着各种POJO(DTO,DO,JO,VO)对象之间的互相转换。当对象比较复杂时,编写转换代码耗时较多,且非常容易出错。以至于可能会呈现写一天代码,半天在写各种convert的囧境。

为了实现主动转换,呈现了BeanUtil和ModelMapper等解决方案。这些计划,在大量对象转换时,性能损耗能够疏忽,然而当转换数量达到一定量级时,这种损耗会对性能产生影响。

本插件能够主动生成POJO之间的转换代码,省去手工转换的麻烦,也不会损失性能。

2、装置

下载SmartConverter.zip,并在Intellij Idea中装置;

3、四个转换函数

  1. 把光标放到函数中,不能是函数内.
  2. 光标挪动到函数体内,按下⌘+N,在弹出的Generate菜单中选择Smart Converter;
  3. 插件主动生成一下四个转换函数
  • A -> B
  • B -> A
  • List-> List
  • List**-> List**

4、单个抓换函数

  1. 在编辑器中,确定返回值和参数,实现空转换函数;

     public static List<UserJO> toDTOList(List<UserDTO> userDTOList) { }
  2. 光标挪动到函数体内,按下⌘+N,在弹出的Generate菜单中选择Smart Converter;
  3. 插件依据入参和出参推断出须要转换的POJO。

5、插件特色

插件主动从转换函数的参数和返回值推断出转换POJO;

反对List之间的转换。

如果存在单个转换的函数,则间接应用

如果不存在单个转换的函数,创立单个转换函数

反对嵌套转换

6、源码解读

6.1.如何将ConvertGeneratorAction 增加到菜单

因为应用SmartConvert是应用alt+insert弹出或者右键点击Generate显示SmartConvertAction,所以依据前文的增加地位不难推断增加在弹出菜单EditorPopupMenu下,这个时候咱们能够从两个方向找他增加的地位。

首先从我的项目的配置文件进入找到plugin.xml下配置的action。由此不难看出它理论是增加在了GenerateGroup这个组上的

 <actions>        <group id="com.zitiger.plugin.converter.generate.group" popup="true">            <separator/>            <!-- Add your actions here -->            <action id="com.zitiger.plugin.converter.action.generator" class="com.zitiger.plugin.converter.action.ConvertGeneratorAction"                    text="Smart Converter" description="Smart Converter">                <keyboard-shortcut keymap="$default" first-keystroke="shift meta N"/>            </action>            <add-to-group group-id="GenerateGroup" anchor="last"/>        </group>    </actions>

这个时候咱们不难看出并没有中央援用这个组,这个时候咱们不防从应用的中央动手,咱们是右键点击Generate或者alt+insert弹出的EditorLangPopupMenu下的Generate的组。这个时候咱们去全局搜寻EditorPopupMenu

发现这里有一个增加到右键菜单下的

 <group id="EditorLangPopupMenu">      <separator/>      <group id="EditorPopupMenu.GoTo" popup="true">        <reference ref="ShowNavBar"/>        <reference ref="GotoDeclaration"/>        <reference ref="GotoImplementation"/>        <reference ref="GotoTypeDeclaration"/>        <reference ref="GotoSuperMethod"/>        <reference ref="GotoTest"/>      </group>      <reference ref="Generate"/>      <separator/>      <group id="EditorPopupMenu.Run">        <reference ref="RunContextPopupGroup"/>      </group>      <separator/>      <reference ref="VersionControlsGroup"/>      <separator/>      <reference ref="ExternalToolsGroup"/>      <add-to-group group-id="EditorPopupMenu" relative-to-action="CompareClipboardWithSelection" anchor="before"/>    </group>

点击后跳转的是

 <action id="Generate" class="com.intellij.codeInsight.generation.actions.GenerateAction"/>

GenerateAction的点击办法actionPerformed内动静生成了ActionGroup

JBPopupFactory.getInstance().createActionGroupPopup(CodeInsightBundle.message("generate.list.popup.title"), wrapGroup(getGroup(),dataContext,project),dataContext,JBPopupFactory.ActionSelectionAid.SPEEDSEARCH, false);

而getGroup() 通过指定groupid 获取到GenerateGroup的Action组

return (DefaultActionGroup)ActionManager.getInstance().getAction(IdeActions.GROUP_GENERATE);

6.2.如何实现实体\~\~\~\~转换

2.1Program Structure Interface (PSI)

https://plugins.jetbrains.com/docs/intellij/psi-files.html

程序结构接口,通常简称为 PSI,负责解析文件并创立语法和语义代码模型,为平台的泛滥性能提供反对。

PSI文件是构造的根,将文件内容示意为特定编程语言中元素的层次结构

PsiFile是所有 PSI 文件的公共基类,而特定语言的文件通常由其子类示意。例如PsiJavaFile类代表一个Java文件,类XmlFile代表一个XML文件。

2.2查看某一个文件的PSI构造

参考文档:PSI Viewer

https://www.jetbrains.com/help/idea/psi-viewer.html?_ga=2.203....

未配置开启查看PIS构造时如下图

开启查看PIS构造 找到idea装置门路下的bin目录下的idea.properties配置如下

idea.is.internal=true

开启后显示了View PSI Structure 和View PSI Structure of Current File

进入要查看构造的文件后点击View PSI Structure of Current File\
查看某一个文件的psi构造

2.3查看插件源码

进入ConvertGeneratorAction的点击事件办法不难看到如下的依据PSI获取以后类和办法的代码

2.4持续跟踪生成办法转换代码

这里次要是依据返回类型获取到了一个MethodGenerator并执行对应的generateCode办法

2.5MethodGenerator下的generateCode

MethodGenerator下的generateCode次要获取了以后办法的入参fromClass与toClass,并进行了字符串的组装和生成代码块。

\
PsiCodeBlock codeBlock = elementFactory.\
createCodeBlockFromText("{" + String.join("\n", statementList) + "}", psiClass);\
源码剖析就到这里,如果有趣味的同学能够自行深入分析并欢送补充。

十二、武魂交融

1.定位

想编写一个什么样的插件(性能)

插件要实现的能力是什么,eg:进行办法入参疾速转为出参、获取抉择的文本增加为笔记、idea激活弹出框、数据库Database...等。

2.拆解

实现插件须要具备哪些能力(性能拆解)

须要页面操作交互能力(java swing)

须要发送http申请能力(增加依赖的能力)

须要增加action的能力(插件须要放在哪里,插件的生命周期是什么等级的等。)

须要读写文件的能里(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file),"utf-8"));)

3.落地

3.1增加一个Action到右键EditorPopupMenu

创立一个action并继承AnAction

package com.test.action;import com.intellij.openapi.actionSystem.AnAction;import com.intellij.openapi.actionSystem.AnActionEvent;public class testAction extends AnAction {@Overridepublic void actionPerformed(AnActionEvent e) {    // TODO: insert action logic here    System.out.println("action点击触发办法 = " + e);}

}

 <actions>    <!-- Add your actions here -->    <action id="testAction" class="com.test.action.testAction" text="testAction" description="testAction">      <add-to-group group-id="EditorPopupMenu" anchor="before" relative-to-action="ShowIntentionsGroup"/>    </action>  </actions>

3.2发动网络申请获取数据

增加spring相干依赖到gradle

dependencies {    testCompile group: 'junit', name: 'junit', version: '4.12'    // https://mvnrepository.com/artifact/org.springframework/spring-web    implementation 'org.springframework:spring-web:5.1.13.RELEASE'    // https://mvnrepository.com/artifact/org.springframework/spring-core    implementation 'org.springframework:spring-core:5.1.13.RELEASE'    // https://mvnrepository.com/artifact/org.springframework/spring-beans    implementation 'org.springframework:spring-beans:5.1.13.RELEASE'}

应用spring-web下的RestTemplate创立网络申请工具(也能够间接应用RestTemplate)

package com.test.http;import org.springframework.http.HttpStatus;import org.springframework.http.ResponseEntity;import org.springframework.web.client.RestTemplate;public class HttpUtil {    public static ResponseEntity<Map> get(String url){        RestTemplate restTemplate = new RestTemplate();        try {            ResponseEntity<Map> forEntity = new RestTemplate().getForEntity(url, Map.class);            return forEntity;        } catch (Exception e) {            e.printStackTrace();            return  null;        }    }}

在需的中央触发网络申请获取数据

public class testAction extends AnAction {    @Override    public void actionPerformed(AnActionEvent e) {        // TODO: insert action logic here        System.out.println("action点击触发办法 = " + e);        ResponseEntity<Map> mapResponseEntity = HttpUtil.get("http://localhost:22200/getPerson");        System.out.println("action点击触发网络申请 = " + mapResponseEntity.toString());    }}

触发验证

3.3回显到idea界面

首先创立一个回显显示的界面

package com.test.view;import com.intellij.openapi.ui.DialogWrapper;import org.jetbrains.annotations.Nullable;import javax.swing.*;import java.awt.*;public class testDialog extends DialogWrapper {    JLabel label;    public testDialog(boolean canBeParent) {        super(canBeParent);        init();//初始化dialog        setTitle("题目");    }    @Override    protected @Nullable JComponent createCenterPanel() {        /*创立一个面板,设置其布局为边界布局*/        JPanel centerPanel = new JPanel(new BorderLayout());        /*创立一个文字标签,来承载内容*/        String text = "aaa11111测试回显内容";        label = new JLabel(text);        /* 设置首先大小*/        label.setPreferredSize(new Dimension(100, 100));        /*将文字标签增加的面板的正中间*/        centerPanel.add(label, BorderLayout.CENTER);        return centerPanel;    }    public void setLabelText(String text) {        label.setText(text);    }}

在action内触发申请网络获取内容并设置到显示的面板上。

public class testAction extends AnAction {    @Override    public void actionPerformed(AnActionEvent e) {        // TODO: insert action logic here        System.out.println("action点击触发办法 = " + e);        ResponseEntity<Map> mapResponseEntity = HttpUtil.get("http://localhost:22200/getPerson");        System.out.println("action点击触发网络申请 = " + mapResponseEntity.getBody());        testDialog testDialog=new testDialog(true);        testDialog.setLabelText(mapResponseEntity.getBody().toString());        testDialog.show();    }}

3.4乱码解决

像上图的题目等间接赋值汉字时会有乱码,从新编码进行解决(这种形式简略的汉字和汉字较少时能够)

 String encodeTitle = new String("题目".getBytes("gbk"), "UTF-8"); title = new EditorTextField(encodeTitle);

3.5获取选中的内容并回显

咱们从action中获取editor对象,在通过editor获取SelectionModel,在获取选中的文本。

弹窗提供一个从新设置抉择文本的办法 testDialog.setContent(selectedText);

 @Override    public void actionPerformed(AnActionEvent e) {        // TODO: insert action logic here        testDialog testDialog = new testDialog(true);        //获取以后编辑器对象        Editor editor = e.getRequiredData(CommonDataKeys.EDITOR);        //获取抉择的数据模型        SelectionModel selectionModel = editor.getSelectionModel();        //获取以后抉择的文本        String selectedText = selectionModel.getSelectedText();        System.out.println(selectedText);        testDialog.setContent(selectedText);        testDialog.show();    }

测试选中内容和回显内容如下图

3.6按钮响应

@Override    protected JComponent createSouthPanel() {        JPanel panel = new JPanel(new FlowLayout());        try {            String encodeTitle = new String("题目".getBytes("gbk"), "UTF-8");            JLabel title = new JLabel(encodeTitle);            String encodeBtnAdd = new String("按钮点击".getBytes("gbk"), "UTF-8");            JButton btnAdd = new JButton(encodeBtnAdd);            //按钮点击事件处理            btnAdd.addActionListener(e -> {                //获取题目                String titleStr = title.getText();                //获取内容                String contentStr = content.getText();                System.out.println("titleStr" + ":" + titleStr);                System.out.println("contentStr" + ":" + contentStr);                label.setText(getHttpText());            });            panel.add(title, BorderLayout.NORTH);            /* 设置首先大小*/            btnAdd.setPreferredSize(new Dimension(200, 100));            panel.add(btnAdd, BorderLayout.CENTER);        } catch (UnsupportedEncodingException e) {            e.printStackTrace();        }        return panel;    }    private String getHttpText() {        ResponseEntity<Map> mapResponseEntity = HttpUtil.get("http://localhost:22200/getPerson");        return mapResponseEntity.toString();    }

如图所示点击按钮拜访本地http服务获取数据后回显

3.7获取控件内数据

3.8保留数据到文件

能够应用java自身的流进行读写,也能够应用模板引擎进行,这里应用freemarker模版引擎\
3.8.1获取按钮点击事件后弹出目录抉择框抉择要保留的文件夹,首先须要革新弹窗的结构器传入以后action的事件Event,从event获取以后的工程

3.8.2按钮点击事件创立文件选择器

有人会有疑难,为什么这样就弹出了文件选择器,其实最初是一个FileChooser->FileChooserDialog

final FileChooserDialog chooser = FileChooserFactory.getInstance().createFileChooser(descriptor, project, parent);    return chooser.choose(project, toSelect);

3.8.3引入freemarker模版引擎依赖并进行文件创建保留

组织数据、获取模版、创立文件、执行创立文件

模版代码创立并获取上图中的组织数据model下的内容

3.9告诉(当有谬误或胜利是弹出告诉事件--IDEA的Event Log)

                       NotificationGroup notificationGroup = new NotificationGroup("testId", NotificationDisplayType.BALLOON, true);                        /**                         * content :  告诉内容                         * type  :告诉的类型,warning,info,error                         */                        Notification notification = notificationGroup.createNotification("测试告诉保留胜利", MessageType.INFO);                        Notifications.Bus.notify(notification);

3.10扩大某一个扩大点

增加一个自定义ToolWindow\
3.10.1创立一个toolwindow

package com.test.view;import com.intellij.openapi.wm.ToolWindow;import javax.swing.*;import java.text.SimpleDateFormat;import java.util.Date;public class MyToolWindow  {    private JButton hideButton;    private JLabel datetimeLabel;    private JPanel myToolWindowContent;    public MyToolWindow(ToolWindow toolWindow) {        init();        hideButton.addActionListener(e -> toolWindow.hide(null));    }    private void init() {        datetimeLabel = new JLabel();        datetimeLabel.setText(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));        hideButton = new JButton("勾销");        myToolWindowContent = new JPanel();        myToolWindowContent.add(datetimeLabel);        myToolWindowContent.add(hideButton);    }    public JPanel getContent() {        return myToolWindowContent;    }}

3.10.2创立ToolWindowFactory的实现类

package com.test.view;import com.intellij.openapi.project.Project;import com.intellij.openapi.wm.ToolWindow;import com.intellij.openapi.wm.ToolWindowFactory;import com.intellij.ui.content.Content;import com.intellij.ui.content.ContentFactory;import org.jetbrains.annotations.NotNull;public class toolWindowExt implements ToolWindowFactory {    @Override    public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) {        MyToolWindow myToolWindow = new MyToolWindow(toolWindow);        //获取内容工厂的实例        ContentFactory contentFactory = ContentFactory.SERVICE.getInstance();        //获取用于toolWindow显示的内容        Content content = contentFactory.createContent(myToolWindow.getContent(), "自定义tool window", false);        //给toolWindow设置内容        toolWindow.getContentManager().addContent(content);    }}

3.10.3申明扩大点对应的扩大

    <extensions defaultExtensionNs="com.intellij">        <!-- Add your extensions here -->        <toolWindow id="增加toolWindow"                    secondary="false"                    anchor="right" factoryClass="com.test.view.toolWindowExt">        </toolWindow>    </extensions>

十三、参考文档:

idea插件官网文档:https://plugins.jetbrains.com/docs/intellij/welcome.html

gradle官网文档:https://docs.gradle.org/current/userguide/userguide.html

freemarker:https://freemarker.apache.org/docs/

京东技术:https://cloud.tencent.com/developer/article/1348741

javaSwing:https://docs.oracle.com/javase/tutorial/uiswing/components/jc...

sdk-code-samples:https://github.com/JetBrains/intellij-sdk-code-samples

十四、其余插件文档传送门

idea插件开发经验总结(一):环境搭建

IDEA插件开发扼要教程

【IDEA插件开发】疾速入门系列01 开发一个简略的Idea插件

IDEA Plugin 插件怎么开发?

作者:京东衰弱 马仁喜

起源:京东云开发者社区