Como posso trocar posições de dois arquivos abertos (em divisões) no vim?

313

Suponha que eu tenha algum layout arbitrário de divisões no vim.

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

Existe uma maneira de trocar onee twoe manter o mesmo layout? É simples neste exemplo, mas estou procurando uma solução que ajude a layouts mais complexos.

ATUALIZAR:

Eu acho que deveria ser mais claro. Meu exemplo anterior foi uma simplificação do caso de uso real. Com uma instância real: texto alternativo

Como eu poderia trocar duas dessas divisões, mantendo o mesmo layout?

Atualizar! Mais de 3 anos depois ...

Coloquei a solução do sgriffin em um plugin do Vim que você pode instalar com facilidade! Instale-o com o seu gerenciador de plugins favorito e experimente: WindowSwap.vim

uma pequena demonstração

nós
fonte
14
Se você é como eu há dois minutos se perguntando "eu realmente preciso de um plugin para isso?", Pare de hesitar e instale-o. Basicamente, existe apenas um comando: <leader> ww, que você pressiona duas vezes, uma vez em cada janela para trocar. Isso é super fácil e você estará executando em 30 segundos.
Mdup

Respostas:

227

Um pouco atrasado para o post, mas me deparei com isso procurando por outra coisa. Escrevi duas funções há algum tempo para marcar uma janela e depois trocar buffers entre janelas. Parece ser o que você está pedindo.

Basta colocá-los no seu .vimrc e mapear as funções como achar melhor:

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>

Para usar (assumindo que o mapleader esteja definido como \), você deve:

  1. Vá para a janela para marcar a troca via movimento ctrl-w
  2. Digite \ mw
  3. Vá para a janela que você deseja trocar
  4. Digite \ pw

Voila! Buffers trocados sem estragar o layout da sua janela!

sgriffin
fonte
17
Eu gostaria de poder te votar dez vezes! Eu tive que usar noremapnos mapeamentos para fazê-lo funcionar. Não sei por que, mas espero que ajude quem descobrir isso mais tarde. : D
quarta
6
Coloquei sua solução no meu primeiro plugin do Vim: WindowSwap.vim . Vinculei esta pergunta e sua resposta no leia-me: D
wes
Coloquei a solução do sgriffin no meu .vimrc há alguns anos e estou limpando atualmente e decidi mudar tudo para um plugin. Fiz a extração e, para testar se ainda funcionava como um pacote, quebrei a janela várias vezes e executei algumas 0r!figlet one[duas, três, etc], depois testei. Antes de prosseguir, verifiquei o github, encontrei seu plugin (wes), com trocas animadas de janelas de figlet e um link para essa mesma resposta (que eu tinha como comentário no meu .vimrc). Eu senti como se já tivesse feito e carregado, e depois esqueci. Enfim, bom trabalho! Salva-me algum trabalho :)
Gary Fixler
293

Começando com isso:

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

Torne 'três' a janela ativa e, em seguida, emita o comando ctrl+ w J. Isso move a janela atual para preencher a parte inferior da tela, deixando você com:

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

Agora, torne 'um' ou 'dois' a janela ativa e, em seguida, emita o comando ctrl+ w r. Isso 'rotaciona' as janelas na linha atual, deixando você com:

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

Agora faça 'dois' a janela ativa e emita o comando ctrl+ w H. Isso move a janela atual para preencher a esquerda da tela, deixando você com:

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

Como você pode ver, a manobra é um pouco aleatória. Com 3 janelas, é um pouco como um daqueles quebra-cabeças de 'jogo de peças'. Eu não recomendo tentar isso se você tiver 4 ou mais janelas - seria melhor fechá-las e abri-las novamente nas posições desejadas.

Eu fiz um screencast demonstrando como trabalhar com janelas divididas no Vim .

nelstrom
fonte
2
Você foi além, criando um screencast, nelstrom, mas não é exatamente o que eu estava procurando. Posso trabalhar com divisões com os comandos básicos de movimento, mas o que me interessa é se há uma maneira de trocar locais divididos em um layout de complexidade arbitrária.
quer
95
Para pessoas que gostam de mim, só quero aprender como trocar duas janelas: ctrl-w rfunciona como um encanto. Obrigado pela dica! Aqui está o meu +1.
precisa saber é o seguinte
Votou o \mw/ \pwe este e tentei usar os dois por uma semana cada. Descobri que usar essa solução "nativa" funciona melhor, pois não preciso continuar instalando plug-ins nas dezenas de instalações do vim que tenho em servidores e máquinas e computadores remotos, desktops, laptops, tablets e todos os outros dispositivos. IOW, aprender esses comandos nativos (por exemplo ctrl-w r) é realmente tudo o que você precisa para se comprometer com a memória muscular e pronto.
22616 Edithan911
96

Dê uma olhada :h ctrl-w_ctrl-xe / ou :h ctrl-w_ctrl-r. Esses comandos permitem trocar ou girar janelas no layout atual.

