Połącz wiele linii (dwa bloki) w Vimie


323

Chciałbym scalić dwa bloki linii w Vimie, tzn. Pobrać linie n..mi dołączyć je do linii a..b. Jeśli wolisz wyjaśnienie pseudokodu:[a[i] + b[i] for i in min(len(a), len(b))]

Przykład:

abc
def
...

123
45
...

powinno stać się

abc123
def45

Czy istnieje dobry sposób na zrobienie tego bez ręcznego kopiowania i wklejania?


Więc ... chcesz dołączyć do linii naprzemiennych? Oznacza to, że chcesz połączyć linię xz łączeniem x+2?
— larsks

1
Nie, mam dwa oddzielne bloki. Pseudocode-ish:[a[i] + b[i] for i in min(len(a), len(b))]
— ThiefMaster

2
Zobacz podobne pytanie (i odpowiedź!) Tutaj
— NWS

Odpowiedzi:


880

Z pewnością możesz to wszystko zrobić za pomocą pojedynczego kopiowania / wklejania (przy użyciu wyboru trybu blokowego), ale zgaduję, że nie tego chcesz.

Jeśli chcesz to zrobić za pomocą tylko poleceń Ex

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

przekształci się

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

w

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

AKTUALIZACJA: Odpowiedź na tak wiele pozytywnych opinii zasługuje na dokładniejsze wyjaśnienie.

W Vimie możesz użyć znaku potoku ( |) do połączenia wielu poleceń Ex, więc powyższe jest równoważne z

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

Wiele poleceń Ex akceptuje zakres wierszy jako argument przedrostka - w powyższym przypadku 5,8przed deli 1,4przed s///określają, na których wierszach działają polecenia.

delusuwa podane linie. Może przyjmować argument rejestru, ale jeśli go nie podano, zrzuca linie do rejestru bez nazwy @", podobnie jak usuwanie w trybie normalnym. let l=split(@")następnie dzieli usunięte linie na listę, używając domyślnego separatora: białe znaki. Aby działać poprawnie na danych wejściowych, które miały białe znaki w usuniętych wierszach, takie jak:

more than 
hour 
our 
never 
ever
after
work is
over
~

