Chèn một số tăng cho mỗi dòng trong một lựa chọn hoặc một trận đấu


10

Tôi có một vấn đề tôi có thể nghĩ ra hai cách tiếp cận chung để giải quyết, nhưng tôi không biết cụ thể cho cả hai cách tiếp cận.

...
Level 1:    cũng    also
Level 1:    và      and
Level 1:    như     like; such as
Level 2:    các     plural marker
Level 2:    của     belonging to
...

Đối với mỗi dòng bắt đầu "Cấp n" Tôi muốn chèn một số, bắt đầu bằng "01". Để đơn giản, hãy chuẩn bị số.

Cách tiếp cận 1: Chọn thủ công tất cả các dòng có cùng cấp. Gọi phép thuật tôi sẽ sớm học được.

Cách tiếp cận 2: Viết tìm kiếm và thay thế phù hợp với tất cả các dòng với Cấp độ đã cho, tại mỗi trận đấu sẽ bao gồm một số trong văn bản thay thế, tăng thêm một với mỗi trận đấu.

Tôi đã tìm thấy các câu hỏi tương tự trên StackOverflow hoặc trên các trang web Vim khác, nhưng mỗi câu hỏi dường như có một hoặc nhiều vấn đề sau:

  1. Là về việc chèn số dòng hiện tại chứ không phải là số tùy ý nhưng tăng dần.
  2. Không zero-pad số.
  3. Không thực sự hoạt động cho các lựa chọn trên Vim 7.4 của tôi chạy trên Windows 7. (Những cái này dẫn đến lỗi E481: No range allowed.)

Tôi đang chạy gVim trong Windows với mswin.vim nhưng một giải pháp hoạt động trên tất cả các cài đặt Vim vanilla mà không phải tùy chỉnh thiết lập có thể là tốt nhất.


Các thẻ tốt nhất tôi có thể nghĩ cho câu hỏi không tồn tại: lựa chọnphạm vi để thoải mái gắn thẻ lại hoặc tạo một trong những thẻ đó.
hà mã

1
Plugin này không phải là một giải pháp hoàn chỉnh cho vấn đề của bạn, nhưng nó rất hữu ích để thêm các cột số: VisIncr . Tài liệu ở đây . FWIW.
lcd047

Câu trả lời:


17

Tương tự như câu trả lời tại https://vi.stackexchange.com/a/818/227 , bạn có thể sử dụng lệnh toàn cầu.

Với nó, bạn có thể hướng dẫn vim tìm kiếm các dòng khớp với một mẫu và sau đó thực hiện các lệnh trên nó.

Trong trường hợp của bạn, bạn muốn thêm văn bản vào các dòng bắt đầu bằng "Cấp N:", vì vậy lệnh toàn cầu của chúng tôi có thể là

:g/^Level \d:/{COMMANDS}

Sử dụng lệnh thay thế (thay thế biểu thức chính quy) cho lệnh

Các lệnh vui hơn. Tôi thường thích thay thế biểu thức chính quy cho những thứ như thế này, vì nó dễ sử dụng các biến.

Ví dụ cho câu hỏi của bạn

:let i = 1 | g/^Level \d:/s/^/\=printf("%02d ", i)/ | let i = i+1

Làm thế nào nó hoạt động

Trong phần thay thế của lệnh thay thế có thể là một biểu thức.

Điều đầu tiên chúng ta sẽ làm là đặt một biến ilà số bắt đầu. Tôi đã chọn 1, nhưng bất kỳ số nào sẽ làm.let i = 1

Sau đó, chúng tôi chạy lệnh toàn cầu của chúng tôi, điều này cho phép chúng tôi thực hiện một hành động trên các dòng khớp. g/^Level \d:/

Chúng ta sẽ có lệnh toàn cầu của chúng ta chèn giá trị và tăng bộ đếm của chúng ta bằng cách sử dụng lệnh thay thế và lệnh let. s/^/\=printf("%02d ", i)/ | let i = i+1

