Рабочий стол OSX: консистентные горячие клавиши
Сергей хочет открыть Slack. Запускает Spotlight и в нем начинает писать «Slack». Печатается «Ыдфсл». Ок, нужно стереть «Ыдфсл», а для этого эксплуатировать лучезапястный сустав в попытке дотянуться до BACKSPACE
… Когда Slack наконец-то (!) открылся, Сергей пишет сообщение и хочет вставить ссылку в текст, используя привычный
, но Slack начинает поиск по личным сообщениям.
Как помочь Сергею, мы сейчас и разберемся. Статья не является туториалом — скорее, набором идей, которые желающий сможет забрать себе.
Проблемы, о решении которых пойдет речь
Долго добираться до нужного приложения.
Болят кисти из-за постоянных сгибов, когда тянешься мизинцем до BACKSPACE, ENTER, =, ], SHIFT, ; …
Во всех приложениях разные горячие клавиши.
Словарь терминов и сокращений
Homerow — клавиши, на которых лежат пальцы в базовой позиции: asdfjkl;
Hotkey — горячие клавиши;
Mapping (маппинг) — соответствие нажимаемых клавиш действию.
— значит, что клавиши нажаты вместе, причемKEY_1
нажимается первой (является модификатором).Vim (вим) — модальный текстовый редактор, идеи которого легко переносимы в другие приложения.
Normal mode — режим в виме, который позволяет манипулировать курсором. Например, нажатие на «j» перемащает курсор на строчку вниз, а не вставляет символ «j».
Insert mode — режим ввода текста в виме, когда нажатие на «j» пишет «j».
Buffer — абстракция хранения открытого файла в виме.
Буфер обмена — место хранения скопированного (clipboard).
Slack — программа, hotkeys которой поручили проектировать тому, кто никогда не пользовалсявв hotkeys других программ.
Инструменты
Karabiner для настройки глобальных горячих клавиш.
Neovim + Neovide для работы с текстом.
VimiumC для vim-подобных горячих клавиш в браузере и pdf-файлах.
WezTerm для возможности использовать normal и visual modes вима в терминале.
Alfred — умный поиск с консистентной раскладкой, хранение регистров.
Открываем то, что нужно, минимальным количеством действий
Поясню, что имеется в виду, через описание двух задач, с которыми часто сталкивается Сергей.
Первая задача: перевести слово с английского на русский. Ее обычно решают так: переходят в браузер и в нем уже открывают переводчик. Когда переводчик загрузится, пишут слово.
Вторая задача: загуглить вопрос. Открывают браузер. Открывают новый таб. В нем пишут запрос.
В обоих примерах есть шанс наткнуться не на ту раскладку. Например, вы можете начать писать переводимое слово на английском с выставленной русской раскладкой. Или же начать писать «Googl» в Spotlight, чтобы обнаружить «Пщщпд».
Один мой приятель сделал hotkey, вроде
, чтобы «прыгнуть» в браузер из любого места, но на мой взгляд, это неверный подход, так как решение не универсально, занимает hotkey, не решает проблему случайной раскладки и требует на одно действие больше, чем то решение, которое опишу ниже.
Alfred — то же самое, что Spotlight, только позволяет гарантировать, что английская раскладка уже выбрана: опция Force Keyboard в Advanced settings. Покажу, как с Alfred решаются описанные выше задачи:
Первая задача: запускаем Aflred нажатием
Обратите внимание, насколько короче стал workflow. Было:
Открыть браузер.
Зайти на Google Translate.
Подождать его загрузку (если не настроен custom search engine или другое решение).
Написать текст.
Дождаться результата.
Стало:
Открыть Alfred.
Написать текст.
Дождаться результата.
С переводом русского слова нужно будет после открытия Alfred поменять раскладку, но я все равно вижу плюс в том, что действие детерминировано — вы всегда оказываетесь в Alfred с английской раскладкой. Не будет случаев, когда ваш «поток» прервется из-за неожиданного ввода не того, что вы хотели.
Вторая задача: запускаем Aflred, пишем запрос. Открывается Google в браузере с введенным запросом. Aflred сам понимает, что текст без команд нужно загуглить.
Перечислю, для чего еще Aflred является универсальной точкой входа (все работает по умолчанию, а если нет, то легко гуглится):
Поиск по истории браузера.
Поиск по закладкам браузера.
Глобальный поиск по внутреннему содержанию файлов (включая PDF).
Открытие программы по названию.
Калькулятор.
Общий с вимом буффер обмена.
Последний пункт заслуживает особого обсуждения.
Консистентный буфер обмена
Большинство людей, как и Сергей, живет в парадигме одной ячейки буффера обмена. Вим же помещает последние 9 «копирований» в регистры, а так же позволяет использовать в качестве регистра любой символ (есть исключения, но не будем об этом).
Регистры вима — удобная фича, но она не доступна за его пределами. Alfred по умолчанию складывает все скопированное к себе, и позволяет искать по нему (Clipboard History).
Сюда же сохраняются сделанные скриншоты. Вы можете настроить, сколько элементов может храниться и как долго.
Я пришел к тому, что чаще всего для копирования и вставки нескольких вещей за раз удобнее использовать Alfred даже в виме. Чтобы это работало, надо всего лишь написать маппинг
. Как это сделать, подробнее написано в Пример1 и Пример2 ниже.
Расположение клавиш не должно приносить боль
Когда я пользуюсь дефолтной клавиатурой на макбуке, мои руки расположены под углом к ней:
Когда нужно нажать BACKSPACE
, ENTER
, SHIFT
, SLASH
, =
, приходится делать небольшой сгиб в лучезапястном суставе (так же делал и Сергей во введении)
Об этой и множестве других проблем хорошо написано тут. Идея из статьи (поддерживаю обеими руками, благо, я перенастроил клавиатуру, — и они целы):
в мелких движениях кистью даже лишних два сантиметра — это разница между жизнью и смертью.
Решается проблема двумя способами — специальная клавиатура и/или ремаппинг клавиш. Мне важно иметь возможность работать на ноутбуке, и всегда носить с собой клавиатуру не очень удобно, остается модификация клавиш.
Поможет в этом Karabiner — могущественный и стабильный кастомизатор, как пишут на сайте. Вопрос в том, какие клавиши исправлять. Мой подход — сперва заменить то, без чего жить нельзя, вроде BACKSPACE
и ENTER
, затем, по мере столкновения с неудобствами, добавлять новые маппинги.
Тут важен один принцип — чем чаще используется клавиша, тем ближе она должна быть к homerow.
В 2017 я об этом не подумал, и использовал функциональные клавиши — например, backspace
поставил на f9
. Возникло 2 проблемы — на макбуках исчезли функциональные клавиши, да и тянуться до их отсутствия них долго и неудобно. Тем не менее, привычка жать на backspace
в стороне f9
безымянным пальцем правой руки осталась, и это повлияло на мою текущую конфигурацию клавиатуры.
Рассказываю об этом, чтобы вы настроили раскладку под себя, например, как это сделано тут:
Мой подход точно такой же. Часть маппингов — попытка избавиться от сгибов запястья с соблюдением принципа близости к homerow:
,
,
,
,
,
, чтобы получить |
.CAPSLOCK —> ESCAPE
ESCAPE —> CAPSLOCK
, потому что ESCAPE
нажимается на несколько порядков чаще.
Часто приходится пользоваться стрелками — например, чтобы выделить слово левее курсора
или сдвинуть фокус в Finder. Для этого использую относительно популярное решение (уже более пяти лет не представляю свою жизнь без него): RIGHT_CMD
+ клавиши hjkl
означают стрелки (как в виме). Настраивается включением доступной по умолчанию в Karabiner модификацией — «Change right_command+hjkl to arrow keys».
, потому что +
по умолчанию — это
, а мои SHIFT
и =
— получаются другими сочетаниями с CMD
.
Если вы программист, то скобки определенно должны быть на homerow:
,
,
,
.
Используется RIGHT
, потому что LEFT
уже занят привычными сочетаниями, например,
— поиск.
,
,
Как описывать и ставить модификации, можно посмотреть в документации, там довольно простой UI. Можно попробовать поискать и что-то готовое, например, биндинги как в Emacs. Ниже по тексту я еще вернусь к Karabiner с детальными примерами.
В целом, это все. Добавлю только, что можно столкнуться с проблемой приоритета (документация), о которой можно не читать, пока все и так работает. В моем случае, сломался
(стрелка налево) + ALT
в виме, чтобы двигать курсор через слово. Решилось добавлением
по приоритету выше, чем
.
Еще одна потенциальная проблем в том, что Karabiner-модификации программы в фокусе применяются и к Alfred. Например, у меня для Neovide
меняется на
, и если во время использования Neovide открыть Alfred, то
работать не будет. Исправляется в настройках Alfred: Appearance —> внизу: Options —> Focusing —> Compatibility Mode.
Везде должны быть одни и те же горячие клавиши
По умолчанию это совсем не так. Несколько примеров, которые первыми приходят на ум:
В виме удалить слово в insert mode —
, в любом другом месте — . В Slack вставить ссылку —
, а практически везде —
.Отмена отмены в большинстве программ
, а в виме
.
Во время пользования компьютером я хочу, чтобы мое состояние потока не спотыкалось о то, в каком я сейчас приложении и какой hotkey используется конкретно тут. Хочется вынести нажатия клавиш на уровень бессознательной привычки. Если надо открыть новый таб, жмем
и таб открывается — не важно, вим это, терминал, идея или браузер.
Не всегда можно изменить hotkey по умолчанию, например, Slack не дает простого способа это сделать. Я безуспешно пытался настроить это несколько лет назад. Недавно попробовал «Interactivity & Shortcuts», но постоянно натыкаюсь на «There’s been a glitch…» page.
Иногда же нажатие вообще не доходит до программы, как в случае с вимом, который открывается внутри терминала.
Пример 1. Вставка ссылки и в Slack, и в Vim, как везде
В Яндекс Почте, Gmail, Google docs, Telegram, MS Word, MS Excel, Notion, Github, Bitbucket, на Хабре, в конце-то концов, для вставки ссылки в текст используется
. Давайте настроим то же самое и в Slack.
Karabiner позволяет «на лету» менять клавиши, которые были нажаты. Вот пример перевода — только для приложения Slack —
в
:
slack.json
{
"title": "Slack",
"rules": [
{
"description": "Insert link",
"manipulators": [
{
"type": "basic",
"from": {
"key_code": "k",
"modifiers": {
"mandatory": [ "left_command" ]
}
},
"to": [
{
"key_code": "u",
"modifiers": [ "left_shift", "left_command" ]
}
],
"conditions": [
{
"type": "frontmost_application_if",
"bundle_identifiers": [
"^com.tinyspeck.slackmacgap$"
]
}
]
}
]
}
]
}
Узнать название frontmost_application можно с помощью утилиты Karabiner Event Viewer, который идет в пакете с Karabiner. Символы^
и $
в bundle_identifiers
— означают начало и конец строки (обычный regexp).
Файл положить в ~/.config/karabiner/assets/complex_modifications/slack.json
.
С вимом же достаточно пары маппингов:
nnoremap 'viw"9di[9](*)'
xnoremap '`>a](*)`<['
О том, как заставить работать CMD, написано в следующем параграфе.
Если вам, как и Сергею, совсем не интересен вим, прошу дать мне шанс убедить вас — посмотрите только пример 5 из этого раздела.
Пример 2. Как выйти из вима
— типичный hotkey для закрытия программы. Основная проблема возникает при использовании вима, так как последний открывается из терминала. Нажатия
крадет терминал.
Мне известны три подхода к решению проблемы выхода из вима отлавливания CMD
в виме, я опишу все.
Первый — разобраться, как достичь этого в вашем терминале или поменять терминал. Kitty умеет так делать (issue), WezTerm тоже.
Второй — открывать вим в специальном клиенте, а не в терминале. Я пользуюсь Neovide — клиентом для neovim.
определен по умолчанию и будет убивать процесс программы. Так же можно маппить любые CMD+
сочетания, например, сохранение файла на
, как это сделано в большинстве программ.
Vimscript example:
nmap :w
imap :wa
Пример 3. Основные hotkey должны работать и в виме
Таким же способом (через
) я описал, чтобы и в insert, и в normal модах вима работали:
— закрыть окно.
— выделить весь буффер.
— отменить действие.
— копировать.
— вставить.
— поиск.
— создать таб.
— уменьшить шрифт.
— увеличить шрифт.
— установить дефолтный шрифт.
Если вас не пугает Fennel (Lisp в мире Lua), то можно посмотреть на мою кофигурацию Neovim тут. Большая часть CMD-маппингов в init.fnl
.
Что касается
то сочетание клавиш убьет процесс приложения, в котором запущен вим, и вы не можете дописать какую-то свою логику — например, сохранить все открытые буферы. Тут мы подходим к третьему подходу.
Опишем модификацию в Karabiner: когда открыт Neovide или VimR (другой клиент), нужно отлавливать сочетание клавиш
и вместо этого отправлять
(который мы замаппить в виме уже сможем):
vim.json
{
"title": "VimQ",
"rules": [
{
"description": "Cmd key",
"manipulators": [
{
"type": "basic",
"from": {
"key_code": "q",
"modifiers": {
"mandatory": [
"left_command"
]
}
},
"to": [
{
"key_code": "q",
"modifiers": [
"left_option"
]
}
],
"conditions": [
{
"type": "frontmost_application_if",
"bundle_identifiers": [
"^com.qvacua.VimR$",
"^com.neovide.neovide$"
]
}
]
}
]
}
]
}
А в .vimrc
пропишем маппинг на
, чтобы сохранить и закрыть все буфферы:
nnoremap :silent! waqa!
По такому же принципу я настроил*, чтобы в Neovide
превращались в
, а
— в
, удаляя целое слово и целую строку соответственно.
(перемещение курсора на слово левее везде, кроме вима), для Neovide превращается в
(hotkey в виме). То же самое для RIGHT
. Пример.
*
Пояснение:
— удаляет слово в вим,
— мой маппинг на BACKSPACE.
Пример 4. Смена раскладки в виме
Использовать глобальную раскладку не получится, даже не стоит тратить время. Как-то Сергей попробовал это сделать, но оказалось, что после выхода в normal mode ничего не работает. Vim знает, что «i» — это insert mode, но не знает, что «ш» должно означать то же самое.
Решается это дело по-разному, мы с Сергеем остановились на таком решении.
По пути
~/.vim/keymap/russian-jcukenmac.vim
кладем вот этот файл.В Karabiner создаем модификацию, чтобы глобальный hotkey на смену раскладки вызывал
— hotkey смены раскладки в виме по умолчанию. В моем случае
— меняет раскладку глобально.Дописываем hotkey для смены раскладки из вима на
в normal mode:
set keymap=russian-jcukenmac
set iminsert=0
set imsearch=0
function! s:SetLangCmd(langName)
let n = (a:langName == "en_US") ? 0 : 1
execute "set iminsert=" . n . " imsearch=" . n
execute "lang " . a:langName . ".UTF-8"
endfunction
function! s:ToggleKeyboard()
call SetLangCmd(&iminsert == 0 ? "ru_RU" : "en_US")
endfunction
xnoremap :call ToggleKeyboard()
nnoremap :call ToggleKeyboard()
Я использую все то же самое, только оно на Fennel.
Пример 5. Normal mode вима в терминале и в браузере
Даже если вы не пользуетесь вимом, имеет смысл разобраться с базовыми командами normal mode и внедрить их в свое окружение. К слову, так я и сам пришел к виму, поставив сперва Vimium-плагин в браузер, затем IdeaVim-плигин в Intellij Idea.
Сейчас у меня стоит VimiumC, который добавляет hotkey вима еще и в PDF.
Чем так хорош этот normal mode?
Удобные клавиши для скролла вниз/вверх (j/k, d/u — down/up), перехода в формы ввода (gi — go insert), перехода в начало/конец страницы (gg/G) и просто эмуляции нажатия мышью в любое место (f — find), а также смена и перемещение табов, закрытие страницы, поиск по тексту.
Все это начинает иметь больше смысла, когда вы замечаете, что те же самые hotkeys доступны и в терминале, и в редакторе кода.
Но давайте по порядку. Редакторы всякие бывают, но почти все дают возможность подключить биндинги вима — IdeaVim для Intellij-продуктов, Vintage Mode для Sublime, Evil для Emacs, Neovim интегрируется в VSCode напрямую.
В терминале у вас уже есть типичные маппинги вима. Запустите man ssh
, и вам откроется интерфейс, где d/u
и j/k
будут скроллить, /
искать, gg
— переходить в начало строки и т.д.
В терминале легко настроить вим-биндинги и для строки ввода: set keymap vi-command
в ~/.inputrc
, или plugin vim
, елси вы используете oh-my-zsh.
Возможность увести курсор за пределы строки ввода в терминале — например, копировать кусочек напечатанного текста, доступна в WezTerm — Copy Mode.
Я выделил кусочек текста без мыши, те ми же hotkey, что пользуюсь везде
Как войти в normal mode консистентно
В обычном vim переход в normal mode осуществляется нажатием на ESCAPE
. В терминале, открытом из Neovim, используется
В normal mode WezTerm —
Переназначить hotkey для WezTerm и для терминала, открытого внутри Neovim, на ESCAPE
нельзя, потому что ESCAPE
нужен для команды отмены и для перехода в normal mode, предоставляемого настрокой set keymap vi-command
или vim
плагином в oh-my-zsh.
Мое решение — использовать
(напоминаю, что ESCAPE
находится на месте CAPSLOCK
).
Для терминала Neovim в Lua конфигах приписываем:
vim.keymap.set("t", "", "", {desc = "Normal mode"})
В самом Neovim это будет работать без всяких изменений.
Для WezTerm в .wezterm
и тоже на Lua пишем:
return {
keys = { { key = 'Escape', mods = 'CMD', action = act.ActivateCopyMode }, },
}
Ссылка на документацию, и еще одна на мою конфигурацию WezTerm.
Пример 6. Горячие клавиши для табов
В большинстве приложений
откроет новый таб,
— выберет следующий таб. Последний маппинг кажется слишком громоздки, давайте заменим его на
(мнемоника для этого hotkey: на клавише ».» нарисован знак »>» — как стрелка направо, показывающая, куда перенесется фокус).
Вот пример настройки смены таба для WezTerm:
return {
keys = {
{ key = '.', mods = 'CMD', action = act.MoveTabRelative(1) },
{ key = ',', mods = 'CMD', action = act.MoveTabRelative(-1) },
},
}
В браузере это можно настроить через Vimium или VimiumC, но не во всех вкладках плагин будет работать. По этой причине я создал модификацию Karabiner для всех приложений, кроме тех, в которых открываю Neovim (подробнее было в примере 1):
Пример Karabiner:
{
"description": "right cmd + ,/. —> cmd + shift + [/]",
"manipulators": [
{
"type": "basic",
"from": {
"key_code": "comma",
"modifiers": {
"mandatory": [
"right_command"
]
}
},
"to": [
{
"key_code": "open_bracket",
"modifiers": [
"shift",
"right_command"
]
}
],
"conditions": [
{
"type": "frontmost_application_unless",
"bundle_identifiers": [
"^com.qvacua.VimR$",
"^com.neovide.neovide$"
]
}
]
},
{
"type": "basic",
"from": {
"key_code": "period",
"modifiers": {
"mandatory": [
"right_command"
]
}
},
"to": [
{
"key_code": "close_bracket",
"modifiers": [
"shift",
"right_command"
]
}
],
"conditions": [
{
"type": "frontmost_application_unless",
"bundle_identifiers": [
"^com.qvacua.VimR$",
"^com.neovide.neovide$"
]
}
]
}
]
}
Вим — исключение, потому что везде табы — это табы, а в виме табы — это что-то вроде layout (набор буфферов). Поэтому есть плагин, который визуально показывает буфферы как табы — bufferline. Используя предоставляемые плагином команды, можно замаппить hotkey на
и т.д. Как маппить CMD, я писал в примере 2.
Пример моего конфига табов для neovim на fennel.
Но можно и без плагинов с вимовскими табами, используя команды: gt
, :tabnew
, :tabmove +1
.
FAQ
— Есть ли альтернатива karabiner для линукса?
— Есть программы вроде input-remapper. В счастливые времена до пандемии я пытался настроить все, что у меня есть, через них, но потерпел фиаско (в Firefox не работали WIN
+ hjkl
, когда к ним добавлялся SHIFT
). Не знаю, как сейчас, но всегда можно настроить xkb по аналогии с тем, как сделано в этом репозитории (и еще раз ссылка на YouTube обзор).
— Karabiner for Windows?
— Microsof powertoys.
— Разве проблема раскладки не решается Punto Switcher?
— Частично решается, но придется настраивать, чтобы он не реагировал на шаблоны автозамены, сокращения из мира программирования, пароли и т.п. Я ходил этой дорогой и не раз, но в итоге отказался.
— Можно интегрировать Vim в текстовые поля браузера (firenvim, wasavi), да и вообще в любое поле ввода текста OSX (kindavim). Почему об этом не сказано?
— Я пользовался всем из вышеперечисленного, но решил, что проблем, багов и неконсистентных маппингов от этого создается больше, чем пользы. Может быть, попробую еще раз позже.
— Если тебе так важно эффективно пользоваться клавиатурой и держать пальцы на homerow, то почему не Colemak или Halmak?
— Меня пугает объем работы: перемапливание вима, поиск альтернативных решений для русской раскладки, полная несовместимость при работе на чужих компьютерах, изменение всех горячих клавиш всех программ.
— Конфиругации Karabiner — громоздки.
— Есть Goku, позволяющий их описать значительно короче.
В заключение
Ряд подходов, что я исползьовал, далеко не идеальны и не зафиксированы. Время от времени дописываются новые hotkey, удаляются старые, меняются утилиты, которые стоят в основе. Например, я больше не пользуюсь приложением Slack — открываю его в браузере (чтобы получить фичи VimiumC и проверку грамматики, которая у меня уже настроена в браузере).
Мои дотфайлы лежат тут, но не рекомендую их брать себе такими, какие они есть. Лучше пользоваться ссылками, что указал выше в статье.
Если найдется время и накопится материала, напишу о том, как настроить управление окнами и темами в OSX. Личность же Сергея оставляю до тех пор не раскрытой.