触发每个字母数字按键的动作

时间:2017-01-15 14:48:32

标签: sublimetext2 sublimetext sublime-text-plugin

我在插件的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 时触发此命令,我想在每次之后触发此命令字母数字按键(AZ,az,0-9,为什么不重音é,à,ç等,也就是我们写作时使用的所有字符)?

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

似乎不起作用。

注意:该插件目前是sublime_plugin.TextCommand的子类,我认为保持这个是必须的。我试图修改的插件是https://github.com/vkocubinsky/SublimeTableEditor,我希望在每次按键后自动重新对齐,而不是在每次按CTRL + SHIFT + A之后:

enter image description here

1 个答案:

答案 0 :(得分:7)

我无法一次将键绑定到大类键,所以为了做你想做的事,你必须一次为一个键创建一个单独的映射,这是相当笨拙,很可能不是一个好主意。

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

从一些简单的测试中可以看出,您感兴趣的键(输入文本)直接由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

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

插件执行与键绑定相同的上下文检查,以确保所有可用的插入符当前位于表行内,就像调用命令重新对齐表一样,否则会引发错误

此外,还会检查添加的最后一个字符是否为空格。这是因为重新格式化似乎删除了尾随空格,这有效地阻止了您在表格中的任何位置输入空格。

假设所有游标都在表格行中并且不只是插入空格,则在当前视图上执行重新对齐表格的命令。

这里要注意的是重新调整表会导致缓冲区的内容被修改,然后再次触发插件监听器,它会在循环中反复重新调整表,直到插件主机崩溃。

要阻止这种情况发生,我们会跟踪应忽略on_modified事件的观看次数,在修改表格之前将当前视图添加到列表中,然后在我们恢复之后将其删除完成。

插件所做的一个副作用是你在一个表内做出的任何改变都会导致两次修改;您所做的更改以及重新对齐表的更改(即使它没有更改)。这意味着,为了撤消表格中的更改,您有时必须按下撤消次数比您想象的更多次。

这会导致潜在的问题。例如,如果您要在表行的开头退格,则该行和前一行将位于同一行。修改调用重新对齐函数,正式使两行成为一个。

现在你处于一种腌制状态,因为你无法撤消这种变化;当你按下一次撤消时,它会撤消重新调整,但这样做会修改缓冲区,从而触发重新调整立即再次发生。

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

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

根据您正在编辑的表的大小,性能可能会受到影响,也可能不受影响。在我的(非常简单的)测试中,小型桌子上的性能在我的机器上可以忽略不计。您的里程可能会有所不同,因此在代码中进行额外检查可能是一个好主意,如果当前文件超过某个行阈值或某些行,则可能会阻止其触发。

[编辑]

实际上,实际上有一种方法可以做一些类似于你原来问的方法。虽然你仍然需要一些胶水插件代码来将所有内容组合在一起(并且有一些可用性问题仍然与表插件如何进行重新格式化有关),因此可以为任何插入的字符分配将触发的键绑定。

所需的插件代码是一个接受字符并按正常方式插入文本的命令,但随后也会触发表格对齐命令。

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参数的命令,它将以通常输入字符的方式插入缓冲区。然后,如果字符不是空格,它还会调用命令重新对齐表格。

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

{ "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 }
    ]
},

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

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

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

第三,following_text上下文被修改,只有当它位于表中列的末尾时才会触发,这样就可以在没有光标的情况下将文本插入到列的中间。位置跳到了列的末尾。

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

这是一个更清洁,并没有与上面的代码相同的问题,因为所有采取的行动都是同一编辑操作的一部分。

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

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