web-dev-qa-db-ja.com

vimで2つの開いているファイルの位置を(分割して)入れ替えるにはどうすればよいですか?

Vimに任意の分割レイアウトがあると仮定します。

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

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

更新:

もっと明確にすべきだと思う。私の前の例は、実際のユースケースの単純化でした。実際のインスタンスの場合: alt text

同じレイアウトを維持しながら、これらのスプリットの2つをどのように交換できますか?

更新! 3年以上後...

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

a little demo

298
wes

投稿に少し遅れましたが、何か他のものを探していることに出くわしました。ウィンドウをマークし、ウィンドウ間でバッファを交換するために、しばらく前に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 movementを使用して、スワップのマークを付けるウィンドウに移動します
  2. タイプ\ mw
  3. 交換したいウィンドウに移動します
  4. タイプ\ pw

出来上がり!ウィンドウレイアウトを台無しにすることなく、バッファを交換します。

219
sgriffin

これから始めます:

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

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

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

「1」または「2」のいずれかをアクティブウィンドウにして、コマンドを発行します ctrl+wr。これにより、現在の行のウィンドウが「回転」し、次のようになります。

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

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

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

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

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

276
nelstrom

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

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

95
Randy Morris

Randy's は、CTRL-W xが同じ列/行にないウィンドウをスワップしたくないという点で正しいです。

CTRL-W HJKLキーは、ウィンドウを操作するときに最も役立つことがわかりました。現在のウィンドウを強制的に現在の場所から移動させ、押したキーの方向によって示されるEdge全体を占有するように指示します。詳細については、 :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 を参照)。

29
Mike Seplowitz

私はスグリフィンのソリューションからわずかに強化されたバージョンを持っています、あなたは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を使用してウィンドウを移動してみてください。本当にクールです:)

10
Pencilcheck

@ -sgriffinの答えにheavilyを構築すると、あなたが求めているものにさらに近いものがあります:

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>

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

3
Geoff Catlin

次のアプローチは、何らかの理由で機能が利用できない場合に便利です(つまり、vimではありません)。

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

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

1
lesnik

また、sgriffinのソリューションに基づいて、交換するウィンドウに移動し、CTRL-w mを押し、交換したいウィンドウに移動して、CTRL-w mをもう一度押します。

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

また、スクリプトから別名「ウィンドウにマークが付いています。ターゲット上で繰り返してください」からフィードバックを受け取りたいのですが、vimscript noobであるため、その方法はわかりません。

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

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

上記の答えはすべて素晴らしいです、残念ながら、これらのソリューションは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
|__________________|
1
Tom Stock

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

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
rking

同様の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>
0
qeatzy