Bagaimana saya bisa menukar posisi dua file terbuka (dalam pembagian) dalam vim?

313

Asumsikan saya punya beberapa tata letak pemisahan yang acak di vim.

____________________
| one       | two  |
|           |      |
|           |______|
|           | three|
|           |      |
|___________|______|

Apakah ada cara untuk swap onedan twodan mempertahankan tata letak yang sama? Sederhana dalam contoh ini, tetapi saya mencari solusi yang akan membantu tata letak yang lebih kompleks.

MEMPERBARUI:

Saya kira saya harus lebih jelas. Contoh saya sebelumnya adalah penyederhanaan case-use yang sebenarnya. Dengan contoh aktual: teks alternatif

Bagaimana saya bisa menukar dua perpecahan itu, mempertahankan tata letak yang sama?

Memperbarui! 3+ tahun kemudian ...

Saya menempatkan solusi sgriffin dalam plugin Vim yang dapat Anda instal dengan mudah! Instal dengan pengelola plugin favorit Anda dan cobalah: WindowSwap.vim

demo kecil

kami
sumber
14
Jika Anda seperti saya dua menit yang lalu bertanya-tanya "apakah saya benar - benar membutuhkan plugin untuk ini?", Berhenti ragu-ragu dan instal. Pada dasarnya hanya ada satu perintah: <leader> ww yang Anda tekan dua kali, sekali di setiap jendela untuk bertukar. Ini sangat mudah dan Anda akan berjalan dalam 30 detik.
mdup

Jawaban:

227

Agak terlambat ke pos, tetapi menemukan ini mencari sesuatu yang lain. Saya menulis dua fungsi beberapa waktu lalu untuk menandai jendela dan kemudian menukar buffer antar windows. Ini sepertinya yang Anda minta.

Hanya menampar ini di .vimrc Anda dan memetakan fungsi yang Anda inginkan:

function! MarkWindowSwap()
    let g:markedWinNum = winnr()
endfunction

function! DoWindowSwap()
    "Mark destination
    let curNum = winnr()
    let curBuf = bufnr( "%" )
    exe g:markedWinNum . "wincmd w"
    "Switch to source and shuffle dest->source
    let markedBuf = bufnr( "%" )
    "Hide and open so that we aren't prompted and keep history
    exe 'hide buf' curBuf
    "Switch to dest and shuffle source->dest
    exe curNum . "wincmd w"
    "Hide and open so that we aren't prompted and keep history
    exe 'hide buf' markedBuf 
endfunction

nmap <silent> <leader>mw :call MarkWindowSwap()<CR>
nmap <silent> <leader>pw :call DoWindowSwap()<CR>

Untuk menggunakan (dengan asumsi pemimpin peta Anda disetel ke \) Anda akan:

  1. Pindah ke jendela untuk menandai swap melalui gerakan ctrl-w
  2. Ketik \ mw
  3. Pindah ke jendela yang ingin ditukar
  4. Ketik \ pw

Voila! Buffer bertukar tanpa mengacaukan tata letak jendela Anda!

