Як я можу поміняти місцями два відкритих файли (у розбиттях) на vim?


313

Припустимо, у мене є декілька довільних компонувань розщеплень in vim.

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

Чи є спосіб змінити oneі twoта підтримувати той же макет? У цьому прикладі це просто, але я шукаю рішення, яке допоможе для складніших макетів.

ОНОВЛЕННЯ:

Я думаю, я повинен бути більш чітким. Мій попередній приклад - це спрощення фактичного випадку використання. З фактичним примірником: alt текст

Як я міг поміняти місцями будь-який з цих розщеплень, зберігаючи однаковий макет?

Оновлення! Через 3 роки ...

Я розміщую рішення sgriffin у плагін Vim, який ви можете встановити з легкістю! Встановіть його за допомогою улюбленого менеджера плагінів і спробуйте: WindowSwap.vim

трохи демонстрації


14
Якщо ви, як я, дві хвилини тому замислювались про те, "чи дійсно мені потрібен плагін для цього?", Перестаньте вагатися та встановіть його. В основному є лише одна команда: <leader> ww, яку ви натискаєте двічі, один раз у кожному вікні, щоб поміняти місцями. Це дуже просто, і ви будете працювати за 30 сек.
mdup

Відповіді:


227

Трохи запізнився на посаду, але натрапив на це в пошуках чогось іншого. Я написав дві функції назад, щоб позначити вікно, а потім поміняти буфери між вікнами. Це, здається, те, про що ви просите.

Просто ляпніть їх у .vimrc і нанесіть на карту функції, як вам здається:

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>

Для використання (при умові, що для вашого Mapleader встановлено значення \), ви:

  1. Перейдіть до вікна, щоб позначити своп за допомогою руху ctrl-w
  2. Введіть \ mw
  3. Перейдіть до вікна, яке ви хочете змінити
  4. Введіть \ pw

Вуаля! Поміняйте буфери, не накручуючи макет вашого вікна!


17
Я б хотів, щоб я міг висилити вас десять разів! Мені довелося використовувати noremapв картах, щоб воно працювало. Не впевнений чому, але, сподіваємось, це допомагає кожному, хто виявить це пізніше. : D
вс

6
Я помістив ваше рішення у свій перший плагін Vim: WindowSwap.vim . Я пов’язав це питання та вашу відповідь у читанні: D
вс

Я поклав розчин sgriffin у свій .vimrc кілька років тому, і я зараз прибираю, і вирішив перенести це все на плагін. Я зробив видобуток, і щоб перевірити, що він все ще працює як пакет, я розділив вікно багато разів і пробіг декілька 0r!figlet one[два, три тощо], а потім перевірив його. Перш ніж йти далі, я перевірив github, знайшов ваш (wes ') плагін, з анімованими замінками вікон філетки та посиланням на цю саму відповідь (яку я мав як коментар у своєму .vimrc). Я відчував, що я вже зробив і завантажив його, а потім забув про це. У будь-якому випадку, приємна робота!
Врятує

293

Починаючи з цього:

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

Зробіть "три" активним вікном, після чого видайте команду ctrl+ w J. Це переміщує поточне вікно, щоб заповнити нижню частину екрана, залишивши вас із:

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

Тепер зробіть активне вікно або "один", або "два", після чого видайте команду ctrl+ w r. Це "обертає" вікна в поточному рядку, залишаючи вас з:

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

Тепер зробіть "два" активним вікном та видайте команду ctrl+ w H. Це переміщує поточне вікно, щоб заповнити ліву частину екрана, залишаючи вас:

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

Як бачимо, маньєвр - це трохи перетасовка. З 3 вікнами це трохи нагадує одну з тих головоломок "гра в плитки". Я не рекомендую пробувати це, якщо у вас 4 або більше вікон - вам краще буде закрити їх, а потім знову відкрити їх у потрібних місцях.

Я зробив скріншот, що демонструє, як працювати з розділеними вікнами у Vim .


2
Ви пройшли додаткову милю, роблячи екранізацію, нельстром, але це не дуже те, що я шукав. Я можу працювати з розщепленнями з основними командами руху, але мені цікаво, якщо є спосіб поміняти розділені місця на макет довільної складності.
вс

95
Для людей, які мені подобаються, просто хочеться навчитися міняти два вікна: ctrl-w rпрацює як шарм. Дякую вам за пораду! Ось мій +1.
ereOn

Я схвалив \mw/ \pwі цей, і спробував використовувати обидва протягом тижня. Я виявив, що використання цього "рідного" рішення найкраще працює, тому що мені не потрібно продовжувати встановлювати плагіни через дванадцять інсталяцій vim, які я маю на серверах та віддалених машинах та настільних комп’ютерах, ноутбуках, планшетах та всіх інших пристроях. IOW, вивчення цих Native команд (наприклад ctrl-w r) - це дійсно все, що потрібно, щоб зайнятись м'язом пам'яті і зробити це потрібно.
eduncan911

96

Погляньте на :h ctrl-w_ctrl-xта / або :h ctrl-w_ctrl-r. Ці команди дозволяють обмінювати або обертати вікна в поточному макеті.

Редагувати: насправді це не буде працювати в цій ситуації, оскільки воно буде змінюватися лише поточним стовпцем або рядком. Ви можете замість цього перейти до кожного з вікон та вибрати цільовий буфер, але це досить багатослівно.


30

Ренді правильно , що CTRL-W xне хоче , щоб своп вікна, які не є в тій же колонці / рядку.

Я виявив, що CTRL-W HJKLключі найкорисніші під час роботи з Windows. Вони змусять ваше поточне вікно вийти з його поточного місця та скажуть йому зайняти весь край, вказаний напрямом натискання клавіші. Дивіться :help window-movingдокладнішу інформацію.

У вашому прикладі вище, якщо ви починаєте у вікні "один", це робить те, що ви хочете:

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

Для зручності ви можете призначити послідовності, необхідні для клавіші відображень (див. :help mapping).


10

У мене є трохи вдосконалена версія рішення sgriffin, ви можете міняти вікна, не використовуючи дві команди, але за допомогою інтуїтивно зрозумілих команд HJKL.

Тож ось як це йде:

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>

Спробуйте перемістити своє вікно, використовуючи капітальний HJKL у звичайному вузлі, це дуже здорово :)


