Remplacer une série de puces astérisques par une liste numérotée

16

Imaginez que j'ai le texte suivant:

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

Je veux remplacer les puces astérisques par des nombres, comme ceci:

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

Comment cela peut-il se faire dans vim?

Brennan Vincent
la source
Pourquoi ne choisissez-vous pas des plugins? Un exemple similaire est increment.vim dans Github
SibiCoder
C'est tellement incroyable et cool que tout le monde a fait incrémenter ses réponses, mais puisque Markdown les numérotera pour vous, pourquoi ne pas tout faire 1.? Le :%s/^* /1. /ferait donc. Cela semble beaucoup moins de travail.
poussins

Réponses:

14

Vous pouvez essayer la commande suivante:

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

D'abord, il initialise la variable c( let c=0), puis il exécute la commande globale gqui recherche le modèle ^*(un début de ligne, suivi d'un astérisque et d'un espace).

Chaque fois qu'une ligne contenant ce motif est trouvée, la commande globale exécute la commande:
let c+=1 | s//\=c.'. '
Elle incrémente la variable c( let c+=1), puis ( |) elle substitue ( s) le motif recherché précédent ( //) avec l'évaluation d'une expression ( \=):
le contenu de la variable cconcaténée ( .) avec la chaîne'. '


Si vous ne souhaitez pas modifier toutes les lignes de votre tampon, mais uniquement un paragraphe spécifique, vous pouvez passer une plage à la commande globale. Par exemple, pour modifier uniquement les lignes dont le nombre est compris entre 5 et 10:

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

Si vous avez un fichier contenant plusieurs listes similaires que vous souhaitez convertir, par exemple quelque chose comme ceci:

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                

Vous pouvez le faire avec la commande suivante:

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

C'est juste une variante de la commande précédente, qui réinitialise la variable clorsque vous passez à une autre liste. Pour détecter si vous êtes dans une autre liste, la variable dest utilisée pour stocker le numéro de la dernière ligne où une substitution a été effectuée.
La commande globale compare le numéro de ligne actuel ( line('.')) avec d+1. S'ils sont identiques, cela signifie que nous sommes dans la même liste qu'avant, donc cest incrémenté ( c+1), sinon cela signifie que nous sommes dans une liste différente, donc cest réinitialisé ( 1).

À l'intérieur d'une fonction, la commande let [c,d]=[line('.')==d+1 ? c+1 : 1, line('.')]pourrait être réécrite comme ceci:

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

Ou comme ça:

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

Pour enregistrer certaines séquences de touches, vous pouvez également définir la commande personnalisée :NumberedLists, qui accepte une plage dont la valeur par défaut est 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.'. '

Quand :NumberedListssera exécuté <line1>et <line2>sera automatiquement remplacé par la plage que vous avez utilisée.

Donc, pour convertir toutes les listes dans le tampon, vous devez taper: :NumberedLists

Seules les listes entre la ligne 10 et 20: :10,20NumberedLists

Seule la sélection visuelle: :'<,'>NumberedLists


Pour plus d'informations, voir:

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

Cela ne fonctionne qu'avec une version récente de Vim (qui a :h v_g_CTRL-A):

  1. Bloc-sélectionner les balles de liste ( *) et les remplacer par 0(curseur est sur la première *): Ctrl-v j j r 0.
  2. Resélectionnez le bloc précédent et incrémentez avec le compteur :gv g Ctrl-a

... et c'est tout :)


(Si vous voulez avoir un point après chaque numéro, changer 1ère étape à: Ctrl-v j j s 0 . Esc)

VanLaser
la source
9

Sélectionnez visuellement les lignes et exécutez cette commande de substitution:

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

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

Pour éviter d'avoir à sélectionner les lignes, des recherches en arrière et en avant avec décalages peuvent être utilisées pour spécifier la plage de substitution comme ceci:

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

Voir :help cmdline-ranges

djjcast
la source
2

Autrement:

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

Vous pouvez également définir des opérateurs personnalisés

Vous pouvez les mapper aux séquences de touches '*et '#. Les marques *et #n'existent pas, vous ne remplacerez donc aucune fonctionnalité par défaut. La raison de choisir 'comme préfixe est d'obtenir une sorte de mnémonique. Vous ajoutez un signe / une marque devant certaines lignes. Et généralement pour aller à une marque, vous utilisez le préfixe '.

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>

Il fonctionne également en mode visuel.
Les commandes Ex sont bonnes pour les scripts, mais pour une utilisation interactive, un opérateur normal est probablement meilleur, car vous pouvez le combiner avec n'importe quel mouvement ou objet texte.

Par exemple, vous pouvez basculer une liste préfixée avec des astérisques ou des signes moins à l'intérieur du paragraphe en cours en appuyant sur '*ip. Ici, '*est un opérateur et ipest l'objet texte sur lequel il fonctionne.

Et faites la même chose pour une liste préfixée avec des nombres sur les 10 lignes suivantes en appuyant sur '#10j. Ici, '#est un autre opérateur et 10jest un mouvement couvrant les lignes sur lesquelles l'opérateur travaille.

L'autre avantage de l'utilisation d'un opérateur personnalisé est que vous pouvez répéter votre dernière édition avec la commande dot.

entrez la description de l'image ici

user9433424
la source