我正在创建一个基本的IntelliJ插件,允许用户定义运行配置(遵循[1]中的教程),并使用所述运行配置在远程服务器上的编辑器中执行打开的文件。
我的运行配置很简单(3个文本字段),我完成了所有工作,但是,在编辑运行配置后,在更改值后单击“应用”或“确定”,输入的值将丢失。
持久化和读回值的正确方法是什么(重新打开运行配置时以及调用运行配置的运行器时)?看起来我可以尝试使用[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
希望这篇文章对IntelliJ插件开发的新手更加清楚,并说明如何实现持久/加载运行配置。请仔细阅读代码注释,因为这是进行大量解释的地方。
此外,SettingsEditorImpl
是我自定义的SettingsEditor
抽象类的实现,同样,RunConfigurationImpl
是我自定义的RunConfigiration
抽象类的实现。
要做的第一件事是通过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。请记住,自定义RunConfiguration将实际代表此配置; SettingsEditor
实现只代表FORM(一个微妙的差异,但很重要)。
现在我们需要一个自定义的RunConfiguration ......
/* 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