Comment puis-je permuter les positions de deux fichiers ouverts (en fractionnements) dans vim?

313

Supposons que j'ai une disposition arbitraire des divisions dans vim.

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

Est - il un moyen d'échanger oneet twoet maintenir la même mise en page? C'est simple dans cet exemple, mais je cherche une solution qui aidera pour des mises en page plus complexes.

METTRE À JOUR:

Je suppose que je devrais être plus clair. Mon exemple précédent était une simplification du cas d'utilisation réel. Avec une instance réelle: texte alternatif

Comment pourrais-je échanger deux de ces divisions, en conservant la même disposition?

Mettre à jour! Plus de 3 ans plus tard ...

J'ai mis la solution de sgriffin dans un plugin Vim que vous pouvez installer facilement! Installez-le avec votre gestionnaire de plugins préféré et essayez-le: WindowSwap.vim

une petite démo

nous s
la source
14
Si vous êtes comme moi il y a deux minutes, vous vous demandez "ai-je vraiment besoin d'un plugin pour cela?", Arrêtez d'hésiter et installez-le. Il n'y a essentiellement qu'une seule commande: <leader> ww sur laquelle vous appuyez deux fois, une fois dans chaque fenêtre pour permuter. C'est super facile et vous courrez en 30 secondes.
mdup

Réponses:

227

Un peu tard pour le poste, mais je suis tombé sur cette recherche d'autre chose. J'ai écrit deux fonctions depuis un certain temps pour marquer une fenêtre, puis permuter les tampons entre les fenêtres. Cela semble être ce que vous demandez.

Il suffit de les gifler dans votre .vimrc et de cartographier les fonctions comme bon vous semble:

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>

Pour utiliser (en supposant que votre mapleader est défini sur \), vous devez:

  1. Déplacer vers la fenêtre pour marquer pour l'échange via le mouvement ctrl-w
  2. Type \ mw
  3. Déplacer vers la fenêtre que vous souhaitez échanger
  4. Tapez \ pw

Voila! Tampons échangés sans visser la disposition de vos fenêtres!

sgriffin
la source
17
J'aimerais pouvoir te voter dix fois! J'ai dû utiliser noremapdans les mappages pour le faire fonctionner. Je ne sais pas pourquoi, mais j'espère que cela aidera toute personne qui trouvera cela plus tard. : D
wes
6
J'ai mis votre solution dans mon premier plugin Vim: WindowSwap.vim . J'ai lié cette question et votre réponse dans le fichier lisez-moi: D
wes
J'ai mis la solution de sgriffin dans mon .vimrc il y a quelques années, et je nettoie actuellement, et j'ai décidé de tout déplacer vers un plugin. J'ai fait l'extraction, et pour tester qu'il fonctionnait toujours comme un bundle, j'ai divisé la fenêtre plusieurs fois et en ai exécuté 0r!figlet one[deux, trois, etc.], puis je l'ai testée. Avant d'aller plus loin, j'ai vérifié github, trouvé votre plugin (wes), avec des échanges de fenêtres de figlet animés, et un lien vers cette même réponse (que j'avais en commentaire dans mon .vimrc). J'avais l'impression de l'avoir déjà fait et téléchargé, puis je l'ai oublié. Bref, beau travail! Me fait économiser du travail :)
Gary Fixler
293

En commençant par ceci:

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

Faites «trois» la fenêtre active, puis lancez la commande ctrl+ w J. Cela déplace la fenêtre actuelle pour remplir le bas de l'écran, vous laissant avec:

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

Faites maintenant «un» ou «deux» la fenêtre active, puis lancez la commande ctrl+ w r. Cela `` fait pivoter '' les fenêtres de la ligne actuelle, vous laissant avec:

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

Maintenant, faites «deux» la fenêtre active et lancez la commande ctrl+ w H. Cela déplace la fenêtre actuelle pour remplir la gauche de l'écran, vous laissant avec:

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

