gitee地址:https://gitee.com/jyq_18792721831/studyplugin.git
​​​idea插件开发入门​​​​idea插件开发–配置​​​​idea插件开发–服务-翻译插件​​​​idea插件开发–组件–编程久坐提醒​

idea插件开发--配置

介绍

很多时候,我们使用插件都需要配置一些信息,只有配置了信息,插件才能更好的进行服务,增加我们的效率。
但是又不能每次使用插件都进行配置,即使我们使用对象将配置缓存下来,但是当idea重启后,又必须重新进行配置,这样的插件是没人使用的。
所以我们需要将配置进行持久化,当idea进行重启后,也能度过读取持久化的配置,再次提供服务。
idea插件提供的持久化方案有多种,这只是最简单,最基础的一种。
即使是最简单,最基础的这种,也能满足绝大多数的配置持久化需求。

存储接口

​PropertiesComponent​​公有三个子类

idea插件开发--配置_intellij idea


首先​​PropertiesComponentImpl​​实现了配置的存储和读取。

然后​​AppPropertiesComponentImpl​​​则是全局的,所有的idea实例只有一份相同的配置的实现,即​​AppPropertiesComponentImpl​​的作用域是对全部的idea生效。所有的idea实例,只要key相同,则值也是相同的。

而​​ProjectPropertiesComponentImpl​​的作用域则是对项目进行隔离,每个idea实例都可以有相同的key,而且对应值可以不同。

具体使用哪一种作用域,则是在编码时的​​getInstance​​​方法决定,​​getInstance​​​方法是个重载方法,一个有参数,参数是​​Project​​,一个没有参数。

很明显,没有参数的就是全局的,作用域是App,有参数的是就是部分的,作用域是Project。

配置界面

插件的配置一般是setting的界面中配置的

idea插件开发--配置_java_02


要想自己的插件也在这里配置,就必须自己决定配置的界面,以及告诉idea,如何展示。

​SearchableConfigurable​​接口。

实现了​​SearchableConfigurable​​接口,就会在settings下面的tools下面展示。

​SearchableConfigurable​​定义了一些方法:

  • getId:获取id,插件内不能重复
  • getDisplayName:获取tools下面展示的名字
  • createComponent:获取展示的界面
  • isModified:是否可用,是否可配置
  • apply:配置完成后,点击了确定或者应用后执行的逻辑

实例

目标

配置一个测试的值,然后使用Action触发读取,并使用通知展示。

创建项目

一定要创建项目,因为当一个项目中存在多个模块,且多个模块都是插件的时候,就存在无法直接启动和调试的问题。

国内开发插件是有点难受,使用gradle的方式吧,依赖好管理,但是idea的sdk实在下载不下来,而且,gradler管理工具也不咋会用。

算了,还是老老实实的用最原始的方式,也是最靠谱的方式。

idea插件开发--配置_idea插件配置保存_03


创建好后,配置​​plugin.xml​​​(其实就是将helloword的​​plugin.xml​​​拷贝过来,修改id和name即可,helloword见​​idea插件开发入门​​)

<extensions defaultExtensionNs="com.intellij">
<!-- Add your extensions here -->
<!-- displayType只有这四种类型, id 在一个插件内不能重复,isLogByDefault表示是否在 Event Log 中记录 -->
<notificationGroup displayType="BALLOON" id="helloword.notification.balloon" isLogByDefault="false"/>
</extensions>

<actions>
<!-- Add your actions here -->
<action id="com.study.plugin.simpleconfig" class="com.study.plugin.simpleconfig.action.TestConfigAction" text="TestConfigAction"
description="TestConfigAction">
<add-to-group group-id="ToolsMenu" anchor="last"/>
<keyboard-shortcut keymap="$default" first-keystroke="ctrl alt SEMICOLON"/>
</action>
</actions>

项目结构如图

idea插件开发--配置_intellij idea_04

工具类封装

​NotificationUtil​​ 封装三种类型的通知

import com.intellij.notification.Notification;
import com.intellij.notification.NotificationGroup;
import com.intellij.notification.NotificationGroupManager;
import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications;

public class NotificationUtil {

// 获取通知组管理器
private static NotificationGroupManager manager = NotificationGroupManager.getInstance();

// 获取注册的通知组
private static NotificationGroup balloon = manager.getNotificationGroup("helloword.notification.balloon");

public static void info(String msg) {
Notification notification = balloon.createNotification(msg, NotificationType.INFORMATION);
Notifications.Bus.notify(notification);
}

public static void warning(String msg) {
Notification notification = balloon.createNotification(msg, NotificationType.WARNING);
Notifications.Bus.notify(notification);
}

public static void error(String msg) {
Notification notification = balloon.createNotification(msg, NotificationType.ERROR);
Notifications.Bus.notify(notification);
}
}

