Thay thế một loạt các dấu đầu dòng dấu hoa thị bằng một danh sách được đánh số


16

Hãy tưởng tượng tôi có văn bản sau:

some random stuff
* asdf
* foo
* bar
some other random stuff

Tôi muốn thay thế các dấu hoa thị bằng số, như vậy:

some random stuff
1. asdf
2. foo
3. bar
some other random stuff

Làm thế nào điều này có thể được thực hiện trong vim?


Tại sao bạn không đi tìm plugin? Một cái tương tự là
tăng.vim

Thật tuyệt vời và tuyệt vời khi mọi người đưa ra câu trả lời của họ tăng số lượng nhưng vì Markdown sẽ đánh số chúng cho bạn tại sao không làm cho tất cả 1.? Vì vậy, :%s/^* /1. /sẽ làm điều đó. Điều đó có vẻ như công việc ít hơn nhiều.
gà con

Câu trả lời:


14

Bạn có thể thử lệnh sau:

:let c=0 | g/^* /let c+=1 | s//\=c.'. '

Đầu tiên, nó khởi tạo biến c( let c=0), sau đó nó thực thi lệnh toàn cầu gtìm mẫu ^*(bắt đầu dòng, theo sau là dấu hoa thị và dấu cách).

Bất cứ khi nào một dòng chứa mẫu này được tìm thấy, lệnh toàn cục sẽ thực thi lệnh:
let c+=1 | s//\=c.'. '
Nó tăng biến c( let c+=1), sau đó ( |) nó thay thế ( s) mẫu tìm kiếm trước đó ( //) bằng cách đánh giá biểu thức ( \=):
nội dung của biến được cnối ( .) với chuỗi'. '


Nếu bạn không muốn sửa đổi tất cả các dòng từ bộ đệm của mình, nhưng chỉ một đoạn cụ thể, bạn có thể chuyển một phạm vi cho lệnh toàn cục. Ví dụ: để chỉ sửa đổi các dòng có số nằm trong khoảng từ 5 đến 10:

:let c=0 | 5,10g/^* /let c+=1 | s//\=c.'. '

Nếu bạn có một tệp chứa một số danh sách tương tự mà bạn muốn chuyển đổi, ví dụ như một cái gì đó như thế này:

some random stuff                 some random stuff                      
* foo                             1. foo                                 
* bar                             2. bar                                 
* baz                             3. baz                                 
some other random stuff           some other random stuff                
                           ==>                                                
some random stuff                 some random stuff                      
* foo                             1. foo                                 
* bar                             2. bar                                 
* baz                             3. baz                                 
* qux                             4. qux                                 
some other random stuff           some other random stuff                

Bạn có thể làm điều đó với lệnh sau:

:let [c,d]=[0,0] | g/^* /let [c,d]=[line('.')==d+1 ? c+1 : 1, line('.')] | s//\=c.'. '

Nó chỉ là một biến thể của lệnh trước, nó đặt lại biến ckhi bạn chuyển sang danh sách khác. Để phát hiện xem bạn có trong danh sách khác hay không, biến dđược sử dụng để lưu trữ số dòng cuối cùng nơi thay thế được thực hiện.
Lệnh toàn cầu so sánh số dòng hiện tại ( line('.')) với d+1. Nếu chúng giống nhau, điều đó có nghĩa là chúng ta nằm trong cùng một danh sách như trước vì vậy cđược tăng ( c+1), nếu không, điều đó có nghĩa là chúng ta nằm trong một danh sách khác, do đó, cđược đặt lại ( 1).

Bên trong một hàm, lệnh let [c,d]=[line('.')==d+1 ? c+1 : 1, line('.')]có thể được viết lại như thế này:

let c = line('.') == d+1 ? c+1 : 1
let d = line('.')

Hoặc như thế này:

if line('.') == d+1
    let c = c+1
else
    let c = 1
endif
let d = line('.')

Để lưu một số tổ hợp phím, bạn cũng có thể xác định lệnh tùy chỉnh :NumberedLists, chấp nhận một phạm vi có giá trị mặc định là 1,$( -range=%):

command! -range=% NumberedLists let [c,d]=[0,0] | <line1>,<line2>g/^* /let [c,d]=[line('.')==d+1 ? c+1 : 1, line('.')] | s//\=c.'. '

Khi nào :NumberedListssẽ được thực thi <line1><line2>sẽ được tự động thay thế bằng phạm vi bạn đã sử dụng.

Vì vậy, để chuyển đổi tất cả các danh sách trong bộ đệm, bạn sẽ gõ: :NumberedLists

Chỉ các danh sách giữa dòng 10 và 20: :10,20NumberedLists

Chỉ có lựa chọn trực quan: :'<,'>NumberedLists


Để biết thêm thông tin, xem:

:help :range
:help :global
:help :substitute
:help sub-replace-expression
:help list-identity    (section list unpack)
:help expr1
:help :command

9

Điều này chỉ hoạt động với một phiên bản Vim gần đây (có :h v_g_CTRL-A):

  1. Chặn chọn danh sách dấu đầu dòng ( *) và thay thế chúng bằng 0(con trỏ ở trên đầu tiên *) : Ctrl-v j j r 0.
  2. Chọn lại khối trước và tăng với bộ đếm :gv g Ctrl-a

... và đó là :)


