Chỉnh sửa : Câu trả lời của Peter Rincker ngắn hơn, dễ giải thích hơn và có thể được lặp lại nhiều lần như bạn muốn. Giải pháp của tôi quá dài và không thể lặp lại trong vài từ. Tôi nên xóa câu trả lời nhưng có lẽ ai đó sẽ quan tâm đến nó, vì vậy tôi để nó ở đây.
Tôi không chắc đây là thứ bạn muốn, nhưng hãy thử đoạn mã sau:
function! NextWordWithLetter(type)
normal! `]vy
let letter = @0
let line = getline('.')
let list = split(line)
for word in list
if matchstr(word, '\%1c.') ==# letter
let col = match(line, word) + 1
execute "normal! " . col . "|"
break
endif
endfor
endfunction
nnoremap <silent> <leader>g :set operatorfunc=NextWordWithLetter<cr>g@
Ánh xạ để gọi hàm là <leader>g<motion>
.
Nếu bạn muốn di chuyển con trỏ đến từ tiếp theo bắt đầu bằng chữ cái p
và ,
là khóa lãnh đạo của bạn, thì bạn có thể nhập ,gfp
.
Mục tiêu
Bạn muốn đi đến từ tiếp theo bắt đầu bằng một chữ cái cụ thể. Tôi không biết nếu vim đã có sẵn một chuyển động như vậy, nhưng hãy giả sử rằng nó không.
Chúng ta cần phải xác định một chuyển động mới (như w
, b
, e
vv). Thông thường, để xác định một chuyển động hoặc đối tượng mới, bạn sử dụng onoremap
lệnh. Nó được giải thích chi tiết ở đây .
Tôi không biết làm thế nào để đạt được mục tiêu của bạn theo cách này, nhưng tôi biết cách xác định một nhà điều hành mới sẽ gián tiếp làm những gì bạn muốn.
Tôi nói gián tiếp vì mục đích của một nhà điều hành mới, thông thường, không phải là để di chuyển đi đâu đó, mà là để làm một cái gì đó trên một đoạn văn bản.
Vim giải thích cách tạo một toán tử mới trong trợ giúp : :h map-operator
. Nó cũng được giải thích ở đây , đây và đây .
Cú pháp có vẻ lạ đối với bạn, nhưng đây là cách nó hoạt động trong vimscript:
g@
là một toán tử đặc biệt (muốn c
thay đổi, d
xóa, y
sao chép, v.v.) cho phép bạn xây dựng toán tử của riêng mình.
Nếu bạn gõ g@{motion}
vim sẽ tìm opfunc
tùy chọn có giá trị được coi là tên của hàm, hãy đặt các dấu `[
và `]
xung quanh văn bản bạn sẽ chuyển qua nếu bạn vừa gõ {motion}
(thêm về điều này sau) và thực hiện chức năng có thể làm bất cứ điều gì bạn muốn trên đoạn văn bản này.
Bạn có thể tạo các toán tử khác nhau bằng cách viết các hàm khác nhau và đặt opfunc
tùy chọn thành tên của hàm bạn muốn sử dụng.
Nhưng trước khi đi sâu vào chức năng của chúng ta, chúng ta hãy xem bản đồ mà chúng ta cần để kích hoạt nó.
Lập bản đồ
Ở cuối đoạn mã, có dòng này:
nnoremap <silent> <leader>g :set operatorfunc=NextWordWithLetter<cr>g@
Nó nói với vim rằng bất cứ khi nào bạn nhấn <leader>g
, nó phải âm thầm ( <silent>
) đặt giá trị của operatorfunc
tùy chọn thành NextWordWithLetter
và sau đó nhấn ở chế độ bình thường g@
.
Toán tử g@
chờ một chuyển động để đặt các dấu `[
và `]
xung quanh văn bản mà bạn sẽ di chuyển qua {motion}
trước khi thực hiện chức năng được lưu trữ trong đó opfunc
. Đây là chức năng NextWordWithLetter()
.
Bạn có thể làm tương tự bằng cách gõ trong chế độ lệnh:
:set operatorfunc=NextWordWithLetter
Sau đó ở chế độ bình thường: g@
Sau đó luôn ở chế độ bình thường:{motion}
Nhưng tất nhiên nó sẽ rất tẻ nhạt, vì vậy chúng tôi tạo ánh xạ <leader>g
tự động đặt operatorfunc
tùy chọn thành tên hàm mong muốn và nhấn g@
cho chúng tôi.
Giả sử khóa lãnh đạo của bạn là ,
và bạn đang tìm từ tiếp theo bắt đầu bằng chữ cái p
. Sử dụng bản đồ của chúng tôi, bạn có thể gõ ,gfp
.
Như một kết quả vim sẽ thiết lập các điểm `[
và `]
xung quanh các văn bản giữa con trỏ và các ký tự tiếp theo p
, thiết lập opfunc
để NextWordWithLetter
, và vì giá trị của tùy chọn này nó sẽ thực hiện các chức năng NextWordWithLetter()
.
Tiếp theo, hãy xem chức năng này làm gì.
Đặt biến
Đầu tiên chúng ta cần cho biết chức năng mà nhân vật chúng ta đang tìm kiếm:
normal! `]vy
Các normal
loại lệnh bất kỳ chuỗi ký tự mà bạn vượt qua nó như thể bạn đang ở chế độ bình thường. Ở đây trình tự các ký tự là `]vy
.
Khi bạn ở trong bộ đệm, bạn có thể đặt dấu ở bất cứ đâu bằng lệnh thông thường m{letter}
.
Nếu bạn muốn đặt dấu a
nơi con trỏ của bạn, bạn sẽ gõ ma
. Sau đó, bạn có thể di chuyển xung quanh và quay lại cùng một dòng với dấu a
bằng lệnh 'a
hoặc đến ký tự chính xác nơi bạn đặt dấu a
bằng lệnh `a
.
Bạn có thể thấy tất cả các dấu của bạn với lệnh :marks
. Các dấu chữ hoa là toàn cục và có thể được sử dụng để nhảy qua các bộ đệm, các chữ thường được đặt cục bộ vào một bộ đệm.
Có hai dấu hiệu đặc biệt mà vim tự động đặt cho chúng tôi: `[
và `]
. Bất cứ khi nào bạn thay đổi hoặc lấy một số văn bản, vim sẽ đặt những dấu đó xung quanh nó ( :h '[
).
Hãy quay lại ví dụ của chúng tôi: bạn muốn đi đến từ tiếp theo bắt đầu bằng chữ cái p
, vì vậy bạn nhấn ,gfp
. fp
là một lệnh di chuyển con trỏ đến ký tự tiếp theo p
.
Tại thời điểm này, vim sẽ tự động đặt các dấu `[
và `]
xung quanh văn bản mà chúng ta sẽ chuyển qua nếu chúng ta vừa gõ {motion}
( fp
trong ví dụ của chúng tôi).
Nó được viết trong phần trợ giúp ( :h g@
):
g @ {motion} Gọi hàm được đặt bởi tùy chọn 'Toán tử'. Dấu '[được định vị ở đầu văn bản được di chuyển qua {motion}, dấu'] trên ký tự cuối cùng của văn bản.
Chúng tôi chưa thay đổi hoặc kéo mạnh bất cứ điều gì, vậy tại sao vim đã đặt những dấu ấn đó? Bởi vì nó nghĩ rằng hàm muốn thực hiện một cái gì đó trên văn bản và hàm sẽ cần những dấu đó để xác định văn bản để hoạt động.
Vì vậy, khi các normal
loại lệnh `]vy
, nó có thể được đọc là:
đi đến dấu `]
(ký tự cuối cùng của văn bản được chuyển qua), chuyển sang chế độ trực quan ( v
) và sao chép lựa chọn trực quan ( y
) .
Vì vậy, bây giờ chúng tôi đã sao chép nhân vật tìm kiếm của bạn (nhân vật p
). Tất cả mọi thứ bạn sao chép là trong một đăng ký. Có nhiều thanh ghi khác nhau trong vim, mỗi thanh ghi được sử dụng cho một mục đích khác nhau. Để xem chúng, hãy nhập :reg
và để biết đăng ký nào lưu trữ những gì :h registers
.
Văn bản cuối cùng bạn sao chép được lưu trữ trong sổ đăng ký 0
. Bạn có thể truy cập nó với @0
: echo @0
.
Nhân vật tìm kiếm của bạn p
là trong đăng ký này. Chúng tôi gán nó cho biến letter
:
let letter = @0
Tiếp theo, chúng ta gán dòng hiện tại cho biến line
:
let line = getline('.')
getline()
là một hàm dựng sẵn trả về dòng có số được truyền dưới dạng đối số và '.'
được mở rộng dưới dạng số của dòng hiện tại.
Tiếp theo, chúng ta gán cho biến list
một danh sách chứa mỗi từ của dòng:
let list = split(line)
split()
là một hàm tích hợp có thể phân tách một chuỗi bất cứ khi nào nó tìm thấy một khoảng trắng (dấu cách, ký tự tab ...). Bạn có thể yêu cầu nó phân chia tại các vị trí khác bằng cách đưa ra một đối số thứ hai. Ví dụ: nếu bạn muốn phân tách bất cứ khi nào có ký tự dấu hai chấm, bạn sẽ sử dụng split(line, ':')
.
Đối với vòng lặp
Các for
lặp loop qua biến list
, và các cửa hàng mỗi mục của nó bên trong biến word
:
for word in list
...
endfor
Sau đó, nó kiểm tra xem chữ cái đầu tiên word
có phải là chữ cái bạn đang tìm không:
if matchstr(word, '\%1c.') ==# letter
...
endif
matchstr()
là một hàm dựng sẵn trả về một mẫu nếu nó được tìm thấy bên trong một chuỗi.
Ở đây chúng tôi đang tìm kiếm mô hình '\%1c.'
bên trong word
. Trong một regex vim, \%1c
là một nguyên tử có độ rộng bằng không. Nó không thêm bất cứ thứ gì vào mẫu, nó chỉ cho công cụ regex phân tích cú pháp regex mà mẫu của chúng ta bắt đầu trên cột đầu tiên của văn bản (để biết thêm thông tin :h /\%c
). Dấu chấm ở cuối regex có nghĩa là bất kỳ ký tự nào.
Vậy '\%1c.'
có nghĩa là: bất kỳ ký tự nào trên cột đầu tiên của văn bản và matchstr(word, '\%1c.')
sẽ trả về ký tự đầu tiên của word
.
Để so sánh chữ cái đầu tiên của word
với letter
chúng tôi sử dụng các ==#
nhà điều hành. Đó là một toán tử phân biệt chữ hoa chữ thường (bất kể giá trị của ignorecase
tùy chọn là gì). Bạn cũng có thể sử dụng ==?
trường hợp không nhạy cảm hoặc trần ==
(trường hợp nhạy cảm hoặc không phụ thuộc vào ignorecase
; không nên sử dụng trường hợp này).
Sau đó, nếu ký tự đầu tiên word
là letter
, đây là chức năng nào:
let col = match(line, word) + 1
Nó lưu chỉ mục của ký tự đầu tiên word
cộng với một trong biến col
. match()
là một hàm dựng sẵn, có cú pháp cơ sở match(string, pattern)
và trả về chỉ mục ở string
nơi pattern
khớp. Chúng tôi thêm một vì vim bắt đầu lập chỉ mục bằng 0.
Cuối cùng, nó thực thi điều này:
execute "normal! " . col . "|"
execute
là một lệnh thực thi nội dung của bất kỳ chuỗi nào bạn truyền cho nó. Đây là chuỗi:"normal! " . col . "|"
Toán tử dấu chấm nối 3 chuỗi "normal !"
, col
(đó là một số nhưng sự ép buộc của vim tự động biến nó thành một chuỗi) và "|"
.
Giả sử nhân vật được tìm kiếm của bạn nằm trên cột thứ 10 của dòng. Trong trường hợp này, chuỗi trước đó sẽ được ước tính thành : "normal! 10|"
. Vì vậy, execute
thực thi "normal! 10|"
, kết quả trong việc normal
gõ lệnh 10|
cho chúng tôi.