Sostituisci una serie di punti elenco asterisco con un elenco numerato


16

Immagina di avere il seguente testo:

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

Voglio sostituire i proiettili di asterisco con i numeri, in questo modo:

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

Come può essere fatto in vim?


Perché non vai per i plugin? Uno simile è increment.vim in Github
SibiCoder

È così sorprendente e bello che tutti hanno fatto aumentare le loro risposte con i numeri, ma dal momento che Markdown le numera per te, perché non crearle tutte 1.? Quindi :%s/^* /1. /lo farebbe. Sembra molto meno lavoro.
pulcini

Risposte:


14

Puoi provare il seguente comando:

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

Prima inizializza la variabile c( let c=0), quindi esegue il comando globale gche cerca il modello ^*(un inizio di riga, seguito da un asterisco e uno spazio).

Ogni volta che viene trovata una riga contenente questo modello, il comando globale esegue il comando:
let c+=1 | s//\=c.'. '
incrementa la variabile c( let c+=1), quindi ( |) sostituisce ( s) il precedente modello cercato ( //) con la valutazione di un'espressione ( \=):
il contenuto della variabile cconcatenato ( .) con la stringa'. '


Se non si desidera modificare tutte le righe dal buffer, ma solo un paragrafo specifico, è possibile passare un intervallo al comando globale. Ad esempio, per modificare solo le righe il cui numero è compreso tra 5 e 10:

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

Se si dispone di un file contenente diversi elenchi simili che si desidera convertire, ad esempio qualcosa del genere:

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                

Puoi farlo con il seguente comando:

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

È solo una variante del comando precedente, che reimposta la variabile cquando si passa a un altro elenco. Per rilevare se ci si trova in un altro elenco, la variabile dviene utilizzata per memorizzare il numero dell'ultima riga in cui è stata effettuata una sostituzione.
Il comando globale confronta il numero di riga corrente ( line('.')) con d+1. Se sono uguali, significa che siamo nella stessa lista di prima così cè incrementato ( c+1), altrimenti significa che siamo in una lista diversa, quindi cè reset ( 1).

All'interno di una funzione, il comando let [c,d]=[line('.')==d+1 ? c+1 : 1, line('.')]potrebbe essere riscritto in questo modo:

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

O così:

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

Per salvare alcune sequenze di tasti, è possibile anche definire il comando personalizzato :NumberedLists, che accetta un intervallo il cui valore predefinito è 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.'. '

Quando :NumberedListsverrà eseguito <line1>e <line2>verrà automaticamente sostituito con l'intervallo utilizzato.

Quindi, per convertire tutti gli elenchi nel buffer, digitare: :NumberedLists

Solo gli elenchi tra le righe 10 e 20: :10,20NumberedLists

Solo la selezione visiva: :'<,'>NumberedLists


Per ulteriori informazioni, vedere:

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

9

Funziona solo con una versione recente di Vim (che ha :h v_g_CTRL-A):

  1. Block-selezionare i proiettili lista ( *) e sostituirli con 0(il cursore si trova sulla prima *): Ctrl-v j j r 0.
  2. Riseleziona il blocco precedente e incrementa con il contatore :gv g Ctrl-a

... e basta :)


(Se si vuole avere un punto dopo ogni numero, cambiare prima fase a: Ctrl-v j j s 0 . Esc)


9

Seleziona visivamente le linee ed esegui questo comando di sostituzione:

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

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

Per evitare di selezionare le linee, è possibile utilizzare ricerche indietro e avanti con offset per specificare l'intervallo di sostituzione in questo modo:

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

Vedere :help cmdline-ranges


2

Un altro modo:

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

0

È inoltre possibile definire operatori personalizzati

Potresti mapparli sulle sequenze chiave '*e '#. I segni *e #non esistono, quindi non sostituirai alcuna funzionalità predefinita. Il motivo per scegliere 'come prefisso è ottenere una sorta di mnemonica. Stai aggiungendo un segno / segno davanti ad alcune righe. E di solito per andare a un segno si usa il prefisso '.

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>

Funziona anche dalla modalità visiva.
I comandi Ex sono utili per gli script, ma per un uso interattivo è probabilmente meglio un normale operatore, perché puoi combinarlo con qualsiasi movimento o oggetto di testo.

Ad esempio, è possibile attivare o disattivare un elenco con prefisso asterischi o segni meno all'interno del paragrafo corrente '*ip. Qui, '*è un operatore ed ipè l'oggetto di testo su cui lavora.

E fai la stessa cosa per un elenco preceduto da numeri attraverso le prossime 10 righe colpendo '#10j. Qui, '#c'è un altro operatore ed 10jè un movimento che copre le linee su cui lavora l'operatore.

L'altro vantaggio dell'utilizzo di un operatore personalizzato è che puoi ripetere l'ultima edizione con il comando punto.

inserisci qui la descrizione dell'immagine

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.