sgriffin
sumber
17
Saya berharap saya bisa membuat Anda marah sepuluh kali! Saya memang harus menggunakan noremappemetaan untuk membuatnya bekerja. Tidak yakin mengapa, tetapi mudah-mudahan itu membantu siapa saja yang menemukan ini nanti. : D
wes
6
Saya memasukkan solusi Anda ke plugin Vim pertama saya: WindowSwap.vim . Saya menautkan pertanyaan ini dan jawaban Anda di readme: D
wes
Saya memasukkan solusi sgriffin ke .vimrc saya beberapa tahun yang lalu, dan saya sedang membersihkannya, dan memutuskan untuk memindahkan semuanya ke sebuah plugin. Saya melakukan ekstraksi, dan untuk menguji bahwa itu masih berfungsi sebagai bundel, saya membelah jendela berkali-kali, dan menjalankan beberapa 0r!figlet one[dua, tiga, dll], lalu mengujinya. Sebelum melangkah lebih jauh, saya memeriksa github, menemukan plugin Anda (wes '), dengan swap jendela figlet animasi, dan tautan ke jawaban yang sama (yang saya miliki sebagai komentar di .vimrc). Saya merasa sudah membuat dan mengunggahnya, dan kemudian melupakannya. Ngomong-ngomong, pekerjaan bagus! Menghemat saya kerja :)
Gary Fixler
293

Dimulai dengan ini:

____________________
| one       | two  |
|           |      |
|           |______|
|           | three|
|           |      |
|___________|______|

Jadikan 'tiga' jendela aktif, lalu jalankan perintah ctrl+ w J. Ini menggerakkan jendela saat ini untuk mengisi bagian bawah layar, memberi Anda:

____________________
| one       | two  |
|           |      |
|___________|______|
| three            |
|                  |
|__________________|

Sekarang jadikan 'satu' atau 'dua' jendela aktif, lalu jalankan perintah ctrl+ w r. Ini 'memutar' jendela di baris saat ini, memberi Anda:

____________________
| two       | one  |
|           |      |
|___________|______|
| three            |
|                  |
|__________________|

Sekarang buat 'dua' jendela aktif, dan keluarkan perintah ctrl+ w H. Ini menggerakkan jendela saat ini untuk mengisi bagian kiri layar, memberi Anda:

____________________
| two       | one  |
|           |      |
|           |______|
|           | three|
|           |      |
|___________|______|

Seperti yang Anda lihat, manuvernya sedikit acak-acakan. Dengan 3 jendela, ini agak seperti salah satu teka-teki 'permainan ubin'. Saya tidak menyarankan mencoba ini jika Anda memiliki 4 jendela atau lebih - Anda lebih baik menutupnya kemudian membukanya lagi di posisi yang diinginkan.

Saya membuat screencast mendemonstrasikan bagaimana cara bekerja dengan windows split di Vim .

nelstrom
sumber
2
Anda bekerja lebih keras membuat screencast, nelstrom, tapi itu tidak benar-benar apa yang saya cari. Saya dapat bekerja dengan split dengan perintah gerakan dasar, tetapi yang saya ingin tahu adalah jika ada cara untuk bertukar lokasi yang terbagi dalam tata letak kompleksitas yang berubah-ubah.
wes
95
Untuk orang-orang yang menyukai saya, hanya ingin belajar cara menukar dua jendela: ctrl-w rbekerja seperti pesona. Terima kasih atas tipnya! Ini +1 saya.
ereOn
Saya membarui \mw/ \pwdan yang ini dan mencoba menggunakan keduanya selama seminggu masing-masing. Saya menemukan menggunakan solusi "asli" ini bekerja paling baik, karena saya tidak harus terus memasang plugin di selusin instalasi vim yang saya miliki di server dan mesin jarak jauh dan desktop, laptop, tablet, dan semua perangkat lainnya. TKI, mempelajari perintah-perintah Asli ini (mis. ctrl-w r) Adalah benar-benar semua yang Anda butuhkan untuk mengkomit otot memori dan dilakukan.
eduncan911
96

Lihatlah :h ctrl-w_ctrl-xdan / atau :h ctrl-w_ctrl-r. Perintah-perintah ini memungkinkan Anda untuk bertukar atau memutar jendela di tata letak saat ini.

Sunting: Sebenarnya, ini tidak akan berfungsi dalam situasi ini karena hanya akan bertukar di kolom atau baris saat ini. Anda bisa pergi ke masing-masing windows dan memilih buffer target, tapi itu cukup bertele-tele.

Randy Morris
sumber
30

Randy benar karena CTRL-W xtidak ingin menukar windows yang tidak ada di kolom / baris yang sama.

Saya telah menemukan bahwa CTRL-W HJKLtombol-tombol ini paling berguna ketika memanipulasi windows. Mereka akan memaksa jendela Anda saat ini keluar dari lokasi saat ini dan mengatakannya untuk menempati seluruh tepi yang ditunjukkan oleh arah tombol yang Anda tekan. Lihat :help window-movinguntuk lebih jelasnya.

Untuk contoh Anda di atas, jika Anda mulai di jendela "satu", ini melakukan apa yang Anda inginkan:

CTRL-W K   # moves window "one" to be topmost,
           #   stacking "one", "two", "three" top to bottom
CTRL-W j   # moves cursor to window "two"
CTRL-W H   # moves window "two" to be leftmost,
           #   leaving "one" and "three" split at right

Untuk kenyamanan, Anda dapat menetapkan urutan yang Anda butuhkan untuk pemetaan kunci (lihat :help mapping).

Mike Seplowitz
sumber
10

Saya memiliki versi yang sedikit ditingkatkan dari solusi sgriffin, Anda dapat menukar windows tanpa menggunakan dua perintah, tetapi dengan perintah HJKL yang intuitif.

Jadi begini caranya:

function! MarkWindowSwap()
    " marked window number
    let g:markedWinNum = winnr()
    let g:markedBufNum = bufnr("%")
endfunction

function! DoWindowSwap()
    let curWinNum = winnr()
    let curBufNum = bufnr("%")
    " Switch focus to marked window
    exe g:markedWinNum . "wincmd w"

    " Load current buffer on marked window
    exe 'hide buf' curBufNum

    " Switch focus to current window
    exe curWinNum . "wincmd w"

    " Load marked buffer on current window
    exe 'hide buf' g:markedBufNum
endfunction

nnoremap H :call MarkWindowSwap()<CR> <C-w>h :call DoWindowSwap()<CR>
nnoremap J :call MarkWindowSwap()<CR> <C-w>j :call DoWindowSwap()<CR>
nnoremap K :call MarkWindowSwap()<CR> <C-w>k :call DoWindowSwap()<CR>
nnoremap L :call MarkWindowSwap()<CR> <C-w>l :call DoWindowSwap()<CR>

Cobalah untuk memindahkan jendela Anda dengan menggunakan modal HJKL di simpul normal, itu sangat keren :)

