Gabungkan blok dengan interleaving lines

15

Apakah ada cara khusus untuk menggabungkan dua blok teks dengan menyisipkan baris, seperti meneruskan dari ini:

a1
a2
a3
a4
  b1
  b2
  b3
  b4

untuk itu:

a1
  b1
a2
  b2
a3
  b3
a4
  b4

dalam beberapa perintah?

EDIT : Saya sangat suka solusi Sato Katsura , berikut ini cara saya mengimplementasikannya:

function! Interleave()
    " retrieve last selected area position and size
    let start = line(".")
    execute "normal! gvo\<esc>"
    let end = line(".")
    let [start, end] = sort([start, end], "n")
    let size = (end - start + 1) / 2
    " and interleave!
    for i in range(size - 1)
        execute (start + size + i). 'm' .(start + 2 * i)
    endfor
endfunction

" Select your two contiguous, same-sized blocks, and use it to Interleave ;)
vnoremap <pickYourMap> <esc>:call Interleave()<CR>
iago-lito
sumber
Sekarang saya ingin tahu - apa kasus penggunaan Anda? Apakah Anda memblokir penggantian nama teks film untuk musim TV?
VanLaser
@ VanLaser Haha, saya tidak. Sebagian besar, saya parsing output dari suatu program, yang saya perlu memeriksa konsistensi mengenai urutan penciptaan / kemudian menunda membaca objek. Blok interleaving memudahkan mencocokkan garis yang sesuai dalam blok output yang tertunda. Saya juga kadang-kadang perlu menyisipkan baris kode dengan instruksi berulang atau serupa untuk logging atau benchmarking. Membuat perintah-perintah itu mudah dengan makro, kemudian menyisipkannya dengan kode aktual sekarang hanya dengan beberapa penekanan tombol saja dengan fungsi ini, yang terasa luar biasa :)
iago-lito
1
@ lago-lito - terima kasih atas jawabannya! Ya, Vim cukup serbaguna :) Ekspresi Anda "eye-parsing" membuat saya juga berpikir di scroll-bindingdua jendela Vim.
VanLaser
Saya mengalami masalah dalam menggunakan ini, bagaimana Anda memilih dua blok berturut-turut? Apakah mereka perlu berdekatan?
cbcoutinho
@cbcoutinho Ya mereka punya :) Saya tidak yakin Anda bisa memilih keduanya jika tidak. Pada contoh yang saya perlihatkan, saya meletakkan kursor saya pada (katakanlah) b1, kemudian saya menekan vipuntuk memilih seluruh potongan, lalu ,ityang mana adalah <map-I've-Picked>. Apakah itu tidak bekerja di pihak Anda?
iago-lito

Jawaban:

8

Tidak ada cara khusus untuk melakukan itu (sejauh yang saya tahu), tapi ya, itu bisa dilakukan dengan beberapa perintah:

function! Interleave(start, end, where)
    if a:start < a:where
        for i in range(0, a:end - a:start)
            execute a:start . 'm' . (a:where + i)
        endfor
    else
        for i in range(a:end - a:start, 0, -1)
            execute a:end . 'm' . (a:where + i)
        endfor
    endif
endfunction

Anda dapat menjalankannya dengan :call Interleave(5, 8, 1). Parameter pertama adalah baris pertama yang akan dipindahkan, yang kedua adalah baris terakhir, dan yang ketiga tempat untuk memindahkannya. Anda mungkin ingin mengaktifkan nomor baris untuk melihat apa yang Anda lakukan ( :set number).

Ini mengasumsikan blok tidak tumpang tindih. Lihat :help :movedan :help range()untuk memahami bagaimana fungsi ini bekerja.

Mungkin ada cara yang lebih baik untuk mengambil dua blok. Ada plugin mengambang di sekitar yang seharusnya memungkinkan Anda bertukar dua blok. Saya tidak dapat mengingat nama plugin, tetapi penulis (mungkin Dr. Chip yang terkenal?) Telah lebih memikirkan menemukan antarmuka daripada yang saya lakukan. :)

Sato Katsura
sumber
Manis! Saya hanya perlu dua argumen karena kedua blok bersebelahan dan memiliki ukuran yang sama: startdan size. Dengan fungsi homebrew yang mengambil nilai-nilai itu dari pilihan, itu akan menjadi sempurna. Saya sedang mengerjakannya. :)
iago-lito
Tautan silang yang menarik ? ;)
iago-lito
13

