IntelliJ IDEA插件开发教程

IntelliJ IDEA插件开发教程

IntelliJ IDEA插件开发教程_第1张图片

我们在使用Android Studio开发的时候都会使用一些插件,来方便我们的开发工作,提升工作效率。IntelliJ IDEA 可能有的人已经很熟悉,强大的开源IDE。Android Studio 就是基于IDEA社区版开发的。下载安装后打开软件我们就会发现,IntelliJ IDEA的界面、菜单、状态栏以及Preference 和 安卓开发ide android studio 几乎是一模一样的。

   IntelliJ IDEA插件开发教程_第2张图片

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

IntelliJ IDEA插件类型

在介绍IDEA插件开发流程之前,先了解下idea插件有哪些类型及其作用。

IntelliJ IDEA插件开发教程_第3张图片

从官方文档可以看出,idea开发的插件最常见的有5种类型。分别是

1、UI主题

UI主题使设计人员能够自定义内置IDE UI元素的外观。自定义UI主题可以:

  • 替代图标,
  • 更改图标和UI控件的颜色,
  • 更改UI控件的边框和插图,
  • 提供自定义编辑器方案,
  • 添加背景图像。

可供下载的UI主题 说明创造的可能性。

2、自定义语言支持

自定义语言支持提供了使用特定编程语言的基本功能。包括:

  • 文件类型识别(File type recognition)
  • 词法分析(Lexical analysis)
  • 词法高亮显示(Syntax highlighting)
  • 格式化(Formatting)
  • 代码洞察和代码完成(Code insight and code completion)
  • 检查和快速修复(Inspections and quick-fixes)
  • 意图行动(Intention actions)

详情点击:自定义语言支持插件开发

3、框架集成(Framework Integration)

框架集成由改进的代码洞察特性组成,这些特性是给定框架的典型特征,以及直接从IDE使用特定于框架的功能的选项。有时它还包括自定义语法或DSL的语言支持元素

  • 具体代码的洞察力(Specific code insight)
  • 直接访问特定于框架的功能(Direct access to framework-specific functionality)

参考案例: Struts 2 plugin开发

4、工具继承(Tool Integration)

工具集成使直接从IDE操作第三方工具和组件成为可能,而无需切换上下文

这就意味着:

  • 额外行动的实施(Implementation of additional actions)
  • 相关的UI组件(Related UI components)
  • 访问外部资源(Access to external resources)

参考案例: Gerrit integration

5、用户界面插件(User interface add-ons)

此类别中的插件对IDE的标准用户界面应用各种更改. 一些新添加的组件是交互式的,并且提供了新的功能,而另一些则仅限于可视化修改.

 

插件开发方式

Mac IDEA配置阿里云国内镜像

  • 1. 下载maven
  • 2. 打开IDEA配置Maven
  • 3. 编辑镜像地址
  • 4. 重启IDEA

IDEA会自动装配IDEA版本的maven,所以只需要配置settings.xml就可以实现变更国内镜像。

 

IDEA插件有两种开发方式:1、DevKit方式 2、Gradle方式

一、DevKit方式

Plugin DevKit_是一个捆绑的IntelliJ IDEA插件,用于使用IntelliJ IDEA为IntelliJ平台开发插件自己的构建系统. 它提供了自定义SDK类型和一组用于在IDE中构建插件的操作.早期的插件项目都是DevKit方式。

IntelliJ IDEA插件开发教程_第4张图片

DevKit的plugin项目,项目工程的配置文件是imi文件。

IntelliJ IDEA插件开发教程_第5张图片

Sandbox

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

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

IntelliJ IDEA插件开发教程_第6张图片

Run/Debug

新建或修改运行/调试配置,选择使用的module后,可直接点击图标运行或调试。

IntelliJ IDEA插件开发教程_第7张图片

Prepare Plugin For Deployment

如果项目中没有任何依赖,打出来的插件包是jar包

IntelliJ IDEA插件开发教程_第8张图片

如果项目中有依赖其他插件或Library,则打出来的插件包是zip文件

IntelliJ IDEA插件开发教程_第9张图片

插件的配置文件是plugin.xml,是整个插件的核心,见Gradle方式中的plugin.xml说明。

二、Gradle方式

Gradle插件是构建IntelliJ插件的推荐解决方案.该插件负责处理插件项目的依赖性 - 基本IDE和插件可能依赖的其他插件.  它还提供了使用插件运行IDE并将插件发布到JetBrains插件存储库的任务.对于新项目,官方推荐Gradle方式。

