¿Cómo puedo intercambiar posiciones de dos archivos abiertos (en divisiones) en vim?

313

Supongamos que tengo un diseño arbitrario de divisiones en vim.

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

¿Hay una manera de intercambiar oney twoy mantener el mismo diseño? Es simple en este ejemplo, pero estoy buscando una solución que ayude a diseños más complejos.

ACTUALIZAR:

Creo que debería ser más claro. Mi ejemplo anterior fue una simplificación del caso de uso real. Con una instancia real: texto alternativo

¿Cómo podría cambiar cualquiera de esas divisiones, manteniendo el mismo diseño?

¡Actualizar! 3+ años después ...

¡Puse la solución de sgriffin en un complemento Vim que puede instalar con facilidad! Instálelo con su administrador de complementos favorito y pruébelo: WindowSwap.vim

una pequeña demo

wes
fuente
14
Si hace dos minutos como yo y se pregunta "¿ Realmente necesito un complemento para esto?", Deje de dudar e instálelo. Básicamente hay un solo comando: <leader> ww que presionas dos veces, una en cada ventana para intercambiar. Esto es súper fácil y estarás corriendo en 30 segundos.
mdup

Respuestas:

227

Un poco tarde a la publicación, pero me encontré con esto buscando otra cosa. Escribí dos funciones hace un tiempo para marcar una ventana y luego intercambiar buffers entre ventanas. Esto parece ser lo que estás pidiendo.

Simplemente déles una palmada en su .vimrc y asigne las funciones como mejor le parezca:

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 (suponiendo que su mapleader esté configurado en \), debería:

  1. Muévase a la ventana para marcar el intercambio mediante el movimiento ctrl-w
  2. Escriba \ mw
  3. Mover a la ventana que desea intercambiar
  4. Escriba \ pw

Voila! ¡Intercambió buffers sin arruinar el diseño de su ventana!

sgriffin
fuente
17
¡Ojalá pudiera votarte diez veces! Tuve que usar noremapen las asignaciones para que funcione. No estoy seguro de por qué, pero espero que eso ayude a cualquiera que encuentre esto más tarde. : D
wes
66
Puse tu solución en mi primer complemento de Vim: WindowSwap.vim . Vinculaba esta pregunta y su respuesta en el archivo Léame: D
wes
Puse la solución de sgriffin en mi .vimrc hace unos años, y estoy limpiando actualmente, y decidí mover todo a un complemento. Hice la extracción, y para probar que todavía funcionaba como un paquete, dividí la ventana muchas veces y ejecuté algunas 0r!figlet one[dos, tres, etc.], luego las probé. Antes de continuar, verifiqué github, encontré su plugin (wes), con intercambios animados de ventana de figlet, y un enlace a esta misma respuesta (que tenía como comentario en mi .vimrc). Sentí que ya lo había hecho y cargado, y luego lo olvidé. De todos modos, buen trabajo! Me ahorra algo de trabajo :)
Gary Fixler
293

Comenzando con esto:

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

Convierta 'tres' en la ventana activa, luego emita el comando ctrl+ w J. Esto mueve la ventana actual para llenar la parte inferior de la pantalla, dejándote con:

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

Ahora haga 'uno' o 'dos' la ventana activa, luego emita el comando ctrl+ w r. Esto 'rota' las ventanas en la fila actual, dejándote con:

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

Ahora haga que 'dos' sea la ventana activa y emita el comando ctrl+ w H. Esto mueve la ventana actual para llenar la izquierda de la pantalla, dejándote con:

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

Como puede ver, la maniobra es un poco aleatoria. Con 3 ventanas, es un poco como uno de esos rompecabezas de 'juego de fichas'. No recomiendo probar esto si tiene 4 o más ventanas; sería mejor cerrarlas y luego volver a abrirlas en las posiciones deseadas.

Hice un screencast que demuestra cómo trabajar con ventanas divididas en Vim .

nelstrom
fuente
2
Hiciste un esfuerzo adicional haciendo un screencast, nelstrom, pero no es realmente lo que estaba buscando. Puedo trabajar con divisiones con los comandos básicos de movimiento, pero lo que me interesa es si hay una manera de intercambiar ubicaciones divididas en un diseño de complejidad arbitraria.
Wes
95
Para las personas que me quieren, solo quieren aprender a intercambiar dos ventanas: ctrl-w rfunciona de maravilla. Gracias a ti por el consejo! Aquí está mi +1.
antes del
Voté tanto el \mw/ \pwcomo este e intenté usar ambos durante una semana cada uno. Descubrí que usar esta solución "nativa" funciona mejor, ya que no tengo que seguir instalando complementos en las más de doce docenas de instalaciones vim que tengo en servidores y máquinas remotas y computadoras de escritorio, computadoras portátiles, tabletas y todos los demás dispositivos. IOW, aprender estos comandos nativos (por ejemplo ctrl-w r) es realmente todo lo que necesita para comprometerse con el músculo de la memoria y listo.
eduncan911
96

Echa un vistazo a :h ctrl-w_ctrl-xy / o :h ctrl-w_ctrl-r. Estos comandos le permiten intercambiar o rotar ventanas en el diseño actual.

