Ответы:
Вероятно, есть более простой способ, но, возможно, вы могли бы попробовать следующее.
Допустим, вы будете использовать register qдля записи своего рекурсивного макроса.
В самом начале записи введите:
:let a = line('.')
Затем, в самом конце записи, вместо того, @qчтобы сделать макрос рекурсивным, введите следующую команду:
:if line('.') == a | exe 'norm @q' | endif
Наконец, завершите запись макроса с помощью q.
Последняя введенная вами команда будет воспроизводить макрос q( exe 'norm @q'), но только если текущий номер строки ( line('.')) совпадает с номером, изначально сохраненным в переменной a.
Команда :normalпозволяет вам вводить обычные команды (например, @qиз режима Ex).
И причина, по которой команда заключена в строку и выполняется командой, :executeсостоит в том, чтобы предотвратить :normalпотребление (ввод) остальной части команды ( |endif).
Пример использования.
Допустим, у вас есть следующий буфер:
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4
И вы хотите увеличить все числа из произвольной строки с помощью рекурсивного макроса.
Вы можете набрать, 0чтобы переместить курсор в начало строки, а затем начать запись макроса:
qqq
qq
:let a=line('.')
<C-a>
w
:if line('.')==a|exe 'norm @q'|endif
q
qqqочищает содержимое регистра, qчтобы при первоначальном вызове его во время определения макроса он не мешалqq начинает запись:let a=line('.') хранит текущий номер строки внутри переменной aw перемещает курсор на следующее число:if line('.')==a|exe 'norm @q'|endif вызывает макрос, но только если номер строки не изменилсяq останавливает записьПосле того, как вы определили свой макрос, если вы поместите курсор на третью строку, нажмите, 0чтобы переместить его в начало строки, а затем нажмите, @qчтобы воспроизвести макрос q, он должен повлиять только на текущую строку, а не на остальные:
1 2 3 4
1 2 3 4
2 3 4 5
1 2 3 4
Сделать макрос рекурсивным после записи
Если вы хотите, вы можете сделать ваш макрос рекурсивным после его записи, используя тот факт, что он хранится в строке внутри регистра и что вы можете объединить две строки с помощью .оператора точки .
Это даст вам несколько преимуществ:
@qбудут добавлены в макрос после того, как он был определен, и после того, как вы перезаписали все старое содержимоеЕсли вы записываете свой макрос как обычно (не рекурсивно), вы можете впоследствии сделать его рекурсивным с помощью следующей команды:
let @q = @q . "@q"
Или даже короче: let @q .= "@q"
.=это оператор, который позволяет добавить строку к другой.
Это должно добавить 2 символа @qв самом конце последовательности нажатий клавиш, хранящихся в регистре q. Вы также можете определить пользовательскую команду:
command! -register RecursiveMacro let @<reg> .= "@<reg>"
Он определяет команду, :RecursiveMacroкоторая ожидает имя регистра в качестве аргумента (из-за -registerпереданного атрибута :command).
Это та же команда , как и прежде, с той лишь разницей заменить каждое вхождение qс <reg>. Когда команда будет выполнена, Vim будет автоматически расширять каждое вхождение <reg>с указанным вами именем регистра.
Теперь все, что вам нужно сделать, это записать ваш макрос как обычно (не рекурсивно), а затем набрать, :RecursiveMacro qчтобы сделать макрос, хранящийся в регистре, qрекурсивным.
Вы можете сделать то же самое, чтобы сделать макрос рекурсивным при условии, что он останется в текущей строке:
let @q = ":let a=line('.')\r" . @q . ":if line('.')==a|exe 'norm @q'|endif\r"
Это то же самое, что описано в начале поста, за исключением того, что вы делаете это после записи. Вы просто объединяете две строки, одну до и одну после любых нажатий клавиш, qсодержащихся в регистре:
let @q = переопределяет содержимое реестра q":let a=line('.')\r"сохраняет текущий номер строки внутри переменной aдо того, как макрос \rвыполнит свою работу , необходимо, чтобы Vim сказал Enter нажать клавишу Enter и выполнить команду, см. :help expr-quoteсписок похожих специальных символов, . @q .объединяет текущее содержимое qрегистра с предыдущей и следующей строкой,":if line('.')==a|exe 'norm @q'|endif\r"вызывает макрос qпри условии, что строка не измениласьОпять же, чтобы сохранить некоторые нажатия клавиш, вы можете автоматизировать процесс, определив следующую пользовательскую команду:
command! -register RecursiveMacroOnLine let @<reg> = ":let a=line('.')\r" . @<reg> . ":if line('.')==a|exe 'norm @<reg>'|endif\r"
И снова, все, что вам нужно сделать, это записать ваш макрос как обычно (не рекурсивно), а затем набрать, :RecursiveMacroOnLine qчтобы сделать макрос, хранящийся в регистре, qрекурсивным при условии, что он останется в текущей строке.
Объединить 2 команды
Вы также можете настроить его :RecursiveMacroтак, чтобы он охватывал 2 случая:
Для этого вы можете передать второй аргумент :RecursiveMacro. Последний будет просто проверять его значение и, в зависимости от значения, будет выполнять одну из 2 предыдущих команд. Это даст что-то вроде этого:
command! -register -nargs=1 RecursiveMacro if <args> | let @<reg> .= "@<reg>" | else | let @<reg> = ":let a=line('.')\r" . @<reg> . ":if line('.')==a|exe 'norm @<reg>'|endif\r" | endif
Или (используя продолжение строки / обратную косую черту, чтобы сделать его немного более читабельным):
command! -register -nargs=1 RecursiveMacro
\ if <args> |
\ let @<reg> .= "@<reg>" |
\ else |
\ let @<reg> = ":let a = line('.')\r" .
\ @<reg> .
\ ":if line('.')==a | exe 'norm @<reg>' | endif\r" |
\ endif
Это то же самое, что и раньше, за исключением того, что на этот раз вы должны предоставить второй аргумент :RecursiveMacro(из-за -nargs=1атрибута).
Когда эта новая команда будет выполнена, Vim автоматически расширится <args>на указанное вами значение.
Если этот 2-й аргумент не равен нулю / true ( if <args>), будет выполнена первая версия команды (та, которая делает макрос рекурсивной безоговорочно), в противном случае, если он равен нулю / false, будет выполнена вторая версия (та, которая делает макрос рекурсивный при условии, что он остается в текущей строке).
Итак, возвращаясь к предыдущему примеру, это дало бы следующее:
qq
<C-a>
w
q
:RecursiveMacro q 0
3G
0@q
qq начинает запись макроса внутри регистра q<C-a> увеличивает число под курсоромw перемещает курсор на следующее числоq заканчивает запись:RecursiveMacro q 0делает макрос, хранящийся в регистре, q
рекурсивным, но только до конца строки (из-за второго аргумента 0)3G перемещает курсор на произвольную строку (например, 3)0@q воспроизводит рекурсивный макрос с начала строкиОн должен дать тот же результат, что и раньше:
1 2 3 4
1 2 3 4
2 3 4 5
1 2 3 4
Но на этот раз вам не нужно было вводить отвлекающие команды во время записи макроса, вы могли бы просто сосредоточиться на создании рабочей.
И на шаге 5, если вы передали ненулевой аргумент в команду, то есть если бы вы набрали :RecursiveMacro q 1вместо :RecursiveMacro q 0, макрос qстал бы безоговорочно рекурсивным, что дало бы следующий буфер:
1 2 3 4
1 2 3 4
2 3 4 5
2 3 4 5
На этот раз макрос остановился бы не в конце 3-й строки, а в конце буфера.
Для получения дополнительной информации см .:
:help line()
:help :normal
:help :execute
:help :command-nargs
:help :command-register
1 2 3 4 5 6 7 8 9 10, я получаю 2 3 4 5 6 7 8 9 10 12вместо 2 3 4 5 6 7 8 9 10 11. Я не знаю почему, может я что-то опечатал. В любом случае это кажется более изощренным, чем мой простой подход, и в нем используются регулярные выражения для описания того, куда макрос должен перемещать курсор, а также список местоположений, который я никогда не видел, использованный таким образом. Мне это очень нравится!
\d\+для описания многозначных чисел.
:lv ...команды, то :llaкоманда может быть использована для перехода к последнему матчу и :lpкоманда может быть использована для продвижения через матчей в обратном порядке.
Рекурсивный макрос остановится, как только встретится с ошибочной командой. Поэтому, чтобы остановиться в конце строки, вам нужна команда, которая не будет выполнена в конце строки.
По умолчанию * lкоманда является такой командой, поэтому вы можете использовать ее для остановки рекурсивного макроса. Если курсор находится не в конце строки, тогда вам просто нужно переместить его обратно с помощью команды h.
Итак, используя тот же пример макроса, что и в saginaw :
qqqqq<c-a>lhw@qq
Сломано:
qqq: Очистить регистр q,qq: Начать запись макроса в qрегистр,<c-a>: Увеличить число под курсором,lh: Если мы находимся в конце строки, отменим макрос. В противном случае ничего не делать.w: Переход к следующему слову в строке.@q: Рекурсq: Остановить запись.Затем вы можете запустить макрос с помощью той же 0@qкоманды, как описано в saginaw.
* 'whichwrap'Опция позволяет вам определить, какие клавиши перемещения будут переходить на следующую строку, когда вы находитесь в начале или конце строки (см. :help 'whichwrap'). Если вы lустановили эту опцию, это нарушит решение, описанное выше.
Однако, вполне вероятно , что вы используете только одну из команд нормального режима на три по умолчанию для продвижения одного символа ( <Space>, lи <Right>), так что если вы lвключены в 'whichwrap'настройках, вы можете удалить тот , который вы не используете из 'whichwrap'вариант, например, для <Space>:
:set whichwrap-=s
Затем вы можете заменить lкоманду на шаге 4 макроса <Space>командой.
virtualedit=onemoreбудет мешать использованию lдля обнаружения конца строки, хотя и не так сильно, как whichwrap=l.
've'
:lv /\%3l\d/g %<CR>qqqqq<C-a>:lne<CR>@qq@q, увеличивает все числа в строке 3. Может быть, есть способ сделать это решение менее хрупким?