Periksa pensil
sumber
3

Membangun berat pada jawaban @ sgriffin ini, inilah sesuatu yang bahkan lebih dekat dengan apa yang Anda minta:

function! MarkWindow()
        let g:markedWinNum = winnr()
endfunction

function! SwapBufferWithMarkedWindow()
        " Capture current window and buffer
        let curWinNum = winnr()
        let curBufNum = bufnr("%")

        " Switch to marked window, mark buffer, and open current buffer
        execute g:markedWinNum . "wincmd w"
        let markedBufNum = bufnr("%")
        execute "hide buf" curBufNum

        " Switch back to current window and open marked buffer
        execute curWinNum . "wincmd w"
        execute "hide buf" markedBufNum
endfunction

function! CloseMarkedWindow()
        " Capture current window
        let curWinNum = winnr()

        " Switch to marked window and close it, then switch back to current window
        execute g:markedWinNum . "wincmd w"
        execute "hide close"
        execute "wincmd p"
endfunction

function! MoveWindowLeft()
        call MarkWindow()
        execute "wincmd h"
        if winnr() == g:markedWinNum
                execute "wincmd H"
        else
                let g:markedWinNum += 1
                execute "wincmd s"
                execute g:markedWinNum . "wincmd w"
                execute "wincmd h"
                call SwapBufferWithMarkedWindow()
                call CloseMarkedWindow()
        endif
endfunction

function! MoveWindowDown()
        call MarkWindow()
        execute "wincmd j"
        if winnr() == g:markedWinNum
                execute "wincmd J"
        else
                execute "wincmd v"
                execute g:markedWinNum . "wincmd w"
                execute "wincmd j"
                call SwapBufferWithMarkedWindow()
                call CloseMarkedWindow()
        endif
endfunction

function! MoveWindowUp()
        call MarkWindow()
        execute "wincmd k"
        if winnr() == g:markedWinNum
                execute "wincmd K"
        else
                let g:markedWinNum += 1
                execute "wincmd v"
                execute g:markedWinNum . "wincmd w"
                execute "wincmd k"
                call SwapBufferWithMarkedWindow()
                call CloseMarkedWindow()
        endif
endfunction

function! MoveWindowRight()
        call MarkWindow()
        execute "wincmd l"
        if winnr() == g:markedWinNum
                execute "wincmd L"
        else
                execute "wincmd s"
                execute g:markedWinNum . "wincmd w"
                execute "wincmd l"
                call SwapBufferWithMarkedWindow()
                call CloseMarkedWindow()
        endif
endfunction

nnoremap <silent> <Leader>wm :call MarkWindow()<CR>
nnoremap <silent> <Leader>ws :call SwapBufferWithMarkedWindow()<CR>
nnoremap <silent> <Leader>wh :call MoveWindowLeft()<CR>
nnoremap <silent> <Leader>wj :call MoveWindowDown()<CR>
nnoremap <silent> <Leader>wk :call MoveWindowUp()<CR>
nnoremap <silent> <Leader>wl :call MoveWindowRight()<CR>

Harap beri tahu saya jika perilaku tersebut tidak sesuai dengan harapan Anda.

Geoff Catlin
sumber
2

Juga berdasarkan solusi sgriffin, buka jendela yang ingin Anda tukarkan, tekan CTRL-w m, buka jendela yang ingin Anda tukarkan dan tekan CTRL-w mlagi.

CTRL-w m adalah pilihan mnemonik yang buruk, jadi jika ada yang membuat yang lebih baik, silakan edit ini.

Juga, saya ingin menerima umpan balik dari skrip alias "Jendela bertanda. Harap ulangi sesuai target", namun karena menjadi vimscript noob, saya tidak tahu bagaimana melakukannya.

