¿Cómo unir todas las líneas juntas que patrón coincidente?

11

Me gustaría unir líneas solo para líneas que tienen cierto patrón (como ;), sin embargo, cuando g/;/jse usa no funciona como se espera a menos que se llame un par de veces.

Por ejemplo el siguiente contenido:

a
1;
2;
3;
4;
5;
b
6;
7;
8;
9;
c

cuando se usa: :g/;/jla salida es:

a
1; 2;
3; 4;
5; b
6; 7;
8; 9;
c

o :g/;/-jda:

a 1; 2; 3; 4; 5;
b 6; 7; 8; 9;
c

similar con: :g/;\_.\{-};/j.

Mi salida esperada es:

a 
1; 2; 3; 4; 5;
b
6; 7; 8; 9;
c

o algo similar, por lo que todas las líneas que contienen el patrón se unen.

¿Cómo se puede lograr esto?

kenorb
fuente
3
FWIW, :g/;/jno funciona porque se realiza en dos pasadas: primero se escanea el búfer, luego se aplica el comando a las líneas coincidentes.
romainl

Respuestas:

12

Posible explicación del problema.

Creo que la razón por la :g/;/jque no funciona es porque el :gcomando funciona con un algoritmo de 2 pasos:

  • durante la primera pasada marca las líneas que contienen el patrón ;
  • durante la segunda pasada opera en las líneas marcadas

Durante la segunda pasada, :gune la línea 1;con la línea 2;porque 1;se marcó durante la primera pasada. Sin embargo, sospecho (no estoy seguro) que no se une 1; 2;con 3;ya que la línea 2;ya no existe, su contenido se ha fusionado con la línea 1;que ya ha sido procesado.

Entonces :gbusca la siguiente línea que se marcó durante el primer pase ( 3;) y la une con la siguiente ( 4;). Después de eso se repite el problema, no pueden unirse 3; 4;con 5;porque la línea 4;no existe más.

Solución 1 (con vimscript)

Tal vez podría llamar a una función siempre que ;se encuentre una línea que contenga para verificar si la línea anterior también contiene un punto y coma:

function! JoinLines()
    if getline(line('.')-1) =~ ';'
        .-1join
    endif
endfunction

Luego use el siguiente comando global:

:g/;/call JoinLines()

O sin una función:

:g/;/if getline(line('.')-1) =~ ';' | -j | endif

Solución 2 (sin vimscript)

:g/;/.,/^[^;]*$/-1j

Cada vez que el comando global :gencuentra el patrón ;, ejecuta el comando: .,/^[^;]*$/-1j

Se puede desglosar así:

:g/pattern/a,bj

Dónde :

pattern = ;
a       = .           = number of current line
b       = /^[^;]*$/-1 = number of next line without any semicolon minus one

b puede desglosarse más de esta manera:

/    = look for the number of the next line matching the following pattern
^    = a beginning of line
[^;] = then any character except a semicolon
 *   = the last character can be repeated 0 or more times
 $   = an end of line
 /   = end of pattern
 -1  = removes one to the number you just got

jes la forma abreviada del comando Ex :joinque, como la mayoría de los otros comandos Ex, puede ir precedida de un rango.
Aquí está precedido por el rango: .,/^[^;]*$/-1( a,b)
Un rango sigue la forma a,bdonde ay bgeneralmente son números de 2 líneas, y le permite operar en un grupo de líneas cuyo número está entre ay b, en lugar de solo uno.

Entonces, el jcomando une todas las líneas entre la actual ( a) y la siguiente que no contiene punto y coma menos uno ( b).

Para más información, ver:

:help :global
:help :join
:help :range
Saginaw
fuente
1

Hago uniones similares todo el tiempo con una búsqueda global y reemplazo:

s /; \ n /; /

\n coincide con nueva línea.

Para buscar y eliminar líneas en blanco:

s / ^ $ \ n //

No estoy seguro de por qué, pero si desea insertar una nueva línea, debe usar \r

rlh100
fuente
ssolo funcionará para una línea, para que sea global, debe usarla %s, pero luego unirá casi todas las líneas, incluidas las que no son ;líneas
kenorb
2
@kenorb Ehm no, creo que puedes usar el :scomando exactamente para lo que quieras. Creo que esto %s/;\n\(.*;\)\@=/;/hace lo que necesitas.
Christian Brabandt el