on_post_save_async 是 Sublime Text 插件中最常用且最稳妥的文件保存监听入口,它在文件写入磁盘后异步触发,不阻塞 UI,也不因保存失败而漏事件。

监听文件保存事件用 on_post_save_async
Sublime Text 的插件里,on_post_save_async 是最常用也最稳妥的保存监听入口。它在文件写入磁盘后异步触发,不阻塞编辑器 UI,也不会因保存失败而漏掉事件。
常见错误是误用 on_pre_save:它在保存前触发,此时文件内容还没落盘,读取 view.substr() 可能拿到旧内容;而且如果用户 Ctrl+Z 撤回、或保存被插件中途拦截,这个钩子就白跑了。
- 只在需要“确认已保存成功”时用
on_post_save_async,比如自动生成摘要、提交 Git、触发构建 - 避免在该函数里做耗时同步操作(如调用外部命令),否则会卡住 Sublime 的异步调度
- 注意:它不保证视图仍处于激活状态,
view.window()可能为None,需先判空
区分当前文件是否属于项目用 view.window().folders()
很多监听逻辑只应在项目内生效(比如只对 src/ 下的 JS 文件格式化),但新手常直接读 view.file_name() 然后硬匹配路径——这在未保存的临时文件(untitled:1)或符号链接下会出错。
view.window().folders() 返回的是当前窗口加载的项目根目录列表,更可靠。只要文件路径以任一 folder 开头,就认为属于该项目。
-
view.file_name()可能为None(新建未保存 tab),必须先检查 -
os.path.commonpath()不适合判断归属,Windows 路径大小写、UNC 路径等容易误判 - 推荐用
any(file_path.startswith(folder) for folder in folders),简单且兼容跨平台
监听多视图切换要靠 on_activated_async + 缓存比对
想在用户切到某个文件时自动运行检查?别只依赖 on_activated_async —— 它会在每次焦点进入视图时触发,包括预览模式(点击侧边栏未双击打开)、重复切回同一视图,频繁触发会导致误操作。
正确做法是缓存上一次激活的 view.id(),仅当 ID 变化时才执行逻辑:
self.last_active_view_id = None
def on_activated_async(self, view):
if view.id() == self.last_active_view_id:
return
self.last_active_view_id = view.id()
# 这里放你的实际逻辑
- 不能用
view.file_name()做判重:未保存文件名相同(都是None),或不同窗口打开同一文件会误判 - 不用
on_load_async替代:它只在文件首次加载时触发,切换已有 tab 不会再进 - 如果插件支持多窗口,记得把
last_active_view_id按view.window().id()分桶存储
调试监听器看不到日志?改用 sublime.status_message() 或写文件
Sublime 插件里 print() 默认不输出到控制台(除非开启 show_panel_on_build 且手动打开 Console),导致监听器“静默失效”,查不出为什么没触发。
最快速验证方式是用 sublime.status_message() 弹临时提示;长期调试建议写入临时文件,避免干扰用户界面:
import datetime
with open("/tmp/st-listener.log", "a") as f:
f.write(f"[{datetime.datetime.now()}] on_post_save: {view.file_name()}\n")
- 别在监听函数里直接调用
sublime.error_message():它会弹窗阻塞,影响体验 - Console 面板本身有缓冲,重启 Sublime 后旧日志会清空,不适合追踪复现路径
- 监听器类必须继承
sublime_plugin.EventListener,且文件名必须是*.py并放在Packages/User/下,否则根本不会加载

















