Làm cách nào tôi có thể hoán đổi vị trí của hai tệp đang mở (trong phần tách) trong vim?


313

Giả sử tôi có một số bố cục phân chia tùy ý trong vim.

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

Có cách nào để trao đổi onetwovà duy trì cách bố trí giống nhau không? Thật đơn giản trong ví dụ này, nhưng tôi đang tìm một giải pháp sẽ giúp cho các bố cục phức tạp hơn.

CẬP NHẬT:

Tôi đoán tôi nên rõ ràng hơn. Ví dụ trước đây của tôi là đơn giản hóa trường hợp sử dụng thực tế. Với một ví dụ thực tế: văn bản thay thế

Làm thế nào tôi có thể trao đổi bất kỳ hai trong số các phân chia đó, duy trì cùng một bố cục?

Cập nhật! Hơn 3 năm sau ...

Tôi đặt giải pháp của sgriffin trong một plugin Vim mà bạn có thể cài đặt dễ dàng! Cài đặt nó với trình quản lý plugin yêu thích của bạn và dùng thử: WindowSwap.vim

một bản demo nhỏ


14
Nếu bạn giống tôi hai phút trước tự hỏi "tôi có thực sự cần một plugin cho việc này không?", Hãy ngừng do dự và cài đặt nó. Về cơ bản chỉ có một lệnh: <leader> ww mà bạn nhấn hai lần, một lần trong mỗi cửa sổ để trao đổi. Điều này cực dễ và bạn sẽ chạy trong 30 giây.
mdup

Câu trả lời:


227

Một chút muộn để đăng bài, nhưng tình cờ tìm thấy cái gì đó khác. Tôi đã viết hai hàm một lúc để đánh dấu một cửa sổ và sau đó trao đổi bộ đệm giữa các cửa sổ. Đây dường như là những gì bạn đang yêu cầu.

Chỉ cần tát chúng trong .vimrc của bạn và ánh xạ các chức năng theo cách bạn thấy phù hợp:

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>

Để sử dụng (giả sử trình ánh xạ của bạn được đặt thành \), bạn sẽ:

  1. Di chuyển đến cửa sổ để đánh dấu trao đổi thông qua chuyển động ctrl-w
  2. Loại \ mw
  3. Di chuyển đến cửa sổ bạn muốn trao đổi
  4. Loại \ pw

Voila! Trao đổi bộ đệm mà không làm hỏng bố cục cửa sổ của bạn!


17
Tôi ước tôi có thể nâng bạn lên mười lần! Tôi đã phải sử dụng noremaptrong các ánh xạ để làm cho nó hoạt động. Không chắc chắn tại sao, nhưng hy vọng rằng sẽ giúp bất cứ ai tìm thấy điều này sau. : D
wes

6
Tôi đưa giải pháp của bạn vào plugin Vim đầu tiên của tôi: WindowSwap.vim . Tôi đã liên kết câu hỏi này và câu trả lời của bạn trong readme: D
wes

