插件事件
插件事件主题、权限要求与投递模式。
插件通过事件总线订阅 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.changed | editor.read | latest | 是 | 活动文档切换 |
editor.document.opened | editor.read | every | 否 | 文档打开 |
editor.document.changed | editor.read | debounce,100 ms | 否 | 文档内容变更 |
editor.document.saved | editor.read | every | 否 | 文档保存 |
editor.document.closed | editor.read | every | 否 | 文档关闭 |
editor.document.selection.changed | editor.read | throttle,50 ms | 否 | 选区变化 |
runtime.session.created | runtime.inspect | every | 否 | 运行时会话创建 |
runtime.session.ended | runtime.inspect | every | 否 | 运行时会话结束 |
runtime.session.state.changed | runtime.inspect | every | 是 | 会话状态变化 |
runtime.program.started | runtime.inspect | every | 否 | 程序开始运行 |
runtime.program.paused | runtime.inspect | every | 否 | 程序暂停 |
runtime.program.resumed | runtime.inspect | every | 否 | 程序恢复 |
runtime.program.finished | runtime.inspect | every | 否 | 程序运行结束 |
runtime.backend.restarted | runtime.inspect | every | 否 | 后端重启,已有对象引用失效 |
runtime.variables.changed | runtime.inspect | latest | 否 | 变量变化 |
view.opened | 无 | every | 否 | 本插件视图打开 |
view.closed | 无 | every | 否 | 本插件视图关闭 |
view.visibility.changed | 无 | latest | 否 | 本插件视图可见性变化 |
view.focused | 无 | every | 否 | 本插件视图获得焦点 |
workspace.opened | file.read | every | 是 | 工作区打开 |
workspace.changed | file.read | every | 否 | 工作区变化 |
file.created | file.read | every | 否 | 文件创建 |
file.changed | file.read | every | 否 | 文件内容变化 |
file.deleted | file.read | every | 否 | 文件删除 |
file.renamed | file.read | every | 否 | 文件重命名 |
device.connected | serial.read | every | 是 | 设备连接 |
device.disconnected | serial.read | every | 否 | 设备断开 |
device.state.changed | serial.read | latest | 是 | 设备状态变化 |
serial.data.received | serial.read | batch,32 ms | 否 | 串口接收数据 |
serial.error | serial.read | every | 否 | 串口错误 |
configuration.changed | 无 | latest | 是 | 插件配置变化 |
编辑器、运行时与配置 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可取every、latest、batch、debounce或throttle。省略时使用上表中的主题默认值。debounce_ms设置batch、debounce和throttle的时间窗口。省略时使用主题窗口,主题未指定时为 100 ms。
五种模式分别解决不同的流量问题:
| 模式 | 行为 | 适用场景 |
|---|---|---|
every | 按顺序投递每个事件 | 打开、关闭、开始、结束等不可合并的状态转换 |
latest | 待处理区只保留最新事件 | 当前活动文档、设备状态、变量快照 |
batch | 在窗口结束时一次投递一组事件 | 串口数据等连续数据流 |
debounce | 事件停止一段时间后投递最后一个 | 文档连续编辑后的分析 |
throttle | 每个窗口最多投递一次,并保留窗口内最新值 | 选区、悬停等高频状态 |
宿主拒绝订阅时,SDK 会移除本地订阅,可通过 callback(error=...) 感知。处理器抛出异常会被记录,但不会终止 Bridge 事件循环或阻断其他订阅。
重放、队列与隔离
标记为“重放最新值”的主题会在宿主保留最后一个 payload。新订阅通过权限和过滤检查后会立即收到匹配的保留值,因此插件无需先制造一次状态变化。运行时整体重启会清空这些保留值。
每个订阅的待处理队列最多保存 256 个 payload。batch 溢出时丢弃最早数据并保留最近 256 个;latest、debounce 和 throttle 本身只保留最新值。every 溢出会将订阅标记为溢出,调用方必须重新获取权威状态,不能假设事件历史仍完整。
订阅键包含 pluginId、sessionId 和订阅 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与处理器映射。