Berikut adalah alternatif lain:

:g/^a/+4t .
:+,+5d 

Pertama salin baris yang 4 baris di bawah ini ke setelah baris saat ini ( :h :t) kemudian hapus berturut-turut b baris ( :h :d)

Perintah ini lebih baik lagi:

 :g/^a//^\s*b/m .

Yang berarti, untuk setiap baris dimulai dengan cari baris berikutnya dimulai dengan 'b' dan pindahkan ke bawah garis saat ini.

Christian Brabandt
sumber
1
Saya mendapat "E16: Range tidak valid" pada perintah kedua. Saya mencoba .+,$dsebagai gantinya, dan itu berhasil (seperti yang saya lakukan .+,.+4d).
Peter Lewerin
Tidak yakin mengapa ini terjadi
Christian Brabandt
1
tidak, tidak. Baca: h: range, Anda selalu dapat menggunakan penomoran langsung alih-alih pencarian regex
Christian Brabandt
2
@ iago-lito Trik kedua selalu berhasil, tetapi Anda harus beralih /^\s*bke yang lain :range. misal: pilih blok 1, jalankan'<,'>g/^/'>+1m.
dedowsdi
1
@ iago-lito Ini pada dasarnya sama dengan jawaban Christian. Tidak ada hardcoded jika Anda memilih secara visual blok 1, '>+1menandai awal blok 2.
dedowsdi
3

Jika Anda ingin bersenang-senang dengan makro dan tanda, Anda dapat mencoba sesuatu seperti ini:

  • Pertama menempatkan tanda (di sini a) pada baris yang berisi a1denganma

  • Pergi ke baris yang berisi b1dan tandai denganmb

  • Mulai merekam makro di register yang Anda inginkan (di sini register q) denganqq

  • Masukkan mengikuti di makro Anda: ddmb'apjma'b

  • Hentikan merekam makro dengan q

  • Mainkan sebanyak yang diperlukan dengan di X@qmana Xjumlah waktu untuk memainkannya.

Untuk detail makro:

dd mb 'a p j ma 'b
 |  |  | | |    |
 |  |  | | |    go back to line marked `b`
 |  |  | | |
 |  |  | | move of one line and replace the mark `a`
 |  |  | insert the deleted line under the line marked `a`
 |  |  go to line marked `a`
 |  mark the future line to move with `b`
 delete the line to move

Sunting Seperti yang disebutkan lago-lito dalam komentar, metode ini akan menimpa tanda dan buffer.

  • Untuk tanda saya tidak berpikir itu masalah nyata: Saya jarang menggunakan semua 26 tanda dalam buffer dan saya pikir sebagian besar waktu akan menemukan 2 tanda gratis.

  • Untuk buffer dimungkinkan untuk menyimpannya dalam variabel sementara: Sebelum merekam penggunaan makro :let saveReg=getreg('"')untuk menyimpan register dan setelah tindakan selesai digunakan :call setreg('"', saveReg)untuk mendapatkan register kembali ke keadaan sebelumnya.

Lagi pula saya harus mengakui bahwa solusi ini hanya solusi cepat dan tidak optimal: Menurut pendapat saya jawaban Christan adalah yang terbaik dan harus diterima karena tidak mengacaukan buffer dan tanda, tidak memaksa pengguna untuk membuat fungsi dan menunjukkan kekuatan perintah global.

statox
sumber
Menarik. Sayangnya, ini menimpa konten tanda dan register, yang mungkin saya gunakan;)
iago-lito
@ lago-lito: memang itu menimpa tanda dan buffer. Untuk nilai saya tidak pernah menggunakan semua 26 nilai di buffer saya jadi saya tidak berpikir itu benar-benar masalah. Untuk buffer itu mungkin lebih merupakan masalah, saya pikir Anda sering dapat menemukan buffer yang tidak digunakan atau jika Anda benar-benar tidak bisa, gunakan variabel sementara dan fungsi getreg()dan setreg()untuk menyimpan buffer Anda. Tetapi saya setuju bahwa ini bukan solusi optimal :-)
statox
1

Saya baru saja melihat pertanyaan serupa lainnya dan solusinya terdiri dari:

Lompat ke tengah plus satu:

Mj

Dan lari:

:,$g/./exe 'm' 2*line('.')-line('$')-1
SergioAraujo
sumber
Menarik :) Berhati-hatilah karena ini akan menyisipkan seluruh file Anda, dan tidak hanya paragraf yang dipilih!
iago-lito