Chuyển đến từ tiếp theo bắt đầu bằng chữ cái cụ thể trên dòng hiện tại


7

Tôi muốn có lệnh được nêu trong tiêu đề. Tôi đã tìm kiếm nó trong lệnh Vim nội tại nhưng không tìm thấy nó. Tôi đã chạy một số hướng dẫn trực tuyến về các chức năng trong vim, Ý tưởng sẽ là một cái gì đó tương tự trong .vimrc:

map <command> <argument>: call nextWordWithLetter(argument)

function nextWordWithLetter(letter)
    " for word i in line
    " if word i's first letter = letter
        " col = column of word i
        " break
    col | "The command to jump to column number 'col'
end function

Câu trả lời:


11

Bạn có thể sử dụng getchar()search()để thực hiện mục tiêu của bạn.

nnoremap <key> :call search('\<' . nr2char(getchar()), 'W', line('.'))<cr>

Ý tưởng là chúng ta sử dụng nr2char(getchar())để chờ một ký tự được gõ và sử dụng search()để tìm kiếm phiên bản tiếp theo của ký tự ở đầu một từ thông qua \<. Chúng tôi giới hạn dòng này cho dòng hiện tại bằng cách cung cấp dòng hiện tại line('.'), làm tham số thứ ba search().

Cá nhân tôi nghĩ rằng tôi chỉ muốn sử dụng /hoặc f, tuy nhiên, có những plugin khác cung cấp chức năng tương tự:

Để được trợ giúp thêm xem:

:h search(
:h getchar(
:h /\<

7

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,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, evv). Thông thường, để xác định một chuyển động hoặc đối tượng mới, bạn sử dụng onoremaplệ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đâ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 cthay đổi, dxóa, ysao 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 opfunctùy chọn có giá trị được coi là tên của hàm, hãy đặt các dấu `[`]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 opfunctù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 operatorfunctùy chọn thành NextWordWithLettervà sau đó nhấn ở chế độ bình thường g@.

Toán tử g@chờ một chuyển động để đặt các dấu `[`]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>gtự động đặt operatorfunctù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 `[`]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 normalloạ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 anơ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 abằng lệnh 'ahoặc đến ký tự chính xác nơi bạn đặt dấu abằ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: `[`]. 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. fplà 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 `[`]xung quanh văn bản mà chúng ta sẽ chuyển qua nếu chúng ta vừa gõ {motion}( fptrong 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 normalloạ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 :regvà để 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 plà 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 listmộ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 forlặ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 wordcó 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, \%1clà 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 wordvới letterchú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 ignorecasetù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 wordletter, đâ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 wordcộ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 ở stringnơi patternkhớ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 . "|"

executelà 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, executethực thi "normal! 10|", kết quả trong việc normalgõ lệnh 10|cho chúng tôi.


Cảm ơn, đó chính xác là những gì tôi đang tìm kiếm. Tôi không hiểu một nửa các lệnh bạn đã sử dụng tho: /
solalito

2
Tôi xin lỗi vì đã không giải thích mã, tôi sẽ chỉnh sửa bài đăng để giải thích nó để bạn có thể điều chỉnh nó theo ý thích nếu bạn muốn. Nó sẽ đưa tôi một chút thời gian.
saginaw

Cảm ơn bạn, dành thời gian của bạn! Tôi luôn muốn biết chính xác các công cụ tôi đang sử dụng.
solalito

Tôi đã cố gắng đơn giản hóa mã và cố hết sức để giải thích chi tiết. Tôi hy vọng sau khi đọc nó, mã sẽ có ý nghĩa hơn.
saginaw

Tôi không bao giờ hy vọng có được một câu trả lời hoàn chỉnh như vậy. Tôi đã đọc nó nhưng tôi sẽ cần phải xem qua nó một cách cẩn thận. +2 nếu tôi có thể!
solalito
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.