Jak mogę zamienić pozycje dwóch otwartych plików (w podziale) w vimie?

313

Załóżmy, że mam w Vimie dowolny układ podziałów.

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

Czy istnieje sposób, aby zamienić onea twoi utrzymać ten sam układ? W tym przykładzie jest to proste, ale szukam rozwiązania, które pomoże w bardziej złożonych układach.

AKTUALIZACJA:

Chyba powinienem być bardziej jasny. Mój poprzedni przykład był uproszczeniem faktycznego przypadku użycia. W rzeczywistej instancji: alternatywny tekst

Jak mogę zamienić dowolne dwa z tych podziałów, zachowując ten sam układ?

Aktualizacja! Ponad 3 lata później ...

Umieściłem rozwiązanie sgriffin we wtyczce Vima, którą możesz łatwo zainstalować! Zainstaluj go ze swoim ulubionym menedżerem wtyczek i wypróbuj: WindowSwap.vim

małe demo

wes
źródło
14
Jeśli jesteś podobny do mnie dwie minuty temu i zastanawiasz się „czy naprawdę potrzebuję do tego wtyczki?”, Przestań się wahać i zainstaluj ją. Zasadniczo jest tylko jedno polecenie: <przywódca> ww, które należy nacisnąć dwukrotnie, raz w każdym oknie, aby zamienić. Jest to bardzo łatwe i będziesz działać za 30 sekund.
wtorek

Odpowiedzi:

227

Trochę za późno na wpis, ale natknąłem się na to, szukając czegoś innego. Wcześniej napisałem dwie funkcje, aby zaznaczyć okno, a następnie zamienić bufory między oknami. Wydaje się, że o to prosisz.

Wystarczy uderzyć nimi w .vimrc i mapować funkcje według własnego uznania:

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>

Aby użyć (zakładając, że twój klon jest ustawiony na \):

  1. Przejdź do okna, aby zaznaczyć zamianę poprzez ruch Ctrl-W
  2. Wpisz \ mw
  3. Przejdź do okna, które chcesz zamienić
  4. Wpisz \ pw

Voila! Zamienione bufory bez zepsucia układu okna!

