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

时间:2010-04-06 17:39:28

标签: layout editor split vim

假设我在vim中有一些任意分割布局。

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

有没有办法交换onetwo并保持相同的布局?在这个例子中它很简单,但我正在寻找一种有助于更复杂布局的解决方案。

更新

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

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

更新! 3年多以后......

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

a little demo

12 个答案:

答案 0 :(得分:272)

从这开始:

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

将'3'设为活动窗口,然后发出命令 ctrl + w J 。这会移动当前窗口以填充屏幕底部,为您留下:

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

现在将“一个”或“两个”设为活动窗口,然后发出命令 ctrl + w r 。这将“旋转”当前行中的窗口,使您显示:

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

现在将'2'设为活动窗口,然后发出命令 ctrl + w H 。这会移动当前窗口以填充屏幕左侧,为您留下:

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

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

我制作了一个能够展示how to work with split windows in Vim的截屏视频。

答案 1 :(得分:217)

这篇文章有点晚了,但却发现了其他搜索内容。我写了两个函数,以标记一个窗口,然后在窗口之间交换缓冲区。这似乎是你要求的。

只需在.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设置为\),你会:

  1. 移动到窗口以标记交换途径 ctrl-w movement
  2. 输入 \ mw
  3. 移至要交换的窗口
  4. 输入 \ pw
  5. 瞧!交换缓冲区而不会搞砸窗口布局!

答案 2 :(得分:95)

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

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

答案 3 :(得分:29)

Randy's正确,因为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)。

答案 4 :(得分:10)

我的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移动窗口,这真的很酷:)

答案 5 :(得分: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>

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

答案 6 :(得分:1)

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

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

我&#34;发明了&#34;这个方法经过多次尝试记忆ctrl-w-something序列,即使是非常简单的布局,如原始问题中的一两三个。

答案 7 :(得分: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>

答案 8 :(得分:1)

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

答案 9 :(得分: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
|__________________|

第二个窗口标记

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

答案 10 :(得分: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>

答案 11 :(得分:-5)

你也可以使用像X-monad那样的平铺窗口管理器