Об’єднайте кілька рядків (два блоки) у Vim


323

Я хотів би об'єднати два блоки рядків у Vim, тобто взяти рядки n..mта додати їх до рядків a..b. Якщо ви віддаєте перевагу пояснення псевдокоду:[a[i] + b[i] for i in min(len(a), len(b))]

Приклад:

abc
def
...

123
45
...

повинні стати

abc123
def45

Чи є приємний спосіб зробити це без копіювання та вставки вручну?


Отже ... ви хочете приєднатися до змінних ліній? Тобто ви хочете приєднатись до лінії xприєднання x+2?
larsks

1
Ні, у мене є два окремих блоки. Псевдокод-іш:[a[i] + b[i] for i in min(len(a), len(b))]
ThiefMaster

2
Дивіться подібне запитання (і відповідь!) Тут
NWS

Відповіді:


880

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

Якщо ви хочете зробити це за допомогою лише команд Ex

:5,8del | let l=split(@") | 1,4s/$/\=remove(l,0)/

перетвориться

work it 
make it 
do it 
makes us 
harder
better
faster
stronger
~

в

work it harder
make it better
do it faster
makes us stronger
~

ОНОВЛЕННЯ: Відповідь з цією кількістю оновлень заслуговує більш ретельного пояснення.

У Vim ви можете використовувати символ pipe ( |) для ланцюжка декількох команд Ex, тому вищезазначене еквівалентно

:5,8del
:let l=split(@")
:1,4s/$/\=remove(l,0)/

Багато команд Ex приймають діапазон рядків як аргумент префікса - у наведеному вище випадку 5,8до delі 1,4передs/// вказати, над якими рядками працюють команди.

delвидаляє задані рядки. Він може взяти аргумент регістру, але коли його не вказано, він скидає рядки в неназваний реєстр, @"як і видалення в звичайному режимі. let l=split(@")потім розбиває видалені рядки до списку, використовуючи роздільник за замовчуванням: пробіл. Щоб правильно працювати над входом, який мав пробіл у видалених рядках, наприклад:

more than 
hour 
our 
never 
ever
after
work is
over
~

нам знадобиться вказати інший роздільник, щоб запобігти поділу "робота" на два елементи списку: let l=split(@","\n") .

Нарешті, в підстановці s/$/\=remove(l,0)/ми замінюємо кінець кожного рядка ( $) значенням виразу remove(l,0). remove(l,0)змінює список l, видаляючи та повертаючи його перший елемент. Це дозволяє нам замінити видалені рядки в тому порядку, в якому ми їх читаємо. Замість цього ми могли замінити видалені рядки у зворотному порядку за допомогою remove(l,-1).


1
хм ... мені потрібно лише один раз натиснути клавішу Enter. І він не вставить пробіл між двома половинами. Якщо на лініях є якийсь пробіл (наприклад, у прикладі "працюй"), він все одно буде. Ви можете позбутися від будь-якого простору, використовуючи s/\s*$/замість цього s/$/.
чемпіон

1
Дякую за пояснення. :sil5,8del | let l=split(@") | sil1,4s/$/\=remove(l,0)/ | call histdel("/", -1) | nohlsздається, ще краще, оскільки вона очищає історію пошуку після запуску. І не відображається повідомлення "x більше / менше рядків", що вимагає від мене натиснути клавішу enter.
ThiefMaster

11
Якщо ви хочете повну посилання VIM для цієї відповіді: :help range, :help :d, :help :let, :help split(), :help :s, :help :s\=, :help remove().
Бенуа

16
Переконайтесь, що такі, як ви хочете, публікувати відповіді, ось чому я став модератором. Добре шоу :)
Tim Post

1
Виникає проблема, якщо після перших 4 речень немає білого простору.
Реман

58

Елегантна і коротка команда Прімера вирішення питання може бути отримана шляхом об'єднання :global, :moveі :joinкоманди. Припускаючи, що перший блок рядків починається з першого рядка буфера і що курсор розташований на лінії, що безпосередньо передує першому рядку другого блоку, команда полягає в наступному.

:1,g/^/''+m.|-j!

Для детального пояснення цієї методики дивіться мою відповідь на аналогічне запитання " Vim paste -d" поведінка поза коробкою? ”.


E16: Invalid Range- але це все одно працює. При видаленні 1,він працює без помилок.
ThiefMaster

І занадто погано, що ви не можете прийняти більше ніж одну відповідь - інакше і у вас буде зелена позначка!
ThiefMaster

3
Дуже хороший! Я не знаю , про :moveта :join!, ні того, що ''мав в виду в якості аргументу діапазону ( :help '') і тим, що +і -мав в виду в якості модифікаторів діапазону ( :help range). Дякую!
чемпіон

@TentistMaster: Команда призначена для використання, коли два діапазони ліній, що з'єднуються, примикають один до одного, так що курсор спочатку розташовується в останньому рядку першого блоку, що безпосередньо передує першому рядку другого блоку. (Див. Пов’язану відповідь і питання.) Команда працює за призначенням, навіть якщо кількість рядків у блоках відрізняється, хоча в цьому випадку дає повідомлення про помилку. Можна придушити повідомлення про помилки, попередньо подавши sil!команду.
ib.

2
@ib.: Я думаю, що було б непоганим вписати в цю відповідь також детальне пояснення.
ThiefMaster

44

Щоб приєднатись до блоків рядка, потрібно виконати наступні дії:

  1. Перехід до третього рядка: jj
  2. Введіть режим візуального блоку: CTRL-v
  3. Прив’язати курсор до кінця рядка (важливо для ліній різної довжини): $
  4. До кінця: CTRL-END
  5. Виріжте блок: x
  6. Переходимо до кінця першого рядка: kk$
  7. Вставте блок сюди: p

Рух не найкращий (я не експерт), але він працює так, як ви хотіли. Сподіваюся, буде коротша його версія.

Ось передумови, щоб ця методика працювала добре:

  • Всі рядки стартового блоку (у прикладі у питанні abcта def) мають однакову довжину XOR
  • перший рядок стартового блоку найдовший, і вам не байдуже про додаткові пробіли між ними) XOR
  • Перший рядок стартового блоку не найдовший, і ви додаткові пробіли до кінця.

Вау, це цікаво! Я ніколи б не подумав, що так буде працювати.
voithos

13
Це працює , як хотілося тільки тому , що abcі defмають однакову довжину. Вибір блоку буде зберігати відступи видаленого тексту, тому якщо курсор знаходиться на короткому рядку під час введення тексту, рядки вставляються між літерами на довших - і пробілами, доданими до коротших, якщо курсор був довше один.
Ізката

Справді ... Чи є спосіб зробити це правильно, використовуючи блок копіювання та вставлення? Це найпростіший спосіб зробити це врешті-решт (особливо якщо ви чомусь не можете знайти тут складніші способи).
ThiefMaster

2
Як каже @Izkata, ви зіткнетеся з проблемою вставлення тексту між довшими рядками. Щоб вирішити це, просто додайте більше пробілів в кінці першого рядка, щоб зробити його найдовшим, а потім вставте блок тексту. Як тільки це буде зроблено, стиснення декількох пробілів до одного стає таким же простим, як:%s/ \+/ /g
Хаджа Мінхаддюін

1
set ve=allВи повинні допомогти, дивіться vimdoc.sourceforge.net/htmldoc/options.html#'virtualedit '
Бен

19

Ось як я це зробив (із курсором у першому рядку):

qama:5<CR>y$'a$p:5<CR>dd'ajq3@a

Вам потрібно знати дві речі:

  • Номер рядка, з якого починається перший рядок другої групи (в моєму випадку 5), і
  • кількість рядків у кожній групі (3 в моєму прикладі).

Ось що відбувається:

  • qaзаписує все до наступного qв "буфер" в a.
  • ma створює позначку на поточному рядку.
  • :5<CR> переходить до наступної групи.
  • y$ тьмає решту лінії.
  • 'a повертається до позначки, встановленої раніше.
  • $p пасти в кінці рядка.
  • :5<CR> повертається до першого рядка другої групи.
  • dd видаляє його.
  • 'a повертається до позначки.
  • jq йде вниз по одному рядку і припиняє запис.
  • 3@a повторює дію для кожного рядка (3 у моєму випадку)

1
Вам потрібно натиснути [Enter]після того, :5як ви введете його обидва рази, або це не спрацює.
Шон Дж. Гофф

1
Я на гвім. Чи є якийсь спосіб скопіювати та вставити цю команду в GVim? ^ V автоматично вставляється у режим вставки (що має сенс, саме цього зазвичай хочуть люди), навіть якщо я зараз перебуваю у звичайному (?) Режимі. Я намагався, :norm qama:5<CR>y$'a$p:5<CR>dd'ajq3@aале це, здається, виконується тільки q.
ThiefMaster

1
ThiefMaster: Спробуйте :let @a="ma:5^My$'a$p:5^Mdd'aj" | normal 4@a, де ^Mсимволи набираються натисканням CTRL-V, а потім Enter.
чемпіон

8

Як було сказано в іншому місці, вибір блоку - це шлях. Але ви також можете використовувати будь-який варіант:

:!tail -n -6 % | paste -d '\0' % - | head -n 5

Цей метод спирається на командний рядок UNIX. pasteУтиліта була створена для обробки такого роду лінія зрощування.

PASTE(1)                  BSD General Commands Manual                 PASTE(1)

NAME
     paste -- merge corresponding or subsequent lines of files

SYNOPSIS
     paste [-s] [-d list] file ...

DESCRIPTION
     The paste utility concatenates the corresponding lines of the given input files, replacing all but the last file's newline characters with a single tab character,
     and writes the resulting lines to standard output.  If end-of-file is reached on an input file while other input files still contain data, the file is treated as if
     it were an endless source of empty lines.

Використання вибору блоків - не єдиний шлях. Не найпростіший. paste -dБажану ( -подобну) поведінку можна реалізувати за допомогою короткої команди Vim, як показано у моїй відповіді .
ib.

3
Крім того, я перебуваю на windows, щоб таке рішення передбачало відкриття SSH-з'єднання до моєї машини Linux та вставку з редактора в термінал і назад.
ThiefMaster

3

Дані вибірки такі ж, як і дані для чемпіонів.

:1,4s/$/\=getline(line('.')+4)/ | 5,8d

3

Я не думаю, що зробити це занадто складно. Я б просто встановив virtualiit на
( :set virtualedit=all)
Виберіть блок 123 і все нижче.
Помістіть його після першого стовпця:

abc    123
def    45
...    ...

і видаліть кратну пробіл між 1 пробілом:

:%s/\s\{2,}/ /g

Питання насправді не вимагає пробілів, я б зробив щось на кшталт gvV:'<,'>s/\s+//g(vim повинен автоматично вставити '<,'>для вас, щоб вам не потрібно було вводити його вручну).
Бен

2

Я б використовував складні повтори :)