sgriffin
źródło
17
Chciałbym móc cię głosować dziesięć razy! Musiałem użyć noremapmapowań, aby działało. Nie jestem pewien, dlaczego, ale mam nadzieję, że to pomoże każdemu, kto znajdzie to później. : D
wes
6
Umieściłem twoje rozwiązanie w mojej pierwszej wtyczce Vima: WindowSwap.vim . Powiązałem to pytanie i twoją odpowiedź w pliku readme: D
wes
Kilka lat temu umieściłem rozwiązanie sgriffin w moim .vimrc i obecnie go sprzątam i postanowiłem przenieść to wszystko do wtyczki. Przeprowadziłem ekstrakcję i aby sprawdzić, czy nadal działa jako pakiet, kilkakrotnie rozdzieliłem okno i uruchomiłem jakieś 0r!figlet one[dwa, trzy itd.], A następnie przetestowałem. Przed pójściem dalej sprawdziłem github, znalazłem wtyczkę (wes ') z animowanymi zamianami okien figlet i link do tej samej odpowiedzi (którą miałem jako komentarz w moim .vimrc). Czułem, że już to zrobiłem i załadowałem, a potem o tym zapomniałem. W każdym razie, dobra robota! Oszczędza mi trochę pracy :)
Gary Fixler,
293

Począwszy od tego:

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

Ustaw „trzy” w aktywnym oknie, a następnie wydaj polecenie ctrl+ w J. To przesuwa bieżące okno, wypełniając dolną część ekranu, pozostawiając:

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

Teraz uaktywnij „jedno” lub „dwa” aktywne okno, a następnie wydaj polecenie ctrl+ w r. To „obraca” okna w bieżącym rzędzie, pozostawiając ci:

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

Teraz ustaw „dwa” na aktywne okno i wydaj polecenie ctrl+ w H. To przesuwa bieżące okno, wypełniając lewą stronę ekranu, pozostawiając:

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

Jak widać, manewr jest trochę przetasowany. Z 3 oknami jest to trochę jak jedna z tych łamigłówek. Nie polecam wypróbowania tego, jeśli masz 4 lub więcej okien - lepiej zamknij je, a następnie otwórz je ponownie w pożądanych pozycjach.

Zrobiłem zrzut ekranu pokazujący, jak pracować z podzielonymi oknami w Vimie .

nelstrom
źródło
2
Zrobiłeś wszystko, co w twojej mocy, robiąc screencast, nelstrom, ale tak naprawdę nie tego szukałem. Mogę pracować z podziałami za pomocą podstawowych poleceń ruchu, ale jestem ciekawy, czy istnieje sposób na zamianę podzielonych lokalizacji w układzie o dowolnej złożoności.
wes
95
Dla ludzi, którzy mnie lubią, po prostu chcą nauczyć się zamieniać dwa okna: ctrl-w rdziała jak urok. Dzięki za wskazówkę! Oto moje +1.
przed
Głosowałem za obu i \mw/ \pwi próbowałem używać obu przez tydzień każdego. Zauważyłem, że użycie tego „rodzimego” rozwiązania działa najlepiej, ponieważ nie muszę instalować wtyczek w ciągu kilkunastu instalacji vima, które mam na serwerach i zdalnych komputerach i komputerach stacjonarnych, laptopach, tabletach i wszystkich innych urządzeniach. IOW, nauka tych natywnych poleceń (np. ctrl-w r) Jest naprawdę wszystkim, czego potrzebujesz, aby zaangażować się w pamięć mięśni i wykonać.
eduncan911
96

Spójrz na :h ctrl-w_ctrl-xi / lub :h ctrl-w_ctrl-r. Te polecenia umożliwiają wymianę lub obrócenie okien w bieżącym układzie.

Edycja: W rzeczywistości nie zadziała to w tej sytuacji, ponieważ zmieni się tylko w bieżącej kolumnie lub wierszu. Zamiast tego możesz przejść do każdego z okien i wybrać bufor docelowy, ale to dość szczegółowe.

Randy Morris
źródło
30

Randy ma rację, CTRL-W xponieważ nie chce zamieniać okien, które nie znajdują się w tej samej kolumnie / wierszu.

Przekonałem się, że CTRL-W HJKLklawisze są najbardziej przydatne podczas manipulowania oknami. Zmuszą bieżące okno do opuszczenia bieżącej lokalizacji i powiedzą mu, aby zajmował całą krawędź wskazaną kierunkiem naciśniętego klawisza. Zobacz :help window-movingpo więcej szczegółów.

W powyższym przykładzie, jeśli zaczynasz w oknie „jeden”, robi to, co chcesz:

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

Dla wygody możesz przypisać sekwencje potrzebne do mapowania klawiszy (patrz :help mapping).

Mike Seplowitz
źródło
10

Mam nieco ulepszoną wersję rozwiązania sgriffin, możesz zamieniać okna bez użycia dwóch poleceń, ale z intuicyjnymi poleceniami HJKL.

Oto, jak to wygląda:

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>

Spróbuj przesunąć okno za pomocą dużego HJKL w normalnym węźle, to jest naprawdę fajne :)

Ołówek
źródło
3

Opierając się w dużej mierze na odpowiedzi @ sgriffin, oto coś jeszcze bliższego temu, o co prosisz:

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>

Daj mi znać, jeśli zachowanie nie spełnia Twoich oczekiwań.

Geoff Catlin
źródło
2

Również w oparciu o rozwiązanie sgriffin, przejdź do okna, które chcesz zamienić, naciśnij CTRL-w m, przejdź do okna, z którym chcesz zamienić i naciśnij CTRL-w mponownie.

CTRL-w m jest złym wyborem mnemonicznym, więc jeśli ktoś wymyśli lepszy, edytuj go.

Chciałbym również otrzymać informację zwrotną od skryptu aka „Okno oznaczone. Proszę powtórzyć na celu”, jednak ponieważ jest to noob vimscript, nie wiem, jak to zrobić.