Editar: en realidad, esto no funcionará en esta situación porque solo se intercambiará en la columna o fila actual. En su lugar, puede ir a cada una de las ventanas y seleccionar el búfer de destino, pero eso es bastante detallado.

Randy Morris
fuente
30

Randy tiene razón en que CTRL-W xno quiere intercambiar ventanas que no están en la misma columna / fila.

He descubierto que las CTRL-W HJKLteclas son más útiles al manipular ventanas. Forzarán su ventana actual fuera de su ubicación actual y le dirán que ocupe todo el borde indicado por la dirección de la tecla que presiona. Ver :help window-movingpara más detalles.

Para su ejemplo anterior, si comienza en la ventana "uno", esto hace lo que desea:

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

Para mayor comodidad, puede asignar las secuencias que necesita a las asignaciones de teclas (consulte :help mapping).

Mike Seplowitz
fuente
10

Tengo una versión ligeramente mejorada de la solución de sgriffin, puede intercambiar ventanas sin usar dos comandos, pero con comandos intuitivos HJKL.

Así que así es como 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>

Intenta mover tu ventana usando HJKL mayúscula en el nodo normal, es realmente genial :)

Pencilcheck
fuente
3

La construcción en gran medida sobre la respuesta de @ sgriffin, aquí hay algo aún más cerca de lo que estás pidiendo:

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>

Avíseme si el comportamiento no coincide con sus expectativas.

Geoff Catlin
fuente
2

También basado en la solución de sgriffin, vaya a la ventana que desea intercambiar, presione CTRL-w m, vaya a la ventana con la que desea intercambiar y presione CTRL-w mnuevamente.

CTRL-w m es una mala elección mnemotécnica, por lo que si a alguien se le ocurre una mejor, edítela.

Además, me gustaría recibir un comentario del script, también conocido como "Ventana marcada. Repita en el objetivo", sin embargo, siendo un novato vimscript, no sé cómo hacerlo.

Todo lo dicho, el guión funciona bien como está

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

El siguiente enfoque puede ser conveniente si las funciones no están disponibles por alguna razón (por ejemplo, no es su vim).

Use el :bufferscomando para encontrar las identificaciones de los buffers abiertos, navegue a la ventana deseada y use comandos como :b 5abrir un buffer (número de buffer 5 en este caso). Repita dos veces y los contenidos de las ventanas se intercambian.

"Inventé" este método después de varios intentos de memorizar ctrl-w-somethingsecuencias incluso para diseños muy simples como uno-dos-tres en la pregunta original.

lesnik
fuente
1

Realmente genial, pero mi propuesta para la asignación es utilizar ^ W ^ J en lugar de J (porque todos HJKL ya tienen significados), además también me tiraría en la nueva memoria intermedia, porque en el momento que desea intercambiar tu alrededor probablemente no quiera continuar editando el búfer en el que ya está. Aquí va:

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

Todas las respuestas anteriores son geniales, desafortunadamente estas soluciones no funcionan bien en combinación con las ventanas QuickFix o LocationList (encontré este problema al intentar que el búfer de mensaje de error Ale funcionara con esto).

Solución

Por lo tanto, agregué una línea de código adicional para cerrar todas estas ventanas antes de hacer el intercambio.

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

El código total que 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 la función de intercambio a Brandon Orther

Por qué es necesario

La razón por la que las funciones de intercambio no funcionan correctamente sin eliminar primero todas las ventanas QuickFix (QF) y LocationList (LL) es porque si el padre del buffer QF / LL se oculta (y no se muestra en ninguna parte de una ventana), el QF La ventana / LL acoplada a ella se elimina. Esto no es un problema en sí mismo, pero cuando la ventana se oculta, todos los números de ventana se reasignan y el intercambio está en mal estado, ya que el número guardado de la primera ventana marcada ya no existe (potencialmente).

Para poner esto en perspectiva:

Primera marca de ventana

____________________
| 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 de ventana

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

Primer interruptor de búfer, la ventana uno se llena con el búfer de la ventana tres. Por lo tanto, la ventana QF se elimina ya que ya no tiene ventana principal. Esto reorganiza los números de Windows. Tenga en cuenta que curNum (el número de la segunda ventana seleccionada) apunta a una ventana que ya no existe.

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

Entonces, al cambiar el segundo búfer, intenta seleccionar la ventana curNum, que ya no existe. Por lo tanto, lo crea y cambia el búfer, lo que da como resultado que una ventana no deseada se abra todavía.

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

Enfoque similar a mark-window-then-swap-buffer, pero también le permite reutilizar el último intercambio.

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
fuente
Como ya tengo set hiddenen .vimrc, no es necesario ocultar manualmente los búferes.
qeatzy
-5

También podría usar un administrador de ventanas de mosaico como X-monad

Guillermo
fuente
Si bien es cierto, esta respuesta no está relacionada con la pregunta del OP. Podría estar usando vim en una máquina Mac o Windows. Vim está disponible en tabletas e incluso teléfonos, ninguno de los cuales ofrece la posibilidad de cambiar su administrador de ventanas.
nsfyn55