Pregunta:
Me gustaría unir líneas solo para líneas que tienen cierto patrón (como ;
), sin embargo, cuando se usa g/;/j
, no funciona como se esperaba 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/;/j
la salida es:
a
1; 2;
3; 4;
5; b
6; 7;
8; 9;
c
o :g/;/-j
da:
a 1; 2; 3; 4; 5;
b 6; 7; 8; 9;
c
similar con:: :g/;\_.\{-};/j
.
Mi resultado esperado 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?
Respuesta:
Posible explicación del problema.
Creo que la razón por la que :g/;/j
no funciona es porque el comando :g
opera 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,: :g
une a la línea 1;
con la línea 2;
porque 1;
fue marcado durante la primera pasada. Sin embargo, sospecho (no estoy seguro) que no se une a 1; 2;
con 3;
porque la línea 2;
ya no existe, su contenido se ha fusionado con la línea 1;
que ya ha sido procesado.
Entonces :g
busca la siguiente línea que se marcó durante la primera pasada ( 3;
) y la une con la siguiente ( 4;
). Después de eso, el problema se repite, no puede unirse a 3; 4;
con 5;
porque la línea 4;
ya no existe.
Solución 1 (con vimscript)
Quizás pueda llamar a una función siempre que una línea contenga ;
se encuentra para comprobar 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 función:
:g/;/if getline(line('.')-1) =~ ';' | -j | endif
Solución 2 (sin vimscript)
:g/;/.,/^[^;]*$/-1j
Siempre que el comando global :g
encuentre el patrón ;
ejecuta el comando: .,/^[^;]*$/-1j
Se puede desglosar así:
:g/pattern/a,bj
Donde :
pattern = ;
a = . = number of current line
b = /^[^;]*$/-1 = number of next line without any semicolon minus one
b
se puede desglosar aún más así:
/ = 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
j
es la forma abreviada del comando Ex :join
que, como la mayoría de los otros comandos Ex, puede ir precedido de un rango.
Aquí está precedido por el rango: .,/^[^;]*$/-1
( a,b
)
Una gama sigue la forma a,b
, donde a
y b
son generalmente 2 números de línea, y le permite operar en un grupo de líneas cuyo número está comprendido entre a
y b
, en lugar de sólo uno.
Entonces, el comando j
une todas las líneas entre la actual ( a
) y la siguiente que no contiene ningún punto y coma menos uno ( b
).
Para más información, ver:
:help :global
:help :join
:help :range