为每个字母数字按键触发一个动作

Trigger an action for every alphanumeric keypress

我在插件的 Default (Windows).sublime-keymap 文件中有以下几行:

...
{ "keys": ["ctrl+shift+a"], "command": "table_editor_align", "context":
    [
        { "key": "setting.enable_table_editor", "operator": "equal", "operand": true, "match_all": true },
        { "key": "preceding_text", "operator": "regex_contains", "operand": "^\s*\|", "match_all": true },
        { "key": "following_text", "operator": "regex_contains", "operand": "$", "match_all": true }
    ]
},
...

不是仅在 ctrl+shift+a 时触发此命令,我'我想在每个字母数字按键(A-Z、a-z、0-9,为什么不也重音 é、à、ç 等,即我们在书写时使用的所有字符)之后触发此命令 )?

"keys": ["[a-zA-Z0-9_]"]

好像不行。

注意:该插件当前是 sublime_plugin.TextCommand, and I think keeping this is mandatory for it to work. The plugin I'm trying to modify is https://github.com/vkocubinsky/SublimeTableEditor 的子类,我希望在每次按键后自动重新对齐,而不是像这里那样在每次 CTRL+SHIFT+A 后自动重新对齐:

据我所知,无法一次将一个键绑定到大量 class 个键,因此为了执行您想要的操作,您必须为每个键创建一个单独的映射一次一个键,这很笨重,很可能不是一个好主意。


[编辑] 实际上有一种方法可以做到这一点;我在答案末尾添加了更多信息 [/编辑]


从一些简单的测试来看,您感兴趣的键(即输入文本)似乎由 Sublime 核心直接处理,除非您为它们提供映射。也就是说,它们颠覆了正常的命令机制,这意味着您不能在插入文本时立即捕获并重新格式化。

按照 MattDMO 的建议,解决此问题的一种方法是使用 on_modified 处理程序来跟踪缓冲区何时被修改,然后以这种方式触发重新对齐。

可以执行类似操作的示例插件如下:

import sublime
import sublime_plugin
import re

class TableFormatEventListener(sublime_plugin.EventListener):
    def __init__(self):
        self._views = dict()
        self.regex = re.compile(r"\s*\|")

    def on_modified(self, view):
        # Only for views with table editing enabled that are not already being
        # modified
        if (view.settings().get("enable_table_editor", False) and
                self._views.get(view.id(), False) is False):

            for s in view.sel():
                line = view.substr(view.line(s.begin()))
                prior = view.substr(s.begin() - 1) if s.begin() > 0 else ""

                # Only if all cursors are inside of a table row and the
                # character prior to the cursor is not a space
                if self.regex.match(line) is None or prior == " ":
                    return

            # Perform the realignment
            self._views[view.id()] = True
            view.run_command("table_editor_align")
            self._views[view.id()] = False

    def on_text_command(self, view, cmd, args):
        # Don't trigger reformatting while an undo is happening
        if cmd == "undo":
            self._views[view.id()] = True

    def on_post_text_command(self, view, cmd, args):
        # Undo is complete; resume reformat handling
        if cmd == "undo":
            self._views[view.id()] = False

这实现了一个事件侦听器,每次修改视图时都会触发该事件侦听器。这意味着文本被添加 删除,并且还包括诸如粘贴之类的内容(字面意思是修改缓冲区的任何内容;稍后会详细介绍)。

该插件执行与键绑定相同类型的上下文检查,以确保所有可用的插入符号当前都在 table 行内,就像您调用命令重新对齐 table 否则会报错。

此外,还会检查最后添加的字符是否为 space。这是因为重新格式化似乎删除了尾随的 space,这实际上阻止了您在 table.

中的任何位置输入 space

假设所有游标都在table行,而不只是插入一个space,重新对齐table的命令在当前视图上执行。

这里要注意的是,重新对齐 table 会导致缓冲区的内容被修改,然后再次触发插件侦听器,从而再次重新对齐 table,一遍又一遍循环结束,直到插件主机崩溃。