Tôi đã đưa giải pháp của sgriffin vào .vimrc của tôi vài năm trước và hiện tôi đang dọn dẹp và quyết định chuyển tất cả sang plugin. Tôi đã thực hiện trích xuất và để kiểm tra xem nó vẫn hoạt động như một gói, tôi đã chia cửa sổ nhiều lần và chạy một số 0r!figlet one[hai, ba, v.v.], sau đó thử nghiệm nó. Trước khi đi xa hơn, tôi đã kiểm tra github, tìm thấy plugin (wes ') của bạn, với các giao dịch hoán đổi cửa sổ hình hoạt hình và một liên kết đến cùng câu trả lời này (mà tôi đã nhận xét trong .vimrc của mình). Tôi cảm thấy như tôi đã thực hiện và tải nó lên, và sau đó quên nó đi. Dù sao, công việc tốt! Tiết kiệm cho tôi một số công việc :)
Gary Fixler

293

Bắt đầu với điều này:

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

Đặt 'ba' cửa sổ hoạt động, sau đó phát lệnh ctrl+ w J. Thao tác này sẽ di chuyển cửa sổ hiện tại để lấp đầy phía dưới màn hình, để lại cho bạn:

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

Bây giờ, đặt 'một' hoặc 'hai' cửa sổ hoạt động, sau đó phát lệnh ctrl+ w r. Điều này 'xoay' các cửa sổ trong hàng hiện tại, để lại cho bạn:

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

Bây giờ, tạo 'hai' cửa sổ hoạt động và đưa ra lệnh ctrl+ w H. Thao tác này sẽ di chuyển cửa sổ hiện tại để lấp đầy bên trái màn hình, để lại cho bạn:

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

Như bạn có thể thấy, manouevre là một chút xáo trộn. Với 3 cửa sổ, nó giống như một trong những câu đố 'trò chơi xếp gạch'. Tôi không khuyên bạn nên thử điều này nếu bạn có 4 cửa sổ trở lên - tốt hơn hết bạn nên đóng chúng lại sau đó mở lại ở các vị trí mong muốn.

Tôi đã tạo một screencast minh họa cách làm việc với các cửa sổ chia nhỏ trong Vim .


2
Bạn đã đi xa hơn để tạo ra một screencast, nelstrom, nhưng nó không thực sự là những gì tôi đang tìm kiếm. Tôi có thể làm việc với các phần tách với các lệnh di chuyển cơ bản, nhưng điều tôi tò mò là liệu có cách nào để hoán đổi các vị trí phân chia trong một bố cục phức tạp tùy ý.
wes

95
Đối với những người thích tôi, chỉ muốn học cách hoán đổi hai cửa sổ: ctrl-w rhoạt động như một lá bùa. Cảm ơn bạn cho tiền boa! Đây là +1 của tôi.
vào

Tôi đã nâng cấp cả \mw/ \pwvà cái này và thử sử dụng cả hai trong một tuần. Tôi thấy sử dụng giải pháp "bản địa" này hoạt động tốt nhất, vì tôi không phải tiếp tục cài đặt các plugin trên hàng chục cài đặt vim tôi có trên máy chủ và máy từ xa và máy tính để bàn, máy tính xách tay, máy tính bảng và tất cả các thiết bị khác. IOW, học các lệnh gốc này (ví dụ ctrl-w r) thực sự là tất cả những gì bạn cần để cam kết với bộ nhớ cơ và được thực hiện.
eduncan911

96

Hãy xem :h ctrl-w_ctrl-xvà / hoặc:h ctrl-w_ctrl-r . Các lệnh này cho phép bạn trao đổi hoặc xoay các cửa sổ trong bố cục hiện tại.

Chỉnh sửa: Trên thực tế, điều này sẽ không hoạt động trong tình huống này vì nó chỉ trao đổi trong cột hoặc hàng hiện tại. Thay vào đó, bạn có thể đi đến từng cửa sổ và chọn bộ đệm đích, nhưng điều đó khá dài dòng.


30

Randy đúng ở chỗ CTRL-W xkhông muốn trao đổi các cửa sổ không nằm trong cùng một cột / hàng.

Tôi đã thấy rằng các CTRL-W HJKLphím là hữu ích nhất khi thao tác với các cửa sổ. Họ sẽ buộc cửa sổ hiện tại của bạn ra khỏi vị trí hiện tại của nó và bảo nó chiếm toàn bộ cạnh được chỉ định bởi hướng của phím bạn nhấn. Xem:help window-moving để biết thêm chi tiết.

Ví dụ của bạn ở trên, nếu bạn bắt đầu trong cửa sổ "một", đây là điều bạn muốn:

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

Để thuận tiện, bạn có thể chỉ định các chuỗi bạn cần cho ánh xạ chính (xem :help mapping).


10

Tôi có một phiên bản nâng cao một chút từ giải pháp của sgriffin, bạn có thể trao đổi các cửa sổ mà không cần sử dụng hai lệnh, nhưng với các lệnh HJKL trực quan.

Vì vậy, đây là cách nó đi:

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>

Hãy thử di chuyển cửa sổ của bạn bằng cách sử dụng vốn HJKL trong nút bình thường, nó thực sự rất tuyệt :)


3

Xây dựng dựa trên câu trả lời của @ sgriffin, đây là một cái gì đó thậm chí gần hơn với những gì bạn yêu cầu:

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>

Xin vui lòng cho tôi biết nếu hành vi không phù hợp với mong đợi của bạn.


2

Cũng dựa trên giải pháp của sgriffin, đi đến cửa sổ bạn muốn trao đổi, nhấn CTRL-w m, đi đến cửa sổ bạn muốn trao đổi và nhấn CTRL-w mlại.

CTRL-w m là một lựa chọn ghi nhớ kém, vì vậy nếu bất cứ ai nghĩ ra một cái tốt hơn, xin vui lòng chỉnh sửa này.

Ngoài ra, tôi muốn nhận được phản hồi từ tập lệnh hay còn gọi là "Cửa sổ được đánh dấu. Vui lòng lặp lại trên mục tiêu", tuy nhiên là một noob vimscript, tôi không biết làm thế nào để làm điều đó.

