Câu trả lời:
Có lẽ có một phương pháp đơn giản hơn nhưng có lẽ bạn có thể thử như sau.
Giả sử bạn sẽ sử dụng đăng ký qđể ghi lại macro đệ quy của mình.
Ở phần đầu của bản ghi, gõ:
:let a = line('.')
Sau đó, ở phần cuối của bản ghi, thay vì nhấn @qđể tạo macro đệ quy, hãy gõ lệnh sau:
:if line('.') == a | exe 'norm @q' | endif
Cuối cùng kết thúc ghi âm của macro với q.
Lệnh cuối cùng bạn đã nhập sẽ phát lại macro q( exe 'norm @q') nhưng chỉ khi số dòng hiện tại ( line('.')) giống với lệnh ban đầu được lưu trong biến a.
Các :normallệnh cho phép bạn gõ lệnh thông thường (như @q) từ chế độ Ex.
Và lý do tại sao lệnh được gói thành một chuỗi và được thực thi bởi lệnh :executelà để ngăn chặn :normalviệc tiêu thụ (gõ) phần còn lại của lệnh ( |endif).
Ví dụ sử dụng.
Giả sử bạn có bộ đệm sau:
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4
Và bạn muốn tăng tất cả các số từ một dòng tùy ý với một macro đệ quy.
Bạn có thể nhập 0để di chuyển con trỏ đến đầu dòng sau đó bắt đầu ghi macro:
qqq
qq
:let a=line('.')
<C-a>
w
:if line('.')==a|exe 'norm @q'|endif
q
qqqxóa nội dung của thanh ghi qđể khi ban đầu bạn gọi nó trong định nghĩa của macro, nó sẽ không can thiệpqq bắt đầu ghi âm:let a=line('.') lưu trữ số dòng hiện tại bên trong biến aw di chuyển con trỏ đến số tiếp theo:if line('.')==a|exe 'norm @q'|endif nhớ lại macro nhưng chỉ khi số dòng không thay đổiq dừng ghi âmKhi bạn đã xác định macro của mình, nếu bạn định vị con trỏ của mình trên dòng thứ ba, nhấn 0để di chuyển nó đến đầu dòng, sau đó nhấn @qđể phát lại macro q, nó chỉ ảnh hưởng đến dòng hiện tại chứ không ảnh hưởng đến dòng khác:
1 2 3 4
1 2 3 4
2 3 4 5
1 2 3 4
Tạo một đệ quy macro sau khi ghi
Nếu bạn muốn, bạn có thể làm cho macro của mình đệ quy sau khi ghi bằng cách sử dụng thực tế là nó được lưu trong một chuỗi bên trong một thanh ghi và bạn có thể nối hai chuỗi với .toán tử dấu chấm .
Điều này sẽ cung cấp cho bạn một số lợi ích:
@qsẽ được thêm vào macro sau khi được xác định và sau khi bạn đã ghi đè lên bất cứ nội dung cũ nào ở đóNếu bạn ghi lại macro của mình như bình thường (không đệ quy), bạn có thể làm cho nó đệ quy sau đó bằng lệnh sau:
let @q = @q . "@q"
Hoặc thậm chí ngắn hơn: let @q .= "@q"
.=là một toán tử cho phép nối một chuỗi với một chuỗi khác.
Điều này sẽ thêm 2 ký tự @qở cuối chuỗi phím được lưu trong thanh ghi q. Bạn cũng có thể xác định một lệnh tùy chỉnh:
command! -register RecursiveMacro let @<reg> .= "@<reg>"
Nó định nghĩa lệnh :RecursiveMacrochờ tên của thanh ghi làm đối số (vì -registerthuộc tính được truyền vào :command).
Đó là lệnh tương tự như trước đây, điểm khác biệt duy nhất là bạn thay thế mọi lần xuất hiện qbằng <reg>. Khi lệnh sẽ được thực thi, Vim sẽ tự động mở rộng mỗi lần xuất hiện <reg>với tên đăng ký bạn cung cấp.
Bây giờ, tất cả những gì bạn phải làm là ghi lại macro của bạn như bình thường (không đệ quy), sau đó nhập :RecursiveMacro qđể làm cho macro được lưu trữ bên trong thanh ghi qđệ quy.
Bạn có thể làm điều tương tự để tạo một đệ quy macro với điều kiện nó nằm trên dòng hiện tại:
let @q = ":let a=line('.')\r" . @q . ":if line('.')==a|exe 'norm @q'|endif\r"
Đó là điều tương tự chính xác được mô tả ở đầu bài, ngoại trừ lần này bạn làm điều đó sau khi ghi. Bạn chỉ cần nối hai chuỗi, một chuỗi trước và một chuỗi sau bất kỳ thao tác gõ phím nào mà qthanh ghi hiện có:
let @q = xác định lại nội dung đăng ký q":let a=line('.')\r"lưu trữ số dòng hiện tại bên trong biến atrước khi macro thực hiện công việc của nó \rlà cần thiết để báo cho Vim nhấn Enter và thực thi lệnh, xem :help expr-quotedanh sách các ký tự đặc biệt tương tự, . @q .nối các nội dung hiện tại của thanh qghi với chuỗi trước đó và chuỗi tiếp theo,":if line('.')==a|exe 'norm @q'|endif\r"nhớ lại macro qvới điều kiện dòng không thay đổiMột lần nữa, để lưu một số tổ hợp phím, bạn có thể tự động hóa quy trình bằng cách xác định lệnh tùy chỉnh sau:
command! -register RecursiveMacroOnLine let @<reg> = ":let a=line('.')\r" . @<reg> . ":if line('.')==a|exe 'norm @<reg>'|endif\r"
Và một lần nữa, tất cả những gì bạn phải làm là ghi lại macro của bạn như bình thường (không đệ quy), sau đó nhập :RecursiveMacroOnLine qđể làm cho macro được lưu trữ bên trong thanh ghi qđệ quy với điều kiện nó nằm trên dòng hiện tại.
Hợp nhất 2 lệnh
Bạn cũng có thể điều chỉnh :RecursiveMacrosao cho nó bao gồm 2 trường hợp:
Để làm điều này, bạn có thể truyền đối số thứ hai cho :RecursiveMacro. Cái sau chỉ đơn giản là kiểm tra giá trị của nó và, tùy thuộc vào giá trị, sẽ thực thi một trong 2 lệnh trước đó. Nó sẽ cung cấp một cái gì đó như thế này:
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
Hoặc (sử dụng tiếp tục dòng / dấu gạch chéo ngược để dễ đọc hơn một chút):
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
Nó giống như trước đây, ngoại trừ lần này bạn phải cung cấp đối số thứ 2 cho :RecursiveMacro(vì -nargs=1thuộc tính).
Khi lệnh mới này sẽ được thực thi, Vim sẽ tự động mở rộng <args>với giá trị bạn cung cấp.
Nếu đối số thứ 2 này khác không / đúng ( if <args>) phiên bản đầu tiên của lệnh sẽ được thực thi (phiên bản tạo macro đệ quy vô điều kiện), nếu không, nếu không / sai thì phiên bản thứ hai sẽ được thực thi (phiên bản thứ hai sẽ được thực thi một đệ quy vĩ mô với điều kiện nó nằm trên dòng hiện tại).
Vì vậy, quay trở lại ví dụ trước, nó sẽ đưa ra điều sau:
qq
<C-a>
w
q
:RecursiveMacro q 0
3G
0@q
qq bắt đầu ghi một macro bên trong thanh ghi q<C-a> tăng số dưới con trỏw di chuyển con trỏ đến số tiếp theoq kết thúc ghi âm:RecursiveMacro q 0làm cho macro được lưu trữ bên trong thanh ghi q
đệ quy nhưng chỉ cho đến cuối dòng (vì đối số thứ hai 0)3G di chuyển con trỏ của bạn đến một dòng tùy ý (ví dụ 3)0@q phát lại macro đệ quy từ đầu dòngNó sẽ cho kết quả tương tự như trước:
1 2 3 4
1 2 3 4
2 3 4 5
1 2 3 4
Nhưng lần này bạn không phải gõ các lệnh gây mất tập trung trong quá trình ghi macro, bạn có thể chỉ cần tập trung vào thực hiện một lệnh làm việc.
Và trong bước 5, nếu bạn đã chuyển một đối số khác không cho lệnh, nghĩa là nếu bạn đã gõ :RecursiveMacro q 1thay vì :RecursiveMacro q 0, macro qsẽ trở thành đệ quy vô điều kiện, sẽ đưa ra bộ đệm sau:
1 2 3 4
1 2 3 4
2 3 4 5
2 3 4 5
Lần này, macro sẽ không dừng ở cuối dòng thứ 3 mà ở cuối bộ đệm.
Để biết thêm thông tin, xem:
:help line()
:help :normal
:help :execute
:help :command-nargs
:help :command-register
1 2 3 4 5 6 7 8 9 10, tôi nhận được 2 3 4 5 6 7 8 9 10 12thay vì 2 3 4 5 6 7 8 9 10 11. Tôi không biết tại sao, có lẽ tôi đã nhầm lẫn một cái gì đó. Dù sao, nó có vẻ phức tạp hơn cách tiếp cận đơn giản của tôi và nó liên quan đến các biểu thức để mô tả nơi macro sẽ di chuyển con trỏ, cũng như một danh sách vị trí mà tôi chưa từng thấy được sử dụng theo cách này. Tôi rất thích nó!
\d\+để mô tả nhiều chữ số.
:lv ...lệnh, :llalệnh có thể được sử dụng để nhảy đến trận đấu cuối cùng và :lplệnh có thể được sử dụng để tiến lên các trận đấu theo thứ tự ngược lại.
Một macro đệ quy sẽ dừng ngay khi gặp lệnh không thành công. Do đó, để dừng ở cuối dòng, bạn cần một lệnh sẽ thất bại ở cuối dòng.
Theo mặc định *, llệnh là một lệnh như vậy, vì vậy bạn có thể sử dụng nó để dừng macro đệ quy. Nếu con trỏ không ở cuối dòng, thì bạn chỉ cần di chuyển nó trở lại sau đó bằng lệnh h.
Vì vậy, sử dụng macro ví dụ tương tự như saginaw :
qqqqq<c-a>lhw@qq
Hỏng:
qqq: Xóa đăng ký q,qq: Bắt đầu ghi một macro trong thanh qghi,<c-a>: Tăng số theo con trỏ,lh: Nếu chúng ta ở cuối dòng, hãy hủy bỏ macro. Nếu không, không làm gì cả.w: Chuyển sang từ tiếp theo trên dòng.@q: Tái diễnq: Dừng ghi âm.Sau đó, bạn có thể chạy macro với cùng một 0@qlệnh như được mô tả bởi saginaw.
* 'whichwrap'Tùy chọn cho phép bạn xác định phím di chuyển nào sẽ quấn quanh dòng tiếp theo khi bạn ở đầu hoặc cuối dòng (Xem :help 'whichwrap'). Nếu bạn đã lđặt trong tùy chọn này, thì nó sẽ phá vỡ giải pháp được mô tả ở trên.
Tuy nhiên, có khả năng là bạn chỉ sử dụng một trong các lệnh bình thường chế độ mặc định cho ba tiến một nhân vật duy nhất ( <Space>, lvà <Right>), vì vậy nếu bạn đã lcó trong bạn 'whichwrap'thiết lập, bạn có thể loại bỏ một mà bạn không sử dụng từ 'whichwrap'tùy chọn, ví dụ <Space>:
:set whichwrap-=s
Sau đó, bạn có thể thay thế llệnh trong bước 4 của macro bằng một <Space>lệnh.
virtualedit=onemoresẽ can thiệp vào việc sử dụng lđể phát hiện cuối dòng, mặc dù không nghiêm trọng như whichwrap=l.
've'
:lv /\%3l\d/g %<CR>qqqqq<C-a>:lne<CR>@qq@qsẽ tăng tất cả các số trên dòng 3. Có thể có cách nào để giải pháp này trở nên dễ vỡ hơn?