ฉันจะหยุดแมโครแบบเรียกซ้ำเมื่อสิ้นสุดบรรทัดได้อย่างไร


13

ฉันจะสร้างมาโครแบบเรียกซ้ำเพื่อให้ทำงานเฉพาะจนถึงจุดสิ้นสุดของบรรทัดได้อย่างไร

หรือวิธีการเรียกใช้แมโครแบบเรียกซ้ำจนกระทั่งจบบรรทัดเท่านั้น

คำตอบ:


11

อาจมีวิธีที่ง่ายกว่า แต่คุณอาจลองวิธีต่อไปนี้

สมมติว่าคุณจะใช้ 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
  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

ทำให้แมโครซ้ำหลังจากการบันทึก

หากคุณต้องการคุณสามารถทำให้แมโครของคุณซ้ำหลังจากการบันทึกโดยใช้ความจริงที่ว่ามันถูกเก็บไว้ในสตริงภายในรีจิสเตอร์และคุณสามารถเชื่อมสองสตริงเข้ากับตัว.ดำเนินการdot

สิ่งนี้จะให้ประโยชน์หลายประการแก่คุณ:

  • ไม่จำเป็นต้องล้างการลงทะเบียนก่อนการบันทึกเนื่องจากตัวละคร@qจะถูกเพิ่มในมาโครหลังจากที่มันถูกกำหนดแล้วและหลังจากที่คุณเขียนทับเนื้อหาเก่า ๆ
  • ไม่จำเป็นต้องพิมพ์อะไรผิดปกติระหว่างการบันทึกคุณสามารถมุ่งเน้นไปที่การสร้างมาโครที่ใช้งานง่าย
  • ความเป็นไปได้ของการทดสอบก่อนที่จะทำซ้ำเพื่อดูว่ามันทำงานอย่างไร

หากคุณบันทึกแมโครของคุณตามปกติ (ไม่วนซ้ำ) คุณสามารถทำให้ซ้ำหลังจากนั้นด้วยคำสั่งต่อไปนี้:

let @q = @q . "@q"

หรือสั้นกว่า: let @q .= "@q"
.=เป็นโอเปอเรเตอร์ที่อนุญาตให้ผนวกสตริงไปยังอีกอันหนึ่งได้

นี้ควรเพิ่ม 2 ตัวอักษรที่ปลายสุดของลำดับของการกดแป้นพิมพ์เก็บไว้ในการลงทะเบียน@q qคุณสามารถกำหนดคำสั่งที่กำหนดเอง:

command! -register RecursiveMacro let @<reg> .= "@<reg>"

มันกำหนดคำสั่ง:RecursiveMacroที่รอชื่อของการลงทะเบียนเป็นอาร์กิวเมนต์ (เพราะ-registerแอตทริบิวต์ที่ส่งผ่านไป:command)
มันเป็นคำสั่งเช่นเดียวกับก่อนที่แตกต่างเพียงอย่างเดียวคือคุณเปลี่ยนทุกการเกิดขึ้นของด้วยq <reg>เมื่อคำสั่งจะถูกดำเนินการเป็นกลุ่มจะขยายโดยอัตโนมัติทุกครั้งที่<reg>มีชื่อลงทะเบียนที่คุณให้ไว้

ตอนนี้สิ่งที่คุณต้องทำคือบันทึกแมโครของคุณตามปกติ (ไม่ใช่แบบเรียกซ้ำ) จากนั้นพิมพ์:RecursiveMacro qเพื่อทำให้แมโครเก็บไว้ใน register 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การทำเช่นนี้คุณสามารถผ่านอาร์กิวเมนต์ที่สองไป หลังจะทดสอบค่าของมันและขึ้นอยู่กับค่าจะดำเนินการหนึ่งใน 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

เหมือนกับก่อนหน้านี้ยกเว้นเวลานี้คุณต้องระบุอาร์กิวเมนต์ที่ 2 ให้กับ:RecursiveMacro(เนื่องจาก-nargs=1แอตทริบิวต์)
เมื่อคำสั่งใหม่นี้จะถูกดำเนินการเป็นกลุ่มจะขยายโดยอัตโนมัติ<args>ด้วยค่าที่คุณให้
หากอาร์กิวเมนต์ที่ 2 นี้ไม่ใช่ศูนย์ / จริง ( if <args>) คำสั่งเวอร์ชันแรกจะถูกดำเนินการ (อันที่ทำให้แมโครเรียกซ้ำโดยไม่มีเงื่อนไข) มิฉะนั้นถ้ามันเป็นศูนย์ / เท็จเวอร์ชันที่สองจะถูกดำเนินการ (อันที่ทำให้ แมโครแบบเรียกซ้ำในเงื่อนไขที่อยู่ในบรรทัดปัจจุบัน)

ดังนั้นกลับไปที่ตัวอย่างก่อนหน้านี้มันจะให้สิ่งต่อไปนี้:

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ทำให้แมโครที่เก็บไว้ใน register 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ฉันไม่รู้ว่าทำไมบางทีฉันพิมพ์ผิดบางอย่าง อย่างไรก็ตามดูเหมือนว่าจะซับซ้อนกว่าวิธีง่าย ๆ ของฉันและมันเกี่ยวข้องกับ regexes เพื่ออธิบายว่ามาโครควรเลื่อนเคอร์เซอร์ไปที่ใดรวมถึงรายการตำแหน่งที่ฉันไม่เคยเห็นมาก่อนด้วยวิธีนี้ ผมชอบมันมาก!
saginaw

@djjcast ขออภัยฉันเพิ่งเข้าใจปัญหามาจาก regex ของฉันฉันควรใช้\d\+เพื่ออธิบายตัวเลขหลายหลัก
saginaw

@djjcast Ah ตอนนี้ฉันเข้าใจสิ่งที่คุณหมายถึงเมื่อคุณกล่าวว่ามาโครไม่ควรเปลี่ยนตำแหน่งของการแข่งขัน แต่ฉันไม่รู้วิธีแก้ปัญหานี้ ความคิดเดียวที่ฉันจะต้องอัปเดตรายการที่ตั้งจากภายในมาโคร แต่ฉันไม่คุ้นเคยกับรายการที่ตั้งมันซับซ้อนเกินไปสำหรับฉันขอโทษจริงๆ
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'
Rich
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.