Tất cả những gì đã nói, kịch bản hoạt động tốt như là

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

Cách tiếp cận sau đây có thể thuận tiện nếu các chức năng không có sẵn vì một số lý do (vì đó không phải là vim của bạn).

Sử dụng :bufferslệnh để tìm ra id của bộ đệm đang mở, điều hướng đến cửa sổ mong muốn và sử dụng lệnh như:b 5 để mở bộ đệm (bộ đệm số 5 trong trường hợp này). Lặp lại hai lần và nội dung của các cửa sổ được hoán đổi.

Tôi "phát minh" ra phương pháp này sau nhiều lần cố gắng ghi nhớ ctrl-w-somethingcác chuỗi ngay cả đối với các bố cục rất đơn giản như một phần hai trong câu hỏi ban đầu.


1

Thực sự mát mẻ, nhưng đề nghị của tôi cho việc lập bản đồ là sử dụng ^ W ^ J thay vì J (vì tất cả các hjkl đã có ý nghĩa), cộng với tôi cũng muốn kéo trong bộ đệm mới, bởi vì theo thời gian bạn muốn trao đổi xung quanh bạn có lẽ không muốn tiếp tục chỉnh sửa bộ đệm mà bạn đã bật. Đây 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>

1

Tất cả các câu trả lời trên đều tuyệt vời, thật không may, các giải pháp này không hoạt động tốt khi kết hợp với cửa sổ QuickFix hoặc LocationList (Tôi đã gặp vấn đề này trong khi cố gắng để bộ đệm thông báo lỗi Ale hoạt động với điều này).

Giải pháp

Do đó, tôi đã thêm một dòng mã bổ sung để đóng tất cả các cửa sổ này trước khi thực hiện trao đổi.

exe ':windo if &buftype == "quickfix" || &buftype == "locationlist" | lclose | endif'

Tổng số mã trông như thế nào;

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

Tín dụng cho chức năng hoán đổi sang Brandon Orther

Tại sao cần thiết

Lý do các hàm hoán đổi không hoạt động đúng mà không loại bỏ tất cả các cửa sổ QuickFix (QF) và LocationList (LL) trước tiên là bởi vì nếu cha mẹ của bộ đệm QF / LL thì ẩn (và không hiển thị trong cửa sổ), QF Cửa sổ / LL cùng với nó được gỡ bỏ. Bản thân đây không phải là vấn đề nhưng khi cửa sổ ẩn tất cả các số cửa sổ được gán lại và việc hoán đổi bị rối do số lưu của cửa sổ được đánh dấu đầu tiên sẽ không còn tồn tại nữa.

Để đặt suy nghĩ này:

Dấu cửa sổ đầu tiên

____________________
| one              | -> winnr = 1    marked first    g:markedWinNum=1
|                  | -> bufnr = 1
|__________________|
| two (QF window   | -> winnr = 2
| coupled to one   |
|__________________|
| three            | -> winnr = 3
|                  | -> bufnr = 2
|__________________|

Dấu cửa sổ thứ hai

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

Công tắc đệm đầu tiên, cửa sổ một được lấp đầy với bộ đệm của cửa sổ ba. Do đó, cửa sổ QF bị loại bỏ vì nó không còn cửa sổ cha nữa. Điều này sắp xếp lại các số cửa sổ. Lưu ý rằng curNum (số lượng cửa sổ được chọn thứ hai) đang trỏ đến một cửa sổ không còn tồn tại nữa.

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

Vì vậy, khi chuyển đổi bộ đệm thứ hai, nó cố gắng chọn cửa sổ curNum, không còn tồn tại nữa. Vì vậy, nó tạo ra nó và chuyển đổi bộ đệm, dẫn đến một cửa sổ không mong muốn được mở.

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

0

Cách tiếp cận tương tự mark-window-then-exchange-buffer, nhưng cũng cho phép bạn sử dụng lại lần hoán đổi cuối cùng.

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>

Vì tôi đã có set hiddentrong .vimrc, nên không cần phải ẩn bộ đệm theo cách thủ công.
qeatzy

-5

Bạn cũng có thể sử dụng trình quản lý cửa sổ ốp lát như X-monad


Mặc dù đúng câu trả lời này không liên quan đến câu hỏi của OP. Có thể sử dụng vim trên máy Mac hoặc Windows. Vim có sẵn trên máy tính bảng và thậm chí cả điện thoại không có khả năng trao đổi trình quản lý cửa sổ của bạn.
nsfyn55
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.