Wie kann ich die Positionen von zwei geöffneten Dateien (in Teilungen) in vim tauschen?

313

Angenommen, ich habe ein beliebiges Layout von Splits in vim.

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

Gibt es eine Möglichkeit zu tauschen oneund twound hält das gleiche Layout? In diesem Beispiel ist es einfach, aber ich suche nach einer Lösung, die für komplexere Layouts hilfreich ist.

AKTUALISIEREN:

Ich denke, ich sollte klarer sein. Mein vorheriges Beispiel war eine Vereinfachung des tatsächlichen Anwendungsfalls. Mit einer tatsächlichen Instanz: Alt-Text

Wie könnte ich zwei dieser Teilungen austauschen und dabei das gleiche Layout beibehalten?

Aktualisieren! 3+ Jahre später ...

Ich habe die Lösung von sgriffin in ein Vim-Plugin integriert, das Sie problemlos installieren können! Installieren Sie es mit Ihrem bevorzugten Plugin-Manager und probieren Sie es aus: WindowSwap.vim

eine kleine Demo

wir s
quelle
14
Wenn Sie wie ich vor zwei Minuten gefragt haben, ob ich wirklich ein Plugin dafür brauche, hören Sie auf zu zögern und installieren Sie es. Grundsätzlich gibt es nur einen Befehl: <Leiter> ww, den Sie zweimal drücken, einmal in jedem Fenster, um zu tauschen. Dies ist super einfach und Sie werden in 30 Sekunden laufen.
Mdup

Antworten:

227

Ein bisschen spät zur Post, bin aber auf diese Suche nach etwas anderem gestoßen. Ich habe vor einiger Zeit zwei Funktionen geschrieben, um ein Fenster zu markieren und dann die Puffer zwischen den Fenstern auszutauschen. Dies scheint das zu sein, wonach Sie fragen.

Schlagen Sie diese einfach in Ihre .vimrc und ordnen Sie die Funktionen so zu, wie Sie es für richtig halten:

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>

Zur Verwendung (vorausgesetzt, Ihr Mapleader ist auf \ eingestellt) würden Sie:

  1. Gehen Sie zum Fenster, um den Wechsel durch Strg-W-Bewegung zu markieren
  2. Geben Sie \ mw ein
  3. Gehen Sie zu dem Fenster, das Sie tauschen möchten
  4. Geben Sie \ pw ein

Voila! Vertauschte Puffer, ohne Ihr Fensterlayout zu vermasseln!

