Scal bloki, przeplatając linie

15

Czy istnieje dedykowany sposób scalenia dwóch bloków tekstu przez przeplatanie linii, na przykład przejście z tego:

a1
a2
a3
a4
  b1
  b2
  b3
  b4

do tego:

a1
  b1
a2
  b2
a3
  b3
a4
  b4

w kilku poleceniach?

EDYCJA : Naprawdę podoba mi się rozwiązanie Sato Katsury , oto jak go wdrożyłem:

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>
iago-lito
źródło
Teraz jestem ciekawy - jaki jest twój przypadek użycia? Czy zmieniasz nazwy napisów blokowych na sezon telewizyjny?
VanLaser,
@VanLaser Haha, nie jestem. Przeważnie analizuję dane wyjściowe z programu, który muszę sprawdzić pod kątem spójności w kolejności tworzenia / następnie opóźnionego odczytu obiektów. Bloki przeplatania ułatwiają dopasowanie odpowiednich linii w opóźnionych blokach wyjściowych. Czasami też muszę przeplatać wiersze kodów z powtarzającymi się, podobnymi instrukcjami rejestrowania lub testów porównawczych. Generowanie tych instrukcji jest łatwe dzięki makrom, a następnie przeplatanie ich z rzeczywistym kodem jest teraz tylko kilka naciśnięć klawiszy dzięki tej funkcji, co jest świetne :)
iago-lito
1
@ lago-lito - dzięki za odpowiedź! Tak, Vim jest dość wszechstronny :) Twoje wyrażenie „parsowanie oczu” skłoniło mnie do myślenia w scroll-bindingdwóch oknach Vima.
VanLaser,
Mam problem z użyciem tego, jak wybieracie dwa kolejne bloki? Czy muszą być obok siebie?
cbcoutinho
@cbcoutinho Tak, mają :) Nie jestem pewien, czy możesz wybrać je oba inaczej. W pokazanym przykładzie kładę kursor (powiedzmy) b1, a następnie uderzam, vipaby zaznaczyć cały fragment, a następnie, ,itktóry jest <map-I've-Picked>. Czy to nie działa po twojej stronie?
iago-lito

Odpowiedzi:

8

Nie ma specjalnego sposobu na zrobienie tego (o ile mi wiadomo), ale tak, można to zrobić za pomocą kilku poleceń:

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

Możesz go uruchomić :call Interleave(5, 8, 1). Pierwszy parametr to pierwszy wiersz do przeniesienia, drugi to ostatni wiersz, a trzeci to, gdzie je przenieść. Prawdopodobnie chcesz włączyć numery linii, aby zobaczyć, co robisz ( :set number).

Zakłada się, że bloki się nie pokrywają. Zobacz :help :movei :help range()zrozum, jak działa ta funkcja.

Prawdopodobnie są lepsze sposoby na podniesienie dwóch bloków. Wokół unosi się wtyczka, która ma umożliwiać zamianę dwóch bloków. Nie pamiętam nazwy wtyczki, ale autor (być może słynny Dr. Chip?) Poświęcił więcej czasu na znalezienie interfejsu niż ja. :)

Sato Katsura
źródło
Słodkie! Potrzebuję tylko dwóch argumentów, ponieważ dwa bloki są ciągłe i mają ten sam rozmiar: starti size. Dzięki funkcji homebrew, która pobiera te wartości z zaznaczenia, będzie to po prostu idealne. Pracuję nad tym. :)
iago-lito
Ciekawe połączenie krzyżowe ? ;)
iago-lito
13

Oto kolejna alternatywa:

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

Najpierw skopiuj linie, które są 4 linie poniżej, za bieżącą linią ( :h :t), a następnie usuń kolejne linie b ( :h :d)

Jeszcze lepsze jest to polecenie:

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

Co oznacza, że ​​dla każdej linii rozpoczynającej się od znalezienia znajdź następną linię rozpoczynającą się od „b” i przenieś ją poniżej bieżącej linii.

Christian Brabandt
źródło
1
Mam drugie polecenie „E16: nieprawidłowy zakres”. Spróbowałem .+,$dzamiast tego i to zadziałało (tak jak zrobiłem .+,.+4d).
Peter Lewerin
Nie jestem pewien, dlaczego tak się dzieje
Christian Brabandt
1
nie, nie robi. Przeczytaj: h: zakres, zawsze możesz użyć bezpośredniej numeracji zamiast wyszukiwania wyrażenia regularnego
Christian Brabandt
2
@ iago-lito Druga sztuczka zawsze działa, ale musisz zmienić /^\s*bna inną :range. np .: wybierz 1. blok, wykonaj'<,'>g/^/'>+1m.
dedowsdi
1
@ iago-lito Zasadniczo to samo co odpowiedź Christiana. Nic nie jest zakodowane, jeśli wizualnie wybierzesz pierwszy blok, '>+1oznacza to początek drugiego bloku.
dedowsdi
3

Jeśli chcesz się trochę zabawić z makrami i znakami, możesz spróbować czegoś takiego:

  • Najpierw postawić znak (tutaj a) w wierszu zawierającym a1zma

  • Przejdź do wiersza zawierającego b1i oznacz gomb

  • Rozpocznij rejestrowanie makra w żądanym rejestrze (tutaj rejestr q)qq

  • Wstaw następujące elementy do swojego makra: ddmb'apjma'b

  • Zatrzymaj rejestrowanie makra za pomocą q

  • Zagraj w nią tyle razy, ile potrzeba, X@qgdzie Xjest czas na grę.

Aby szczegółowo opisać makro:

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

Edytuj Jak wspomniał o tym lago-lito w komentarzach, metoda ta nadpisze znaki i bufory.

  • Jeśli chodzi o oceny, nie sądzę, aby to był prawdziwy problem: rzadko używam wszystkich 26 znaków w buforze i myślę, że przez większość czasu można znaleźć 2 darmowe oceny.

  • W przypadku bufora możliwe jest zapisanie go w zmiennej tymczasowej: Przed zarejestrowaniem makra użyj :let saveReg=getreg('"')do zapisania rejestru, a po wykonaniu akcji użyj, :call setreg('"', saveReg)aby przywrócić rejestr do poprzedniego stanu.

W każdym razie muszę przyznać, że to rozwiązanie jest tylko szybkim obejściem i nie jest optymalne: Moim zdaniem odpowiedź Christana jest najlepsza i powinna zostać zaakceptowana, ponieważ nie psuje buforów i znaków, nie zmusza użytkownika do tworzenia funkcja i pokazuje siłę globalnego polecenia.

statox
źródło
Ciekawy. Niestety nadpisuje to treść znaków i rejestrów, których mogę używać;)
iago-lito
@ lago-lito: w rzeczy samej zastępuje znaki i bufory. W przypadku ocen nigdy nie używam wszystkich 26 znaków w moich buforach, więc nie sądzę, że to naprawdę problem. W przypadku buforów może to być większy problem, myślę, że często możesz znaleźć nieużywany bufor lub, jeśli naprawdę nie możesz, użyć zmiennej tymczasowej i funkcji getreg()i setreg()zapisać bufor. Ale zgadzam się, że nie jest to optymalne rozwiązanie :-)
statox
1

Właśnie widziałem inne podobne pytanie, a rozwiązanie polega na:

Skocz na środek plus jeden:

Mj

I biegnij:

:,$g/./exe 'm' 2*line('.')-line('$')-1
SergioAraujo
źródło
Ciekawe :) Uważaj jednak, że to przeplata cały plik, a nie tylko wybrany akapit!
iago-lito