Объединить блоки путем чередования строк

15

Существует ли специальный способ объединения двух блоков текста путем чередования строк, например, переходя от этого:

a1
a2
a3
a4
  b1
  b2
  b3
  b4

к тому, что:

a1
  b1
a2
  b2
a3
  b3
a4
  b4

в нескольких командах?

РЕДАКТИРОВАТЬ : Мне очень нравится решение Сато Кацура , вот как я его реализовал:

function! Interleave()
    " retrieve last selected area position and size
    let start = line(".")
    execute "normal! gvo\<esc>"
    let end = line(".")
    let [start, end] = sort([start, end], "n")
    let size = (end - start + 1) / 2
    " and interleave!
    for i in range(size - 1)
        execute (start + size + i). 'm' .(start + 2 * i)
    endfor
endfunction

" Select your two contiguous, same-sized blocks, and use it to Interleave ;)
vnoremap <pickYourMap> <esc>:call Interleave()<CR>
Яго-лито
источник
Теперь мне любопытно - какой у вас сценарий использования? Вы переименовываете субтитры для телевизионного сезона?
VanLaser
@ VanLaser Ха-ха, я нет. В основном, я анализирую вывод из программы, который мне нужно проверить на согласованность относительно порядка создания / затем отложенного чтения объектов. Чередование блоков облегчает сопоставление соответствующих строк в задержанных выходных блоках. Мне также иногда нужно чередовать строки кода с повторяющимися, похожими инструкциями для регистрации или тестирования. Генерировать эти инструкции легко с помощью макросов, а затем чередовать их с реальным кодом теперь можно всего за пару нажатий клавиш с помощью этой функции, которая прекрасно себя чувствует :)
iago-lito
1
@ lago-lito - спасибо за ответ! Да, Vim довольно универсален :) Ваше выражение "разбор глаз" заставило меня задуматься и о scroll-bindingдвух окнах Vim.
VanLaser
У меня возникли проблемы с использованием этого, как вы выбираете два последовательных блока? Они должны быть рядом?
cbcoutinho
@cbcoutinho Да, они есть :) Я не уверен, что вы могли бы выбрать их обоих в противном случае. В примере, который я показываю, я помещаю курсор (скажем) b1, затем нажимаю, vipчтобы выбрать весь кусок, а затем ,it- это <map-I've-Picked>. Разве это не работает на вашей стороне?
Яго-Лито

Ответы:

8

Нет специального способа сделать это (насколько я знаю), но да, это можно сделать с помощью нескольких команд:

function! Interleave(start, end, where)
    if a:start < a:where
        for i in range(0, a:end - a:start)
            execute a:start . 'm' . (a:where + i)
        endfor
    else
        for i in range(a:end - a:start, 0, -1)
            execute a:end . 'm' . (a:where + i)
        endfor
    endif
endfunction

Вы можете запустить его с :call Interleave(5, 8, 1). Первый параметр - это первая строка для перемещения, вторая - последняя строка, а третий - куда их перемещать. Вы, вероятно, хотите включить номера строк, чтобы увидеть, что вы делаете ( :set number).

Это предполагает, что блоки не перекрываются. Смотрите :help :moveи :help range()понимайте, как работает функция.

Вероятно, есть более эффективные способы подобрать два блока. Вокруг плавающего плагина, который должен поменять местами два блока. Я не могу вспомнить название плагина, но автор (возможно, знаменитый доктор Чип?) Уделил больше внимания поиску интерфейса, чем я. :)

Сато Кацура
источник
Сладкий! Мне нужны только два аргумента, так как два блока являются смежными и имеют одинаковый размер: startи size. С функцией доморощенного, которая извлекает эти значения из выбора, это будет просто идеально. Я работаю над этим. :)
Яго-Лито
Интересная ссылка ? ;)
Яго-Лито
13

Вот еще одна альтернатива:

:g/^a/+4t .
:+,+5d 

Сначала скопируйте строки, расположенные на 4 строки ниже, после текущей строки ( :h :t), затем удалите последовательные строки b ( :h :d)

Еще лучше эта команда:

 :g/^a//^\s*b/m .

Это означает, что для каждой строки, начинающейся с поиска, найдите следующую строку, начинающуюся с 'b', и переместите ее ниже текущей строки.

Кристиан Брабандт
источник
1
Я получил «E16: Invalid range» по второй команде. Я попытался .+,$dвместо этого, и это сработало (как сделал .+,.+4d).
Питер Леверин
Не уверен, почему это происходит
Кристиан Брабандт
1
нет, это не так. Читайте: h: диапазон, вы всегда можете использовать прямую нумерацию вместо поиска по регулярному выражению
Кристиан Брабандт
2
@ iago-lito Второй трюк всегда работает, но вам нужно перейти /^\s*bна другой :range. Например: выбрать 1-й блок, выполнить'<,'>g/^/'>+1m.
dedowsdi
1
@ iago-lito По сути, это то же самое, что и ответ Кристиана. Ничто не будет жестко закодировано, если вы визуально выберете 1-й блок, '>+1помечает начало 2-го блока.
dedowsdi
3

Если вы хотите немного повеселиться с макросами и метками, вы можете попробовать что-то вроде этого:

  • Сначала поставить знак (здесь a) на строке , содержащей a1сma

  • Перейти к строке, содержащей b1и пометить ееmb

  • Начните запись макроса в регистр, который вы хотите (здесь регистр q) сqq

  • Вставьте следующее в ваш макрос: ddmb'apjma'b

  • Прекратить запись макроса с q

  • Играйте в нее столько раз, сколько необходимо, с X@qуказанием Xколичества времени, которое вы можете воспроизвести.

Чтобы детализировать макрос:

dd mb 'a p j ma 'b
 |  |  | | |    |
 |  |  | | |    go back to line marked `b`
 |  |  | | |
 |  |  | | move of one line and replace the mark `a`
 |  |  | insert the deleted line under the line marked `a`
 |  |  go to line marked `a`
 |  mark the future line to move with `b`
 delete the line to move

Редактировать Как упоминал Лаго-Лито в комментариях, этот метод будет перезаписывать метки и буферы.

  • Что касается отметок, я не думаю, что это реальная проблема: я редко использую все 26 отметок в буфере, и я думаю, что большую часть времени можно найти 2 свободных отметки.

  • Для буфера можно сохранить его во временной переменной: перед записью макроса используйте :let saveReg=getreg('"')для сохранения регистра, а после завершения действия используйте :call setreg('"', saveReg)для возврата регистра в его предыдущее состояние.

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

statox
источник
Интересный. К сожалению, это перезаписывает содержимое меток и регистров, которые я могу использовать;)
iago-lito
@ lago-lito: действительно, он перезаписывает метки и буферы. Для отметок я никогда не использую все 26 отметок в своих буферах, поэтому я не думаю, что это действительно проблема. Для буферов может быть больше проблем, я думаю , вы часто можете найти неиспользуемый буфер или , если вы действительно не можете использовать временную переменный и функцию getreg()и setreg()сохранить буфер. Но я согласен, что это не оптимальное решение :-)
statox
1

Я только что видел другой подобный вопрос, и решение состоит из:

Перейти к середине плюс один:

Mj

И запустить:

:,$g/./exe 'm' 2*line('.')-line('$')-1
SergioAraujo
источник
Интересно :) Остерегайтесь, что при этом чередуется весь ваш файл, а не только выбранный абзац!
Яго-Лито