Editar: Na verdade, isso não funcionará nesta situação, porque somente será trocado na coluna ou linha atual. Você poderia ir a cada uma das janelas e selecionar o buffer de destino, mas isso é bem detalhado.

Randy Morris
fonte
30

Randy está correto, CTRL-W xpois não deseja trocar janelas que não estão na mesma coluna / linha.

Eu descobri que as CTRL-W HJKLchaves são mais úteis ao manipular janelas. Eles forçarão sua janela atual a sair de seu local atual e solicitarão que ocupe toda a borda indicada pela direção da tecla pressionada. Veja :help window-movingpara mais detalhes.

Para o seu exemplo acima, se você iniciar na janela "one", faça o que deseja:

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

Por conveniência, você pode atribuir as seqüências necessárias aos mapeamentos de teclas (consulte :help mapping).

Mike Seplowitz
fonte
10

Eu tenho uma versão ligeiramente aprimorada da solução da sgriffin, você pode trocar o Windows sem usar dois comandos, mas com comandos HJKL intuitivos.

Então, aqui está como vai:

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>

Tente mover sua janela usando HJKL maiúsculo no nó normal, é muito legal :)

Pencilcheck
fonte
3

Construir fortemente na resposta de @ sgriffin, aqui está algo ainda mais perto do que você está pedindo:

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>

Entre em contato se o comportamento não corresponder às suas expectativas.

Geoff Catlin
fonte
2

Também com base na solução de sgriffin, vá para a janela que você deseja trocar, pressione CTRL-w m, vá para a janela que você deseja trocar e pressioneCTRL-w m novamente.

CTRL-w m é uma má escolha mnemônica; portanto, se alguém tiver uma melhor, edite-a.

Além disso, gostaria de receber um feedback do script conhecido como "Janela marcada. Por favor, repita no destino", no entanto, sendo um noob vimscript, não sei como fazer isso.

Tudo isso dito, o script funciona bem como é

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

A abordagem a seguir pode ser conveniente se as funções não estiverem disponíveis por algum motivo (por exemplo, não é o seu vim).

Use o :bufferscomando para descobrir IDs de buffers abertos, navegue até a janela desejada e use comandos como:b 5 para abrir um buffer (neste caso, número de buffer 5). Repita duas vezes e o conteúdo das janelas é trocado.

"Inventei" esse método depois de várias tentativas de memorizar ctrl-w-somethingsequências, mesmo para layouts muito simples, como um-dois-três na pergunta original.

lesnik
fonte
1

Muito legal, mas a minha proposta para o mapeamento é usar ^ W ^ J em vez de J (porque todos hjkl já têm significados), além de também eu puxar no novo buffer, porque pelo tempo que você quiser trocar em torno de você provavelmente não deseja continuar editando o buffer em que você já está. Aqui vai:

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
fonte
1

Todas as respostas acima são ótimas, infelizmente essas soluções não funcionam bem em combinação com as janelas QuickFix ou LocationList (eu corri nesse problema ao tentar fazer com que o buffer da mensagem de erro do Ale funcionasse com isso).

Solução

Portanto, adicionei uma linha extra de código para fechar todas essas janelas antes de fazer a troca.

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

O código total se parece;

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

Créditos para a função de swap para Brandon Orther

Por que é necessário

A razão pela qual as funções de troca não funcionam corretamente sem remover todas as janelas QuickFix (QF) e LocationList (LL) é porque, se o pai do QF / LL armazena o buffer em oculto (e não é mostrado em nenhum lugar em uma janela), o QF / LL acoplada a ele é removida. Isso não é um problema em si, mas quando a janela oculta todos os números de janela são reatribuídos e a troca é confusa, pois o número salvo da primeira janela marcada é (potencialmente) que não existe mais.

Para colocar essa perspectiva:

Primeira marca da janela

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

Segunda marca da janela

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

Primeiro comutador de buffer, a janela um é preenchida com o buffer da janela três. Assim, a janela QF é removida, pois não tem mais janela pai. Isso reorganiza os números do Windows. Observe que curNum (o número da janela selecionada em segundo lugar) está apontando para uma janela que não existe mais.

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

Portanto, ao alternar o segundo buffer, ele tenta selecionar a janela curNum, que não existe mais. Portanto, ele o cria e alterna o buffer, resultando em uma janela indesejada a ser aberta ainda.

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

Abordagem similar de marcar a janela e trocar o buffer, mas também permite reutilizar a última troca.

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
fonte
Como eu já tenho o set hidden.vimrc, não há necessidade de ocultar manualmente os buffers.
qeatzy
-5

Você também pode usar um gerenciador de janelas lado a lado como o X-monad

William
fonte
Embora verdadeira, essa resposta não tem relação com a pergunta do OP. Pode estar usando o vim em uma máquina Mac ou Windows. O Vim está disponível em tablets e até telefones, nenhum dos quais oferece a capacidade de trocar o gerenciador de janelas.
Nsfyn55