3

Будівля сильно на @ sgriffin відповідають, ось дещо - що ще ближче до того , що ви просите:

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>

Будь ласка, повідомте мене, якщо поведінка не відповідає вашим очікуванням.


2

Виходячи з рішення sgriffin, перейдіть до вікна, яке ви хочете поміняти, натисніть CTRL-w m, перейдіть до вікна, з яким потрібно помінятися, і натисніть CTRL-w mще раз.

CTRL-w m - це поганий мнемонічний вибір, тому якщо хтось придумає кращий, будь ласка, відредагуйте це.

Крім того, я хотів би отримати зворотній зв'язок зі сценарієм, який називався "Вікно відмічено. Будь ласка, повторіть у цілі", проте, будучи noob vimscript, я не знаю, як це зробити.

Все, що сказано, сценарій працює добре, як є

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

Наступний підхід може бути зручним, якщо функції з якихось причин недоступні (бо це не ваш vim).

Використовуйте :buffersкоманду для пошуку ідентифікаторів відкритих буферів, перейдіть до потрібного вікна та використовуйте команду, як :b 5щоб відкрити буфер (буфер номер 5 у цьому випадку). Повторіть два рази і вміст вікон заміниться.

Я "винайшов" цей метод після кількох спроб запам'ятовування ctrl-w-somethingпослідовностей навіть для дуже простих макетів, таких як одна-дві-три в оригінальному питанні.


1

Дійсно остигати, але моя пропозиція для відображення є використання ^ W ^ J замість J (бо все HJKL вже мають значення), а також і я б тягнути в новому буфері, тому що на той час , коли ви хочете поміняти місцями навколо вас напевно, не хочуть продовжувати редагувати буфер, який ви вже використовуєте. Ось:

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

Усі вищезазначені відповіді чудові, на жаль, ці рішення не працюють добре в поєднанні з вікнами QuickFix або LocationList (я зіткнувся з цією проблемою, намагаючись примусити буфер повідомлення про помилку Ale працювати з цим).

Рішення

Тому я додав додатковий рядок коду, щоб закрити всі ці вікна, перш ніж робити своп.

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

Загальний код схожий;

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

Кредити за функцію підкачки Брендону Ортеру

Для чого це потрібно

Причина функцій підкачки не працює належним чином, не видаляючи спочатку всі вікна QuickFix (QF) та LocationList (LL), тому що, якщо батьківський буфер QF / LL буфер отримує приховано (і ніде не відображається у вікні), QF / Приєднане до нього вікно LL видаляється. Це само по собі не є проблемою, але коли вікно приховує всі номери вікна перепризначені, а підкачка замішана, оскільки збережене число першого позначеного вікна вже (можливо) більше не існує.

Щоб поставити це невпевнено:

Перша позначка вікна

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

Друга позначка вікна

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

Перший буферний перемикач, перше вікно заповнено буфером вікна три. Таким чином, вікно QF видаляється, оскільки воно вже не має батьківського вікна. Це переставляє номери вікон. Зауважте, що curNum (номер другого вибраного вікна) вказує на вікно, яке вже не існує.

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

Тому при перемиканні другого буфера він намагається вибрати вікно curNum, яке більше не існує. Так він створює його і перемикає буфер, в результаті чого одне небажане вікно залишається відкритим.

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

0

Аналогічний підхід «буфер-вікно-потім-підміна», але також дозволяє повторно використовувати останню заміну.

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>

Оскільки я вже був set hiddenу .vimrc, немає потреби вручну приховувати буфери.
qeatzy

-5

Ви також можете використовувати керуючий віконним диспетчером, наприклад X-monad


Незважаючи на те, що ця відповідь не пов'язана з питанням ОП. Неможливо використовувати vim на комп'ютері Mac чи Windows. Vim доступний на планшетах і навіть телефонах, жоден з яких не надає можливість поміняти менеджера вікон.
nsfyn55
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.