运行配置与 Execution API¶
如果插件需要运行程序、测试、脚本、服务器、外部命令或调试会话,就会接触 IntelliJ Platform 的 Execution API。它把“用户可配置的运行参数”和“实际启动进程并展示输出”拆成两层:Run Configuration 与 Execution。
核心模型¶
| 概念 | 作用 |
|---|---|
RunProfile |
可执行对象的通用接口 |
RunConfiguration |
用户可管理、可持久化的运行配置 |
ConfigurationType |
运行配置类型的入口和展示 |
ConfigurationFactory |
创建配置模板和配置实例 |
SettingsEditor |
配置 UI |
RunProfileState |
准备命令行、环境变量、工作目录等执行状态 |
Executor |
执行方式,例如 Run、Debug、Coverage |
ProgramRunner |
选择并执行某类 RunProfile |
ExecutionEnvironment |
一次执行所需的上下文集合 |
ExecutionResult |
执行结果,包含 Console 和 ProcessHandler |
ProcessHandler |
控制和监听被执行进程 |
通常你只需要实现配置类型、配置类、配置 UI 和 RunProfileState;自定义 Executor 和 ProgramRunner 是少数高级场景。
注册运行配置类型¶
<extensions defaultExtensionNs="com.intellij">
<configurationType implementation="com.example.ExampleConfigurationType"/>
</extensions>
单 factory 的配置可以继承 SimpleConfigurationType:
class ExampleConfigurationType : SimpleConfigurationType(
"ExampleRunConfiguration",
"Example",
"Run an Example target",
NotNullLazyValue.createValue { ExampleIcons.Run }
) {
override fun createTemplateConfiguration(project: Project): RunConfiguration {
return ExampleRunConfiguration(project, this)
}
}
如果配置类型有多个 factory,使用 ConfigurationTypeBase 并在构造函数中 addFactory()。
RunConfiguration¶
配置类保存用户可编辑参数,并负责创建执行状态:
class ExampleRunConfiguration(
project: Project,
factory: ConfigurationFactory
) : RunConfigurationBase<Any>(project, factory, "Example") {
var scriptPath: String = ""
var arguments: String = ""
override fun getConfigurationEditor(): SettingsEditor<out RunConfiguration> {
return ExampleSettingsEditor()
}
override fun checkConfiguration() {
if (scriptPath.isBlank()) {
throw RuntimeConfigurationError("Script path is required")
}
}
override fun getState(
executor: Executor,
environment: ExecutionEnvironment
): RunProfileState {
return ExampleCommandLineState(environment, this)
}
}
校验异常选择:
| 异常 | 含义 |
|---|---|
RuntimeConfigurationWarning |
有问题但不影响执行 |
RuntimeConfigurationException |
非致命错误,用户仍可执行 |
RuntimeConfigurationError |
致命错误,必须修复后才能执行 |
SettingsEditor¶
SettingsEditor 负责 UI 与配置对象之间的数据同步:
class ExampleSettingsEditor : SettingsEditor<ExampleRunConfiguration>() {
private val scriptField = TextFieldWithBrowseButton()
private val argsField = JBTextField()
override fun createEditor(): JComponent {
return panel {
row("Script:") { cell(scriptField).align(AlignX.FILL) }
row("Arguments:") { cell(argsField).align(AlignX.FILL) }
}
}
override fun resetEditorFrom(configuration: ExampleRunConfiguration) {
scriptField.text = configuration.scriptPath
argsField.text = configuration.arguments
}
override fun applyEditorTo(configuration: ExampleRunConfiguration) {
configuration.scriptPath = scriptField.text
configuration.arguments = argsField.text
}
}
复杂运行配置推荐使用 Fragmented Settings Editor,把高级选项折叠进 “Modify options” 模式,避免配置页臃肿。
执行外部进程¶
最常见实现是继承 CommandLineState:
class ExampleCommandLineState(
environment: ExecutionEnvironment,
private val configuration: ExampleRunConfiguration
) : CommandLineState(environment) {
override fun startProcess(): ProcessHandler {
val commandLine = GeneralCommandLine(configuration.scriptPath)
.withWorkDirectory(environment.project.basePath)
.withParameters(ParametersListUtil.parse(configuration.arguments))
val handler = OSProcessHandler(commandLine)
ProcessTerminatedListener.attach(handler)
return handler
}
}
如果运行的是 JVM 程序,可以使用 JavaCommandLineState,但这意味着插件需要 Java 插件依赖。
输出会自动进入 Run Tool Window 的 Console。需要把输出中的路径、URL 或错误位置变成可点击链接时,加 Console Filter:
addConsoleFilters(RegexpFilter(project, "\$FILE_PATH\$:\$LINE\$"))
或注册:
<extensions defaultExtensionNs="com.intellij">
<consoleFilterProvider implementation="com.example.ExampleConsoleFilterProvider"/>
</extensions>
从上下文创建配置¶
右键文件、方法或测试时自动生成运行配置,需要实现 LazyRunConfigurationProducer:
<extensions defaultExtensionNs="com.intellij">
<runConfigurationProducer implementation="com.example.ExampleRunConfigurationProducer"/>
</extensions>
关键方法:
getConfigurationFactory():返回配置 factory。setupConfigurationFromContext():判断当前 PSI/Location 是否适用,并填充配置。isConfigurationFromContext():判断已有配置是否匹配当前上下文,避免重复创建。
如果不访问索引,实现 DumbAware。
Gutter 运行图标¶
如果某个 PSI 元素天然可运行,例如测试函数、脚本入口,可以实现 RunLineMarkerContributor:
<extensions defaultExtensionNs="com.intellij">
<runLineMarkerContributor language="Example" implementationClass="com.example.ExampleRunLineMarkerContributor"/>
</extensions>
建议:
getInfo()只做快速判断。- 慢查询放到
getSlowInfo(),避免图标闪烁和编辑器卡顿。 - 标准 Run/Debug action 可用
ExecutorAction.getActions()。
编程执行与修改配置¶
执行已有配置:
ProgramRunnerUtil.executeConfiguration(settings, DefaultRunExecutor.getRunExecutorInstance())
创建并持久化配置:
val runManager = RunManager.getInstance(project)
val settings = runManager.createConfiguration("Example", factory)
runManager.addConfiguration(settings)
runManager.selectedConfiguration = settings
修改已有配置没有统一平台级扩展点。不同语言插件提供不同的 RunConfigurationExtensionBase 子类,例如 Java、Python 各有专属扩展点。不要假设所有 IDE 都有 Java 运行配置。
Before Run Task、宏与环境变量¶
Before Run Task 通过 BeforeRunTaskProvider 注册:
<extensions defaultExtensionNs="com.intellij">
<stepsBeforeRunProvider implementation="com.example.ExampleBeforeRunTaskProvider"/>
</extensions>
环境变量支持 $PATH$ 这种引用形式。真正执行前需要展开变量,例如使用 EnvironmentUtil.inlineParentOccurrences()。
运行配置中可使用宏,例如 $ProjectFileDir$。扩展自定义宏时实现 Macro 并注册:
<extensions defaultExtensionNs="com.intellij">
<macro implementation="com.example.ExampleMacro"/>
</extensions>