musielibyśmy określić inny separator, aby zapobiec „praca” od bycia podzielony na dwie listy elementów: let l=split(@","\n").

Wreszcie w podstawieniu s/$/\=remove(l,0)/zamieniamy koniec każdej linii ( $) wartością wyrażenia remove(l,0). remove(l,0)zmienia listę l, usuwając i zwracając jej pierwszy element. To pozwala nam zastąpić usunięte linie w kolejności, w jakiej je czytamy. Zamiast tego możemy zastąpić usunięte linie w odwrotnej kolejności za pomocą remove(l,-1).


1
hmm ... Muszę tylko raz nacisnąć Enter. I nie wstawi spacji między dwiema połówkami. Jeśli w wierszach jest trochę wolnego miejsca (jak w przykładzie „work it”), to nadal tam będzie. Można pozbyć się wszelkich powierzchni spływu, używając s/\s*$/zamiast s/$/.
— szaleństwo w

1
Dziękuję za wyjaśnienie. :sil5,8del | let l=split(@") | sil1,4s/$/\=remove(l,0)/ | call histdel("/", -1) | nohlswydaje się być jeszcze lepszy, ponieważ czyści historię wyszukiwania po uruchomieniu. I nie wyświetla komunikatu „x więcej / mniej linii” wymagającego naciśnięcia Enter.
— ThiefMaster

11
Jeśli chcesz pełne odniesienie vim dla tej odpowiedzi: :help range, :help :d, :help :let, :help split(), :help :s, :help :s\=, :help remove().
— Benoit

16
Dopilnowanie, aby tacy ludzie jak Ty chcieli publikować odpowiedzi takie jak to, stałem się moderatorem. Dobry koncert :)
— Tim Post

1
Istnieje problem, jeśli po pierwszych 4 zdaniach nie ma białego pola.
— Reman

58

Elegancki i zwięzły komenda Ex rozwiązywania problemu można uzyskać poprzez połączenie :global, :moveoraz :joinpoleceń. Zakładając, że pierwszy blok linii zaczyna się w pierwszym wierszu bufora i że kursor znajduje się w linii bezpośrednio poprzedzającej pierwszą linię drugiego bloku, polecenie jest następujące.

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

Aby uzyskać szczegółowe wyjaśnienie tej techniki, zobacz moją odpowiedź na podobne pytanie „ zachowanie wklejania VIM-a” po wyjęciu z pudełka? ”.


E16: Invalid Range- ale i tak działa. Po usunięciu 1,działa bezbłędnie.
— ThiefMaster

I szkoda, że ​​nie możesz zaakceptować więcej niż jednej odpowiedzi - w przeciwnym razie twój też miałby zielony znak!
— ThiefMaster

3
Bardzo dobrze! Nie wiedziałem, o :move, a :join!, ani co ''rozumie jako argument zakres ( :help '') i co +i -rozumie jako modyfikatory Range ( :help range). Dzięki!
— szaleństwo

@ ThiefMaster: Polecenie jest przeznaczone do użycia, gdy dwa zakresy łączących się linii sąsiadują ze sobą, tak że kursor początkowo znajduje się w ostatnim wierszu pierwszego bloku, który bezpośrednio poprzedza pierwszy wiersz drugiego bloku. (Zobacz połączoną odpowiedź i pytanie.) Polecenie działa zgodnie z przeznaczeniem, nawet jeśli liczba wierszy w blokach różni się, chociaż w takim przypadku wyświetla komunikat o błędzie. Komunikaty o błędach można ukryć, przygotowując sil!się do polecenia.
— ib.

2
@ib .: Myślę, że dobrym pomysłem byłoby również zamieścić szczegółowe wyjaśnienie w tej odpowiedzi.
— ThiefMaster

44

Aby połączyć bloki linii, musisz wykonać następujące czynności:

  1. Przejdź do trzeciej linii: jj
  2. Wejdź do trybu bloku wizualnego: CTRL-v
  3. Zakotwicz kursor na końcu linii (ważne dla linii o różnej długości): $
  4. Idź do końca: CTRL-END
  5. Wytnij blok: x
  6. Idź na koniec pierwszego wiersza: kk$
  7. Wklej tutaj blok: p

Ten ruch nie jest najlepszy (nie jestem ekspertem), ale działa tak, jak chciałeś. Mam nadzieję, że będzie krótsza wersja.

Oto warunki wstępne, aby ta technika działała dobrze:

  • Wszystkie wiersze bloku początkowego (w przykładzie w pytaniu abci def) mają tę samą długość XOR
  • pierwsza linia bloku początkowego jest najdłuższa i nie przejmujesz się dodatkowymi spacjami pomiędzy nimi) XOR
  • Pierwsza linia bloku początkowego nie jest najdłuższa, a na końcu są dodatkowe spacje.

Woah, to interesujące! Nigdy bym nie pomyślał, że tak będzie.
— voithos

13
Działa tak, jak chciał, ponieważ abci defsą tej samej długości. Wybór bloku zachowa wcięcia usuniętego tekstu, więc jeśli kursor znajduje się w krótkiej linii podczas wstawiania tekstu, linie są wstawiane między literami na dłuższych - a spacje są dodawane do krótszych, jeśli kursor znajduje się na dłuższym jeden.
— Izkata

Rzeczywiście ... Czy istnieje sposób, aby zrobić to poprawnie za pomocą funkcji kopiowania i wklejania bloków? Jest to w końcu najłatwiejszy sposób (zwłaszcza jeśli z jakiegoś powodu nie możesz znaleźć bardziej skomplikowanych sposobów).
— ThiefMaster

2
Jak mówi @Izkata, napotkasz problem wstawiania tekstu między dłuższymi liniami. Aby obejść ten problem, po prostu dodaj więcej spacji na końcu pierwszego wiersza, aby był najdłuższy, a następnie wklej blok tekstu. Po wykonaniu tej czynności skompresowanie wielu spacji do jednego jest tak proste, jak:%s/ \+/ /g
— Khaja Minhajuddin

1
— Ben

19

Oto jak bym to zrobił (z kursorem w pierwszym wierszu):

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

Musisz wiedzieć dwie rzeczy:

  • Numer linii, od której zaczyna się pierwsza linia drugiej grupy (w moim przypadku 5), oraz
  • liczba linii w każdej grupie (3 w moim przykładzie).

Oto co się dzieje:

  • qazapisuje wszystko do następnego qw „buforze” w a.
  • ma tworzy znak w bieżącej linii.
  • :5<CR> przechodzi do następnej grupy.
  • y$ szarpnie resztę linii.
  • 'a wraca do znaku, ustawionego wcześniej.
  • $p wkleja na końcu linii.
  • :5<CR> wraca do pierwszej linii drugiej grupy.
  • dd usuwa to.
  • 'a wraca do znaku.
  • jq przechodzi w dół o jedną linię i zatrzymuje nagrywanie.
  • 3@a powtarza akcję dla każdej linii (3 w moim przypadku)