Biểu thức chính quy của lệnh thay thế tìm thấy phần đầu của dòng ^và thay thế nó bằng một biểu thức và biểu thức của chúng ta sẽ là kết quả của một bản in được định dạng. Giống như trong ngôn ngữ C, printf của vim lấy các tham số định dạng. %02dcó nghĩa là chuyển đổi một đối số như thể đó là một số thập phân d, chiếm ít nhất 2 khoảng trắng 2và dấu bằng 0 0. Để biết chi tiết và các tùy chọn chuyển đổi khác (bao gồm định dạng dấu phẩy động), xem :help printf. Chúng tôi cung cấp cho printf biến đếm của chúng tôi ivà nó cho chúng tôi 01lần đầu tiên, 02lần thứ hai, v.v ... Điều này được sử dụng bởi lệnh thay thế để thay thế đầu dòng, chèn hiệu quả kết quả của printf ở đầu.

Lưu ý rằng tôi đặt một khoảng trắng sau d : "%02d ". Bạn đã không hỏi nó trong câu hỏi (và tôi không thấy đầu ra ví dụ), nhưng tôi nghi ngờ bạn muốn tách số này khỏi từ "Cấp độ". Xóa khoảng trắng khỏi chuỗi được cung cấp cho printf để có số được chèn ngay bên cạnh L in Level.

Cuối cùng, điều đó làm let i = i + 1tăng bộ đếm của chúng tôi sau mỗi lần thay thế.

Điều này có thể được áp dụng chung để thay thế các phần của dòng được khớp với các tiêu chí khác bằng dữ liệu chức năng tùy ý.

Sử dụng các lệnh thông thường kết hợp

Điều này là tốt cho chèn đơn giản hoặc chỉnh sửa phức tạp. Giống như với thay thế, chúng tôi sẽ sử dụng toàn cục để khớp, nhưng thay vì thay thế biểu thức thông thường, chúng tôi sẽ thực hiện một loạt các hoạt động như thể được gõ bởi người dùng.

Ví dụ cho câu hỏi của bạn

:let i = 1 | g/^Level \d:/execute "normal! I" . printf("%02d ", i) | let i = i+1

Làm thế nào nó hoạt động

Các giá trị được sử dụng rất giống với thay thế (chúng tôi vẫn đang sử dụng printf để định dạng số của chúng tôi để làm cho nó 0 được đệm với 2 chữ số), nhưng hoạt động thì khác.

Ở đây chúng ta sử dụng lệnh exec, lấy một chuỗi và chạy chuỗi dưới dạng một lệnh ex ( :help :exe). Chúng tôi xây dựng một chuỗi kết hợp "bình thường! I" với dữ liệu của chúng tôi, đó sẽ là "bình thường! I01" lần đầu tiên và "bình thường! I02" lần thứ hai, v.v.

Các normallệnh Thực hiện các hoạt động như thể trong chế độ bình thường. Trong ví dụ này, lệnh thông thường của chúng ta là I, chèn vào đầu dòng. Nếu chúng ta đã sử dụng ddnó sẽ xóa dòng, osẽ mở một dòng mới sau dòng phù hợp. Như thể bạn tự gõ I(hoặc bất kỳ thao tác nào khác) ở chế độ bình thường. chúng tôi sử dụng !sau normalđể đảm bảo không có ánh xạ nào cản đường chúng tôi. Xem :help :normal.

Giá trị được chèn vào sau đó là giá trị của printf của chúng tôi, như trong ví dụ đầu tiên về việc sử dụng thay thế.

Phương pháp này có thể lạ hơn regex, bởi vì bạn có thể thực hiện những việc như execute "normal! ^2wy" . i . "th$p", sẽ đi đến phần đầu của văn bản ^, di chuyển về phía trước 2 từ 2w, kéo dài cho đến khi ký tự thứ ih y" . i . "th, di chuyển đến cuối dòng $và dán p.

Điều này gần giống như chạy macro, nhưng thực tế không sử dụng hết thanh ghi và có thể kết hợp các chuỗi từ bất kỳ biểu thức nào. Tôi thấy điều này rất mạnh mẽ.

Cách tiếp cận nơi mỗi cấp có bộ đếm riêng

Bạn có thể muốn mỗi cấp độ có được bộ đếm riêng của mình. Nếu bạn biết số cấp tối đa trước thời hạn, bạn có thể thực hiện các thao tác sau (thêm mã bổ sung để tìm mức lớn nhất có thể không quá khó, nhưng sẽ khiến câu trả lời này quá dài. Điều này sẽ kéo dài).