Semua yang dikatakan, skrip bekerja dengan baik apa adanya

" <CTRL>-w m : mark first window
" <CTRL>-w m : swap with that window
let s:markedWinNum = -1

function! MarkWindowSwap()
    let s:markedWinNum = winnr()
endfunction

function! DoWindowSwap()
    "Mark destination
    let curNum = winnr()
    let curBuf = bufnr( "%" )
    exe s:markedWinNum . "wincmd w"
    "Switch to source and shuffle dest->source
    let markedBuf = bufnr( "%" )
    "Hide and open so that we aren't prompted and keep history
    exe 'hide buf' curBuf
    "Switch to dest and shuffle source->dest
    exe curNum . "wincmd w"
    "Hide and open so that we aren't prompted and keep history
    exe 'hide buf' markedBuf
endfunction

function! WindowSwapping()
    if s:markedWinNum == -1
        call MarkWindowSwap()
    else
        call DoWindowSwap()
        let s:markedWinNum = -1
    endif
endfunction

nnoremap <C-w>m :call WindowSwapping()<CR>
tpo
sumber
1

Pendekatan berikut mungkin nyaman jika fungsi tidak tersedia karena beberapa alasan (karena itu bukan vim Anda).

Gunakan :buffersperintah untuk mengetahui id buffer terbuka, navigasikan ke jendela yang diinginkan dan gunakan perintah seperti :b 5untuk membuka buffer (buffer number 5 dalam hal ini). Ulangi dua kali dan isi windows ditukar.

Saya "menemukan" metode ini setelah beberapa upaya untuk menghafal ctrl-w-somethingurutan bahkan untuk tata letak yang sangat sederhana seperti satu-dua-tiga dalam pertanyaan awal.

lesnik
sumber
1

Benar-benar dingin, namun usulan saya untuk pemetaan adalah dengan menggunakan ^ W ^ J bukannya J (karena semua hjkl sudah memiliki makna), ditambah juga aku akan menarik di buffer baru, karena pada saat Anda ingin menukar sekitar Anda mungkin tidak ingin melanjutkan mengedit buffer yang sudah ada. Ini dia:

function! MarkSwapAway()
    " marked window number
    let g:markedOldWinNum = winnr()
    let g:markedOldBufNum = bufnr("%")
endfunction
function! DoWindowToss()
    let newWinNum = winnr()
    let newBufNum = bufnr("%")
    " Switch focus to marked window
    exe g:markedOldWinNum . "wincmd w"
    " Load current buffer on marked window
    exe 'hide buf' newBufNum
    " Switch focus to current window
    exe newWinNum . "wincmd w"
    " Load marked buffer on current window
    exe 'hide buf' g:markedOldBufNum
    " …and come back to the new one
    exe g:markedOldWinNum . "wincmd w"
endfunction
nnoremap <C-w><C-h> :call MarkSwapAway()<CR> <C-w>h :call DoWindowToss()<CR>
nnoremap <C-w><C-j> :call MarkSwapAway()<CR> <C-w>j :call DoWindowToss()<CR>
nnoremap <C-w><C-k> :call MarkSwapAway()<CR> <C-w>k :call DoWindowToss()<CR>
nnoremap <C-w><C-l> :call MarkSwapAway()<CR> <C-w>l :call DoWindowToss()<CR>
rking
sumber
1

Semua jawaban di atas sangat bagus, sayangnya solusi ini tidak bekerja dengan baik jika dikombinasikan dengan QuickFix atau LocationList windows (saya mengalami masalah saat mencoba mendapatkan buffer pesan kesalahan Ale untuk bekerja dengan ini).

Larutan

Karena itu saya menambahkan baris kode tambahan untuk menutup semua jendela ini sebelum melakukan swap.

exe ':windo if &buftype == "quickfix" || &buftype == "locationlist" | lclose | endif'

Kode total terlihat seperti;

" Making swapping windows easy
function! SwapWindowBuffers()
    exe ':windo if &buftype == "quickfix" || &buftype == "locationlist" | lclose | endif'
    if !exists("g:markedWinNum")
        " set window marked for swap
        let g:markedWinNum = winnr()
        :echo "window marked for swap"
    else
        " mark destination
        let curNum = winnr()
        let curBuf = bufnr( "%" )
        if g:markedWinNum == curNum
            :echo "window unmarked for swap"
        else
            exe g:markedWinNum . "wincmd w"
            " switch to source and shuffle dest->source
            let markedBuf = bufnr( "%" )
            " hide and open so that we aren't prompted and keep history
            exe 'hide buf' curBuf
            " switch to dest and shuffle source->dest
            exe curNum . "wincmd w"
            " hide and open so that we aren't prompted and keep history
            exe 'hide buf' markedBuf
            :echo "windows swapped"
        endif
        " unset window marked for swap
        unlet g:markedWinNum
    endif