sgriffin
quelle
17
Ich wünschte, ich könnte dich zehnmal positiv bewerten! Ich musste noremapin den Zuordnungen verwenden, damit es funktioniert. Ich weiß nicht warum, aber hoffentlich hilft das jedem, der das später findet. : D
wes
6
Ich habe Ihre Lösung in mein erstes Vim-Plugin eingefügt : WindowSwap.vim . Ich habe diese Frage und Ihre Antwort in der Readme-
Datei
Ich habe vor ein paar Jahren die Lösung von sgriffin in meine .vimrc-Datei eingefügt, räume gerade auf und habe beschlossen, alles in ein Plugin zu verschieben. Ich habe die Extraktion durchgeführt und um zu testen, ob es immer noch als Bundle funktioniert, habe ich das Fenster viele Male geteilt und einige 0r!figlet one[zwei, drei usw.] ausgeführt und es dann getestet. Bevor ich weiter ging, überprüfte ich Github, fand Ihr (wes ') Plugin mit animierten Figlet-Fensterwechseln und einem Link zu derselben Antwort (die ich als Kommentar in meiner .vimrc hatte). Ich hatte das Gefühl, ich hätte es bereits gemacht und hochgeladen und es dann vergessen. Wie auch immer, gute Arbeit! Spart mir etwas Arbeit :)
Gary Fixler
293

Beginnen Sie damit:

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

Machen Sie 'drei' zum aktiven Fenster und geben Sie den Befehl ctrl+ ein w J. Dadurch wird das aktuelle Fenster so verschoben, dass es den unteren Bildschirmrand ausfüllt. Sie erhalten Folgendes:

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

Machen Sie nun entweder 'eins' oder 'zwei' zum aktiven Fenster und geben Sie den Befehl ctrl+ ein w r. Dadurch werden die Fenster in der aktuellen Zeile "gedreht", sodass Sie Folgendes erhalten:

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

Machen Sie nun 'zwei' zum aktiven Fenster und geben Sie den Befehl ctrl+ ein w H. Dadurch wird das aktuelle Fenster so verschoben, dass es den linken Bildschirmrand ausfüllt und Sie Folgendes erhalten:

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

Wie Sie sehen können, ist das Manöver ein bisschen gemischt. Mit 3 Fenstern ist es ein bisschen wie eines dieser 'Kachelspiel'-Rätsel. Ich empfehle nicht, dies zu versuchen, wenn Sie 4 oder mehr Fenster haben - Sie sollten sie besser schließen, als sie an den gewünschten Positionen wieder zu öffnen.

Ich habe einen Screencast erstellt, der zeigt, wie man mit geteilten Fenstern in Vim arbeitet .

Nelstrom
quelle
2
Du bist die Extrameile gegangen, um einen Screencast zu machen, Nelstrom, aber es ist nicht wirklich das, wonach ich gesucht habe. Ich kann mit den grundlegenden Bewegungsbefehlen mit Teilungen arbeiten, aber ich bin gespannt, ob es eine Möglichkeit gibt, geteilte Positionen in einem Layout beliebiger Komplexität auszutauschen.
Wes
95
Für Leute, die mich mögen, möchte ich nur lernen, wie man zwei Fenster austauscht: ctrl-w rfunktioniert wie ein Zauber. Danke an dich für den Tipp! Hier ist meine +1.
ereOn
Ich habe sowohl das \mw/ \pwals auch dieses hochgestuft und versucht, beide jeweils eine Woche lang zu verwenden. Ich fand, dass die Verwendung dieser "nativen" Lösung am besten funktioniert, da ich nicht ständig Plugins für die über vierzehn Dutzend vim-Installationen installieren muss, die ich auf Servern und Remotecomputern und Desktops, Laptops, Tablets und allen anderen Geräten habe. IOW, das Erlernen dieser nativen Befehle (z. B. ctrl-w r) ist wirklich alles, was Sie brauchen, um sich auf den Gedächtnismuskel einzulassen und fertig zu sein.
eduncan911
96

Schauen Sie sich an :h ctrl-w_ctrl-xund / oder :h ctrl-w_ctrl-r. Mit diesen Befehlen können Sie Fenster im aktuellen Layout austauschen oder drehen.

Bearbeiten: In dieser Situation funktioniert dies nicht, da nur die aktuelle Spalte oder Zeile ausgetauscht wird. Sie könnten stattdessen zu jedem der Fenster gehen und den Zielpuffer auswählen, aber das ist ziemlich ausführlich.

Randy Morris
quelle
30

Randy ist richtig darin, dass er CTRL-W xkeine Fenster tauschen möchte, die sich nicht in derselben Spalte / Zeile befinden.

Ich habe festgestellt, dass die CTRL-W HJKLTasten beim Bearbeiten von Fenstern am nützlichsten sind. Sie zwingen Ihr aktuelles Fenster aus seiner aktuellen Position und weisen es an, die gesamte Kante einzunehmen, die durch die Richtung der Taste angezeigt wird, die Sie drücken. Siehe :help window-movingfür weitere Details.

Wenn Sie in Ihrem obigen Beispiel im Fenster "Eins" beginnen, tun Sie Folgendes:

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

Der Einfachheit halber können Sie den Tastenzuordnungen die Sequenzen zuweisen, die Sie benötigen (siehe :help mapping).

Mike Seplowitz
quelle
10

Ich habe eine leicht verbesserte Version von sgriffins Lösung. Sie können Fenster austauschen, ohne zwei Befehle zu verwenden, aber mit intuitiven HJKL-Befehlen.

Also hier ist, wie es geht:

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>

Versuchen Sie, Ihr Fenster mit Großbuchstaben HJKL im normalen Knoten zu verschieben, es ist wirklich cool :)

Bleistiftcheck
quelle
3

Der Aufbau stark auf @ sgriffin Antwort, hier ist etwas , noch näher an , was Sie fragen nach:

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>

Bitte lassen Sie mich wissen, wenn das Verhalten nicht Ihren Erwartungen entspricht.

Geoff Catlin
quelle
2

Gehen Sie auch basierend auf der Lösung von sgriffin zu dem Fenster, das Sie tauschen möchten, drücken Sie CTRL-w m, gehen Sie zu dem Fenster, mit dem Sie tauschen möchten, und drücken Sie CTRL-w merneut.

CTRL-w m ist eine schlechte mnemonische Wahl. Wenn also jemand eine bessere findet, bearbeiten Sie diese bitte.