Враховуючи це:

aaa
bbb
ccc

AAA
BBB
CCC

За допомогою курсору в першому рядку натисніть на наступне:

qa}jdd''pkJxjq

а потім натисніть @a(і згодом ви можете використовувати@@ ) стільки разів, скільки потрібно.

Ви повинні закінчити:

aaaAAA
bbbBBB
cccCCC

(Плюс новий рядок.)

Пояснення:

  • qa починає запис складного повтору в a

  • } переходить до наступного порожнього рядка

  • jdd видаляє наступний рядок

  • '' повертається в положення до останнього стрибка

  • p вставити видалений рядок під поточний

  • kJ додайте поточний рядок до кінця попереднього

  • xвидалити простір, який Jдодається між комбінованими лініями; ви можете опустити це, якщо хочете місця

  • j перейти до наступного рядка

  • q завершити складний повторний запис

Після цього ви використовуєте @aдля запуску складного повтору, що зберігається в a, а потім ви можете використовувати @@для повторного останнього запущеного складного повтору.


1

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

припустимо, перший блок знаходиться у рядку 1, а другий блок починається з рядка 10 з початкового положення курсора у рядку №1.

(\ n означає натискання клавіші введення.)

1. abc
   def
   ghi        

10. 123
    456
    789

з макросом за допомогою команд: скопіюйте, вставте та з'єднайте.

qaqqa: + 9y \ npkJjq2 @ a10G3dd

з макросом за допомогою команд переміщують рядок по n-му рядку і приєднуються.

qcqqc: 10м. \ nkJjq2 @ c

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.