跳转至

Tool Window、设置页与持久化

真实插件往往需要一个可见的工作区、一组配置项,以及跨 IDE 重启保存状态的能力。本章覆盖 Tool Window、Settings UI 和持久化状态。

Tool Window

Tool Window 是 IDE 主窗口边缘的子窗口,用来展示插件自己的信息面板,例如运行结果、分析报告、任务列表或交互式工具。

常见方式有两种:

方式 适合场景
声明式注册 工具窗口按钮长期可见,用户随时打开
编程式注册 某个 Action 执行后临时展示结果

声明式注册

<extensions defaultExtensionNs="com.intellij">
  <toolWindow
      id="Example"
      anchor="right"
      icon="/icons/example.svg"
      factoryClass="com.example.ExampleToolWindowFactory"/>
</extensions>

ToolWindowFactory 会在用户第一次打开窗口时创建内容。这样未使用的 Tool Window 不会拖慢启动。

class ExampleToolWindowFactory : ToolWindowFactory {
    override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {
        val panel = JPanel(BorderLayout())
        panel.add(JLabel("Example"), BorderLayout.NORTH)

        val content = ContentFactory.getInstance()
            .createContent(panel, "Overview", false)
        toolWindow.contentManager.addContent(content)
    }
}

条件显示

如果 Tool Window 只对某类项目有意义,可以在 factory 中判断适用性。条件通常在项目加载时计算一次;如果要运行时动态显示/隐藏,改用编程式注册。

class ExampleToolWindowFactory : ToolWindowFactory {
    override suspend fun isApplicableAsync(project: Project): Boolean {
        return true
    }
}

Tool Window 内容管理

Tool Window 可以包含多个 tab,API 中称为 Content:

  • toolWindow.contentManager 管理 tab。
  • Content.setPreferredFocusableComponent() 指定默认聚焦组件。
  • 有资源需要释放时,用 Content.setDisposer() 挂接 Disposable
  • 需要关闭 tab 时,注册时要显式允许 canCloseContents

如果内容依赖索引,Tool Window 在 Dumb Mode 下默认不可用;若完全不访问索引,可以让 factory 实现 DumbAware

设置页

插件设置页通过 applicationConfigurableprojectConfigurable 注册:

<extensions defaultExtensionNs="com.intellij">
  <applicationConfigurable
      parentId="tools"
      instance="com.example.AppSettingsConfigurable"
      id="com.example.AppSettingsConfigurable"
      displayName="Example"/>
</extensions>

项目级设置:

<extensions defaultExtensionNs="com.intellij">
  <projectConfigurable
      parentId="tools"
      instance="com.example.ProjectSettingsConfigurable"
      id="com.example.ProjectSettingsConfigurable"
      displayName="Example"
      nonDefaultProject="true"/>
</extensions>

常用父节点:

parentId 设置分组
appearance Appearance & Behavior
editor Editor
build Build, Execution, Deployment
language Languages & Frameworks
tools Tools / third-party integrations

不要把设置页放进 other,那是兜底分组,不适合新插件。

Configurable 生命周期

Configurable 负责创建设置 UI、判断是否修改、应用、重置和释放资源:

class AppSettingsConfigurable : SearchableConfigurable {
    private var panel: JPanel? = null

    override fun getId(): String = "com.example.settings"

    override fun getDisplayName(): String = "Example"

    override fun createComponent(): JComponent {
        panel = JPanel(BorderLayout())
        return panel!!
    }

    override fun isModified(): Boolean = false

    override fun apply() {
        // save UI values
    }

    override fun reset() {
        // load stored values into UI
    }

    override fun disposeUIResources() {
        panel = null
    }
}

构造函数不要创建复杂 Swing UI。平台可能在后台线程实例化配置对象,UI 应放在 createComponent() 中创建。

持久化状态

插件配置通常放在服务中,并实现 PersistentStateComponent

@Service
@State(
    name = "ExampleSettings",
    storages = [Storage("example-settings.xml")]
)
class ExampleSettings : SerializablePersistentStateComponent<ExampleSettings.State>(State()) {
    data class State(
        @JvmField val enabled: Boolean = true,
        @JvmField val endpoint: String = ""
    )

    var enabled: Boolean
        get() = state.enabled
        set(value) {
            updateState { it.copy(enabled = value) }
        }
}

建议:

  • Application 级状态存全局偏好。
  • Project 级状态存项目配置。
  • 临时、不可漫游的小值可以用 PropertiesComponent
  • 密码、Token 等敏感信息不要放普通 XML,使用平台的敏感数据存储。
  • 可同步设置要谨慎,机器相关路径不应漫游。

参考来源