Макросы в Vim — это просто

  • Tutorial

Макросы в Vim


Очень странно, с одной стророны тема довольно банальная, а с другой ни тут, ни на просторах интернета не видно хорошего понимания такой важной темы, которая может иногда сильно упростить задачу редактирования текста. К написанию меня подтолкнуло обсуждение макросов в Emacs в блоге Avdi Grimm'а, где в своё время никто не смог внятно объяснить, что в Vim есть то, чего ожидает автор поста от достойного текстового редактора. Исправим это, в первую очередь в себе, а потом пойдём и скажем этим из интернета, что они не правы.

Какие задачи редактирования текста помогут решить макросы? Например, такую: вот в этой самой строке, которую вы читаете, взять и все запятые поменять на 0. Можно вспомнить регулярные выражения, но не все их любят, и уж тем более не все помнят, как вставлять с их помощью перенос строки, а если вам нужно заменять какие-то символы, которые используются в регулярных выражениях как служебные, вы уже отвлеклись от своей основной задачи и вместо редактирования текста занимаетесь написанием регулярного выражения для поиска и замены, пробуя и ошибаясь.
Макросы позволят это сделать быстрее, с меньшим отвлечением на вспоминание и написание чего-либо. Запомнить как работают макросы очень легко, и это войдёт в ваш ежедневный аресенал наряду с прыжками по тексту.

В этом топике мы научимся использовать макросы с этой самой задачей.

Давайте сначала вспомним, как это сделать вручную.
f,r0
После чего можно нажимать последовательно ; для повторного поиска и . для повторения предыдущего действия. Но в какой-то момент это может стать не так просто, например, вам нужно добавить пару каких-то знаков между делом. Точка уже не сработает и нужно будет опять нажимать r0. Или этих запятых окажется довольно много, и щёлкать поочерёдно ; и . придётся довольно много раз. В этот момент что-то вам уже подсказывает, что такой механический труд можно автоматизировать.

Основы
Многократные повторения и рекурсивный вызов
Редактирование макросов
Вечные макросы
Всякое

Основы


Для записи и хранения макросов в Vim используются регистры. Те самые, в которые можно копировать текст и те самые, на которые можно ставить пометки. Регистры обозначаются латинскими буквами без учёта регистра (a и A — один и тот же регистр), цифрами, и даже специальными символами, то есть хватит их на всех.

Важно. В регистрах хранится не что-то магическое, в регистрах хранится простой текст.

Для записи макроса в регистр a вопспользуемся следующей командой:
qaf,r0q
Первый кю q запускает запись макроса, и так до нажатия q для подтверждения записи.
Теперь, чтобы запустить макрос повторно, нам нужно нажать @a, и запустится на выполнение макрос, хранящийся в регистре a. Мы можем традиционно для Vim написать 100@a, и макрос будет выполнен 100 раз, заменив 100 или меньше (если в строке их меньше) запятых на 0.

Многократные повторения и рекурсивный вызов


Иногда сколько у нас запятых в строке мы не знаем, поэтому после запуска 100@a нам приходится вручную проверять, не осталось ли ещё.
Можно довольно просто сделать так, чтобы макрос вызывал сам себя. Запишем тот же самый макрос, который нужно будет вызывать всего один раз, а дальше он будет сам вызывать себя:
qaf,r0@aq
В данном случае, его запись ещё не произозшла, но запуск уже указан. Важно, чтобы в этом регистре ничего на момент записи не хранилось. Для этого есть два способа. Первый — это чистить содержимое регистра перед записью ещё тремя нажамиями, qaq, то есть записью пустой последовательности. Второй — это делать вызов последнего вызыванного макроса с помощью @@.

Важно. Выполнение цепочки или серии исполнения макросов приостанавливается в тот самый момент, когда одна из команд fFtT, то есть поиска символа в строке, не удаётся. То есть, например, у нас не осталось больше в строке запятых. Естественно, даже если вы указали, что макрос нужно выполнить 100 раз, а запятых было всего две, макрос выполнится два раза, а на третий остановится на поиске и больше продолжать не будет. Точно так же с рекурсивным макросом.

Удобно то, что при записи макроса происходит его первое исполнение, то есть мы сразу видим, правильно ли его записали.

Редактирование макросов


Если что-то пошло не так, и хочется увидеть, что же мы записали, можно воспользоваться стандартной функцией Vim, и вставить содержимое регистра a прямо в текст: "ap.
Прямо в редакторе вы увидите текст, который является последовательностью команд:
af,r0@a

Допустим, вы решили, что менять текст нужно не на 0, а на 1, и вы редактируете текст макроса:
af,r1@a

и копируете его в регистр a с помощью 0"ay$.

Вечные макросы


