vimで2つの開いているファイルの位置を(スプリットで)入れ替えるにはどうすればよいですか?


313

vimで分割の任意のレイアウトを持っていると仮定します。

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

交換する方法があるonetwo同じレイアウトを維持するとは?この例では簡単ですが、より複雑なレイアウトに役立つソリューションを探しています。

更新:

もっと明確にすべきだと思います。前の例は、実際のユースケースを単純化したものです。実際のインスタンスで: 代替テキスト

同じレイアウトを維持しながら、これらの分割の2つを交換するにはどうすればよいですか?

更新!3年以上後...

sgriffinのソリューションを、簡単にインストールできるVimプラグインに入れました!お気に入りのプラグインマネージャーを使用してインストールし、試してみてください:WindowSwap.vim

少しデモ


14
2分前に「本当にプラグイン必要なのか?」と思った場合は、迷わずにインストールしてください。コマンドは基本的に1つしかありません。<leader> wwを2回押すと、各ウィンドウで1回スワップします。これは非常に簡単で、30秒で実行されます。
mdup

回答:


227

投稿に少し遅れましたが、何か他のものを探しているのに出くわしました。ウィンドウをマークし、ウィンドウ間でバッファをスワップするために、以前は2つの関数を書きました。これはあなたが求めているもののようです。

これらを.vimrcで平手打ちし、関数が適切と思われるようにマッピングします。

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>

使用するには(マップリーダーが\に設定されている場合)、次のようにします。

  1. ウィンドウに移動し、ctrl-w移動でスワップをマークします。
  2. \ mwと入力します
  3. 入れ替えたいウィンドウに移動する
  4. \ pwと入力します

出来上がり!ウィンドウレイアウトを台無しにせずにバッファーを交換!


17
私はあなたに10回賛成できればいいのに!noremapそれを機能させるために、マッピングで使用する必要がありました。理由は不明ですが、後でこれを見つけた人に役立つことを願っています。:D
ウェス

6
私はあなたのソリューションを最初のVimプラグインであるWindowSwap.vimに入れました。この質問とあなたの回答をreadmeにリンクしました:D
wes

私は数年前に.vimrcにsgriffinのソリューションを入れましたが、現在クリーンアップしていて、すべてをプラグインに移動することにしました。私は抽出を行い、それがまだバンドルとして機能することをテストするために、ウィンドウを何度も分割し、いくつか0r!figlet one(2、3など)実行してから、テストしました。さらに進む前に、私はgithubをチェックし、アニメーションのフィグレットウィンドウスワップで(wes ')プラグインを見つけ、これと同じ回答へのリンク(私は.vimrcにコメントとして記載していました)を見つけました。もう作ってアップロードした気がして、忘れてしまいました。とにかく、いい仕事だ!いくつかの作業を節約できます:)
節約できます Gary Fixler '22

293

これで始まる:

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

「3」をアクティブウィンドウにして、コマンドctrl+を発行しますw J。これにより、現在のウィンドウが画面の下部に移動し、次のようになります。

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

次に 'one'または 'two'をアクティブウィンドウにして、コマンドctrl+を発行しますw r。これにより、現在の行のウィンドウが「回転」し、次のようになります。

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

次に 'two'をアクティブウィンドウにして、コマンドctrl+ を発行しますw H。これにより、現在のウィンドウが画面の左いっぱいに移動し、次のようになります。

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

ご覧のように、このマヌーヴルは少しシャッフルです。3つのウィンドウがあるため、「タイルゲーム」パズルの1つに少し似ています。4つ以上のウィンドウがある場合は、これを試すことはお勧めしません。ウィンドウを閉じて、目的の位置で再度開くことをお勧めします。

Vimで分割ウィンドウを操作する方法を示すスクリーンキャストを作成しました。


2
スクリーンキャストnelstromを作成するためにさらに1マイル進みましたが、それは私が探していたものではありません。基本的な移動コマンドで分割を操作できますが、不思議なのは、任意の複雑さのレイアウトで分割位置を交換する方法があるかどうかです。
wes

95
私が好きな人は、2つのウィンドウを交換する方法を学びたいだけですctrl-w r。ヒントをありがとう!これが私の+1です。
ereOn 2013

私は\mw/ \pwとこれの両方を賛成し、両方をそれぞれ1週間使ってみました。サーバーとリモートマシン、デスクトップ、ラップトップ、タブレット、その他すべてのデバイスにインストールしたvimの14のインストール全体にプラグインをインストールし続ける必要がないため、この「ネイティブ」ソリューションの使用が最も効果的であることがわかりました。IOW、これらのネイティブコマンド(例:)を学ぶctrl-w rことは、メモリの筋肉にコミットして実行するために必要なすべてです。
eduncan911 2016

96

:h ctrl-w_ctrl-xおよび/またはを見てください:h ctrl-w_ctrl-r。これらのコマンドを使用すると、現在のレイアウトでウィンドウを交換または回転できます。

編集:実際には、現在の列または行でのみスワップされるため、この状況では機能しません。代わりに、各ウィンドウに移動してターゲットバッファーを選択することもできますが、これはかなり冗長です。


30

ランディCTRL-W xは、同じ列/行にないウィンドウを交換したくないという点で正しいです。

CTRL-W HJKLウィンドウを操作するときにキーが最も役立つことがわかりました。彼らはあなたの現在のウィンドウをその現在の場所から強制的に外し、あなたが押すキーの方向によって示されるエッジ全体を占めるようにそれを告げるでしょう。詳細については:help window-moving、を参照してください。

上記の例では、ウィンドウ「one」から開始すると、次のようになります。

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

便宜上、必要なシーケンスをキーマッピングに割り当てることができます(を参照:help mapping)。


10

私はsgriffinのソリューションから少し拡張されたバージョンを持っています。2つのコマンドを使用せずに、直感的なHJKLコマンドを使用してウィンドウを交換できます。

これがどのようになるかです:

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>

通常のノードで大文字のHJKLを使用してウィンドウを移動してみてください。本当にクールです:)