IntelliJ IDEA插件开发教程_第10张图片

Gradle的plugin项目,项目工程的配置文件是build.gradle文件

IntelliJ IDEA插件开发教程_第11张图片

gradle插件源码及版本信息

IntelliJ IDEA插件开发教程_第12张图片

兼容性

IntelliJ IDEA插件开发教程_第13张图片

如果依赖的插件版本和intellij版本不兼容,编译则会报如下图错误

IntelliJ IDEA插件开发教程_第14张图片

关于兼容性,官方也作了说明

IntelliJ IDEA插件开发教程_第15张图片

例如koltin插件,可以去JetBrains Plugins Repository查询各版本及其兼容的IDE版本

IntelliJ IDEA插件开发教程_第16张图片

IntelliJ IDEA插件开发教程_第17张图片

可以根据项目需要添加更多的配置

IntelliJ IDEA插件开发教程_第18张图片

plugin.xml

plugin.xml是整个项目的核心配置文件,一些Action、Extension组件需要在这里先进行注册和声明,和Android 开发的AndroidManifest.xml注册组件类似。

 


  
  MyPlugin
  
  com.example.plugin.myplugin
  
  my plugin description
  
  Initial release of the plugin.
  
  1.0
  
  
  
  MyFirstPlugin

com.jetbrains.php
com.intellij.modules.platform
  
  
  
  
    
      
      com.foo.Component1Interface
      
      com.foo.impl.Component1Impl
    
  
  
  
    
      
      com.foo.Component2
    
  
  
  
    
      com.foo.Component3
    
  
  
  
    ...
  
  
  
    ...
  
  
  
    ...
  

Action

概念

Action 用于描述一个动作、行为,可以通过快捷键、点选的方式进行触发。一个 Action 是一个 class,是 AnAction 的子类,actionPerformed 方法在菜单Item或者标题栏按钮被选中的时候会被调用。

 

Action 允许添加到右键菜单或者Toolbar菜单上面。Action也可以成组添加到具体的一个Group下面。

创建action

继承AnAction,并重写actionPerformed方法,此方法是action触发执行的入口。

IntelliJ IDEA插件开发教程_第19张图片

注册action

IntelliJ IDEA插件开发教程_第20张图片

  • Action ID: action 唯一 id,推荐使用全类名
  • Class Name: 要被创建的 action class 名称
  • Name: menu item 的文本
  • Description: action 描述,toolbar 上按钮的提示文本,可选
  • Add to Group:选择新 action 要被添加到的 action group(Groups, Actions)以及相对其他 actions 的位置(Anchor),比如 EditMenu 就是顶部菜单栏的 Edit 菜单。
  • Keyboard Shortcuts:指定 action 的第一和第二快捷键

 

Components

插件组件是插件集成的基础概念,我们插件定义的每一个组件(component) 应该在配置文件中配置相关的接口(可选)和实现类。有三种组件:

Components 类型

Components 接口类型 描述
Application Component IDEA启动时会初始化,IDEA生命周期中仅存在一个实例。
Project Component IDEA 会为每一个 Project 实例创建一个 Project 级别的component
Module Component IDEA 会为每一个 Project 的加载过的Module实例Module级别的component

创建 Component

Application Component

public class TestApplication implements ApplicationComponent {
    public TestApplication() {
    }
    @Override
    public void initComponent() {
        // TODO: insert component initialization logic here
    }
    @Override
    public void disposeComponent() {
        // TODO: insert component disposal logic here
    }
    @Override
    @NotNull
    public String getComponentName() {
        return "TestApplication";
    }
}

Project Component

public class TestProject implements ProjectComponent {
    public TestProject(Project project) {
    }
    @Override
    public void initComponent() {
        
    }
    @Override
    public void disposeComponent() {
        
    }
    @Override
    @NotNull
    public String getComponentName() {
        return "TestProject";
    }
    @Override
    public void projectOpened() {
        // called when project is opened
    }
    @Override
    public void projectClosed() {
        // called when project is being closed
    }
}

Module Component

public class TestModule implements ModuleComponent {
    public TestModule(Module module) {
    }
    @Override
    public void initComponent() {
        // TODO: insert component initialization logic here
    }
    @Override
    public void disposeComponent() {
        // TODO: insert component disposal logic here
    }
    @Override
    @NotNull
    public String getComponentName() {
        return "TestModule";
    }
    @Override
    public void moduleAdded() {
        // Invoked when the module corresponding to this component instance has been completely
        // loaded and added to the project.
    }
}