Comme vous pouvez le voir, la manouevre est un peu aléatoire. Avec 3 fenêtres, c'est un peu comme un de ces puzzles de «jeu de tuiles». Je ne recommande pas d'essayer ceci si vous avez 4 fenêtres ou plus - vous feriez mieux de les fermer puis de les rouvrir dans les positions souhaitées.

J'ai fait une capture d'écran montrant comment travailler avec des fenêtres fractionnées dans Vim .

nelstrom
la source
2
Vous avez fait un effort supplémentaire pour faire un screencast, nelstrom, mais ce n'est pas vraiment ce que je cherchais. Je peux travailler avec des divisions avec les commandes de mouvement de base, mais ce qui m'intéresse, c'est s'il existe un moyen d'échanger les emplacements de division dans une disposition de complexité arbitraire.
wes
95
Pour les gens qui m'aiment, je veux juste apprendre à échanger deux fenêtres: ctrl-w rça fonctionne comme un charme. Merci à vous pour l'astuce! Voici mon +1.
ereOn
J'ai surévalué à la fois le \mw/ \pwet celui-ci et essayé d'utiliser les deux pendant une semaine chacun. J'ai trouvé que l'utilisation de cette solution «native» fonctionnait mieux, car je n'ai pas à continuer d'installer des plugins sur les douze douzaines d'installations vim que j'ai sur les serveurs et les machines et ordinateurs distants, les ordinateurs de bureau, les ordinateurs portables, les tablettes et tous les autres appareils. IOW, l'apprentissage de ces commandes natives (par exemple ctrl-w r) est vraiment tout ce dont vous avez besoin pour vous engager dans la mémoire musculaire et terminer.
eduncan911
96

Jetez un oeil à :h ctrl-w_ctrl-xet / ou :h ctrl-w_ctrl-r. Ces commandes vous permettent d'échanger ou de faire pivoter des fenêtres dans la disposition actuelle.

Modifier: En fait, cela ne fonctionnera pas dans cette situation, car il ne fera qu'échanger dans la colonne ou la ligne actuelle. Vous pouvez à la place aller dans chacune des fenêtres et sélectionner le tampon cible, mais c'est assez détaillé.

Randy Morris
la source
30

Randy a raison de CTRL-W xne pas vouloir échanger les fenêtres qui ne sont pas dans la même colonne / ligne.

J'ai trouvé que les CTRL-W HJKLtouches sont les plus utiles lors de la manipulation de fenêtres. Ils forceront votre fenêtre actuelle hors de son emplacement actuel et lui diront d'occuper tout le bord indiqué par la direction de la touche sur laquelle vous appuyez. Voir :help window-movingpour plus de détails.

Pour votre exemple ci-dessus, si vous démarrez dans la fenêtre "un", cela fait ce que vous voulez:

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

Pour plus de commodité, vous pouvez affecter les séquences dont vous avez besoin aux mappages de touches (voir :help mapping).

Mike Seplowitz
la source
10

J'ai une version légèrement améliorée de la solution de sgriffin, vous pouvez échanger des fenêtres sans utiliser deux commandes, mais avec des commandes HJKL intuitives.

Voici donc comment ça se passe:

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>

Essayez de déplacer votre fenêtre en utilisant HJKL majuscule dans le noeud normal, c'est vraiment cool :)

Vérification au crayon
la source
3

S'appuyant fortement sur la réponse de @ sgriffin, voici quelque chose d'encore plus proche de ce que vous demandez:

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>

Veuillez me faire savoir si le comportement ne correspond pas à vos attentes.

Geoff Catlin
la source
2

Également basé sur la solution de sgriffin, accédez à la fenêtre que vous souhaitez permuter, appuyez sur CTRL-w m, accédez à la fenêtre avec laquelle vous souhaitez permuter et appuyez à CTRL-w mnouveau.