1
Musisz nacisnąć [Enter]po :5każdym wpisaniu, inaczej to nie zadziała.
— Shawn J. Goff

1
Jestem na gvimie. Czy jest jakiś sposób, aby skopiować i wkleić to polecenie w GVim? ^ V automatycznie wkleja w trybie wstawiania (co ma sens, tego zwykle ludzie chcą), nawet jeśli aktualnie jestem w trybie normalnym (?). Próbowałem, :norm qama:5<CR>y$'a$p:5<CR>dd'ajq3@aale wydaje się, że to tylko wykonanie q.
— ThiefMaster

1
ThiefMaster: Spróbuj :let @a="ma:5^My$'a$p:5^Mdd'aj" | normal 4@a, gdzie ^Mznaki są wpisane przez naciśnięcie CTRL-V, a następnie Enter.
— szaleństwo

8

Jak wspomniano w innym miejscu, najlepszym rozwiązaniem jest wybór bloku. Możesz jednak użyć dowolnego wariantu:

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

Ta metoda opiera się na wierszu poleceń systemu UNIX. pasteNarzędziowy został stworzony do obsługi tego rodzaju linii łączących.

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.

Korzystanie z wyboru bloku nie jest jedyną drogą. Nie jest też najprostszy. Pożądane ( paste -dpodobne) zachowanie można zrealizować za pomocą krótkiego polecenia Vima, jak pokazano w mojej odpowiedzi .
— ib.

3
Poza tym jestem w systemie Windows, więc rozwiązanie wymagałoby otwarcia połączenia SSH z moją maszyną z systemem Linux i wklejenia z edytora do terminala iz powrotem.
— ThiefMaster

3

Przykładowe dane są takie same jak dane rampionu.

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

3

Nie sądzę, żeby to skomplikowało. Chciałbym tylko ustawić virtualedit na
( :set virtualedit=all)
Wybierz blok 123 i wszystkie poniżej.
Umieść po pierwszej kolumnie:

abc    123
def    45
...    ...

i usuń wielokrotne spacje od do 1 spacji:

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

Pytanie faktycznie nie wymaga spacji, zrobiłbym coś takiego gvV:'<,'>s/\s+//g(vim powinien automatycznie wstawić '<,'>dla ciebie, więc nie musisz ręcznie pisać).
— Ben

2

Użyłbym skomplikowanych powtórzeń :)

Biorąc to pod uwagę:

aaa
bbb
ccc

AAA
BBB
CCC

Po ustawieniu kursora w pierwszym wierszu naciśnij następujące polecenie:

qa}jdd''pkJxjq

a następnie naciśnij @a(a następnie możesz użyć @@) tyle razy, ile potrzeba.

Powinieneś skończyć z:

aaaAAA
bbbBBB
cccCCC

(Plus nowa linia.)

Wyjaśnienie:

  • qa rozpoczyna nagrywanie złożonego powtórzenia w a

  • } przeskakuje do następnej pustej linii

  • jdd usuwa następny wiersz

  • '' wraca do pozycji przed ostatnim skokiem

  • p wklej usuniętą linię pod bieżącą

  • kJ dołącz bieżącą linię na końcu poprzedniej

  • xusuń spację, która Jdodaje między połączone linie; możesz to pominąć, jeśli chcesz miejsca

  • j przejdź do następnej linii

  • q zakończyć złożone powtarzanie nagrywania

Następnie użyjesz @ado uruchomienia złożonego powtórzenia złożonego zapisanego w a, a następnie możesz użyć @@do ponownego uruchomienia ostatniego uruchomionego powtórzenia złożonego.


1

Istnieje wiele sposobów osiągnięcia tego celu. Scalę dwa bloki tekstu przy użyciu dowolnej z dwóch poniższych metod.

załóżmy, że pierwszy blok znajduje się na linii 1, a drugi blok zaczyna się od linii 10 z początkową pozycją kursora na linii nr 1.

(\ n oznacza naciśnięcie klawisza enter.)

1. abc
   def
   ghi        

10. 123
    456
    789

z makrem za pomocą poleceń: kopiuj, wklej i dołącz.

qaqqa: + 9y \ npkJjq2 @ a10G3dd

za pomocą makra za pomocą poleceń przenieś linię na n-ty numer linii i połącz.

qcqqc: 10m. \ nkJjq2 @ c

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.