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


313

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

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

有没有一种方法来交换onetwo维护相同的布局?在此示例中,这很简单,但是我正在寻找一种解决方案,该解决方案将有助于更复杂的布局。

更新:

我想我应该更清楚。我之前的示例是实际用例的简化。对于实际实例: 替代文字

如何交换这些拆分中的任何两个,并保持相同的布局?

更新!3年后...

我将sgriffin的解决方案放在可以轻松安装的Vim插件中!使用您喜欢的插件管理器进行安装,然后尝试一下:WindowSwap.vim

一点演示


14
如果您在两分钟前像我一样想知道“我真的需要插件吗?”,请不要犹豫并安装它。基本上只有一个命令:<leader> ww,您按两次,在每个窗口中都可以交换一次。这非常容易,您将在30秒内运行。
mdup '16

Answers:


227

发布时间有点晚,但是遇到了其他问题。我回写了两个函数以标记一个窗口,然后在窗口之间交换缓冲区。这似乎是您要的。

只需在.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移动标记交换
  2. 键入\ mw
  3. 移至您要交换的窗口
  4. 输入\ pw

瞧!交换缓冲区而不会破坏窗口布局!


17
希望我能投票十次!我确实必须noremap在映射中使用它才能使其工作。不知道为什么,但是希望能对以后找到它的人有所帮助。:D
wes

6
我将您的解决方案放入第一个Vim插件:WindowSwap.vim。我联系了这个问题,并在自述你的答案:d
韦斯

几年前,我将sgriffin的解决方案放入.vimrc中,目前正在清理,并决定将其全部移至插件。我进行了提取,并测试它仍然可以作为一个捆绑包使用,我多次拆分了窗口,然后运行了一些0r!figlet one[2、3等],然后对其进行了测试。在继续之前,我检查了github,找到了您的(wes)插件,其中包含动画figlet窗口交换,以及指向该答案的链接(我在.vimrc中作为评论)。我觉得自己已经制作并上传了,然后就忘了。无论如何,干得好!为我节省了一些工作:)
加里·FIX勒

293

从此开始:

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

使“三个”成为活动窗口,然后发出命令ctrl+ w J。这会将当前窗口移动到屏幕底部,使您拥有:

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

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

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

现在将“ two”设置为活动窗口,并发出命令ctrl+ w H。这会将当前窗口移动到屏幕左侧,使您拥有:

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

如您所见,操作方式有点混乱。具有3个窗口,有点像那些“平铺游戏”难题。如果您有4个或更多的窗口,我不建议您尝试-最好关闭它们,然后在所需位置再次打开它们。

我进行了截屏演示,演示了如何在Vim中使用拆分窗口


2
纳尔斯特罗姆(Nerstrom)付出了很多努力,进行了截屏,但这并不是我真正想要的。我可以使用基本的移动命令来处理拆分,但是我很好奇的是,是否可以在任意复杂的布局中交换拆分位置。
韦斯

95
对于喜欢我的人,只想学习如何交换两个窗口:ctrl-w r就像一个魅力。感谢您的小费!这是我的+1。
ereOn

我同时投票了\mw/ \pw和这个,并尝试将两者都使用一周。我发现使用此“本机”解决方案效果最佳,因为我不必在服务器,远程计算机,台式机,笔记本电脑,平板电脑和所有其他设备上的十二打vim安装中继续安装插件。IOW,学习这些本机命令(例如ctrl-w r)实际上是您致力于记忆力和完成任务所需的全部。
eduncan911 '16

96

看一下:h ctrl-w_ctrl-x和/或:h ctrl-w_ctrl-r。这些命令使您可以交换或旋转当前布局中的窗口。

编辑:实际上,这在这种情况下将不起作用,因为它只会在当前列或行中交换。您可以改为进入每个窗口并选择目标缓冲区,但这很冗长。


30

兰迪(Randy)的正确之处在于,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的解决方案中获得了一个稍微增强的版本,您可以在不使用两个命令的情况下交换窗口,但是可以使用直观的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号缓冲区)。重复两次,窗口内容被交换。

在多次尝试记住ctrl-w-something序列后,即使对于非常简单的布局(如原始问题中的1-2),我也“发明”了这种方法。


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>

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的父级缓冲了获取的内容(并且在窗口中未显示),则该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

类似于标记窗口然后交换缓冲区的方法,但是还允许您重用上一次交换。

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年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.