CTRL-w m est un mauvais choix mnémotechnique, donc si quelqu'un en trouve un meilleur, veuillez le modifier.

De plus, j'aimerais recevoir un retour du script alias "Fenêtre marquée. Veuillez répéter sur la cible", mais étant un noob vimscript, je ne sais pas comment faire.

Cela dit, le script fonctionne bien tel quel

" <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
la source
1

L'approche suivante peut être pratique si les fonctions ne sont pas disponibles pour une raison quelconque (par exemple, ce n'est pas votre vim).

Utilisez la :bufferscommande pour trouver les identifiants des tampons ouverts, accédez à la fenêtre souhaitée et utilisez la commande comme :b 5pour ouvrir un tampon (tampon numéro 5 dans ce cas). Répétez deux fois et le contenu des fenêtres est échangé.

J'ai "inventé" cette méthode après plusieurs tentatives de mémorisation de ctrl-w-somethingséquences même pour des mises en page très simples comme un-deux-trois en question originale.

lesnik
la source
1

Vraiment cool, mais ma proposition de cartographie est d'utiliser ^ W ^ J au lieu de J (parce que tous ont déjà HJKL significations), plus aussi je aimerais tirer dans le nouveau tampon, parce que le temps que vous voulez échanger autour de vous vous ne voulez probablement pas continuer à éditer le tampon sur lequel vous êtes déjà. Voici:

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
la source
1

Toutes les réponses ci-dessus sont excellentes, malheureusement ces solutions ne fonctionnent pas bien en combinaison avec les fenêtres QuickFix ou LocationList (j'ai rencontré ce problème en essayant d'obtenir le tampon de message d'erreur Ale pour fonctionner avec cela).

Solution

J'ai donc ajouté une ligne de code supplémentaire pour fermer toutes ces fenêtres avant de faire le swap.

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

Le code total ressemble à;

" 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édits pour la fonction swap à Brandon Orther

Pourquoi est-il nécessaire

La raison pour laquelle les fonctions d'échange ne fonctionnent pas correctement sans supprimer toutes les fenêtres QuickFix (QF) et LocationList (LL) est d'abord parce que si le parent du tampon QF / LL est get caché (et nulle part affiché dans une fenêtre), le QF / La fenêtre LL qui lui est couplée est supprimée. Ce n'est pas un problème en soi, mais lorsque la fenêtre se cache, tous les numéros de fenêtre sont réaffectés et l'échange est foiré car le numéro enregistré de la première fenêtre marquée n'existe (potentiellement) plus.

Pour mettre cette perspective en perspective:

Première marque de fenêtre

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

Deuxième marque de fenêtre

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

Premier commutateur de tampon, la fenêtre un est remplie du tampon de la fenêtre trois. Ainsi, la fenêtre QF est supprimée car elle n'a plus de fenêtre parente. Cela réorganise les numéros de fenêtres. Notez que curNum (le numéro de la deuxième fenêtre sélectionnée) pointe vers une fenêtre qui n'existe plus.

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

Ainsi lors du basculement du second buffer, il essaie de sélectionner la fenêtre curNum, qui n'existe plus. Ainsi, il le crée et commute le tampon, ce qui entraîne l'ouverture d'une fenêtre indésirable.

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

Approche Mark-Window-Then-Swap-Buffer similaire, mais vous permet également de réutiliser le dernier échange.

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
la source
Comme j'ai déjà set hiddenen .vimrc, il n'est pas nécessaire de masquer manuellement les tampons.
qeatzy
-5

Vous pouvez également utiliser un gestionnaire de fenêtres en mosaïque comme X-monad

William
la source
Bien que vraie, cette réponse n'est pas liée à la question du PO. Peut utiliser vim sur un ordinateur Mac ou Windows. Vim est disponible sur les tablettes et même sur les téléphones dont aucun ne permet d'échanger votre gestionnaire de fenêtres.
nsfyn55