Đầu tiên, hãy để tôi tự do, trong trường hợp chúng tôi đã sử dụng nó như một số nguyên. Chúng tôi không thể chuyển đổi tôi thành một danh sách, chúng tôi phải tạo ra nó theo cách đó.

:unlet! i

Tiếp theo, hãy đặt i thành một danh sách chứa số cấp. Bạn đã cho thấy 2 trong câu hỏi của bạn, nhưng hãy giả sử 10 cho niềm vui của nó. Vì lập chỉ mục danh sách là 0 dựa trên và tôi không muốn sửa lỗi cho 1 dựa trên danh sách của bạn, chúng tôi sẽ chỉ tạo đủ các yếu tố (11) và không bao giờ sử dụng chỉ mục 0.

:let j = 0
:let i = []
:while j < 11 | let i += [1] | let j += 1 | endwhile

Tiếp theo, chúng ta cần một cách để có được số cấp. May mắn thay, thay thế cũng có sẵn như là một chức năng, vì vậy chúng tôi sẽ cung cấp cho dòng của chúng tôi và trích xuất số cấpsubstitute(getline("."), "^Level \\(\\d\\):.*", "\\=submatch(1)", "")

Vì tôi bây giờ là một danh sách gồm 11 1giây (mỗi chỉ số là bộ đếm cho cấp độ của chúng tôi), bây giờ chúng tôi có thể điều chỉnh một trong các ví dụ trên để sử dụng kết quả của sự thay thế này:

Thông qua lệnh thay thế:

:unlet! i | unlet! j | let j = 0 | let i = [] | while j < 11 | let i += [1] | let j += 1 | endwhile
:g/^Level \d:/let ind=str2nr(substitute(getline("."), "^Level \\(\\d\\):.*", "\\=submatch(1)", "")) | s/^/\=printf("%02d ", i[ind])/ | let i[ind] += 1

Thông qua lệnh thông thường:

:unlet! i | unlet! j | let j = 0 | let i = [] | while j < 11 | let i += [1] | let j += 1 | endwhile
:g/^Level \d:/let ind=str2nr(substitute(getline("."), "^Level \\(\\d\\):.*", "\\=submatch(1)", "")) | execute "normal! I" . printf("%02d ", i[ind]) | let i[ind] += 1

Ví dụ đầu vào:

Level 1: stuff

Level 1: Stuff

Some text
Level 3: Other

Level 1: Meh

Level 2: More

Ví dụ đầu ra:

01 Level 1: stuff

02 Level 1: Stuff

Some text
01 Level 3: Other

03 Level 1: Meh

01 Level 2: More

Wow, rất bách khoa. Tôi chỉ đọc phần đầu tiên và cảm thấy như tôi đã học được nhiều hơn về Vim chỉ từ đó so với những gì tôi có trong vài năm qua. Đây là loại câu trả lời giúp Stack Exchange tốt hơn so với các trang web Hỏi & Đáp trung bình và cũng cho thấy những lợi ích của việc có một SE cụ thể của Vim.
hà mã

3

Bạn có thể xây dựng từ https://stackoverflow.com/a/4224454/15934 để zero pad số của bạn.

" A reminder: how to start numbers at the first line
:'<,'>s/^\s*\zs/\=(line('.') - line("'<")+1).'. '

Nhưng để đơn giản hóa hành động của các số đệm, tôi sẽ tìm một cặp hàm và một lệnh:

command!  -range=% -nargs=? PrependNumbers <line1>,<line2>call s:Prepend(<args>)

function! s:ReplExpr(nb_digits, number)
  return repeat('0', a:nb_digits - strlen(a:number)).a:number
endfunction

function! s:Prepend(...) range
  let pattern = a:0 > 0 ? '\ze'. a:1 : '^'
  let nb_values = (a:lastline - a:firstline) + 1
  let nb_digits = strlen(nb_values)
  exe ':'.a:firstline.','a:lastline.'s#'.pattern.'#\=s:ReplExpr(nb_digits, 1+ line(".")-'.a:firstline.')." "#'
endfunction