接着把插件的id,name等等的常量做个封装

public class SimpleConfigKeys {
public static final String SETTING_ID = "com.study.plugin.simpleconfig.setting.id";
public static final String SETTING_NAME = "简单配置测试";
public static final String SETTING_TEST_CONFIG_KEY = "text.config";
}

还有最重要的一个,配置值的存储工具类

import com.intellij.ide.util.PropertiesComponent;

public class SimpleConfigUtil {

private static final PropertiesComponent propertiesComponent = PropertiesComponent.getInstance();

public static String getString(String key) {
// key
// key defaultValue
return propertiesComponent.getValue(key);
}

public static void save(String key, Object value) {
if (value instanceof String) {
save(key, (String) value);
} else if (value instanceof Integer) {
save(key, (int) value);
} else if (value instanceof Boolean) {
save(key, (boolean) value);
} else if (value instanceof Float) {
save(key, (float) value);
} else {
NotificationUtil.error("type error : " + value);
}
}

private static void save(String key, String value) {
// key value
propertiesComponent.setValue(key, value);
}

private static void save(String key, int value) {
// key value defaultValue
propertiesComponent.setValue(key, value, 0);
}

private static void save(String key, boolean value) {
// key value
// key value defaultValue
propertiesComponent.setValue(key, value);
}

private static void save(String key, float value) {
// key value defaultValue
propertiesComponent.setValue(key, value, 0.0f);
}

}

配置存储全局作用域即可。

setting界面

创建UI

idea插件开发--配置_ide_05


增加后,会生成java类和form文件

idea插件开发--配置_intellij-idea_06

界面结构如下,需要什么,可以拖动控件放到需要的位置上

idea插件开发--配置_ide_07


最顶层是一个JPanel,属性名字是rootPanel

idea插件开发--配置_java_08


为了方便管理,我们增加一个二级Pannel,不需要名字,增加边框

idea插件开发--配置_java_09


选中Panel,并设置border的属性即可

二级Panel下有两个控件:JLabel,用于展示,JTextField,用于输入。

因为我们需要获取输入的值,所以需要给JTextField设置名字。

此时查看java类,是没有代码生成的,我们需要idea帮我们将form转为java代码

idea插件开发--配置_idea插件配置保存_10


然后设置module的启动配置

idea插件开发--配置_idea插件配置保存_11


有了启动配置后,进行编译,编译成功后,java类中就会自动生成代码

idea插件开发--配置_java_12


前面说过,我们需要获取输入框的控件,然后获取输入的值,设置动作监听等等,所以使用lombok将JTextField控件暴露出去

idea插件开发--配置_java_13


将lombok加入到classpath中(lombok在高版本的idea中自动支持)

加入后会下载相关的jar,有可能会非常慢

idea插件开发--配置_intellij-idea_14


需要注意,rootJPanel也需要增加getter方法

idea插件开发--配置_intellij-idea_15


虽然自动生成了getRootComponent方法,但是这个方法不是给我们编码用的

idea插件开发--配置_java_16


注释的很明白,不要在你的代码中使用这些方法

idea插件开发--配置_ide_17

在java文件中,右键,自动生成测试方法

idea插件开发--配置_ide_18


idea插件开发--配置_java_19


一定要给最顶层的JPanel设置属性名字,才能自动生成测试方法

idea插件开发--配置_idea插件配置保存_20


你的UI就会展示出来

idea插件开发--配置_intellij idea_21


如果还设置了动作监听,也能进行测试的,按钮什么的,都可以进行测试。

配置界面到setting中

写好了界面,我们就需要把界面配置到setting中,也就是需要编写实现​​SearchableConfigurable​​的类了

import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.options.SearchableConfigurable;
import com.intellij.openapi.util.NlsContexts;
import com.study.plugin.simpleconfig.ui.SimpleConfigSettingUI;
import com.study.plugin.simpleconfig.util.NotificationUtil;
import com.study.plugin.simpleconfig.util.SimpleConfigKeys;
import com.study.plugin.simpleconfig.util.SimpleConfigUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.JComponent;
import javax.swing.JTextField;