为了阻止这种情况发生,我们跟踪应该忽略其 on_modified 事件的视图,在修改 table 之前将当前视图添加到列表中,然后将其删除一旦我们完成了。

该插件的副作用是您在 table 中所做的任何更改都会导致两次修改;您所做的更改,以及重新对齐 table 的更改(即使它没有更改)。这意味着,为了撤消 table 中的更改,您有时需要按“撤消”的次数比您想象的要多。

这会导致潜在问题。例如,如果您要在 table 行的开头返回 space,则该行和上一行将在同一行。修改调用realignment函数,正式将两行合为一

现在您有点为难了,因为您无法撤消更改;当您按一次撤消时,它会撤消重新对齐,但这样做会修改缓冲区,从而触发重新对齐立即再次发生。

为了解决这个问题,事件侦听器还会侦听 undo 命令即将发生的时间,并确保在撤消发生时,修改处理程序不会重新对齐 table。

由于我不使用这个特定的插件,因此对于需要以这种方式处理的其他场景,可能存在类似的边缘情况。

性能可能会或可能不会受到影响,具体取决于您正在编辑的 table 有多大。在我的(极其简单的)测试中,小 tables 的性能影响在我的机器上可以忽略不计。您的里程可能会有所不同,因此如果当前文件超过某个行阈值或符合这些行的某些内容,那么在代码中进行额外检查以阻止它触发可能是个好主意。


[编辑]

碰巧,实际上有一种方法可以做类似于您最初要求的事情。可以分配一个键绑定来触发任何插入的字符,尽管您仍然需要一些胶水插件代码将所有内容绑定在一起(并且一些可用性问题仍然与 table 插件的工作方式有关重新格式化)。

所需的插件代码是一个命令,它将接受一个字符并像往常一样插入文本,但随后也会触发 table 对齐命令。

import sublime
import sublime_plugin

class TextAndAlignTableCommand(sublime_plugin.TextCommand):
    def run(self, edit, character):
        self.view.run_command("insert", {"characters": character})
        if character != " ":
            self.view.run_command("table_editor_align")

这定义了一个带有单个参数 character 的命令,它将按照通常输入字符的方式将其插入缓冲区。然后,如果字符不是 space,它还会调用重新对齐 table.

的命令

有了这个,要创建的键绑定是:

{ "keys": ["<character>"], "command": "text_and_align_table", "context":
    [
        { "key": "setting.enable_table_editor", "operator": "equal", "operand": true, "match_all": true },
        { "key": "preceding_text", "operator": "regex_contains", "operand": "^\s*\|", "match_all": true },
        { "key": "following_text", "operator": "regex_match", "operand": "\s*\|.*$", "match_all": true }
    ]
},

这基本上就是您上面提到的键绑定(即基础 table 插件的默认设置以触发重新格式化),稍作修改。

首先,键绑定到键 <character>,这使得它可能触发任何字符,否则将被插入缓冲区。

其次,它使用我们上面的自定义命令,首先插入文本,然后重新格式化 table。

第三,修改following_text上下文,使其仅在table中某列的末尾时触发,这样就可以在table的中间插入文本没有光标位置跳到列末尾的列。

以这种方式绑定时,绑定将触发任何单个字符,并将调用提供的命令,该命令带有一个参数,告诉您该字符是什么。绑定本身不能有修饰符(例如 ctrlshift 等),但您得到的字符是本来可以输入的字符。

这更简洁,并且没有与上面的代码相同的问题 w/regards 撤消,因为采取的所有操作都是同一编辑操作的一部分。

此外,由于 table 重新对齐移动了光标位置,因此在列主体内部时不触发绑定消除了能够在列中间插入文本的问题。这确实使 table 未对齐,但在这种情况下可以使用现有的键绑定来纠正问题。

另一方面,在列值的末尾仍然不可能有 spaces(在重新格式化之后),因为 table 重新格式化想要删除它们。为了阻止这种情况发生,必须修改底层插件以不这样做,但这似乎会在某种程度上损害 table 格式。