Como unir todas as linhas que padrão correspondente?

11

Eu gostaria de unir linhas apenas para linhas que possuem certo padrão (como ;), no entanto, quando o uso g/;/jnão funciona como esperado, a menos que seja chamado algumas vezes.

Por exemplo, o seguinte conteúdo:

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

ao usar: :g/;/ja saída é:

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

ou :g/;/-jdá:

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

semelhante com: :g/;\_.\{-};/j.

Minha saída esperada é:

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

ou algo semelhante, para que todas as linhas que contenham o padrão sejam unidas.

Como isso pode ser alcançado?

kenorb
fonte
3
FWIW, :g/;/jnão funciona porque é feito em duas passagens: primeiro o buffer é verificado, depois o comando é aplicado às linhas correspondentes.
Romainl

Respostas:

12

Possível explicação do problema

Eu acho que a razão pela qual :g/;/jnão funciona é porque o :gcomando opera com um algoritmo de 2 passagens:

  • durante a primeira passagem, ele marca as linhas que contêm o padrão ;
  • durante o segundo passe, opera nas linhas marcadas

Durante a segunda passagem, :gjunta a linha 1;com a linha 2;porque 1;foi marcada durante a primeira passagem. No entanto eu suspeito que (não tenho certeza) que não se juntar 1; 2;com 3;porque a linha 2;não existe mais, o seu conteúdo tem sido fundida com a linha 1;que já foi processado.

Então, :gprocura a próxima linha que foi marcada durante a primeira passagem ( 3;) e a une à seguinte ( 4;). Depois que se repete problema, ele não pode se juntar 3; 4;com 5;porque a linha 4;não existe mais.

Solução 1 (com vimscript)

Talvez você possa chamar uma função sempre que uma linha contendo ;for encontrada para verificar se a linha anterior também contém um ponto e vírgula:

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

Em seguida, use o seguinte comando global:

:g/;/call JoinLines()

Ou sem uma função:

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

Solução 2 (sem vimscript)

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

Sempre que o comando global :gencontra o padrão, ;ele executa o comando: .,/^[^;]*$/-1j

Pode ser dividido assim:

:g/pattern/a,bj

Onde :

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

b pode ser dividido ainda mais assim:

/    = 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é a forma abreviada do comando Ex :joinque, como a maioria dos outros comandos Ex, pode ser precedido por um intervalo.
Aqui, é precedido pelo intervalo: .,/^[^;]*$/-1( a,b)
Um intervalo segue a forma a,bonde ae bgeralmente são 2 números de linha e permite operar em um grupo de linhas cujo número está entre ae b, em vez de apenas um.

Portanto, o jcomando une todas as linhas entre a atual ( a) e a próxima que não contém ponto e vírgula menos uma ( b).

Para mais informações, veja:

:help :global
:help :join
:help :range
saginaw
fonte
1

Faço associações semelhantes o tempo todo com uma pesquisa global e substituo:

s /; \ n /; /

\n corresponde à nova linha.

Para localizar e excluir linhas em branco:

s / ^ $ \ n //

Não sei por que, mas se você quiser inserir uma nova linha, precisará usar \r

rlh100
fonte
ssó irá funcionar apenas para uma linha, para torná-lo global, você precisa usar %s, mas, em seguida, ele vai juntar-se quase todas as linhas, incluindo os não ;linhas
kenorb
2
@kenorb Ehm não, acho que você pode usar o :scomando exatamente para o que deseja. Eu acho que isso %s/;\n\(.*;\)\@=/;/faz o que você precisa.
Christian Brabandt