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 :normal
lệ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 :execute
là để ngăn chặn :normal
việ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
qqq
xó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 a
w
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:
@q
sẽ đượ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 :RecursiveMacro
chờ tên của thanh ghi làm đối số (vì -register
thuộ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 q
bằ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à q
thanh 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 a
trước khi macro thực hiện công việc của nó \r
là cần thiết để báo cho Vim nhấn Enter và thực thi lệnh, xem :help expr-quote
danh 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 q
ghi với chuỗi trước đó và chuỗi tiếp theo,":if line('.')==a|exe 'norm @q'|endif\r"
nhớ lại macro q
vớ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 :RecursiveMacro
sao 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=1
thuộ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 0
là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 1
thay vì :RecursiveMacro q 0
, macro q
sẽ 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 12
thay 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, :lla
lệnh có thể được sử dụng để nhảy đến trận đấu cuối cùng và :lp
lệ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 *, l
lệ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 q
ghi,<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@q
lệ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>
, l
và <Right>
), vì vậy nếu bạn đã l
có 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ế l
lệnh trong bước 4 của macro bằng một <Space>
lệnh.
virtualedit=onemore
sẽ 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@q
sẽ 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?