注册 Components

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

Application Component


  
    com.example.test.TestApplication
  

Project Component


    
        com.example.test.TestProject
    

Module Component


  
    com.example.test.TestModule
  

注意:

  1. 一个 interface-class 不能有多个 implementation-class。
  2. 若组件没有创建 interface 类,而是直接实现了 ApplicationComponent 等接口,interface 和 implementation 可以指定为同一个类。
  3. 每一个组件都应该有一个唯一的名字,通过 getComponentName() 返回,推荐使用 . 格式。

获取 Component 实例

获取 Application Component

通过 ApplicationManager获取

//获取application容器中的组件
TestApplication testApplication = ApplicationManager.getApplication().getComponent(TestApplication.class);

获取project

通过action获取

DataContext dataContext = anActionEvent.getDataContext();
// DataConstants 被标记为 @deprecated
mProject = (Project)dataContext.getData(DataConstants.PROJECT);

Project project = anActionEvent.getData(PlatformDataKeys.PROJECT);

通过PsiElement获取

/**
 * The common base interface for all elements of the PSI tree.
 * 

* Please see IntelliJ Platform Architectural Overview * for high-level overview. */ public interface PsiElement extends UserDataHolder, Iconable { /** * The empty array of PSI elements which can be reused to avoid unnecessary allocations. */ PsiElement[] EMPTY_ARRAY = new PsiElement[0]; ArrayFactory ARRAY_FACTORY = count -> count == 0 ? EMPTY_ARRAY : new PsiElement[count]; /** * Returns the project to which the PSI element belongs. * * @return the project instance. * @throws PsiInvalidElementAccessException if this element is invalid */ @NotNull @Contract(pure=true) Project getProject() throws PsiInvalidElementAccessException; }

获取module

通过project获取

TestModule testModule = project.getComponent(TestModule.class);

通过action获取

DataContext dataContext = anActionEvent.getDataContext();
// DataConstants 被标记为 @deprecated
mModule =(Module)dataContext.getData(DataConstants.MODULE);

Module module = anActionEvent.getData(LangDataKeys.MODULE);
@Deprecated
@SuppressWarnings({"JavadocReference"})
public interface DataConstants {
  /**
   * Returns {@link com.intellij.openapi.project.Project}
   *
   * @deprecated use {@link PlatformDataKeys#PROJECT} instead
   */
  @Deprecated String PROJECT = CommonDataKeys.PROJECT.getName();

  /**
   * Returns {@link com.intellij.openapi.module.Module}
   *
   * @deprecated use {@link com.intellij.openapi.actionSystem.LangDataKeys#MODULE} instead
   */
  @Deprecated @NonNls String MODULE = "module";
}

Component 生命周期

Component加载

Application 级别的 components 在 IDEA 启动时加载,Project 和 Module 级别的 components 在项目启动时共同加载。

一个组件加载过程:

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

ModuleComponent 的生命周期方法中比 ProjectComponent 多一个 moduleAdded(),用于通知 module 已经被添加到 project 中。

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

示例:

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

Component 卸载

一个组件卸载过程:

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

持久化

对于IDEA插件的一些配置,一般情况下都不会希望用户每次使用插件时都要配置一遍,所以 IntelliJ Platform 提供了一些 API,来做数据的持久化。

PropertiesComponent

对于简单的 key - value 数据结构,可以使用 PropertiesComponent,用于保存 application 和 project 级别的数据。用法如下:

//获取 application 级别的 PropertiesComponent
PropertiesComponent propertiesComponent = PropertiesComponent.getInstance();
//获取 project 级别的 PropertiesComponent,指定相应的 project
PropertiesComponent propertiesComponent = PropertiesComponent.getInstance(Project);
// set & get
propertiesComponent.setValue(name, value)
propertiesComponent.getValue(name)

所有的 PropertiesComponent设置的键值对共用同一个namespance,所以需要避免key冲突。

PersistentStateComponent

对于复杂的数据结构,可以使用 PersistentStateComponent,PersistentStateComponent 可以指定持久化的存储位置。

  • 需要提供一个 PersistentStateComponent 的实现类,T代表需要持久化的数据结构类型,然后重写 getState() 和 loadState() 方法。T可以是任意的类,或者是实现类自身。
  • 若需要指定存储位置,则在实现类上增加 @State 注解
  • 若不希望其中的某个字段被持久化,可以在该字段上增加 @Transient 注解
