Установил плагин Markdown Editing, синтаксис Markdown GFM. Хочу обернуть текст в код или квадратные скобки. Когда нахожусь в английской клавиатурной раскладке, проблем не возникает:
Но когда раскладка русская, я уже не могу обернуть текст:
Это неудобно, раздражает. Причём такое сразу с несколькими плагинами: в английской раскладке горячие клавиши работают, а в русской нет.
Подскажите, что делать?
Ответ
1. Суть проблемы
Откроем консоль: Ctrl+` (машинописный обратный апостроф, символ расположен на той же клавише, где кириллическая «ё») → вводим в открывшееся поле sublime.log_input(True). Нажмём сначала Ctrl+k в английской раскладке, затем Ctrl+л в русской. Посмотрим, что выведется в консоли:
key evt: control+k
key evt: control+k
Если с системой нет проблем, результат должен быть аналогичным. А теперь нажмём k в английской, потом л в русской.
chr evt: k (0x6b)
chr evt: л (0x43b)
Sublime Text воспринимает k и л как разные символы, если они нажимаются не вместе со служебными клавишами. Если используется двойное нажатие хоткеев, Ctrl+k,k и Ctrl+л,л Sublime Text также видит по-разному.
Т.е. когда в качестве шортката используется одинарный символ, чтобы результат в русской раскладке был таким же, как в английской, придётся раскладку переключать. Так что если будете сами задавать сочетание для команды, без использования служебных клавиш, продублируйте запись и для русской раскладки, пример:
// Предпросмотр вики-страницы в плагине Mediawiker
{ "keys": ["ctrl+7", "p"], "command": "mediawiker_preview" },
{ "keys": ["ctrl+7", "з"], "command": "mediawiker_preview" },
2. Попытки урегулирования проблемы с разработчиками
Заявка в issue tracker Sublime Text висит долгое время, её приоритет обозначен как «minor». Есть предложение использовать в качестве идентификатора клавиши keycode вместо keysym, но по состоянию на сентябрь 2016 оно не реализовано на практике.
Разработчик плагинов отказался принимать мой pull request, добавляющий хоткеи для русской раскладки.
Значит, придётся настраивать Sublime Text для русской раскладки самостоятельно.
3. Решение
На примере плагина BetterFindBuffer.
Устанавливаем плагин PackageResourceViewer: Ctrl+Shift+P → PackageResourseViewer:Open Resourse (благодаря fuzzy поиску достаточно набрать просто prvor) → MarkdownEditing → Default.sublime-keymap (иногда Default(ваша операционная система).sublime-keymap) → в открывшемся файле ищем шорткаты, состоящие из одного символа, которого в русской раскладке нет или же он расположен на другой клавише. Пример:
{
"keys": ["o"],
"command": "find_in_files_open_file",
"context": [
{ "key": "selector", "operator": "equal", "operand": "text.find-in-files" }
]
},
Заменяем o на соответствующую ей в русской раскладке букву, это щ
{
"keys": ["щ"],
"command": "find_in_files_open_file",
"context": [
{ "key": "selector", "operator": "equal", "operand": "text.find-in-files" }
]
},
Preferences → Key Bindings - User → вставляем в открывшийся файл код с буквой щ. Проделываем данное действие со всеми шорткатами без служебных клавиш, где это необходимо.
После того, как произвели действия, проверьте, всё ли работает нормально. Пример: в Preferences → Key Bindings - Default есть такой код:
{ "keys": ["\""], "command": "insert_snippet", "args": {"contents": "\"$0\""}, "context":
[
{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
{ "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
{ "key": "following_text", "operator": "regex_contains", "operand": "^(?:\t| |\\)|]|\\}|>|$)", "match_all": true },
{ "key": "preceding_text", "operator": "not_regex_contains", "operand": "[\"a-zA-Z0-9_]$", "match_all": true },
{ "key": "eol_selector", "operator": "not_equal", "operand": "string.quoted.double - punctuation.definition.string.end", "match_all": true }
]
},
(\" — символ "кавычки-ёлочки". Для использования кавычки как литерала в JSON-файлах требуется её экранировать.)
Вы вставили в свой кеймап:
{ "keys": ["э"], "command": "insert_snippet", "args": {"contents": "\"$0\""}, "context":
[
{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
{ "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
{ "key": "following_text", "operator": "regex_contains", "operand": "^(?:\t| |\\)|]|\\}|>|$)", "match_all": true },
{ "key": "preceding_text", "operator": "not_regex_contains", "operand": "[\"a-zA-Z0-9_]$", "match_all": true },
{ "key": "eol_selector", "operator": "not_equal", "operand": "string.quoted.double - punctuation.definition.string.end", "match_all": true }
]
},
Но теперь когда вы просто вводите букву э, вводятся двойные кавычки. Код с буквой э не нужен, удаляем его из нашего файла. Проверяем — буква э пишется теперь нормально. Если после ваших действий с назначением команд клавишам русской раскладки символы русской раскладки заработают неправильно, значит, скорее всего, вы вставили лишнее, подлежащее удалению.
4. Поддержка русской раскладки для дефолтных хоткеев без служебных клавиш
Можно добавлять строки ниже в файл, открывающийся после Preferences → Key Bindings - User, сразу после установки Sublime Text, чтобы не приходилось переключаться при оборачивании текста в различные виды кавычек и скобок, а также при использовании Vintage Mode. Проверял, ошибок возникнуть не должно.
Обёртка текста в скобки и кавычки
//
//
// Дефолтные сочетания в русской раскладке клавиатуры
//
//
// Выделение {фигурными скобками}
{ "keys": ["Х"], "command": "wrap_block", "args": {"begin": "{", "end": "}"}, "context":
[
{ "key": "indented_block", "match_all": true },
{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
{ "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
{ "key": "following_text", "operator": "regex_match", "operand": "^$", "match_all": true },
]
},
{ "keys": ["Х"], "command": "insert_snippet", "args": {"contents": "{${0:$SELECTION}}"}, "context":
[
{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }
]
},
{ "keys": ["Ъ"], "command": "move", "args": {"by": "characters", "forward": true}, "context":
[
{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
{ "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
{ "key": "following_text", "operator": "regex_contains", "operand": "^\\}", "match_all": true }
]
},
// Выделение "кавычками-ёлочками"
{ "keys": ["Э"], "command": "insert_snippet", "args": {"contents": "\"${0:$SELECTION}\""}, "context":
[
{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }
]
},
{ "keys": ["Э"], "command": "move", "args": {"by": "characters", "forward": true}, "context":
[
{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
{ "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
{ "key": "following_text", "operator": "regex_contains", "operand": "^\"", "match_all": true },
{ "key": "selector", "operator": "not_equal", "operand": "punctuation.definition.string.begin", "match_all": true },
{ "key": "eol_selector", "operator": "not_equal", "operand": "string.quoted.double - punctuation.definition.string.end", "match_all": true },
]
},
// Выделение 'одинарными кавычками'
{ "keys": ["э"], "command": "insert_snippet", "args": {"contents": "'${0:$SELECTION}'"}, "context":
[
{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }
]
},
{ "keys": ["э"], "command": "move", "args": {"by": "characters", "forward": true}, "context":
[
{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
{ "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
{ "key": "following_text", "operator": "regex_contains", "operand": "^'", "match_all": true },
{ "key": "selector", "operator": "not_equal", "operand": "punctuation.definition.string.begin", "match_all": true },
{ "key": "eol_selector", "operator": "not_equal", "operand": "string.quoted.single - punctuation.definition.string.end", "match_all": true },
]
},
// Выделение [квадратными скобками]
{ "keys": ["х"], "command": "insert_snippet", "args": {"contents": "[$SELECTION]$0"}, "context":
[
{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true },
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown", "match_all": true }
]
},
{ "keys": ["х"], "command": "insert_snippet", "args": {"contents": "[${0:$SELECTION}]"}, "context":
[
{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }
]
},
{ "keys": ["ъ"], "command": "move", "args": {"by": "characters", "forward": true}, "context":
[
{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
{ "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
{ "key": "following_text", "operator": "regex_contains", "operand": "^\\]", "match_all": true }
]
},
Vintage Mode
Вставляем отсюда, (на всякий случай архивная копия, также см. ответ
5. Поддержка русской раскладки в некоторых плагинах
Чтобы не мучились настраивать русскую раскладку для этих плагинов самостоятельно, готовый код:
BetterFindBuffer
// BetterFindBuffer, русская раскладка
{
"keys": ["щ"],
"command": "find_in_files_open_file",
"context": [
{ "key": "selector", "operator": "equal", "operand": "text.find-in-files" }
]
},
{
"keys": ["ф"],
"command": "find_in_files_open_all_files",
"context": [
{ "key": "selector", "operator": "equal", "operand": "text.find-in-files" }
]
},
{
"keys": ["т"],
"command": "find_in_files_jump_file",
"context": [
{ "key": "selector", "operator": "equal", "operand": "text.find-in-files" }
]
},
{
"keys": ["з"],
"command": "find_in_files_jump_file",
"args": { "forward": false },
"context": [
{ "key": "selector", "operator": "equal", "operand": "text.find-in-files" }
]
},
{
"keys": ["о"],
"command": "find_in_files_jump_match",
"context": [
{ "key": "selector", "operator": "equal", "operand": "text.find-in-files" }
]
},
{
"keys": ["л"],
"command": "find_in_files_jump_match",
"args": { "forward": false },
"context": [
{ "key": "selector", "operator": "equal", "operand": "text.find-in-files" }
]
},
{
"keys": ["а"],
"command": "bfb_fold_and_move_to_next_file",
"context": [
{"key": "selector", "operator": "equal", "operand": "text.find-in-files" }
]
},
{
"keys": [","],
"command": "bfb_toggle_popup_help",
"context": [
{"key": "selector", "operator": "equal", "operand": "text.find-in-files" }
]
},
Markmon
// Markmon, русская раскладка
{ "keys": ["ctrl+k", "ь"], "command": "markmon_toggle", "args":
{
"enable": true
}
},
MarkdownEditing
// MarkdownEditing, русская раскладка
{ "keys": ["ё"], "command": "insert_snippet", "args": {"contents": "`${0:$SELECTION}`"}, "context":
[
{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true },
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown", "match_all": true }
]
},
{ "keys": ["ё"], "command": "run_macro_file", "args": {"file": "Packages/MarkdownEditing/macros/Skip Closing Character.sublime-macro"}, "context":
[
{ "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true },
{ "key": "following_text", "operator": "regex_contains", "operand": "^`", "match_all": true },
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown", "match_all": true }
]
},
{ "keys": ["Ё"], "command": "insert_snippet", "args": {"contents": "~~${0:$SELECTION}~~"}, "context":
[
{ "key": "setting.auto_match_enabled", "operator": "equal", "operand": true },
{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true },
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown", "match_all": true },
{ "key": "selector", "operator": "not_equal", "operand": "markup.raw", "match_all": true }
]
},
{ "keys": ["№"], "command": "insert_snippet", "args": {"contents": "#${0: ${SELECTION/(^ | $)//g} }#"}, "context":
[
{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true },
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown", "match_all": true },
{ "key": "preceding_text", "operator": "not_regex_contains", "operand": "#{6}", "match_all": true },
{ "key": "setting.mde.match_header_hashes", "operator": "equal", "operand": true },
{ "key": "selector", "operator": "not_equal", "operand": "markup.raw", "match_all": true }
]
},
{ "keys": ["№"], "command": "insert_snippet", "args": {"contents": "#${0: ${SELECTION/(^ | $)//g}}"}, "context":
[
{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true },
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown", "match_all": true },
{ "key": "preceding_text", "operator": "not_regex_contains", "operand": "#{6}", "match_all": true },
{ "key": "setting.mde.match_header_hashes", "operator": "equal", "operand": false },
{ "key": "selector", "operator": "not_equal", "operand": "markup.raw", "match_all": true }
]
},
{ "keys": ["№"], "command": "run_macro_file", "args": {"file": "Packages/MarkdownEditing/macros/Padded Headline.sublime-macro"}, "context":
[
{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true },
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown", "match_all": true },
{ "key": "preceding_text", "operator": "regex_contains", "operand": "#{6}", "match_all": true },
{ "key": "following_text", "operator": "regex_contains", "operand": "#+", "match_all": true },
{ "key": "setting.mde.match_header_hashes", "operator": "equal", "operand": true },
{ "key": "selector", "operator": "not_equal", "operand": "markup.raw", "match_all": true }
]
},
{ "keys": ["№"], "command": "run_macro_file", "args": {"file": "Packages/MarkdownEditing/macros/Padded Headline.sublime-macro"}, "context":
[
{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true },
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown", "match_all": true },
{ "key": "preceding_text", "operator": "regex_contains", "operand": "#{6}", "match_all": true },
{ "key": "setting.mde.match_header_hashes", "operator": "equal", "operand": false },
{ "key": "selector", "operator": "not_equal", "operand": "markup.raw", "match_all": true }
]
},
{ "keys": ["Ю"], "command": "run_macro_file", "args": {"file": "Packages/MarkdownEditing/macros/Convert to Blockquote.sublime-macro"}, "context":
[
{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true },
{ "key": "selector", "operator": "equal", "operand": "text.html.markdown", "match_all": true },
{ "key": "selector", "operator": "not_equal", "operand": "markup.raw", "match_all": true }
]