Відповіді:
Мабуть, є більш простий метод, але, можливо, ви можете спробувати наступне.
Скажімо, ви будете використовувати регістр 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('.')
зберігає номер поточного рядка всередині змінної a
w
переміщує курсор на наступне число: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 та виконати команду, див. :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
. Останній просто перевірить своє значення і, залежно від значення, виконав би одну з двох попередніх команд. Це дало б щось подібне:
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>
із вказаним вами значенням.
Якщо цей другий аргумент не дорівнює нулю / 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. Можливо, є спосіб зробити це рішення менш крихким?