Come posso scambiare posizioni di due file aperti (in split) in vim?


313

Supponiamo di avere un layout arbitrario di divisioni in VIM.

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

C'è un modo per scambiare onee twoe mantenere lo stesso layout? In questo esempio è semplice, ma sto cercando una soluzione che aiuti per layout più complessi.

AGGIORNARE:

Immagino che dovrei essere più chiaro. Il mio esempio precedente era una semplificazione del caso d'uso reale. Con un'istanza effettiva: testo alternativo

Come posso scambiare due di queste divisioni, mantenendo lo stesso layout?

Aggiornare! 3+ anni dopo ...

Ho inserito la soluzione di sgriffin in un plugin Vim che puoi installare facilmente! Installalo con il tuo gestore di plugin preferito e provalo: WindowSwap.vim

una piccola demo


14
Se sei come me due minuti fa chiedendoti "ho davvero bisogno di un plugin per questo?", Smetti di esitare e installalo. C'è fondamentalmente un solo comando: <leader> ww che premi due volte, una volta in ogni finestra per scambiare. È semplicissimo e funzionerai in 30 secondi.
mdup,

Risposte:


227

Un po 'in ritardo al post, ma mi sono imbattuto in questo alla ricerca di qualcos'altro. Ho scritto due funzioni qualche tempo fa per contrassegnare una finestra e quindi scambiare i buffer tra le finestre. Questo sembra essere quello che stai chiedendo.

Basta schiaffeggiarli nel tuo .vimrc e mappare le funzioni come meglio credi:

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>

Per usare (supponendo che il tuo mapleader sia impostato su \) dovresti:

  1. Passa alla finestra per contrassegnare lo scambio tramite il movimento ctrl-w
  2. Digita \ mw
  3. Passa alla finestra che desideri scambiare
  4. Digita \ pw

Ecco! Buffer scambiati senza rovinare il layout della finestra!


17
Vorrei poterti votare dieci volte! Ho dovuto usare noremaple mappature per farlo funzionare. Non so perché, ma spero che ciò aiuti chiunque lo trovi in ​​seguito. : D
mer

6
Ho inserito la tua soluzione nel mio primo plug-in Vim: WindowSwap.vim . Ho collegato questa domanda e la tua risposta nel readme: D
wes

Ho messo la soluzione di sgriffin nel mio .vimrc alcuni anni fa, e sto pulendo attualmente, e ho deciso di spostare tutto su un plugin. Ho fatto l'estrazione e per testare che funzionava ancora come un fascio, ho diviso la finestra molte volte, ho eseguito alcuni 0r!figlet one[due, tre, ecc.], Quindi l'ho provato. Prima di andare oltre ho controllato github, ho trovato il tuo plugin (wes '), con scambi di finestre animate di figlet e un collegamento a questa stessa risposta (che avevo come commento nel mio .vimrc). Mi sembrava di averlo già fatto e caricato, e poi me ne sono dimenticato. Comunque, bel lavoro! Mi fa risparmiare un po 'di lavoro :)
Gary Fixler,

293

A partire da questo:

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

Imposta 'tre' sulla finestra attiva, quindi emetti il ​​comando ctrl+ w J. Questo sposta la finestra corrente per riempire la parte inferiore dello schermo, lasciandoti con:

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

Ora imposta 'uno' o 'due' la finestra attiva, quindi emetti il ​​comando ctrl+ w r. Questo "ruota" le finestre nella riga corrente, lasciandoti con:

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

Ora imposta 'due' la finestra attiva ed esegui il comando ctrl+ w H. Questo sposta la finestra corrente per riempire la parte sinistra dello schermo, lasciandoti con:

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

Come puoi vedere, la manouevre è un po 'casuale. Con 3 finestre, è un po 'come uno di quei rompicapo "a tessere". Non consiglio di provare questo se hai 4 o più finestre: faresti meglio a chiuderle e riaprirle nelle posizioni desiderate.

Ho realizzato uno screencast che dimostra come lavorare con le finestre divise in Vim .


2
Hai fatto il possibile per realizzare uno screencast, nelstrom, ma non è proprio quello che stavo cercando. Posso lavorare con le divisioni con i comandi di movimento di base, ma ciò di cui sono curioso è se c'è un modo per scambiare posizioni divise in un layout di complessità arbitraria.
mer

95
Per le persone a cui piaccio, voglio solo imparare a scambiare due finestre: ctrl-w rfunziona come un fascino. Grazie a te per il suggerimento! Ecco il mio +1.
Il

Ho votato sia a \mw/ \pwche a questo e ho provato a usarli entrambi per una settimana ciascuno. Ho scoperto che usare questa soluzione "nativa" funziona meglio, dato che non devo continuare a installare i plug-in su una dozzina di installazioni di vim che ho su server e macchine remote e desktop, laptop, tablet e tutti gli altri dispositivi. IOW, l'apprendimento di questi comandi nativi (ad es. ctrl-w r) È davvero tutto ciò che serve per impegnarsi nella memoria muscolare ed essere fatto.
eduncan911,

96

Dai un'occhiata a :h ctrl-w_ctrl-xe / o :h ctrl-w_ctrl-r. Questi comandi consentono di scambiare o ruotare le finestre nel layout corrente.

