保存前执行操作 (`on_pre_save`)

Perform action before save (`on_pre_save`)

import sublime_plugin

class Test(sublime_plugin.EventListener):

    def on_pre_save(self, view):
        view.set_syntax_file("Packages/Python/Python.tmLanguage")

这是一个简单的例子。从逻辑上讲(从我的角度来看),它应该在保存之前更改语法,因此,文件应该保存为 <filename>.py.

但实际上, 保存操作后语法会发生变化。所以,如果我最初使用 js 文件,它将被保存为 js,而不是 py

我想知道为什么 on_pre_save 工作起来很奇怪,或者换句话说,on_pre_saveon_post_save 之间有什么区别吗?此外,这也是我的实际兴趣,我如何在保存之前执行一些任意 (1) 操作?

(1) 我特地使用了 "arbitrary" 这个词,因为我不仅仅指语法更改。它可能是不同的东西。例如,将字体从 Consolas 更改为 Times New Roman。

on_pre_save 事件恰好在文件缓冲区写入磁盘之前发生,并允许您在磁盘上的文件更改之前执行您可能想要执行的任何操作,例如对缓冲区的内容(例如 "reformat on save")。

on_post_save 事件在文件缓冲区写入磁盘后立即发生,允许您在保存操作后执行任何可能想要执行的操作,例如检查缓冲区的内容"final"(例如 "lint on save",如果通过外部工具完成,则需要将更改保存在磁盘上,而不仅仅是内存中)。

无论哪种情况,文件的文件名在事件发生时都已被用户选择。对于新文件,这意味着 on_pre_save 在他们选择了文件的名称和位置之后才会发生。对于现有文件,save 仅使用相同的文件名重新保存。

要回答您的问题,您可以在 on_pre_save 中做任何您想做的大部分 "arbitrary" 事情,让它在保存发生之前发生。如果您真的愿意,也可以在那种情况下更改文件名。

但是请注意,在不先询问用户的情况下从用户名下更改文件名绝对是糟糕的用户体验。此外,如果您将文件名更改为 on_pre_save 中已经存在的文件,sublime 将盲目地覆盖该文件而不会发出警告,这也是 Bad Mojo。

对于要更改磁盘上文件的名称和位置的内容,更合适的方法是让用户必须明确调用一个命令来实现这一点,以便他们完全了解发生了什么继续。


根据评论中的要求和完整性,这里有一个示例可以执行您希望上面的示例代码执行的操作。

这里要注意的重要一点是,您必须非常小心触发此事件的情况。如上所述,您的插件将因此而无法保存任何类型的文件改为切换到 python 文件。

在此示例中,它被限制为仅对文本文件生效,将其转换为 python 文件。但是请注意,如果该位置已经存在同名的 python 文件,它会在不警告您即将发生的情况下覆盖它。

请格外小心此代码;很容易意外地阻止您自己使用正确的名称保存文件,例如,这可能会阻止您使用 Sublime 修复代码,以及其他令人讨厌的问题。

import sublime_plugin
import os

class TestListener(sublime_plugin.EventListener):
    def on_pre_save(self, view):
        # This part is extremely important because as mentioned above it's
        # entirely disconcerting for your save operation to gank your
        # filename and make it suddenly be something else without any
        # warning. If you're not careful you might destroy your ability to
        # use sublime to fix your plugin, for example.
        if not view.file_name().endswith(".txt"):
            print("Doing nothing for: ", view.file_name())
            return

        # HUGE WARNING: This CAN and WILL willfully clobber over any file
        # that already happens to exist without any warning to you
        # whatsoever, and is most decidedly a Bad Idea(tm)
        python_name = os.path.splitext(view.file_name())[0] + ".py"
        view.retarget(python_name)