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?

ThiefMaster
źródło
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).

szaleństwo
źródło
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? ”.

ib.
źródło
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.
mliebelt
źródło
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
set ve=allpowinien pomóc, patrz vimdoc.sourceforge.net/htmldoc/options.html#'virtualedit '
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)
Kenneth Ballenegger
źródło
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.
Kevinlawler
źródło
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
kev
źródło
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
Zmieniać załogę
źródło
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.

Gerardo Marset
źródło
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

dvk317960
źródło