跳转至

运行配置与 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;自定义 ExecutorProgramRunner 是少数高级场景。

注册运行配置类型

<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>

参考来源