To powiedziawszy, skrypt działa dobrze

" <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>
tpo
źródło
1

Poniższe podejście może być wygodne, jeśli funkcje z jakiegoś powodu nie są dostępne (na przykład nie jest to twój vim).

Użyj :bufferspolecenia, aby znaleźć identyfikator otwartych buforów, przejdź do żądanego okna i użyj polecenia, jak :b 5otworzyć bufor (w tym przypadku bufor numer 5). Powtórz dwa razy, a zawartość okien zostanie zamieniona.

„Wynalazłem” tę metodę po kilku próbach zapamiętania ctrl-w-somethingsekwencji nawet dla bardzo prostych układów, takich jak jeden-dwa-trzy w pierwotnym pytaniu.

lesnik
źródło
1

Naprawdę fajne, ale moja propozycja dla mapowania jest użycie ^ W ^ J zamiast J (ponieważ wszystkie hjkl mają już znaczenia), plus też bym ciągnąć w nowym buforze, ponieważ do czasu chcesz zamienić wokół ciebie prawdopodobnie nie chcesz kontynuować edycji bufora, na którym już jesteś. Tutaj idzie:

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>
rking
źródło
1

Wszystkie powyższe odpowiedzi są świetne, niestety te rozwiązania nie działają dobrze w połączeniu z oknami QuickFix lub LocationList (uruchomiłem ten problem, próbując zmusić bufor komunikatów o błędach Ale do pracy z tym).

Rozwiązanie

Dlatego dodałem dodatkowy wiersz kodu, aby zamknąć wszystkie te okna przed wykonaniem zamiany.

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

Cały kod wygląda;

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

Kredyty dla funkcji zamiany Brandon Orther

Dlaczego jest to potrzebne

Powodem, dla którego funkcje wymiany nie działają poprawnie bez uprzedniego usunięcia wszystkich okien QuickFix (QF) i LocationList (LL), jest to, że jeśli element nadrzędny bufora QF / LL get jest ukryty (i nigdzie nie pokazano w oknie), QF Połączone z nim okno / LL jest usuwane. Nie jest to problem sam w sobie, ale gdy okno się ukrywa, wszystkie numery okien są ponownie przypisywane, a zamiana jest pomieszana, ponieważ zapisana liczba pierwszego zaznaczonego okna już (potencjalnie) już nie istnieje.

Aby umieścić to w innej perspektywie:

Znak pierwszego okna

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

Drugi znak okna

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

Pierwszy przełącznik bufora, okno pierwsze jest wypełnione buforem okna trzeciego. W ten sposób okno QF jest usuwane, ponieważ nie ma już okna nadrzędnego. To zmienia kolejność numerów Windows. Zauważ, że curNum (numer drugiego wybranego okna) wskazuje na okno, które już nie istnieje.

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

Więc przełączając drugi bufor, próbuje wybrać okno curNum, które już nie istnieje. Tworzy go i przełącza bufor, co powoduje, że jedno niechciane okno jest nadal otwarte.

____________________
| three            | -> winnr = 1                    g:markedWinNum=1
|                  | -> bufnr = 2
|__________________|
| three            | -> winnr = 2
|                  | -> bufnr = 2
|__________________|
| one              | -> winnr = 3                     curNum=3
|                  | -> bufnr = 1                     curBuf=2
|__________________|
Tom Stock
źródło
0

Podobne podejście do mark-window-then-swap-buffer, ale pozwala również na ponowne użycie ostatniej zamiany.

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>
qeatzy
źródło
Ponieważ mam już set hiddenplik .vimrc, nie ma potrzeby ręcznego ukrywania buforów.
qeatzy
-5

Możesz także użyć menedżera okien kafelkowych, takiego jak X-monad

William
źródło
Chociaż prawda, odpowiedź ta nie ma związku z pytaniem PO. Może używać vima na komputerze Mac lub Windows. Vim jest dostępny na tabletach, a nawet telefonach, z których żaden nie umożliwia wymiany menedżera okien.
nsfyn55