@State(name = "PersistentStateComponentImpl", 
       storages = {
           @Storage(value = "PersistentStateComponentImpl.xml")
       })
class PersistentStateComponentImpl implements PersistentStateComponent {
    State myState;
    // 当组件被创建或 xml 文件被外部改变(比如git更新)时被调用
    public State getState() {
        return myState;
    }
    
    // 当 settings 被保存时,该方法会被调用并保存状态值。
    public void loadState(State state) {
        myState = state;
    }
}
class State {
    public State() {}
    
    // 支持基本的数据类型、Map、Collection、enum
    public String value;
    
    @Transient
    public String disableSave;
}
  • 若是 application 级别的组件
    • 运行调试时 xml 文件的位置: ~/IdeaICxxxx/system/plugins-sandbox/config/options
    • 正式安装时 xml 文件的位置: ~/IdeaICxxxx/config/options
  • 若是 project 级别的组件
    • 默认为项目的 .idea/misc.xml
    • 若指定为 StoragePathMacros.WORKSPACE_FILE,则会被保存在 .idea/worksapce.xml

注册持久化

持久化组件可以声明为 Service,也可以声明为 Component,声明为 Component 则与前面介绍注册与获取的方法一致,声明为Service如下:

获取方式为:






扩展以及扩展点

Intellij 平台提供了允许一个插件与其他插件或者 IDE 交互的 extensions 以及 extension points 的概念。

extensions

如果你想要你的插件扩展其他插件或者 Intellij 平台,你必须声明一个或多个 extensions

extension points

如果你想要你的插件可以被其他插件使用,那么你必须在你的插件内声明一个或多个扩展点(extension points)。每个扩展点定义了允许访问这个点的类或者接口。

注册 extensions 以及 extension points

你可以在你的 plugin.xml 中的 块中定义 extensions 以及 extension points。

    
        
    

GUI

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

IntelliJ IDEA插件开发教程_第21张图片

IntelliJ IDEA插件开发教程_第22张图片

IntelliJ IDEA插件开发教程_第23张图片

面板编辑完成之后,点击Toolbar工具条那里的编译按钮。编译完成后,GUI的代码会生成在 对应的 Java文件里面。

IntelliJ IDEA插件开发教程_第24张图片

Gradle Task

Gradle task 和 android studio的 类似,常用的也是打包、调试和发布task,只不过task 名不同。

IntelliJ IDEA插件开发教程_第25张图片

更多说明和示例可见JetBrains/gradle-intellij-plugin

buildPlugin

buildPlugin  构建插件,常说的打包 插件包默认输出目录为build/distributions/插件名.zip

IntelliJ IDEA插件开发教程_第26张图片

runIde

runIde 调试运行

现在双击runIde即可调出另外一个安装了这个插件的IDEA界面,然后可以看运行结果进行调试。 runIde还支持debug模式,不过运行时要右击选择:

IntelliJ IDEA插件开发教程_第27张图片

如果setIdeDirectory设置为Android studio路径,则调试过程如下,会重新启动一个main(Android studio)

 

IntelliJ IDEA插件开发教程_第28张图片

publishPlugin

publishPlugin 发布插件

上传插件市场

1、官方插件市场

官方文档:http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/publishing_plugin.html

IntelliJ IDEA插件开发教程_第29张图片

2、自定义个人/公司插件存储服务器

IntelliJ IDEA插件开发教程_第30张图片

   IntelliJ IDEA插件开发教程_第31张图片

Install

如果要安装的插件非JetBrains官方插件市场发布的插件,则需要配置插件存储服务器地址,才能在Marketplace搜索到然后install。

搜索但还未安装时,右侧会显示updatePlugins.xml文件中配置的插件信息;从硬盘选择安装或已安装的插件,会显示plugin.xml文件中配置的插件信息。

IntelliJ IDEA插件开发教程_第32张图片

最后

到这里,一个IDE插件的的开发、配置、调试运行、发布、安装的基本流程就介绍完毕了。插件的应用非常广泛,可以应用所有基于IntelliJ Platform开发的IDE软件。如果想深入学习IDE插件开发,建议阅读官方文档。官方文档内容更加全面丰富,但是由于文档精炼、简洁,所以在学习过程中,很多地方需要我们去揣摩、整理和总结。

你可能感兴趣的:(intellij,idea)