endfunction

nmap <silent> <leader>mw :call SwapWindowBuffers()<CR>

Kredit untuk fungsi swap Brandon Orther

Mengapa itu dibutuhkan?

Alasan fungsi swap tidak berfungsi dengan baik tanpa menghapus semua jendela QuickFix (QF) dan LocationList (LL) terlebih dahulu adalah karena jika induk dari buffer QF / LL get's hidden (dan tidak ditampilkan di jendela), QF / LL jendela digabungkan dengan itu dihapus. Ini bukan masalah dalam dirinya sendiri tetapi ketika jendela menyembunyikan semua nomor jendela dipindahkan dan swap kacau karena nomor yang disimpan dari jendela yang ditandai pertama tidak (berpotensi) tidak ada lagi.

Untuk menempatkan perspektif ini:

Tanda jendela pertama

____________________
| one              | -> winnr = 1    marked first    g:markedWinNum=1
|                  | -> bufnr = 1
|__________________|
| two (QF window   | -> winnr = 2
| coupled to one   |
|__________________|
| three            | -> winnr = 3
|                  | -> bufnr = 2
|__________________|

Tanda jendela kedua

____________________
| one              | -> winnr = 1                    g:markedWinNum=1
|                  | -> bufnr = 1
|__________________|
| two (QF window   | -> winnr = 2
| coupled to one)  |
|__________________|
| three            | -> winnr = 3    marked second    curNum=3
|                  | -> bufnr = 2                     curBuf=2
|__________________|

Switch buffer pertama, window satu diisi dengan buffer window tiga. Dengan demikian jendela QF dihapus karena tidak memiliki jendela induk lagi. Ini mengatur ulang nomor windows. Perhatikan bahwa curNum (jumlah jendela yang dipilih kedua) menunjuk ke jendela yang tidak ada lagi.

____________________
| three            | -> winnr = 1                    g:markedWinNum=1
|                  | -> bufnr = 2
|__________________|
| three            | -> winnr = 2                     curNum=3
|                  | -> bufnr = 2                     curBuf=2
|__________________|

Jadi ketika mengganti buffer kedua, ia mencoba untuk memilih jendela curNum, yang tidak ada lagi. Jadi itu menciptakannya dan mengaktifkan buffer, menghasilkan satu jendela yang tidak diinginkan untuk tetap diam.

____________________
| three            | -> winnr = 1                    g:markedWinNum=1
|                  | -> bufnr = 2
|__________________|
| three            | -> winnr = 2
|                  | -> bufnr = 2
|__________________|
| one              | -> winnr = 3                     curNum=3
|                  | -> bufnr = 1                     curBuf=2
|__________________|
Tom Stock
sumber
0

Pendekatan mark-window-then-swap-buffer yang serupa, tetapi juga memungkinkan Anda menggunakan kembali swapping terakhir.

function! MarkWindowSwap()
    unlet! g:markedWin1
    unlet! g:markedWin2
    let g:markedWin1 = winnr()
endfunction

function! DoWindowSwap()
    if exists('g:markedWin1')
        if !exists('g:markedWin2')
            let g:markedWin2 = winnr()
        endif
        let l:curWin = winnr()
        let l:bufWin1 = winbufnr(g:markedWin1)
        let l:bufWin2 = winbufnr(g:markedWin2)
        exec g:markedWin2 . 'wincmd w'
        exec ':b '.l:bufWin1
        exec g:markedWin1 . 'wincmd w'
        exec ':b '.l:bufWin2
        exec l:curWin . 'wincmd w'
    endif
endfunction

nnoremap ,v :call DoWindowSwap()<CR>
nnoremap ,z :call MarkWindowSwap()<CR>
qeatzy
sumber
Karena saya sudah ada set hiddendi .vimrc, tidak perlu menyembunyikan buffer secara manual.
qeatzy
-5

Anda juga bisa menggunakan tiling window manager seperti X-monad

William
sumber
Meskipun benar jawaban ini tidak terkait dengan pertanyaan OP. Bisa menggunakan vim di mesin Mac atau Windows. Vim tersedia di tablet dan bahkan ponsel tidak ada yang mampu menukar window manager Anda.
nsfyn55