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

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

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

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

更新:

我想我应该说得更清楚些。我前面的例子是对实际用例的简化。有一个实际的例子: alt text

.

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

更新!3年多后……

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

a little demo

88524 次浏览

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

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

兰迪的正确,因为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)。

从这个开始:

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

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

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

现在将'one'或'two'设置为活动窗口,然后发出命令ctrl+w r。这将“旋转”当前行中的窗口,留给你:

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

现在将'two'设为活动窗口,并发出命令ctrl+w H。这将移动当前窗口以填充屏幕的左侧,给你留下:

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

如你所见,这个动作有点乱。它有3个窗口,有点像“瓷砖游戏”谜题。如果你有4个或更多的窗口,我不建议你尝试这个方法——你最好关闭它们,然后在需要的位置重新打开它们。

我做了一个视频演示如何工作与分裂的窗口在Vim

有点晚了,但在寻找其他东西时看到了这个。我写了两个函数来标记一个窗口,然后在窗口之间交换缓冲区。这似乎就是你想要的。

只要把这些放在你的.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运动< / em >
  2. 类型\兆瓦
  3. 移动到要交换的窗口
  4. 类型\ pw

瞧!交换缓冲区没有搞砸你的窗口布局!

在@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>

如果你的行为不符合你的期望,请告诉我。

如果函数由于某些原因(例如,它不是你的vim)不可用,下面的方法可能会很方便。

使用:buffers命令来查找打开的缓冲区的id,导航到所需的窗口,并使用类似:b 5的命令来打开缓冲区(在本例中是5号缓冲区)。重复两次,窗口的内容将被交换。

在几次尝试记忆ctrl-w-something序列后,我“发明”了这种方法,即使是非常简单的布局,如原始问题中的1-2-3。

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

真的很酷,但我对映射的建议是使用^W^J而不是J(因为所有的H J K L已经有含义),加上我也会将拉到新的缓冲区,因为当你想要交换的时候,你可能不想继续编辑你已经在的缓冲区。是:

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>

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

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

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

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>

上面所有的答案都很好,不幸的是,这些解决方案与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>

credit for swap函数为Brandon Orther

为什么需要它

如果不先删除所有QuickFix (QF)和LocationList(LL)窗口,swap函数就不能正常工作的原因是,如果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
|__________________|