Як зупинити рекурсивний макрос в кінці рядка?


13

Як я можу створити рекурсивний макрос, щоб він працював лише до кінця рядка?

Або як запустити рекурсивний макрос лише до кінця рядка?

Відповіді:


11

Мабуть, є більш простий метод, але, можливо, ви можете спробувати наступне.

Скажімо, ви будете використовувати регістр 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
  1. qqqочищає вміст реєстру, qтак що коли ви спочатку викликаєте його під час визначення макросу, він не заважатиме
  2. qq починає запис
  3. :let a=line('.') зберігає номер поточного рядка всередині змінної a
  4. Ctrl+ aзбільшує число під курсором
  5. w переміщує курсор на наступне число
  6. :if line('.')==a|exe 'norm @q'|endif нагадує макрос, але лише якщо номер рядка не змінився
  7. 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даний момент реєстр містить:

  1. let @q = перевизначає зміст регістра q
  2. ":let a=line('.')\r"зберігає номер поточного рядка всередині змінної aперед тим, як макрос виконує свою роботу.
    \rПотрібно сказати Vim, щоб натиснути Enter та виконати команду, див. :help expr-quoteсписок подібних спеціальних символів,
  3. . @q .з'єднує поточний вміст qрегістра з попереднім рядком і наступним,
  4. ":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
  1. qq починається запис макросу всередині регістра q
  2. <C-a> збільшує число під курсором
  3. w переміщує курсор на наступне число
  4. q завершує запис
  5. :RecursiveMacro q 0робить макрос, що зберігається всередині реєстру, q рекурсивним, але лише до кінця рядка (через другий аргумент 0)
  6. 3G переміщує курсор до довільної лінії (наприклад 3)
  7. 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

2
Список місцеположень можна використовувати для переходу до пошукових збігів у макросі, доки макрос не змінить положення збігів, наприклад :lv /\%3l\d/g %<CR>qqqqq<C-a>:lne<CR>@qq@q, збільшить усі числа у рядку 3. Можливо, є спосіб зробити це рішення менш крихким?
djjcast

@djjcast Ви можете опублікувати це як відповідь, я спробував це, і він працює дуже добре. Є лише один випадок, якого я не розумію, коли я виконую макрос у наступному рядку 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. Я не знаю чому, можливо, я щось неправильно ввів. У будь-якому випадку це здається більш складним, ніж мій простий підхід, і він включає в себе регулярні вирази, щоб описати, куди макрос повинен переміщувати курсор, а також список місцеположень, який я ніколи не бачив, щоб використовувався таким чином. Мені це дуже подобається!
saginaw

@djjcast Вибачте, я щойно зрозумів, що проблема виникла просто з мого регулярного виразу, я повинен був використовувати \d\+для опису кількох цифр.
saginaw

@djjcast А тепер я розумію, що ти мав на увазі, коли ти сказав, що макрос не повинен змінювати позицію сірників. Але я не знаю, як вирішити це питання. Єдина моя ідея - оновити список розташування зсередини макросу, але я не звик до списку місць, він занадто складний для мене, дійсно вибачте.
saginaw

1
@saginaw Ітерація над матчами у зворотному порядку здається, що це вирішить проблему в більшості випадків, оскільки здається, що макрос менше шансів змінити позиції попередніх матчів. Отже після :lv ...команди :llaкоманда може використовуватися для переходу до останнього матчу, а :lpкоманда може використовуватися для переходу на відповідність у зворотному порядку.
djjcast

9

Рекурсивний макрос припиняється, як тільки він зустріне команду, яка не працює. Тому, щоб зупинитися в кінці рядка, вам потрібна команда, яка не завершиться в кінці рядка.

За замовчуванням * lкоманда є такою командою, тому ви можете використовувати її для зупинки рекурсивного макросу. Якщо курсор не знаходиться в кінці рядка, то вам просто потрібно повернути його назад за допомогою команди h.

Отже, використовуючи той же приклад макросу, що і saginaw :

qqqqq<c-a>lhw@qq

Зломаний:

  1. qqq: Очистити регістр q,
  2. qq: Почніть записувати макрос в qрегістр,
  3. <c-a>: Збільшення числа під курсором,
  4. lh: Якщо ми в кінці рядка, скасуємо макрос. Інакше нічого не робіть.
  5. w: Перехід до наступного слова на рядку.
  6. @q: Рекурс
  7. q: Зупинка запису.

Потім можна запустити макрос за допомогою тієї самої 0@qкоманди, що описана saginaw.


* Цей 'whichwrap'параметр дозволяє визначити, які клавіші руху будуть переходити до наступного рядка, коли ви знаходитесь на початку чи в кінці рядка (Див. :help 'whichwrap'). Якщо ви lвстановили цей параметр, то він порушить описане вище рішення.

Однак, цілком ймовірно , що ви використовуєте тільки одну з команд нормального режиму на три по замовчуванням для просування одного символу ( <Space>, lі <Right>), так що якщо ви lвключені в 'whichwrap'настройках, ви можете видалити той , який ви НЕ використовуєте з 'whichwrap'варіант, наприклад для <Space>:

:set whichwrap-=s

Потім ви можете замінити lкоманду на кроці 4 макросу <Space>командою.


1
Зауважте також, що налаштування virtualedit=onemoreбуде заважати використанню lдля виявлення кінця рядка, хоча і не настільки сильно, як whichwrap=l.
Кевін

@Kevin Добрий момент! Я 've'
Багатий
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.