Как я могу поменять местами два открытых файла (в разделениях) в vim?


313

Предположим, у меня есть произвольная схема разбиения в vim.

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

Есть ли способ поменяться one и twoи поддерживать тот же макет? В этом примере все просто, но я ищу решение, которое поможет для более сложных макетов.

ОБНОВИТЬ:

Я думаю, мне следует быть более ясным. Мой предыдущий пример был упрощением фактического варианта использования. С фактическим экземпляром: альтернативный текст

Как я мог поменять местами любые два из этих разделений, сохраняя тот же макет?

Обновить! 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. Тип \ мвт
  3. Перейти к окну, которое вы хотите поменять
  4. Тип \ pw

Вуаля! Поменяйте местами буферы, не портя расположение окон!


17
Хотел бы я проголосовать за тебя десять раз! Мне пришлось использовать noremapв сопоставлениях, чтобы это работало. Не уверен, почему, но, надеюсь, это поможет любому, кто найдет это позже. : D
WES

6
Я поместил ваше решение в мой первый плагин Vim: WindowSwap.vim . Я связал этот вопрос и ваш ответ в файле readme: D
Wes

Я поместил решение 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
Вы приложили дополнительные усилия, снимая скринкаст, nelstrom, но это не совсем то, что я искал. Я могу работать с разбиениями с помощью основных команд перемещения, но мне интересно, есть ли способ поменять местами разделения в макете произвольной сложности.
Уэс

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

Я проголосовал за \mw/ \pwи этот и пытался использовать оба в течение недели каждый. Я обнаружил, что использование этого «нативного» решения работает лучше всего, так как мне не нужно постоянно устанавливать плагины во время установки дюжины дюжин, которые у меня есть, на серверах и удаленных компьютерах и настольных компьютерах, ноутбуках, планшетах и ​​всех других устройствах. Итак, изучение этих нативных команд (например ctrl-w r) - это действительно все, что вам нужно, чтобы посвятить себя мышечной памяти и все, что нужно.
eduncan911

96

Посмотрите на :h ctrl-w_ctrl-xи / или :h ctrl-w_ctrl-r. Эти команды позволяют вам менять или поворачивать окна в текущем макете.

Изменить: На самом деле, это не будет работать в этой ситуации, потому что это только поменяется местами в текущем столбце или строке. Вместо этого вы можете перейти к каждому из окон и выбрать целевой буфер, но это довольно многословно.


30

Рэнди прав в том, CTRL-W xчто не хочет менять окна, которые не находятся в одном столбце / строке.

Я обнаружил, что CTRL-W HJKLключи наиболее полезны при работе с окнами. Они вынудят ваше текущее окно из его текущего местоположения и скажут ему занять весь край, указанный направлением нажатой клавиши. Смотрите :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 это плохой мнемонический выбор, поэтому, если кто-нибудь придумает лучший, пожалуйста, отредактируйте его.

Кроме того, я хотел бы получить отзыв от скрипта, также известного как «Окно помечено. Пожалуйста, повторите на цели», но, будучи нубом 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 буферизирует get, то скрытый (и нигде не показанный в окне), 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

Подобный подход mark-window-then-swap-buffer, но также позволяет повторно использовать последний обмен.

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.