Hơn, chọn các dòng và loại của :PrependNumberbạn (bạn sẽ thấy :'<,'>PrependNumber. [Lưu ý: Lệnh có một tham số tùy chọn: mẫu trước khi số sẽ được chèn]


Nhưng không line(".")có nghĩa là "sử dụng số dòng hiện tại"? Đó là vấn đề của tôi 1. với những câu trả lời trước đây tôi có thể tìm thấy.
hà mã

1
Vâng, đúng vậy. Đó là lý do tại sao tôi trừ số dòng của dòng đầu tiên trong phạm vi.
Luc Hermitte

À, tôi chưa thử nó vì tôi thậm chí không thể nhớ cách nhập một tập lệnh trong Vim ... tôi phải đọc nó trước (-:
hippietrail

1
Khi bạn ở dưới cửa sổ, sao chép dán mã vào $HOME/vimfiles/plugin/whatevernameyouwish.vim. Hoặc thậm chí của bạn $HOME/_vimrc(tên tệp windows) nếu bạn thích (trong lần đầu tiên). Nếu bạn không chắc chắn $ HOME là gì trên máy của mình, hãy hỏi vim xem nó nghĩ gì ->:echo $HOME
Luc Hermitte

1
Bạn thậm chí không cần :sourcenếu tập lệnh vim được cài đặt đúng trong thư mục bên phải ({rtp} / plugin hoặc {rtp} / ftplugin / {filetype} / ftplugin cho các plugin cụ thể của filetype). :sourcelà những gì chúng ta đã phải chơi với hơn một thập kỷ rưỡi trước.
Luc Hermitte

2

Bạn có thể tiếp cận nó bằng cách ghi lại một macro. Bắt đầu với tệp gốc của bạn, hãy thêm số đầu tiên vào số đó.

01 Level 1:    cũng    also
Level 1:    và      and
Level 1:    như     like; such as
Level 2:    các     plural marker
Level 2:    của     belonging to

Di chuyển con trỏ của bạn đến 01, chọn và kéo nó với yiw. Bây giờ bạn muốn ghi lại hành động của bạn từ thời điểm này.

qq/^Level 1<CR>P<C-A>A<space><esc>0yiwq

  • qq Bắt đầu một macro trong đăng ký q
  • /^Level 1<CR> Tìm kiếm một dòng bắt đầu bằng "Cấp 1"
  • P dán trước con trỏ (chứa số của bạn)
  • <C-A> tăng số
  • A<space><esc> Chèn một khoảng trắng sau số
  • 0 Di chuyển đến điểm bắt đầu
  • yiw số hiện tại
  • q Kết thúc macro

Sau đó lặp lại macro này bằng cách sử dụng @q.


1
<C-A>kiểm soát + a? Điều đó chọn mọi thứ trong Vim trên Windows.
hà mã

Đó là Kiểm soát + a. Trong vim nó nên tăng một số. Nếu bạn đã thay đổi các phím bấm của mình để thực hiện các hành động Windows thay vì các hành động Vim thì bạn sẽ không thể thực hiện hầu hết những điều mọi người đăng ở đây.
jecxjo

Không có phiên bản Vim cho Windows đi kèm với các ràng buộc khác nhau do sự khôn ngoan vô hạn của ai đó. Tôi chỉ đang sử dụng các ràng buộc ngoài hộp vani. Tôi chưa bao giờ thay đổi ràng buộc khóa Vim trong hơn hai mươi năm, mà tôi có thể nhớ lại (-:
hà mã

Không nên như vậy, cài đặt Windows của tôi có các ràng buộc vim bình thường. Có thể bạn đã cài đặt bản dựng vim của người khác? Hoặc bạn có thể chạy vim ở chế độ "Easy" không? Tôi tin rằng windows cài đặt các biểu tượng máy tính để bàn với các tùy chọn chế độ bình thường và dễ dàng.
jecxjo

3
Không, bởi vì cài đặt mặc định trong windows tải mswin.vim. Nếu bạn tạo vimrc của riêng bạn và không tải mswin.vim thì bạn có được các ràng buộc vim bình thường. Tôi giữ một vimrc duy nhất cho tất cả các cài đặt của tôi (Linux, Mac và Windows) và không bao giờ giao dịch với mswin.vim. Để biết thêm thông tin về vấn đề này, hãy xem stackoverflow.com/questions/289681/ từ
jecxjo
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.