Не забывай свои корни, помни, есть вещи на порядок выше…
Каста здесь
История из жизни
Многие начинали свою компьютерную жизнь с игр. Кто не начинал, тот играет в шахматы на деревянной доске, как какой-нибудь президент. Я не играю в шахматы, я играю в Starcraft и другие подобные игры. Когда-то я играл многими часами, но время прошло и игры перешли в серьёзные занятия, дающие какой-то результат. В то же время хоть игры и переродились, но привычки остались старые. Иногда хочется поиграть.
Чего нет в шахматах? В шахматах нет живых патрулей, отрядов, минных полей в самых излюбленных местах и много чего ещё, чего так порой не хватает, в то время как ты перекомпилируешь какую-нибудь программу, редактируя её. Ностальгия захватывает и мы медленно начинаем искать что-нибудь подобное в программном окружении. И находим!
Сегодня мы поговорим о весёлых макросах.
В Emacs'е есть два вида макросов — скучные и весёлые. Скучные — это макросы в лиспе, а весёлые — это клавиатурные макросы, хорошо знакомые многим. Вот про вторые и пойдёт речь.
Затравка — осваиваем кнопки
Когда запускаешь игру в первый раз, видишь перед собой одного строителя, который стоит и ничего не делает. Погоняв его по окрестностям, начинаешь что-то строить, а заодно замечаешь выделенные буквы в названиях зданий и действий. Эти буквы — ключи доступа, через которые можно быстро выполнять действия. В дальнейшем игра сводится к последовательному нажатию этих букв в нужное время.
Что такое макрос?
Все знают, что есть такие штуки в программах, которые называются «макросы», да и сам Emacs в корне своего названия содержит это понятие (Editor MACroS), поэтому не знать это понятие при пользовании Emacs'ом и не попробовать их — стыдобе подобно. Одновременно с этим не все знают макросы дальше F3 и F4 по аналогии с макросами в других программах, в которых они, действительно, находятся в зачаточном состоянии.
Какие в Emacs'е макросы?
Макросы в Emacs'е, вообще говоря, из общего с другими программами только клавиатурное записывание и имеют. В остальном — это мощнейший аппарат, который можно использовать на полную катушку, до которой, как правило, никто не доходит. Один парнишка на просторах Интернета посреди множества приличных настроек для Emacs'а даже предложил на макросное сочетание повесить другое действие, потому что оно мол не используется никогда. Судя по всему, он думал, что макросы Emacs'а такие же, как и везде, и сводятся к двум клавишам — записи и воспроизведению.
В Emacs'е же дело обстоит совсем по-другому:
Во-первых, при записи макросов они попадают в кольцо макросов, для которого определён ряд операций. То есть макросы туда не просто записываются для воспоминаний о них, а их можно выбирать, гуляя по кольцу туда и обратно. И можно не просто гулять по кольцу, а ещё и заходить в каждый макрос, внося в него изменения.
Во-вторых, это кольцо само по себе не простое. К нему привязаны такие клавиши управления, которые расчитаны на постоянные операции с ним, из-за чего начальная комбинация клавиш для доступа к кольцу запоминается Emacs'ом, чтобы её не нужно было каждый раз нажимать.
Если нажать Ctrl + x + Ctrl + k, то произойдёт вход в клавишное пространство имён. После этого, удерживая Ctrl, можно нажимать многие клавиши, отвечающие за действия с кольцом и текущим макросом.
Рассмотрим на примере:
1) Запишем два макроса. Первый макрос будет вставлять число 1, а второй — число 2.
F3 1 F4
F3 2 F4
Снизу после каждого нажатия на F3 будет видно, что запись началась, а после каждого нажатия на F4 будет видно, что макрос записан. После этих действий в буфере выведено 12 и у нас есть невидимое кольцо, в котором два макроса. Второй макрос является выбранным (или активным).
2) Запустим выбранный макрос. Многие бы захотели сейчас нажать F4, ведь это так просто, но мы не будем её нажимать. Мы сейчас проходим кольцо, а не то, как макрос можно по-быстрому выполнить.
Нажимаем длинный запуск
Ctrl + x + Ctrl + k + Ctrl + k
Увидели, что вставилась цифра 2, при этом мы Ctrl не отпускаем, а продолжаем дальше нажимать кнопки
Ctrl + p + Ctrl + k
И сначала после p видим, что внизу (в минибуфере) что-то пишется (цифра 1), а потом после k видим, как вставляется цифра 1. Всё так же, удерживая Ctrl, мы нажимаем дальше
Ctrl + n + Ctrl + k
И сначала после n видим, что внизу точно так же пишется информация о текущем макросе (цифра 2), а потом после k видим, как вставляется цифра 2.
3) Отпускаем все кнопки. На экране осталось 12212. Первые две цифры участвовали в записи макросов, остальные три цифры — результат выполнения макросов.
Что же произошло? Мы записали два простых макроса, а потом вошли в клавишное пространство имён через нажатие префиксной комбинации Ctrl + x + Ctrl + k. Действуя в этом пространстве имён, мы сначала выполнили второй макрос, потом переключились со второго макроса на первый и выполнили первый, а потом переключились с первого макроса обратно на второй и снова выполнили второй.
Если вы делали всё правильно, то левый Ctrl после входа в пространство ни разу не отпускался и при этом правой рукой нажимались только клавиши k p k n k.
В этом и заключается различие в запуске макроса: если у нас макрос-одиночка, то нам удобнее запускать его через F4; если же у нас несколько макросов в кольце и нужно запускать их попеременно, то мы пользуемся длинным запуском.
Что ещё можно делать с этим кольцом?
Само кольцо можно вращать, меняя текущий элемент, поэтому у кольца нет фиксированного начала или конца. В каждый момент времени последним элементом в кольце считается текущий элемент, а первым — элемент, следующий за ним. Когда будет записываться новый макрос, он будет добавляться в кольцо после текущего элемента, становясь последним элементом в кольце. Так можно формировать желаемый порядок макросов в кольце.
Большая часть операций с кольцом (переходы вперёд n и назад p, выполнение k, удаление d, редактирование e макроса) действует точно так же, требуя лишь нажатия одной кнопки, что делает использование кольца скоростным и удобным. Но встречаются и операции, которые, по идее, должны работать так же, но не работают. Возможно, они были добавлены позже другим автором, который не заметил этой экосистемы.
Чтобы почистить кольцо, удалив некоторые элементы или вообще все, нужно нажать
Ctrl + x + Ctrl + k + Ctrl + d
А потом просто, удерживая Ctrl, нажимать d d d.
А поменять очерёдность макросов можно через
Ctrl + x + Ctrl + k + Ctrl + t
Иногда это требуется совместно с чисткой кольца от неудавшихся макросов.
И вот это — как раз тот случай, когда удаление элементов срабатывает в сокращённом варианте, а обмен элементов — только в полном. Так что чаще всего нужно просто знать, какие действия надо набирать в полном виде. Как правило, объединяет такие неудобные действия между собой то, что они используются реже основных.
По длине ограничение кольца составляет 8 макросов, но в настройках Emacs'а, понятное дело, его легко можно увеличить (например, поставить 32). Много это или мало — для отдельных макросов, записанных вручную, может показаться, что 8-ми достаточно. Но чтобы серьёзно этим пользоваться, 8 — маловато.
Делаем первых бойцов
Погоняв строителя, когда мы построили первые здания, открытые для строительства, мы обнаруживаем бараки, в которых можно делать простейших бойцов. Обычно их два: один — для близкого боя, другой — для дальнего. По мере их создания мы точно так же замечаем выделенные буквы для быстрого доступа к данным бойцам и их действиям.
Как можно создать макрос?
Поначалу мы, конечно же, записываем их через F3 F4. Потом мы находим редактор макросов, в котором обнаруживаем единственную возможность редактирования уже записанных макросов, и всё возвращается обратно к F3 F4.
На самом деле, есть два состояния кольца: в кольце ничего нет и в кольце что-то есть. И для этих двух состояний есть свои способы создания нового макроса.
Когда в кольце макросов ничего нет и мы просто хотим создать макрос, мы можем применить следующие способы:
1) Запустить редактор макроса, который предложит создать макрос.
Ctrl + x + Ctrl + k + Ctrl + e y
Этот способ требует, чтобы в макрос было обязательно что-то записано, пустой макрос создать не получится.
2) Применить хак к F3 F4.
F3 + Ctrl + u + F4
Emacs в минибуфере пишет ошибку, но абсолютно пустой макрос создаётся.
Wrong type argument: integerp, (4)
3) Запустить команду чтения макроса из текста.
Ctrl + SPC + Ctrl + SPC + Alt + x + read-kbd-macro
Здесь есть побочный эффект: так как мы ставим и убираем маркер, меняется кольцо маркеров, которое где-то может быть нужно. Через Ctrl + u + Ctrl + SPC обычно можно гулять по этому кольцу, таким образом может появиться лишняя позиция.
Когда в кольце макросов есть хотя бы один макрос и мы просто хотим добавить макрос, мы можем применить следующие способы:
1) Скопировать макрос через однострочный редактор макросов.
Ctrl + x + Ctrl + k + SPC + Ctrl + k
Откроется редактор для текущего макроса, но из-за Ctrl + k макрос скопируется в новый макрос, отбросив всё содержимое.
2) Применить хак к F3 F4.
F3 + Ctrl + u + F4
Здесь способ тот же самый, как и с пустым кольцом.
Когда в кольце есть макросы, команда read-kbd-macro не будет создавать новый макрос, а выполнит запись в выбранный. Поэтому здесь способ с этой командой не работает. Можно только перезатереть выбранный макрос, который может содержать что-то важное.
Тут у многих может возникнуть вопрос «а зачем нам пустой макрос?». Дело в том, что макросы бывают большие и маленькие, сложные и простые. Записывание макроса может потребовать гораздо больше времени, чем его быстрая запись через редактор. К тому же не всегда кажущаяся правильной запись макроса оказывается такой при его выполнении. Нужно уметь пользоваться редактором, тем более, что он элементарный.
Как можно редактировать макрос?
Для следующего эксперимента нам нужно будет сделать два макроса и приготовить третий совершенно пустой
F3 123 F4
F3 456 F4
F3 Ctrl+u F4
У нас есть три макроса в кольце: один — вставляет 123, другой — вставляет 456, третий макрос пустой и является выбранным. Нам нужно в третий макрос записать содержимое первых двух.
Мы переходим в первый макрос
Ctrl + x + Ctrl + k + Ctrl + n
Открываем его на редактирование
Ctrl + e
Мы видим
;; Keyboard Macro Editor. Press C-c C-c to finish; press C-x k RET to cancel.
;; Original keys: 123
Command: last-kbd-macro
Key: none
Macro:
123 ;; self-insert-command * 3
Переходим в строку наверху, где написано
;; Original keys: 123
И копируем в буфер 123.
Потом просто закрываем буфер
Ctrl + x + k + <RET>
Затем переходим во второй макрос
Ctrl + x + Ctrl + k + Ctrl + n
Открываем его на редактирование
Ctrl + e
Мы видим
;; Keyboard Macro Editor. Press C-c C-c to finish; press C-x k RET to cancel.
;; Original keys: 456
Command: last-kbd-macro
Key: none
Macro:
456 ;; self-insert-command * 3
Переходим в строку наверху, где написано
;; Original keys: 456
И копируем в буфер 456.
Потом просто закрываем буфер
Ctrl + x + k + <RET>
Затем переходим в третий макрос
Ctrl + x + Ctrl + k + Ctrl + n
Открываем его на редактирование
Ctrl + e
Мы видим
;; Keyboard Macro Editor. Press C-c C-c to finish; press C-x k RET to cancel.
;; Original keys:
Command: last-kbd-macro
Key: none
Macro:
И вставляем после строки «Macro:» через Ctrl + y, Alt + y, Ctrl + y наши скопированные строки 456 и 123.
;; Keyboard Macro Editor. Press C-c C-c to finish; press C-x k RET to cancel.
;; Original keys:
Command: last-kbd-macro
Key: none
Macro:
123
456
Дальше сохраняем его через Ctrl + c + Ctrl + c.
Теперь у нас в кольце три макроса: первый — 123, второй 456, третий — 123456.
Естественно, мы могли бы всё это написать вручную (123456), ничего не копируя, но эти макросы слишком простые. В более сложных случаях записать последовательность сходу будет не так просто, а записывание через F3 крупного макроса может утонуть в ошибках. Поэтому записываются друг за другом небольшие макросы, а сверху в редакторе всегда есть их полная запись, которую можно просто скопировать.
Второй способ, который тоже часто используется, — это через read-kbd-macro. Мы просто выделяем текст и выполняем read-kbd-macro. Тогда на каком бы макросе мы не находились (полном или пустом), он перезапишется этим текстом. Только нужно помнить, что пробелы и переводы строк исчезнут.
Пробелы в редакторе пишутся в виде SPC SPC SPC, поэтому копирование через редактор мало когда требует дополнительных действий.
Что есть в макросе?
Из чего состоит макрос — из трёх вещей: последовательность нажатий, счётчик и форматная строка счётчика. Есть ещё две вещи, которые часто можно заметить, — имя макроса и его клавиатурное сочетание. Если первые три вещи есть в каждом макросе, то оставшиеся две являются внешними и крепятся к макросу по желанию через дополнительный код. Поэтому их можно как привязать к макросу, так и отвязать от него.
Сам счётчик внутри макроса может сбить с толку, так как сразу не очевидно, как он меняется при вызове макроса. Ответ прост: он никак не меняется. Этот счётчик просто хранится в макросе и работает совершенно независимо от него. А форматная строка относится только к счётчику, определяя его вывод.
Из того, что часто используется из макросных внутренностей, можно выделить только «запрос на продолжение». Суть его сводится к следующему: во время работы макрос по специальному признаку в своей последовательности определяет, что нужно остановиться и спросить пользователя о дальнейших действиях. Зачем это нужно? У команды выполнения макроса есть аргумент, который задаёт количество его повторений. И когда мы макросом обрабатываем какие-нибудь неизвестные данные, используя аргумент для автоматического повторения макроса, часто их нужно обрабатывать селективно, потому что макрос во время такого выполнения может находить фрагменты, которые нужно пропустить.
Если даже мы записали макрос без этого «запроса на продолжение», то мы его можем добавить через редактор макросов в любой момент. Если же мы его добавили, а он стал не нужен, то через редактор его можно так же быстро убрать.
И выглядит он так
C-x q
Можно прямо открыть макрос 123 и между цифрами вставить такие строки, а потом запустить его и увидеть запрос на продолжение
;; Keyboard Macro Editor. Press C-c C-c to finish; press C-x k RET to cancel.
;; Original keys: 1 C-x q 2 C-x q 3
Command: last-kbd-macro
Key: none
Macro:
1 ;; self-insert-command
C-x q ;; kbd-macro-query
2 ;; self-insert-command
C-x q ;; kbd-macro-query
3 ;; self-insert-command
Если мы запустим теперь макрос с аргументом
Alt + 5 + F4
При выполнении он будет каждый раз в соответствующих местах спрашивать, записать цифру и продолжить или не записывать цифру и продолжить.
Если кто не заметил, то сейчас всё уже готово к проверке этого эксперимента. Вы можете прямо из этого текста скопировать макрос из строки с ";; Original keys:" в буфер, открыть редактор макросов у себя в Emacs'е через Ctrl + x + Ctrl + k + Ctrl + e, вставить эту строку после строки «Macro:», нажать Ctrl + c + Ctrl + c, а потом нажать Alt + 5 + F4.
Какие макросы делать, крупные или мелкие?
Когда делаешь макросы в любых программах, возникает вопрос, как лучше сделать, один крупный или много мелких. Опыт подсказывает, что крупный макрос даже во время записывания может навернуться и его придётся перезаписывать с таким же риском. Если же говорить о хранении, то при малейшем изменении данных, которые он преобразует, нужно будет заниматься его изменением, а изменение приводит к отладке. Легко увязнуть в таком макросе, просто начав редактирование через него какого-то неважного текста.
Есть ещё один аспект — макросы требуют повторений. Часто бывает так, что нужно повторить лишь малое подмножество действий всего макроса циклически. Для этого из монолита пришлось бы их выделять и точно так же делать из них маленький макрос, как можно было бы сделать с самого начала.
Поэтому мелкие макросы лучше. Их можно повторять, их можно заменять, их можно править и легко менять их порядок без разрушения всей системы.
Отправляем солдат в первый поход
Когда первые солдатики сделаны, они отправляются в поход, чтобы что-нибудь разломать и с кем-нибудь подраться. Так выясняется сила, броня и скорость разных видов бойцов, чтобы потом можно было их правильно распределять при спланированных походах.
Какие макросы бывают?
Очень часто макросы применяются только к тексту, так как остальные их возможности неочевидны. В Emacs'е макросы можно разделить на три вида: для изменения текста, для изменения внешнего вида и для выполнение функций.
Большинство действий по созданию окон во фрейме и изменению их размеров мы выполняем многократно, но немногие знают, что всё это можно делать макросами. Сейчас мы запишем простой макрос и поиграем с ним.
1) Откроем редактор и запишем первый макрос.
C-x 2 C-x 3 C-x o C-x o C-x 3 C-x o
2) Запишем второй макрос.
123
3) Теперь в чистом окне выберем первый макрос и запустим его три раза.
Alt + 3 + Ctrl + x + Ctrl + k + Ctrl + k
Не отпуская Ctrl, перейдём ко второму макросу
Ctrl + n
Не отпуская Ctrl, запустим его
Ctrl + k
Если сделали правильно, увидите такую картину
![Макросы в Emacs'е: формируем отряды для обработки - 2 Макросы в Emacs'е: формируем отряды для обработки - 2](https://www.pvsm.ru/images/2016/04/13/makrosy-v-Emacs'e-formiruem-otryady-dlya-obrabotki-2.png)
Несложно сделать и такую картинку
![Макросы в Emacs'е: формируем отряды для обработки - 3 Макросы в Emacs'е: формируем отряды для обработки - 3](https://www.pvsm.ru/images/2016/04/13/makrosy-v-Emacs'e-formiruem-otryady-dlya-obrabotki-3.png)
И такую
![Макросы в Emacs'е: формируем отряды для обработки - 4 Макросы в Emacs'е: формируем отряды для обработки - 4](https://www.pvsm.ru/images/2016/04/13/makrosy-v-Emacs'e-formiruem-otryady-dlya-obrabotki-4.png)
Как видите, мы всего лишь записали простой макрос разделения окон, а делать им можно такие разные узоры.
Более полезный пример — это открытие двух независимых консолей. Если мы программируем, то часто нужно в одной консоли запускать одно, а в другой — другое. Одновременно с этим нам нужно редактировать исходник.
Запишите макрос и выполните
C-x 2 C-x o C-x 3 C-u <<shell>> shell-compil RET C-x o C-u <<shell>> shell-exec RET M-- 2 C-x o
Увидите текстовый буфер с двумя разными консолями под ним
![Макросы в Emacs'е: формируем отряды для обработки - 5 Макросы в Emacs'е: формируем отряды для обработки - 5](https://www.pvsm.ru/images/2016/04/13/makrosy-v-Emacs'e-formiruem-otryady-dlya-obrabotki-5.png)
А теперь запишите такой макрос
C-x o C-x k RET yes RET C-x o C-x k RET yes RET C-x 0 C-x 0
Это обратное действие — закрытие окон с консолями. Теперь набор консолей можно одним макросом открывать, а другим — закрывать. А переключаться между этими макросами можно уже известным нам способом.
Вот ещё пример макроса, но уже не обрабатывающего текст или создающего окна, а выполняющего какой-то набор действий в режиме файлового менеджера.
Запишите макрос
+ d i r <f3> 2*RET C-x C-f f i l e . t x t RET a b c d C-x C-s C-x k RET g ^ g
Установите ему счётчик
Ctrl + x + Ctrl + k + Ctrl + c
1
RET
Создайте где-нибудь директорию и перейдите в неё через Ctrl + x + d. Выполнив в ней макрос пять раз, создадутся пять директорий, где в каждой директории будет свой файл.
![Макросы в Emacs'е: формируем отряды для обработки - 6 Макросы в Emacs'е: формируем отряды для обработки - 6](https://www.pvsm.ru/images/2016/04/13/makrosy-v-Emacs'e-formiruem-otryady-dlya-obrabotki-6.png)
Так можно создавать и удалять деревья каталогов, менять содержимое файлов или их внешние данные вроде прав или имён. Точно так же можно применять макросы в консолях для автоматического компилирования и запуска программ. К тому же один макрос может взаимодействовать с разными консолями, переключаясь между ними.
Вот ещё пример
echo SPC x RET C-x o echo SPC y RET C-x o
![Макросы в Emacs'е: формируем отряды для обработки - 7 Макросы в Emacs'е: формируем отряды для обработки - 7](https://www.pvsm.ru/images/2016/04/13/makrosy-v-Emacs'e-formiruem-otryady-dlya-obrabotki-7.png)
Готовим солдат на базе
Когда мы выяснили в первом бою возможности разных видов солдатиков, мы можем уже делать конкретные виды для решения уже определённых задач и накапливать их в безопасном месте, где никто не будет их трепать. Начинаем выяснять, какой состав и в каком количестве эффективнее действует.
Как припрятать макросы?
Когда в течение сессии Emacs'а мы наделали много всяких удобных макросов, их становится жалко выбрасывать. Поиски в Интернете мало что дают, на лучших страницах, посвящённым Emacs'у, написано только о преобразовании макроса в лисповый вид и дальнейшее сохранение в виде обычной функции. Кажется, это то, что нужно, но тут сталкиваешься с известной проблемой: у этой функции нужно запоминать имя, которое будет забыто уже через пару дней. Есть ещё сложнее вариант — когда будет забыт сам макрос. При сложении эти двух вещей получается <забытый макрос> + <забытое имя> = 0. Но самое главное, и это основная проблема, — такие макросы не вставляются в кольцо макросов, поэтому выпадают из этого чудесного аппарата. Получается расслоение макросов на те, которые были сохранены, и те, которые записаны во время текущей сессии.
Чтобы преобразовать макрос в лисповый вид, мы на него переключаемся и запускаем insert-kbd-macro.
Вот макросы 123 и 456
(setq last-kbd-macro
"123")
(setq last-kbd-macro
"456")
Кажется, простенько и со вкусом, но для загрузки они не подходят, так как оба загружаются в последний макрос, а для этого придётся делать пустой макрос. Чтобы создать макросы, пригодные для загрузки, нужно дать им обоим имена через Ctrl + x + Ctrl + k + n (не путать с переключением на следующий макрос).
Когда у них есть имена onetwothree и fourfivesix, команда insert-kbd-macro работает уже по-другому
(fset 'onetwothree
(lambda (&optional arg)
"Keyboard macro."
(interactive "p")
(kmacro-exec-ring-item (quote ("123" 0 "%d")) arg)))
(fset 'fourfivesix
(lambda (&optional arg)
"Keyboard macro."
(interactive "p")
(kmacro-exec-ring-item (quote ("456" 0 "%d")) arg)))
Вот это теперь закидывается в лисповый файл, который подключается к файлам инициализации Emacs'а. Это считается классическим вариантом сохранения макросов, которые при следующей загрузке Emacs'а будут доступны под этими именами. При этом не учитывается, что кольцо макросов будет пустым.
Свой способ был найден быстро. Пришлось это всё расковырять и добраться до кольца, а потом научиться в него заталкивать макросы без всяких имён.
(defun make-my-macros()
"Make macros."
(interactive)
(add-to-list 'kmacro-ring '("123" 0 "%d"))
(add-to-list 'kmacro-ring '("456" 0 "%d")))
(make-my-macros)
Теперь, выполнив этот код через eval-buffer, можно в любой момент загрузить макросы в кольцо, при этом кольцо остаётся открытым к записи новых макросов. Если этот код закинуть в файл инициализации, то для перезагрузки макросов достаточно будет вызывать функцию.
Почему это лучше обычных функций?
Даже если вы хорошо знаете лисп (даже не сам лисп, а названия внутренностей Emacs'а), в любом случае написание одной функции будет занимать больше времени, чем запись макроса. В то же время внесение любых изменений в функцию будет пресекаться из-за сложностей отладки, сохранения и перезагрузки функции. Соместная работа одной функции в симбиозе с другими тоже не будет ладиться, так как многие неважные вещи придётся запоминать наизусть. А макросы — сегодня они здесь, а завтра — там. Они более свободные. Их можно менять на ходу и копировать без каких-либо проблем и страха потери или страха последующей тяжёлой отладки. Если в макросе ошибка, он просто копируется и все эксперименты проводятся на копии в редакторе макросов. Когда всё готово, старый вариант удаляется и новый передвигается на его место. При этом эффект от одного маленького макроса зачастую равен эффекту от кучи функциональных конструкций.
Нам нужны отряды и командование
Подобрав наиболее подходящие единицы, мы делаем несколько разных отрядов. Один отряд сносит башни, другой отряд защищает первый отряд, третий отряд идёт на прорыв границы, а четвёртый отряд транспортирует раненых лечиться и заменяет их на свежие силы. И для всего этого требуется система, чтобы не путать отряды во время боя.
Как управлять несколькими функциями?
Не так давно я разбирался с режимами Emacs'а. Тогда нужно было избавиться от путаницы с множеством функций, которые не только хотелось иметь одновременно у себя в арсенале, но и чтобы они не конфликтовали по именам и клавиатурным сочетаниям. Тогда я изучил, как делать это естественным для Emacs'а образом — и всё закончилось к созданием одного главного режима и нескольких побочных, которые к нему прицепляются через хуки.
В общем, чтобы иметь несколько функций в упорядоченном виде, нужно создать малый режим. А внутри режима уже можно вызовы функций привязать к клавиатурным сочетаниям, которые будут устанавливаться при включении режима и сниматься при выключении. Так мы можем создать несколько малых режимов для разных контекстов, а потом наполнить каждый режим его индивидуальными макросами.
У меня есть несколько режимов: один — для Хабры, другой — для Ютьюба, остальные — для форумов. И в некоторых из них уже сложилась своя система функций и макросов. В каждом режиме макросы сгруппированы по задачам и для каждой задачи можно загрузить в кольцо её набор макросов. К тому же саму задачу и состав макросов в ней можно описать в комментарии в функции, который потом легко просматривать через помощь режима, доступную через Ctrl + h + m.
А вот как выглядит помощь к режиму для Хабры
Habra minor mode (indicator Habr):
Mode for `http://www.habrahabr.ru'.
key binding
--- -------
C-c Prefix Command
C-c m Prefix Command
C-c m h Prefix Command
C-c m h c Prefix Command
C-c m h m Prefix Command
C-c m h q habra-wrap-quote
C-c m h m g habra-make-macro-general
C-c m h c 1 habra-wrap-code
C-c m h c 2 habra-wrap-source
А так выглядит помощь к режиму для Ютьюба
Youtube minor mode (indicator YouTube):
Mode for `https://www.youtube.com'.
key binding
--- -------
C-c Prefix Command
C-c m Prefix Command
C-c m y Prefix Command
C-c m y m Prefix Command
C-c m y m g youtube-make-macro-general
C-c m y m p youtube-make-macro-playlist
Всегда можно вспомнить имена функций таким образом, а потом зайти в эти функции и посмотреть их комментарий про макросы в них.
Сам режим для Хабры выглядит так
;; Habra mode
(defun habra-wrap-quote()
"Wrap selection to <blockquote></blockquote> tags."
(interactive)
(if (not (mark))
(set-mark (point)))
(narrow-to-region (mark) (point))
(goto-char (point-min))
(insert "<blockquote>")
(goto-char (point-max))
(insert "</blockquote>")
(set-mark (point-min))
(widen))
(defun habra-wrap-code()
"Wrap selection to <code></code> tags."
(interactive)
(if (not (mark))
(set-mark (point)))
(narrow-to-region (mark) (point))
(goto-char (point-min))
(insert "<code>n")
(goto-char (point-max))
(insert "n</code>")
(set-mark (point-min))
(widen))
(defun habra-wrap-source()
"Wrap selection to <source></source> tags."
(interactive)
(if (not (mark))
(set-mark (point)))
(narrow-to-region (mark) (point))
(goto-char (point-min))
(insert "<source>n")
(goto-char (point-max))
(insert "n</source>")
(set-mark (point-min))
(widen))
(defun habra-make-macro-general()
"Make general macros."
(interactive)
;; Add a title to spoiler
(add-to-list 'kmacro-ring '("C-s<spoilerC-m title=""C-bC-uC-xq" 0 "%d"))
;; Replace * to <h1>
(add-to-list 'kmacro-ring '([?C-u ?C-s ?^ ?\ ?* ? ?C-m ?C-x ?q ?M-2 ?C-? ?M-\ ?< ?h ?1 ?> ?C-e ?M-\ ?< ?/ ?h ?1 ?>] 0 "%d"))
;; Replace ** to <h2>
(add-to-list 'kmacro-ring '([?C-u ?C-s ?^ ?\ ?* ?\ ?* ? ?C-m ?C-x ?q ?M-3 ?C-? ?M-\ ?< ?h ?2 ?> ?C-e ?M-\ ?< ?/ ?h ?2 ?>] 0 "%d"))
;; Change < to <
(add-to-list 'kmacro-ring '("C-s<C-mC-xqC-?<" 0 "%d"))
;; Change > to >
(add-to-list 'kmacro-ring '("C-s>C-mC-xqC-?>" 0 "%d"))
;; Change leading space to
(add-to-list 'kmacro-ring '("C-uC-s^ C-mC-xqC-? " 0 "%d"))
;; Change double space to
(add-to-list 'kmacro-ring '("C-uC-s C-mC-xqC-? " 0 "%d"))
)
(defvar habra-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-c m h q") 'habra-wrap-quote)
(define-key map (kbd "C-c m h c 1") 'habra-wrap-code)
(define-key map (kbd "C-c m h c 2") 'habra-wrap-source)
(define-key map (kbd "C-c m h m g") 'habra-make-macro-general)
map)
"Keymap for `habra-mode'.")
(define-minor-mode habra-mode
"Mode for `http://www.habrahabr.ru'.
\{habra-mode-map}"
nil
" Habr"
nil
(if habra-mode
(progn
(habra-make-macro-general)
(add-to-list 'yas-extra-modes 'habra-mode)
(yas-minor-mode t))
(progn
(set 'yas-extra-modes (delete 'habra-mode yas-extra-modes)))))
(provide 'habra-mode)
Здесь видно, что сами режимы разделены по пространствам имён. C-c + m означает войти в один из своих малых режимов, дальше идёт первая буква названия — h (habra), а уже дальше, внутри режима, выбирается действие — m (macro) и потом подвид этого действия — g (general). Так, действительно, можно наделать много макросных групп, все их хорошо описать и перезагружать в любое время. При этом несколько малых режимов могут быть включены одновременно и ни в чём не мешать друг другу.
Если вы присмотритесь ещё лучше, то увидите, как разделены теги кода в режиме для Хабры. Сама клавиша c (code) сделана в виде пространства имён, а клавиши 1 и 2 являются именами в этом пространстве. То же самое можно делать и с макросными группами, создавая иерархию макросных групп.
Тут нужно отметить, что в любом малом режиме общая группа макросов должна загружаться автоматически сразу при подключении. Так показала практика, без этого для доступа к макросам нужно было постоянно вызывать комбинацию. Но остальные макросы автоматически не загружаются, потому что мы помним, что кольцо макросов имеет ограничение по длине. Поэтому в режиме для Ютьюба общие макросы грузятся при включении режима, а специальные макросы (для трансляции списка видео в таблицу для org-mode) всегда ждут загрузки через клавиатурное сочетание.
Содержимое режима для Ютьюба
;; YouTube mode
(defun youtube-make-macro-general()
"Make general macros."
(interactive)
(add-to-list 'kmacro-ring '("https://www.youtube.comC-j" 0 "%d"))
)
(defun youtube-make-macro-playlist()
"Make macros for saving playlist."
(interactive)
;; Скачивает ссылку из кольца
(add-to-list 'kmacro-ring
'([?M-x ?b ?r ?o ?w ?s ?e ?- ?u ?r ?l ?- ?e ?m ?a ?c ?s return ?C-y return] 0 "%d"))
;; Копирует область ссылок в кольцо
(add-to-list 'kmacro-ring
'("C-s<trC-aC-s</tbodyC-a367" 0 "%d"))
;; Выделяет тройку (ссылка, описание, время)
(add-to-list 'kmacro-ring
'([?C-s ?< ?a ? ?h ?r ?e ?f ?= ?" ?C-m ?C-w ?h ?t ?t ?p ?s ?: ?/ ?/ ?w ?w ?w ?. ?y ?o ?u ?t ?u ?b ?e ?. ?c ?o ?m ?C-s ?& ?C-m ?C-b ?C-j ?C-s ?< ?a ? ?c ?l ?a ?s ?s ?= ?" ?p ?l ?- ?v ?i ?d ?e ?o ?- ?t ?i ?t ?l ?e ?C-m ?C-w ?C-s ?d ?a ?t ?a ?- ?s ?e ?s ?s ?i ?o ?n ?l ?i ?n ?k ?= ?C-m ?C-w ?C-s ?> ?C-m ?C-w ?C-k ?M-\ ?C-e ?M-\ ?C-j ?C-s ?c ?l ?a ?s ?s ?= ?" ?t ?i ?m ?e ?s ?t ?a ?m ?p ?C-m ?C-w ?C-s ?l ?a ?b ?e ?l ?C-m ?C-w ?C-s ?> ?C-m ?C-w ?C-s ?< ?C-b ?C-j] 0 "%d"))
;; Вставляет заголовок таблицы
(add-to-list 'kmacro-ring
'("|id|Url|S|Desc|Time|C-j|-C-j||||<40>||C-mC-a" 0 "%d"))
;; Преобразует тройку (ссылка, описание, время) в строку таблицы
(add-to-list 'kmacro-ring
'("|C-xC-kC-i|[[C-e][link]]|-|C-kC-e|C-kC-e|C-eC-nC-a" 1 "%d"))
)
(defvar youtube-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-c m y m g") 'youtube-make-macro-general)
(define-key map (kbd "C-c m y m p") 'youtube-make-macro-playlist)
map)
"Keymap for `youtube-mode'.")
(define-minor-mode youtube-mode
"Mode for `https://www.youtube.com'.
\{youtube-mode-map}"
nil
" YouTube"
nil
(if youtube-mode
(progn
(youtube-make-macro-general)
(add-to-list 'yas-extra-modes 'youtube-mode)
(yas-minor-mode t))
(progn
(set 'yas-extra-modes (delete 'youtube-mode yas-extra-modes)))))
(provide 'youtube-mode)
Пора отправлять отряды на большую войну
Смеркалось. На улице шёл дождь, в три часа ночи орки напали на людей. Катапульты били по башням, первый отряд пошёл на прорыв, загребая на себя рыцарей, берсеркеры лупили по любой подмоге, прущей к катапультам, и только маленький строитель у всех за спинами потихоньку строил башенки для первого укреплённого блок-поста.
Автор: samo-delkin