Außerdem möchte ich ein Feedback vom Skript erhalten, auch bekannt als "Fenster markiert. Bitte auf Ziel wiederholen". Da ich jedoch ein Vimscript-Neuling bin, weiß ich nicht, wie ich das machen soll.

Alles in allem funktioniert das Skript so wie es ist

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

Der folgende Ansatz kann nützlich sein, wenn Funktionen aus irgendeinem Grund nicht verfügbar sind (z. B. nicht Ihr Ziel).

Verwenden Sie den :buffersBefehl, um die IDs der geöffneten Puffer herauszufinden, navigieren Sie zum gewünschten Fenster und verwenden Sie den Befehl like:b 5 zum Öffnen eines Puffers (in diesem Fall Puffernummer 5). Wiederholen Sie diesen Vorgang zweimal und der Inhalt der Fenster wird ausgetauscht.

Ich habe diese Methode nach mehreren Versuchen "erfunden", ctrl-w-somethingSequenzen selbst für sehr einfache Layouts wie eins, zwei, drei in der ursprünglichen Frage zu speichern .

lesnik
quelle
1

Wirklich cool, aber mein Vorschlag für die Zuordnung zu verwenden ist ^ W ^ J anstelle von J (weil alle HJKL bereits Bedeutungen haben), und auch würde ich ziehen in den neuen Puffer, weil durch die Zeit , die Sie um Sie zu tauschen wollen Wahrscheinlich möchten Sie den Puffer, in dem Sie sich bereits befinden, nicht weiter bearbeiten. Hier geht:

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

Alle oben genannten Antworten sind großartig. Leider funktionieren diese Lösungen in Kombination mit QuickFix- oder LocationList-Fenstern nicht gut (ich habe dieses Problem ausgeführt, als ich versucht habe, den Ale-Fehlermeldungen-Puffer dazu zu bringen, damit zu funktionieren).

Lösung

Daher habe ich eine zusätzliche Codezeile hinzugefügt, um alle diese Fenster vor dem Austausch zu schließen.

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

Der Gesamtcode sieht aus wie;

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

Credits für die Swap-Funktion an Brandon Orther

Warum es gebraucht wird

Der Grund, warum die Swap-Funktionen nicht ordnungsgemäß funktionieren, ohne zuerst alle QuickFix- (QF) und LocationList- (LL) Fenster zu entfernen, liegt darin, dass das QF ausgeblendet wird (und nirgendwo in einem Fenster angezeigt wird), wenn das übergeordnete Element des QF / LL-Puffers das Get versteckt Das damit gekoppelte / LL-Fenster wird entfernt. Dies ist an sich kein Problem, aber wenn das Fenster ausgeblendet ist, werden alle Fensternummern neu zugewiesen und der Austausch wird durcheinander gebracht, da die gespeicherte Nummer des ersten markierten Fensters (möglicherweise) nicht mehr vorhanden ist.

Um dies in die richtige Perspektive zu rücken:

Erste Fenstermarkierung

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

Zweite Fenstermarke

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

Erster Pufferschalter, Fenster eins wird mit dem Puffer von Fenster drei gefüllt. Somit wird das QF-Fenster entfernt, da es kein übergeordnetes Fenster mehr hat. Dadurch werden die Fensternummern neu angeordnet. Beachten Sie, dass curNum (die Nummer des zweitausgewählten Fensters) auf ein Fenster verweist, das nicht mehr existiert.

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

Beim Umschalten des zweiten Puffers wird versucht, das nicht mehr vorhandene curNum-Fenster auszuwählen. Es erstellt es also und wechselt den Puffer, was dazu führt, dass ein unerwünschtes Fenster noch geöffnet ist.

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

Ähnliche Mark-Window-Then-Swap-Buffer-Methode, aber Sie können auch das letzte Swapping wiederverwenden.

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
quelle
Da ich bereits set hiddenin .vimrc bin, müssen die Puffer nicht manuell ausgeblendet werden.
Qeatzy
-5

Sie können auch einen Kachelfenstermanager wie X-monad verwenden

Wilhelm
quelle
Diese Antwort ist zwar richtig, hat aber nichts mit der Frage des OP zu tun. Möglicherweise wird vim auf einem Mac oder Windows-Computer verwendet. Vim ist auf Tablets und sogar auf Telefonen verfügbar, von denen keines die Möglichkeit bietet, Ihren Fenstermanager auszutauschen.
nsfyn55