(Nếu bạn muốn có một dấu chấm sau mỗi số, hãy thay đổi bước 1 thành Ctrl-v j j s 0 . Esc:)


9

Trực quan chọn các dòng và thực hiện lệnh thay thế này:

:'<,'>s/*/\=line('.') - line("'<") + 1 . '.'

Xem :help sub-replace-expression, :help line():help '<.

Để tránh phải chọn các dòng, có thể sử dụng các tìm kiếm lùi và tiến với độ lệch để chỉ định phạm vi thay thế như sau:

:?^[^*]?+1,/^[^*]/-1s/*/\=line('.') - search('^[^[:digit:]]', 'bn') . '.'

Xem :help cmdline-ranges


2

Cách khác:

:let n = 1 | g/^* /s//\=printf('%d. ', n)/g | let n = n + 1

0

Bạn cũng có thể xác định toán tử tùy chỉnh

Bạn có thể ánh xạ chúng đến các chuỗi chính '*'#. Các dấu *#không tồn tại, vì vậy bạn sẽ không ghi đè bất kỳ chức năng mặc định nào. Lý do để chọn 'làm tiền tố là để có được một số loại ghi nhớ. Bạn đang thêm một dấu / đánh dấu trước một số dòng. Và thông thường để đi đến một dấu bạn sử dụng tiền tố '.

fu! s:op_list_bullet(...) abort range

    if a:0
        let [lnum1, lnum2] = [line("'["), line("']")]
    else
        let [lnum1, lnum2] = [line("'<"), line("'>")]
    endif

    if !empty(matchstr(getline(lnum1), '^\s*\d\s*\.'))
        let pattern     = '\d\s*\.\s\?'
        let replacement = '* '

    elseif count(['-', '*'], matchstr(getline(lnum1), '\S'))
        let pattern     = '\v\S\s*'
        let replacement = ''

    else
        let pattern     = '\v\ze\S'
        let replacement = '* '
    endif

    let cmd = 'keepj keepp %s,%s s/%s/%s'

    sil exe printf(cmd, lnum1, lnum2, pattern, replacement)
endfu

fu! s:op_list_digit(...) abort range
    let l:c = 0

    if a:0
        let [lnum1, lnum2] = [line("'["), line("']")]
    else
        let [lnum1, lnum2] = [a:firstline, a:lastline]
    endif

    if count(['-', '*'], matchstr(getline(lnum1), '\S'))
        let pattern     = '\S\s*'
        let replacement = '\=l:c.". "'

    elseif !empty(matchstr(getline(lnum1), '^\s*\d\s*\.'))
        let pattern     = '\d\s*\.\s\?'
        let replacement = ''

    else
        let pattern     = '\v^\s*\zs\ze\S'
        let replacement = '\=l:c.". "'
    endif

    let cmd = 'keepj keepp %s,%s g/%s/let l:c = line(".") == line("'']")+1 ?
                                                \ l:c+1 : 1 |
                                                \ keepj keepp s/%s/%s'

    sil exe printf(cmd, lnum1, lnum2, pattern, pattern, replacement)
endfu

nno <silent> '*     :<C-U>set opfunc=<SID>op_list_bullet<CR>g@
nno <silent> '**    :<C-U>set opfunc=<SID>op_list_bullet
                    \<Bar>exe 'norm! ' . v:count1 . 'g@_'<CR>
xno <silent> '*     :call <SID>op_list_bullet()<CR>

nno <silent> '#     :<C-U>set opfunc=<SID>op_list_digit<CR>g@
nno <silent> '##    :<C-U>set opfunc=<SID>op_list_digit
                    \<Bar>exe 'norm! ' . v:count1 . 'g@_'<CR>
xno <silent> '#     :call <SID>op_list_digit()<CR>

Nó cũng hoạt động từ chế độ trực quan.
Các lệnh ex tốt cho kịch bản, nhưng để sử dụng tương tác, một toán tử bình thường có thể tốt hơn, bởi vì bạn có thể kết hợp nó với bất kỳ chuyển động hoặc đối tượng văn bản nào.

Ví dụ: bạn có thể chuyển một danh sách có tiền tố bằng dấu hoa thị hoặc dấu trừ bên trong đoạn hiện tại bằng cách nhấn '*ip. Ở đây, '*là một toán tử và iplà đối tượng văn bản mà nó hoạt động.

Và làm điều tương tự cho một danh sách có tiền tố với các số trong 10 dòng tiếp theo bằng cách nhấn '#10j. Ở đây, '#là một toán tử khác và 10jlà một chuyển động bao gồm các dòng mà toán tử làm việc.

Lợi ích khác của việc sử dụng toán tử tùy chỉnh là bạn có thể lặp lại phiên bản cuối cùng của mình bằng lệnh dot.

nhập mô tả hình ảnh ở đây

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.