public class SimpleConfigSetting implements SearchableConfigurable {

private SimpleConfigSettingUI form = new SimpleConfigSettingUI();

@Override
public @NotNull
@NonNls
String getId() {
return SimpleConfigKeys.SETTING_ID;
}

@Override
public @NlsContexts.ConfigurableName String getDisplayName() {
return SimpleConfigKeys.SETTING_NAME;
}

@Override
public @Nullable
JComponent createComponent() {
return form.$$$getRootComponent$$$();
}

@Override
public boolean isModified() {
return true;
}

@Override
public void apply() throws ConfigurationException {
JTextField simpleConfigInput = form.getSimpleConfigInput();
String inputValue = simpleConfigInput.getText();
SimpleConfigUtil.save(SimpleConfigKeys.SETTING_TEST_CONFIG_KEY, inputValue);
NotificationUtil.info("save " + inputValue + " success!");
}
}

其实很简单,就是把输入框中的值,保存到持久化中。
编写完成后,需要将我们编写的类配置到idea中。
可以把整个idea想象成一个spring的项目,在spring中,是spring去管理对象实例的,在idea中,也类似差不多。在spring中有注解和xml方式注入bean,在idea中主要是在​​​plugin.xml​​​中将我们自己写的实例注入到idea中的。
所以需要将​​​SearchableConfigurable​​接口的实现类的实例,注入到idea中

<extensions defaultExtensionNs="com.intellij">
<!-- Add your extensions here -->
<!-- displayType只有这四种类型, id 在一个插件内不能重复,isLogByDefault表示是否在 Event Log 中记录 -->
<notificationGroup displayType="BALLOON" id="simpleconfig.notification.balloon" isLogByDefault="false"/>
<applicationConfigurable parentId="tools" instance="com.study.plugin.simpleconfig.config.SimpleConfigSetting"
id="com.study.plugin.simpleconfig.setting.config.id" displayName="测试配置菜单"/>
</extensions>

可以看到我们注入的是全局的配置,如果是项目的配置,就需要使用​​projectConfigurable​​​的标签了,这里面配置的类都是​​configurable​​接口的实现类。

读取配置

读取配置是在Action中的,也很简单,从持久化工具类中获取值,然后通知输出即可

import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.study.plugin.simpleconfig.util.NotificationUtil;
import com.study.plugin.simpleconfig.util.SimpleConfigKeys;
import com.study.plugin.simpleconfig.util.SimpleConfigUtil;

public class TestConfigAction extends AnAction {

@Override
public void actionPerformed(AnActionEvent e) {
String configValue = SimpleConfigUtil.getString(SimpleConfigKeys.SETTING_TEST_CONFIG_KEY);
if(StringUtil.isEmpty(configValue)) {
NotificationUtil.error("empty");
}
NotificationUtil.info(configValue);
}
}

验证

至此,整个实例就完成了,完整的项目结构如下

idea插件开发--配置_java_22


启动测试下(注意启动正确的module)

首先是setting的配置页

idea插件开发--配置_intellij-idea_23


可以看到,这里配置的就是我们在​​plugin.xml​​中配置的数据生效了。

接下来是配置界面

idea插件开发--配置_ide_24


也是一样的,我们试着配置

idea插件开发--配置_intellij-idea_25


然后使用tools下的菜单触发

idea插件开发--配置_intellij idea_26


因为我们使用的是唯一的沙盒,所有会有helloword的插件。

idea插件开发--配置_idea插件配置保存_27


点击,发现和预期的一样,通知里面是我们配置的值。

接下来重启idea(沙盒的)

idea插件开发--配置_idea插件配置保存_28


可以看到,重启沙盒的idea后,原本开发的idea的调试关联就丢失了

idea插件开发--配置_ide_29


在非调试的状态下,重新触发读取操作,发现还是能够读取到我们配置的值,即使我们重启过idea(沙盒)

idea插件开发--配置_intellij idea_30


这样就达到了我们的目的。

扩展

如果我想每次配置的时候,都把已有的配置放在输入框中呢?

因为现在打开配置,默认是空的,这样如果打开配置,直接点击确定,会把空值写入。

idea插件开发--配置_intellij idea_31


要实现这个小功能,也很简单,在获取配置的JPanel的之前,将配置中的值,设置到控件中即可。

idea插件开发--配置_intellij idea_32


还记得吗,我们之前配置的值是“测试1”,现在打开配置界面,看看输入框中是不是

idea插件开发--配置_intellij-idea_33


预期一致。

打包

打包后会生成可安装的压缩包

idea插件开发--配置_java_34


和helloword中的不同,helloword中的是jar包,这里是zip包。

打包后生成jar包还是zip包,取决于是否有第三方依赖。

在simpleconfig中,我们有lombok的第三方依赖,在打包的时候,会把lombok的包也打进去,所以是zip包,在helloword中,没有第三方包,所以是jar包。