Запуск действия для каждого алфавитно-цифрового нажатия

У меня есть следующие строки в файле плагина 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, как здесь:

введите описание изображения здесь

Ответ 1

Я не знаю, как я могу связать ключ с большим классом ключей одновременно, поэтому для того, чтобы делать то, что вы хотите, вам нужно было бы создать индивидуальное сопоставление для каждого из ключей по одному, что довольно громоздко и, скорее всего, не очень хорошая идея.


[править] На самом деле есть способ сделать это; Я добавил дополнительную информацию в конце своего ответа [/Править]


Из некоторого простого тестирования оказалось, что ключи, которые вас интересуют (этот вводный текст), напрямую обрабатываются ядром 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 больше, чем вы могли бы подумать.

Это вызывает потенциальные проблемы. Например, если вы должны были вернуться в начале строки таблицы, эта строка и предыдущая строка будут в одной строке. Модификация вызывает функцию перегруппировки, которая формально делает две строки одной.

Теперь у вас немного рассол, потому что вы не можете отменить изменение; когда вы один раз отжимаете отменить, он отменяет реорганизацию, но при этом он модифицирует буфер, который снова вызывает повторную перестройку.

Чтобы решить эту проблему, прослушиватель событий также прослушивает, когда должна произойти команда 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 изменяется так, что он запускается только тогда, когда он находится в конце столбца в таблице, так что можно вставлять текст в середину столбца, не переместив позицию курсора до конца столбца.

При привязке этого способа привязка будет запускаться для любого отдельного символа и вызовет команду, предоставленную с аргументом, который сообщает вам, что это за символ. Сама привязка не может иметь на ней модификатора (например, ctrl, shift и т.д.), Но персонаж, который вы получаете, тот, который в противном случае был бы напечатан.

Это намного чище и не имеет такой же проблемы, как и код выше w/касается отмены, потому что все предпринятые действия являются частью одной и той же операции редактирования.

Кроме того, если привязка не запускается в то время как внутри тела столбца удаляет проблему с возможностью вставки текста в середине столбца, так как перестановка таблицы сдвигает местоположение курсора. Это приводит к тому, что таблица не выравнивается, но существующая привязка ключей может использоваться в этой ситуации для исправления проблемы.

С другой стороны, по-прежнему невозможно иметь пробелы в конце значения столбца (после переформатирования), потому что tableformat хочет удалить их. Чтобы не допустить этого, базовый плагин должен быть изменен, чтобы этого не сделать, но похоже, что он несколько ухудшил бы форматирование таблицы.