首页 文章

如何在vim中交换两个打开文件(在拆分中)的位置?

提问于
浏览
283

假设我在vim中有一些任意的拆分布局 .

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

有没有办法交换 onetwo 并保持相同的布局?它's simple in this example, but I'正在寻找一种有助于实现更复杂布局的解决方案 .

更新:

我想我应该更清楚 . 我之前的例子是对实际用例的简化 . 使用实际实例:
alt text

我怎么能交换任何两个分裂,保持相同的布局?

更新! 3年后......

我把sgriffin的解决方案放在你可以轻松安装的Vim插件中!用您喜欢的插件管理器安装它并尝试一下:WindowSwap.vim

a little demo

12 回答

  • 25

    这个帖子有点晚了,但却发现了其他的东西 . 我写了两个函数,一段时间后标记一个窗口,然后在窗口之间交换缓冲区 . 这似乎是你要求的 .

    只需在你的.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>
    

    要使用(假设你的mapleader设置为\),你会:

    • 移动到窗口以通过ctrl-w移动标记交换

    • 键入\ mw

    • 移动到要交换的窗口

    • 键入\ pw

    瞧!交换缓冲区而不会搞砸窗口布局!

  • 215

    我有一个sgriffin解决方案稍微增强的版本,你可以不使用两个命令交换窗口,但使用直观的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移动窗口,这真的很酷:)

  • 1

    真的很酷,但我对映射的建议是使用^ W ^ J而不是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>
    
  • 3

    在@sgriffin _486654上构建 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>
    

    如果行为与您的期望不符,请告诉我 .

  • 87

    同样基于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>
    
  • 255

    您还可以使用像X-monad这样的平铺窗口管理器

  • 10

    类似于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>
    
  • 1

    Randy's正确的是 CTRL-W x 在同一列/行中没有't want to swap windows that aren't .

    我发现 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) .

  • -5

    所有上述答案都很棒,遗憾的是这些解决方案与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的父缓冲区被隐藏了(并且在窗口中没有显示),则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
    |__________________|
    

    第二个窗口标记

    ____________________
    | 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(第二个选定窗口的编号)指向不再存在的窗口 .

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

    因此,当切换第二个缓冲区时,它会尝试选择curNum窗口,该窗口不再存在 . 因此它创建它并切换缓冲区,导致一个不需要的窗口仍然打开 .

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

    看看 :h ctrl-w_ctrl-x 和/或 :h ctrl-w_ctrl-r . 这些命令允许您在当前布局中交换或旋转窗口 .

    编辑:实际上,这不会在这种情况下起作用,因为它只是将交换当前列或行 . 您可以转到每个窗口并选择目标缓冲区,但这非常详细 .

  • 1

    从这开始:

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

    使'three'成为活动窗口,然后发出命令ctrl w J.这会移动当前窗口以填充屏幕底部,让您:

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

    现在将'one'或'two'设为活动窗口,然后发出命令ctrl w r . 这'rotates'当前行中的窗口,让您:

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

    现在将'two'设为活动窗口,并发出命令ctrl w H.这会移动当前窗口以填充屏幕左侧,让您:

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

    正如你所看到的,manouevre有点混乱 . 有3个窗口,它有点像那些'平铺游戏'谜题之一 . 如果你有4个或更多的窗户,我不建议尝试这个 - 你最好关闭它们然后再在期望的位置打开它们 .

    我做了一个演示how to work with split windows in Vim的截屏视频 .

  • 1

    如果函数由于某种原因不可用,则以下方法可能很方便(例如,它不是你的vim) .

    使用 :buffers 命令查找打开缓冲区的id,导航到所需的窗口并使用 :b 5 之类的命令打开缓冲区(在这种情况下缓冲区编号为5) . 重复两次,交换窗口内容 .

    I "invented"这个方法经过多次尝试记忆 ctrl-w-something 序列,即使是非常简单的布局,如原始问题中的一两三 .

相关问题