3

建物重く @ sgriffinの答え、さらに近いあなたが求めているものに、ここでの何かに:

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>

動作が期待と一致しない場合はお知らせください。


2

また、sgriffinの解決策に基づいて、交換CTRL-w mするウィンドウに移動し、を押して、交換するウィンドウに移動して、CTRL-w mもう一度押します。

CTRL-w m ニーモニックの選択としては適切ではないため、誰かがより良いものを思いついた場合は、これを編集してください。

また、「ウィンドウにマークを付けました。ターゲット上で繰り返してください」というスクリプトからフィードバックを受け取りたいのですが、vimscriptの初心者なので、その方法がわかりません。

そうは言っても、スクリプトはそのままの状態で機能します

" <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>

1

次の方法は、何らかの理由で関数が使用できない場合に便利です(vimでない場合)。

:buffersコマンドを使用して開いているバッファのIDを見つけ、目的のウィンドウに移動し、次のようなコマンドを使用します:b 5てバッファ(この場合はバッファ番号5)を開きます。2回繰り返して、ウィンドウの内容を入れ替えます。

ctrl-w-somethingは、元の質問の1〜2〜3のような非常に単純なレイアウトであっても、シーケンスを記憶しようと何度か試みた後、この方法を「発明」しました。


1

本当にクールですが、私のマッピングの提案は、Jの代わりに^ W ^ Jを使用することです(すべてのHJKLには既に意味があるため)。また、あなたがあなたの周りを入れ替えるときまで、新しいバッファをプルます。おそらく、すでに使用しているバッファの編集を続けたくないでしょう。ここに行く:

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>

1

上記の回答はすべて素晴らしいものですが、残念ながらこれらのソリューションはQuickFixまたはLocationListウィンドウとの組み合わせではうまく機能しません(Aleエラーメッセージバッファーをこれで機能させようとしたときにこの問題が発生しました)。

解決

したがって、スワップを実行する前にこれらのウィンドウをすべて閉じるためのコード行を追加しました。

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

全体のコードは次のようになります。

" 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>

Brandon Ortherへのスワップ機能のクレジット

なぜ必要なのか

すべてのQuickFix(QF)およびLocationList(LL)ウィンドウを最初に削除しないと、スワップ関数が適切に機能しない理由は、QF / LLの親がgetを非表示にすると(ウィンドウのどこにも表示されない)、QFがそれに結合された/ LLウィンドウは削除されます。これ自体は問題ではありませんが、ウィンドウが非表示になると、すべてのウィンドウ番号が再割り当てされ、最初にマークされたウィンドウの保存番号が(潜在的に)存在しないため、スワップがめちゃくちゃになります。

この見方を変えるには:

最初のウィンドウマーク

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

2番目のウィンドウマーク

____________________
| 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
|__________________|

最初のバッファスイッチ、ウィンドウ1はウィンドウ3のバッファで埋められます。したがって、親ウィンドウがなくなったため、QFウィンドウは削除されます。これにより、ウィンドウ番号が再配置されます。curNum(2番目に選択されたウィンドウの番号)が、存在しないウィンドウを指していることに注意してください。

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

そのため、2番目のバッファを切り替えると、もう存在しないcurNumウィンドウが選択されます。したがって、それを作成してバッファを切り替え、不要なウィンドウを1つ開いたままにします。

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

0

mark-window-then-swap-bufferアプローチと同様ですが、最後のスワッピングを再利用することもできます。

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>

私はすでにset hidden.vimrcにいるので、手動でバッファを非表示にする必要はありません。
qeatzy

-5

X-monadのようなタイリングウィンドウマネージャーを使用することもできます。


本当ですが、この答えはOPの質問とは無関係です。MacまたはWindowsマシンでvimを使用している可能性があります。Vimはタブレットだけでなく、ウィンドウマネージャーを交換する機能を備えていない電話でも利用できます。
nsfyn55 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.