Стоит заметить, что регистры постоянны, если вы выйдете из Vim, а потом зайдёте снова, содержимое регистра останется неприкосновенным. Хранится оно по умолчанию в файле ~/.viminfo.

Важно. Содержимое регистра легко случайно перезаписать, и если вы хотите, чтобы какие-то макросы существовали в каких-то регистрах постоянно, имеет смысл добавить их в .vimrc, или лучше в отдельный файл с макросами, включаемый в .vimrc:
  let @a='af,r1@a'


Всякое


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

Чтобы избежать случайных переходов по нажатию Q в Ex режим, имеет смысл назначить Q на вызов предыдущего макроса:
    nnoremap Q @@


Полезная информация:
:help registers
:help :registers
:help :recording

Для дальнейшего изучения:
Продвинутые макросы Vim (англ.)
  • +29
  • 16,6k
  • 8
Поделиться публикацией
Комментарии 8
    +4
    Дополнения:

    1) Если мы записывали макрос в регистр a, закончили запись нажатием q и поняли, что сделали ошибку, забыв в конце, например, переход на новую строку с помощью j, в этом случае, чтобы не переписывать макрос (иногда они получаются очень большими), можно нажать qA (регистр a заглавная буква) и дописать недостающие команды.

    2) Иногда полезно запускать выполнение макроса не последовательно, а параллельно (этот трюк я подсмотрел в Vim Edit Text at the Speed of Thought). Например, если нам надо обработать 100 строк, вместо того, чтобы записывать макрос с переходом на следующую строку j и затем указывать количество раз выполнения макроса, например, как в статье 100@a, можно выделить последовательность строк с помощью V, нажать : и затем в командном режиме вызвать макрос через normal. Команда будет выглядеть так :'<,'>normal @a. Плюс этого подхода в том, что макрос выполнится на всех строках, даже если какая-то строка могла нарушить ход выполнения последовательности макросов, при обычном подходе.
      +1
      1) Почему при этом произойдёт дописывание в регистр a, а не переписывание регистра A? Какие условия должны быть выполнены, чтобы писать всё-таки в A?
        0
        Исправил в топике, ошибочно было указано, что a и code>A — разные регистры, на самом деле один и тот же, а при использовании дозаписи нужно брать тот, который в верхнем регистре, работает это как с [y]ank, так и с записью [q].
          +1
          Вместо «макросов в vimrc» лучше иметь привязки. Привязки тоже можно вызывать рекурсивно и они тоже остановятся на провалившемся поиске.
          Хотя лучше всё же освоить регулярные выражения и другие конструкции VimL и не добавлять в vimrc то, что зависит от привязок — и макросы, и привязки без nore обладают таким нехорошим свойством, потому их у меня в vimrc практически нет (редко, но иногда мне всё же нужна именно зависимость от других привязок). А макросов так вообще — их слишком легко перезаписать — хотя использую я макросы часто.

          Суть в том, что работоспособность того, что у меня есть в vimrc, должна быть максимально независима. Иначе с некоторого момента вам придётся начать думать «а могу ли я добавить эту привязку/макрос/изменить эту настройку? Не сделает ли это прошлогоднюю, но используемую привязку/макрос, неработоспособной?»
      +3
      Вот что бы в этой статье не помешало — так это список реальных задач, для решения которых удобнее всего использовать макросы. Желательно с учётом того, что регулярные выражения мы всё-таки знаем, любим, и запятые на нули обычно меняем именно с их помощью.

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

      Обычно, если возникает достаточно сложная, и в то же время повторяющаяся задача, которую нельзя решить регуляркой s/// — макросами она тоже не решается т.к. изменения требуется вносить хоть и однообразные, но мелкие детали зависят от конкретного места, так что с одной стороны описать эту логику макросом займёт больше времени, чем сделать всё вручную (ну, относительно — обычно это «вручную» подразумевает ручное выделение блока и применение к нему пары вручную выбранных регулярок из нескольких вариантов похожих регулярок в истории). Тем более, что задача обычно уникальна, и писать макрос «на будущее» точно нет смысла.
        +2
        Я обычно рефакторю код с помощью макросов, хотя да — регулярка тоже сработает, но кому то проще макросами
          +1
          Я часто использую макросы для массового добавления кавычек, скобочек, запятых в конце строки, в общем, переформатирвоание списков — очень удобно,
          отформатировал первую строчку, а дальше 100@q…
          Еще приходиться переформатировать копипастнутый hmtl с однотипными элементами (вложенными) — в таких случаях на придумывание регулярки ушло бы гораздо больше времени, чем на макрос.
            +1
            Регулярно использую макросы, чтобы добавить в файл DNS зоны энное число записей вида «server01 IN A 1.2.3.1» с последовательно идущей нумерацией хостов и айпишников, очень удобно — qa C-a $ C-a yy p q и наяривай сколько угодно.

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое