插件事件

插件事件主题、权限要求与投递模式。

插件通过事件总线订阅 IDE 发布的事件。订阅返回 Subscription,可随时 dispose();插件卸载时总线自动释放全部订阅。订阅通常使用 SDK 的类型化辅助方法(如 self.documents.on_opened(...)self.runtime.on_program_finished(...)),也可用通用的 self.events.subscribe(topic, handler, ...)

事件主题与权限

EventTopicRegistry 是宿主允许订阅的主题闭集。未知主题会以 unknown_topic 拒绝;需要权限的主题会在订阅时检查当前插件会话,权限不足返回 permission_denied。表中的“默认投递”只在订阅没有显式设置 delivery 时生效。

主题所需权限默认投递重放最新值语义
editor.activeDocument.changededitor.readlatest活动文档切换
editor.document.openededitor.readevery文档打开
editor.document.changededitor.readdebounce,100 ms文档内容变更
editor.document.savededitor.readevery文档保存
editor.document.closededitor.readevery文档关闭
editor.document.selection.changededitor.readthrottle,50 ms选区变化
runtime.session.createdruntime.inspectevery运行时会话创建
runtime.session.endedruntime.inspectevery运行时会话结束
runtime.session.state.changedruntime.inspectevery会话状态变化
runtime.program.startedruntime.inspectevery程序开始运行
runtime.program.pausedruntime.inspectevery程序暂停
runtime.program.resumedruntime.inspectevery程序恢复
runtime.program.finishedruntime.inspectevery程序运行结束
runtime.backend.restartedruntime.inspectevery后端重启,已有对象引用失效
runtime.variables.changedruntime.inspectlatest变量变化
view.openedevery本插件视图打开
view.closedevery本插件视图关闭
view.visibility.changedlatest本插件视图可见性变化
view.focusedevery本插件视图获得焦点
workspace.openedfile.readevery工作区打开
workspace.changedfile.readevery工作区变化
file.createdfile.readevery文件创建
file.changedfile.readevery文件内容变化
file.deletedfile.readevery文件删除
file.renamedfile.readevery文件重命名
device.connectedserial.readevery设备连接
device.disconnectedserial.readevery设备断开
device.state.changedserial.readlatest设备状态变化
serial.data.receivedserial.readbatch,32 ms串口接收数据
serial.errorserial.readevery串口错误
configuration.changedlatest插件配置变化

编辑器、运行时与配置 API 提供类型化辅助方法。工作区、文件、设备、串口和视图主题可通过 self.events.subscribe(...) 订阅。

通用订阅

subscription = self.events.subscribe(
    "editor.document.changed",
    self._on_document_changed,
    delivery="debounce",
    debounce_ms=200,
    filter={"languageId": "python"},
)
  • filter 进行 payload 顶层字段的浅层相等匹配。
  • delivery 可取 everylatestbatchdebouncethrottle。省略时使用上表中的主题默认值。
  • debounce_ms 设置 batchdebouncethrottle 的时间窗口。省略时使用主题窗口,主题未指定时为 100 ms。

五种模式分别解决不同的流量问题:

模式行为适用场景
every按顺序投递每个事件打开、关闭、开始、结束等不可合并的状态转换
latest待处理区只保留最新事件当前活动文档、设备状态、变量快照
batch在窗口结束时一次投递一组事件串口数据等连续数据流
debounce事件停止一段时间后投递最后一个文档连续编辑后的分析
throttle每个窗口最多投递一次,并保留窗口内最新值选区、悬停等高频状态

宿主拒绝订阅时,SDK 会移除本地订阅,可通过 callback(error=...) 感知。处理器抛出异常会被记录,但不会终止 Bridge 事件循环或阻断其他订阅。

重放、队列与隔离

标记为“重放最新值”的主题会在宿主保留最后一个 payload。新订阅通过权限和过滤检查后会立即收到匹配的保留值,因此插件无需先制造一次状态变化。运行时整体重启会清空这些保留值。

每个订阅的待处理队列最多保存 256 个 payload。batch 溢出时丢弃最早数据并保留最近 256 个;latestdebouncethrottle 本身只保留最新值。every 溢出会将订阅标记为溢出,调用方必须重新获取权威状态,不能假设事件历史仍完整。

订阅键包含 pluginIdsessionId 和订阅 ID,投递时还会核对 generation。会话停止后,宿主清除该会话的所有订阅,因此上一次会话不会收到新会话的事件。

连续失败达到性能保护阈值后,宿主会暂停该插件的事件投递。暂停期间普通事件不会进入订阅队列;启用了最新值重放的主题仍更新宿主保留值。用户恢复投递后,新事件继续按原订阅策略处理。需要完整历史的插件应重新读取对应 API 的权威状态,而不是依赖暂停期间的事件补发。

控制帧(IDE → 插件)

除了业务事件,IDE 还会向插件视图推送控制帧,由 SDK 自动处理:

  • ide.view.ack / ide.view.nack — 快照/补丁确认(nack 后自动 resync)。
  • ide.view.resync — 要求插件重发快照。
  • ide.view.route.sync — 回推权威路由栈。
  • ide.view.event — 组件/节点交互事件。
  • ide.view.visibility.changed — 视图可见性变化。
  • ide.event.emit — 宿主事件帧(投递给订阅)。

示例

class Watcher(UiPlugin):
    def on_start(self):
        self.runtime.on_program_finished(self._on_finished)
        self.documents.on_opened(self._on_opened)

    def on_dispose(self):
        # 订阅由会话自动释放,无需手动清理
        pass

    def _on_finished(self, event):
        print("程序结束", event)

事件处理器在插件的 Bridge 事件循环中执行。刷新界面时更新视图模型,不要尝试访问 Flutter 组件或假设处理器运行在 Flutter UI isolate。

IDE 端实现

  • lib/core/sdk/plugin_event_bus.dart 定义主题注册表、过滤、投递策略、重放缓存和有界队列。
  • lib/core/sdk/plugin_event_bus_provider.dart 将事件投递绑定到当前 PluginRunManager,并校验会话与代数。
  • lib/core/sdk/api/events_api.dart 处理订阅和取消订阅请求。
  • lib/core/sdk/plugin_metrics.dart 记录队列高水位、连续失败和投递暂停状态。
  • SDK 的 src/pyrite_sdk/api/events.py 保存本地 Subscription 与处理器映射。

On this page