Modifica: in realtà, questo non funzionerà in questa situazione perché cambierà solo nella colonna o riga corrente. Potresti invece andare su ciascuna delle finestre e selezionare il buffer di destinazione, ma è piuttosto dettagliato.


30

Randy ha ragione nel CTRL-W xnon voler scambiare finestre che non sono nella stessa colonna / riga.

Ho scoperto che i CTRL-W HJKLtasti sono molto utili quando si manipolano Windows. Forzeranno la finestra corrente fuori dalla sua posizione corrente e gli diranno di occupare l'intero bordo indicato dalla direzione del tasto premuto. Vedi :help window-movingper maggiori dettagli.

Per il tuo esempio sopra, se inizi nella finestra "uno", questo fa quello che vuoi:

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

Per comodità, è possibile assegnare le sequenze necessarie ai mapping dei tasti (vedere :help mapping).


10

Ho una versione leggermente migliorata dalla soluzione di sgriffin, puoi scambiare Windows senza usare due comandi, ma con comandi HJKL intuitivi.

Quindi ecco come va:

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>

Prova a spostare la finestra usando HJKL maiuscolo nel nodo normale, è davvero bello :)


3

Basandosi pesantemente sulla risposta di @ sgriffin, ecco qualcosa di ancora più vicino a ciò che stai chiedendo:

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>

Per favore fatemi sapere se il comportamento non corrisponde alle vostre aspettative.


2

Basato anche sulla soluzione di sgriffin, vai alla finestra che vuoi scambiare, premi CTRL-w m, vai alla finestra con cui vuoi scambiare e premi di CTRL-w mnuovo.

CTRL-w m è una pessima scelta mnemonica, quindi se qualcuno ne esce con una migliore, per favore modificala.

Inoltre, mi piacerebbe ricevere un feedback dallo script noto anche come "Finestra contrassegnata. Per favore, ripeti sul bersaglio", tuttavia essendo un noob vimscript, non so come farlo.

Detto questo, la sceneggiatura funziona bene così com'è

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

Il seguente approccio può essere utile se le funzioni non sono disponibili per qualche motivo (ad esempio, non è il tuo vim).

Utilizzare il :bufferscomando per scoprire gli ID dei buffer aperti, passare alla finestra desiderata e utilizzare il comando like :b 5per aprire un buffer (buffer numero 5 in questo caso). Ripeti due volte e il contenuto di Windows viene scambiato.

Ho "inventato" questo metodo dopo diversi tentativi di memorizzare ctrl-w-somethingsequenze anche per layout molto semplici come uno-due-tre nella domanda originale.


1

Davvero bello, ma la mia proposta per la mappatura è di usare ^ W ^ J invece di J (perché tutti gli HJKL hanno già dei significati), in più anche io tirerei nel nuovo buffer, perché per il momento si desidera scambiare intorno a te probabilmente non vuoi continuare a modificare il buffer su cui sei già. Ecco qui:

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

Tutte le risposte di cui sopra sono ottime, purtroppo queste soluzioni non funzionano bene in combinazione con le finestre QuickFix o LocationList (ho riscontrato questo problema durante il tentativo di far funzionare il buffer dei messaggi di errore Ale).

Soluzione

Pertanto ho aggiunto una riga aggiuntiva di codice per chiudere tutte queste finestre prima di eseguire lo scambio.

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

Il codice totale sembra;

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

Crediti per la funzione di scambio a Brandon Orther

Perché è necessario

Il motivo per cui le funzioni di scambio non funzionano correttamente senza rimuovere prima tutte le finestre QuickFix (QF) e LocationList (LL) è perché se il genitore del buffer QF / LL viene nascosto (e non mostrato in nessuna finestra), il QF La finestra / LL accoppiata ad essa viene rimossa. Questo non è un problema in sé, ma quando la finestra nasconde tutti i numeri di finestra vengono riassegnati e lo scambio è incasinato poiché il numero salvato della prima finestra contrassegnata non esiste (potenzialmente) non esiste più.

Per dirla in questa prospettiva:

Primo segno della finestra

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

Segno della seconda finestra

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

Primo interruttore buffer, la finestra uno è riempita con il buffer della finestra tre. Pertanto, la finestra QF viene rimossa poiché non ha più una finestra padre. Questo riorganizza i numeri di Windows. Nota che curNum (il numero della finestra selezionata per seconda) punta a una finestra che non esiste più.

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

Quindi, quando si cambia il secondo buffer, tenta di selezionare la finestra curNum, che non esiste più. Quindi lo crea e cambia il buffer, risultando in una finestra indesiderata per essere ancora aperta.

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

0

Approccio simile mark-window-then-swap-buffer, ma consente anche di riutilizzare l'ultimo scambio.

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>

Dal momento che ho già set hiddenin .vimrc, non è necessario nascondere manualmente i buffer.
qeatzy,

-5

Puoi anche usare un gestore di finestre di piastrellatura come X-monad


Se vera, questa risposta non è correlata alla domanda del PO. Potrebbe utilizzare vim su un computer Mac o Windows. Vim è disponibile su tablet e persino telefoni, nessuno dei quali offre la possibilità di cambiare il gestore delle finestre.
nsfyn55,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.