如何persist/read-back 运行 配置Intellij插件中的参数

How to persist/read-back Run Configuration parameters in Intellij plugin

我正在制作一个基本的 IntelliJ 插件,它允许用户定义 运行 配置(按照 [1] 处的教程),并使用所说的 运行 配置来执行在远程服务器上的编辑器。

我的 运行 配置很简单(3 个文本字段),并且一切正常,但是,在编辑 运行 配置后,单击 "Apply" 或 "OK" 更改值后,输入的值将丢失。

保存和回读值的正确方法是什么(重新打开 运行 配置以及调用 运行 配置的 运行ner 时) ?看起来我可以尝试使用 [2] 创建自定义持久性,但是,插件框架似乎应该已经有办法处理这个问题,或者至少在按下 Apply/OK 时挂钩。

[1] https://www.jetbrains.org/intellij/sdk/docs/tutorials/run_configurations.html

[2] https://www.jetbrains.org/intellij/sdk/docs/basics/persisting_state_of_components.html

参见com.intellij.execution.configurations.RunConfigurationBase#readExternal 以及com.intellij.execution.configurations.RunConfigurationBase#loadState 和com.intellij.execution.configurations.RunConfigurationBase#writeExternal

希望这个 post 对于 IntelliJ 插件开发的新手来说更清楚一点,并说明了如何实现 persisting/loading 运行 配置。请通读代码注释,因为这是大部分解释的地方。

现在 SettingsEditorImpl 是我对 SettingsEditor 抽象 class 的自定义实现,同样,RunConfigurationImpl 是我对 RunConfigiration 的自定义实现摘要 class.

首先要做的是通过 SettingsEditorImpl 上的自定义 getter 公开表单字段(即 getHost()

public class SettingsEditorImpl extends SettingsEditor<RunConfigurationImpl> {
    private JPanel configurationPanel; // This is the outer-most JPanel
    private JTextField hostJTextField;

    public SettingsEditorImpl() {
        super();
    }

    @NotNull
    @Override
    protected JComponent createEditor() {
        return configurationPanel;
    }

    /* Gets the Form fields value */
    private String getHost() {
        return hostJTextField.getText();
    }

    /* Copy value FROM your custom runConfiguration back INTO the Form UI; This is to load previously saved values into the Form when it's opened. */
    @Override
    protected void resetEditorFrom(RunConfigurationImpl runConfiguration) {
        hostJTextField.setText(StringUtils.defaultIfBlank(runConfiguration.getHost(), RUN_CONFIGURATION_HOST_DEFAULT));
    }

    /* Sync the value from the Form UI INTO the RunConfiguration which is what the rest of your code will interact with. This requires a way to set this value on your custom RunConfiguration, ie. RunConfigurationImpl@#setHost(host)  */
    @Override
    protected void applyEditorTo(RunConfigurationImpl runConfiguration) throws ConfigurationException {
        runConfiguration.setHost(getHost());
    }
}

现在,支持表单 UI 的自定义 SettingsEditor 设置为同步字段值 In 和 Out of itself。请记住,自定义 运行Configuration 将实际代表此配置; SettingsEditor 实现仅表示 FORM(细微差别,但很重要)。

现在我们需要自定义 运行配置 ...

/* Annotate the class with @State and @Storage, which is used to define how this RunConfiguration's data will be persisted/loaded. */
@State(
        name = Constants.PLUGIN_NAME,
        storages = {@Storage(Constants.PLUGIN_NAME + "__run-configuration.xml")}
)
public class RunConfigurationImpl extends RunConfigurationBase {
     // Its good to 'namespace' keys to your component;
    public static final String KEY_HOST = Constants.PLUGIN_NAME + ".host";


    private String host;

    public RunConfigurationImpl(Project project, ConfigurationFactory factory, String name) {
        super(project, factory, name);
    }

    /* Return an instances of the custom SettingsEditor ... see class defined above */
    @NotNull
    @Override
    public SettingsEditor<? extends RunConfiguration> getConfigurationEditor() {
        return new SettingsEditorImpl();
    }

    /* Return null, else we'll get a Startup/Connection tab in our Run Configuration UI in IntelliJ */
    @Nullable
    @Override
    public SettingsEditor<ConfigurationPerRunnerSettings> getRunnerSettingsEditor(ProgramRunner runner) {
        return null;
    }

    /* This is a pretty cool method. Every time SettingsEditor#applyEditorTo() is changed the values in this class, this method is run and can check/validate any fields! If RuntimeConfigurationException is thrown, the exceptions message is shown at the bottom of the Run Configuration UI in IntelliJ! */
    @Override
    public void checkConfiguration() throws RuntimeConfigurationException {
        if (!StringUtils.startsWithAny(getHost(), "http://", "https://")) {
            throw new RuntimeConfigurationException("Invalid host");
        }
    }

    @Nullable
    @Override
    public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment executionEnvironment) throws ExecutionException {
        return null;
    }

    /* This READS any prior persisted configuration from the State/Storage defined by this classes annotations ... see above.
    You must manually read and populate the fields using JDOMExternalizerUtil.readField(..).
    This method is invoked at the "right time" by the plugin framework. You dont need to call this.
    */
    @Override
    public void readExternal(Element element) throws InvalidDataException {
        super.readExternal(element);
        host = JDOMExternalizerUtil.readField(element, KEY_HOST);
    }

    /* This WRITES/persists configurations TO the State/Storage defined by this classes annotations ... see above.
    You must manually read and populate the fields using JDOMExternalizerUtil.writeField(..).
    This method is invoked at the "right time" by the plugin framework. You dont need to call this.
    */
    @Override
    public void writeExternal(Element element) throws WriteExternalException {
        super.writeExternal(element);
        JDOMExternalizerUtil.writeField(element, KEY_HOST, host);

    }

    /* This method is what's used by the rest of the plugin code to access the configured 'host' value. The host field (variable) is written by
    1. when writeExternal(..) loads a value from a persisted config.
    2. when SettingsEditor#applyEditorTo(..) is called when the Form itself changes.
    */
    public String getHost() {
        return host;
    }

    /* This method sets the value, and is primarily used by the custom SettingEditor's SettingsEditor#applyEditorTo(..) method call */
    public void setHost(String host) {
        this.host = host;
    }

}

要在其他地方读取这些配置值,例如自定义 ProgramRunner,您可以执行以下操作:

final RunConfigurationImpl runConfiguration = (RunConfigurationImpl) executionEnvironment.getRunnerAndConfigurationSettings().getConfiguration();
runConfiguration.getHost(); // Returns the configured host value