Bagaimana cara menggabungkan semua garis bersama pola mana yang cocok?

11

Saya ingin menyatukan garis hanya untuk garis yang memiliki pola tertentu (seperti ;), namun ketika menggunakannya g/;/jtidak berfungsi seperti yang diharapkan kecuali dipanggil beberapa kali.

Misalnya konten berikut:

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

saat menggunakan: :g/;/joutputnya adalah:

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

atau :g/;/-jmemberi:

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

sama dengan: :g/;\_.\{-};/j.

Output yang saya harapkan adalah:

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

atau yang serupa, jadi semua garis yang mengandung pola digabungkan bersama.

Bagaimana ini bisa dicapai?

kenorb
sumber
3
FWIW, :g/;/jtidak berfungsi karena dilakukan dalam dua lintasan: pertama buffer dipindai, kemudian perintah diterapkan ke baris yang cocok.
romainl

Jawaban:

12

Kemungkinan penjelasan masalah

Saya pikir alasan mengapa :g/;/jtidak bekerja adalah karena :gperintah beroperasi dengan algoritma 2-pass:

  • selama pass pertama itu menandai garis yang mengandung pola ;
  • selama pass kedua beroperasi pada baris yang ditandai

Selama lintasan kedua, :ggabungkan garis 1;dengan garis 2;karena 1;ditandai selama lintasan pertama. Namun saya curiga (tidak yakin) bahwa itu tidak bergabung 1; 2;dengan 3;karena garis 2;tidak ada lagi, isinya telah digabung dengan garis 1;yang telah diproses.

Jadi :gcari baris berikutnya yang ditandai saat pass pertama ( 3;) dan gabung dengan yang berikutnya ( 4;). Setelah itu masalah berulang, itu tidak bisa bergabung 3; 4;dengan 5;karena garis 4;tidak ada lagi.

Solusi 1 (dengan vimscript)

Mungkin Anda bisa memanggil fungsi kapan pun baris yang ;ditemukan ditemukan untuk memeriksa apakah baris sebelumnya juga mengandung tanda titik koma:

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

Kemudian gunakan perintah global berikut:

:g/;/call JoinLines()

Atau tanpa fungsi:

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

Solusi 2 (tanpa skrip)

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

Setiap kali perintah global :gmenemukan pola ;itu menjalankan perintah: .,/^[^;]*$/-1j

Itu bisa dipecah seperti ini:

:g/pattern/a,bj

Dimana:

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

b dapat dirinci lebih lanjut seperti ini:

/    = 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

jadalah bentuk disingkat dari perintah Ex :joinyang seperti kebanyakan perintah Ex lainnya dapat didahului oleh suatu range.
Di sini diawali oleh rentang: .,/^[^;]*$/-1( a,b)
Suatu rentang mengikuti bentuk di a,bmana adan bbiasanya 2 nomor garis, dan memungkinkan Anda untuk beroperasi pada sekelompok garis yang jumlahnya antara adan b, bukan hanya satu.

Jadi jperintah bergabung dengan semua baris antara yang sekarang ( a) dan yang berikutnya yang tidak mengandung titik koma minus satu ( b).

Untuk informasi lebih lanjut, lihat:

:help :global
:help :join
:help :range
saginaw
sumber
1

Saya melakukan hal serupa saat bergabung dengan pencarian global dan ganti:

s /; \ n /; /

\n cocok dengan baris baru.

Untuk menemukan dan menghapus baris kosong:

s / ^ $ \ n //

Saya tidak yakin mengapa, tetapi jika ingin memasukkan baris baru yang harus Anda gunakan \r

rlh100
sumber
ssendiri akan bekerja hanya untuk satu baris, untuk menjadikannya global, Anda perlu menggunakan %s, tetapi kemudian ia akan bergabung dengan hampir semua baris, termasuk non- ;baris
kenorb
2
@kenorb Ehm tidak, saya pikir Anda dapat menggunakan :sperintah tepat untuk apa yang Anda inginkan. Saya pikir ini %s/;\n\(.*;\)\@=/;/melakukan apa yang Anda butuhkan.
Christian Brabandt