Reemplace una serie de puntos de asterisco con una lista numerada

16

Imagina que tengo el siguiente texto:

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

Quiero reemplazar las viñetas de asterisco con números, así:

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

¿Cómo se puede hacer esto en vim?

Brennan Vincent
fuente
¿Por qué no vas por complementos? Una similar es increment.vim en Github
SibiCoder
Es tan asombroso y genial que todos hicieron que sus respuestas aumentaran los números, pero como Markdown las numerará por usted, ¿por qué no las hace todas 1.? Entonces :%s/^* /1. /lo haría. Eso parece mucho menos trabajo.
pollitos

Respuestas:

14

Puedes probar el siguiente comando:

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

Primero inicializa la variable c( let c=0), luego ejecuta el comando global gque busca el patrón ^*(un comienzo de línea, seguido de un asterisco y un espacio).

Cada vez que se encuentra una línea que contiene este patrón, el comando global ejecuta el comando:
let c+=1 | s//\=c.'. '
incrementa la variable c( let c+=1), luego ( |) sustituye ( s) el patrón buscado anterior ( //) con la evaluación de una expresión ( \=):
el contenido de la variable cconcatenada ( .) con la cadena'. '


Si no desea modificar todas las líneas de su búfer, pero solo un párrafo específico, puede pasar un rango al comando global. Por ejemplo, para modificar solo las líneas cuyo número está entre 5 y 10:

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

Si tiene un archivo que contiene varias listas similares que desea convertir, por ejemplo, algo como esto:

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                

Puedes hacerlo con el siguiente comando:

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

Es solo una variante del comando anterior, que restablece la variable ccuando cambia a otra lista. Para detectar si está en otra lista, la variable dse utiliza para almacenar el número de la última línea donde se realizó una sustitución.
El comando global compara el número de línea actual ( line('.')) con d+1. Si son iguales, significa que estamos en la misma lista que antes, por lo que cse incrementa ( c+1), de lo contrario significa que estamos en una lista diferente, por lo que cse restablece ( 1).

Dentro de una función, el comando let [c,d]=[line('.')==d+1 ? c+1 : 1, line('.')]podría reescribirse así:

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

O así:

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

Para guardar algunas pulsaciones de teclas, también puede definir el comando personalizado :NumberedLists, que acepta un rango cuyo valor predeterminado es 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.'. '

Cuándo :NumberedListsse ejecutará <line1>y <line2>se reemplazará automáticamente con el rango que utilizó.

Entonces, para convertir todas las listas en el búfer, escribiría: :NumberedLists

Solo las listas entre las líneas 10 y 20: :10,20NumberedLists

Solo la selección visual: :'<,'>NumberedLists


Para más información, ver:

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

Esto solo funciona con una versión reciente de Vim (que tiene :h v_g_CTRL-A):

  1. Bloque-seleccionar las viñetas de una lista ( *) y reemplazarlos con 0(cursor está en la primera *): Ctrl-v j j r 0.
  2. Vuelva a seleccionar el bloque anterior e incremente con el contador :gv g Ctrl-a

... y eso es :)


(Si usted quiere tener un punto después de cada número, cambiar primero paso para: Ctrl-v j j s 0 . Esc)

VanLaser
fuente
9

Seleccione visualmente las líneas y ejecute este comando de sustitución:

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

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

Para evitar tener que seleccionar las líneas, se pueden usar búsquedas hacia atrás y hacia adelante con desplazamientos para especificar el rango de sustitución de esta manera:

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

Ver :help cmdline-ranges

djjcast
fuente
2

De otra manera:

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

También podría definir operadores personalizados

Puede asignarlos a las secuencias de teclas '*y '#. Las marcas *y #no existen, por lo que no anulará ninguna funcionalidad predeterminada. La razón para elegir 'como prefijo es obtener algún tipo de mnemotecnia. Estás agregando un signo / marca delante de algunas líneas. Y generalmente para ir a una marca usas el prefijo '.

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>

También funciona desde el modo visual.
Los comandos Ex son buenos para las secuencias de comandos, pero para un uso interactivo, un operador normal es probablemente mejor, porque puede combinarlo con cualquier movimiento u objeto de texto.

Por ejemplo, puede alternar una lista prefijada con asteriscos o signos menos dentro del párrafo actual presionando '*ip. Aquí, '*es un operador y ipes el objeto de texto en el que trabaja.

Y haga lo mismo para una lista prefijada con números en las siguientes 10 líneas presionando '#10j. Aquí, '#hay otro operador y 10jes un movimiento que cubre las líneas en las que trabaja el operador.

El otro beneficio de usar un operador personalizado es que puede repetir su última edición con el comando punto.

ingrese la descripción de la imagen aquí

usuario9433424
fuente