Wstaw numer przyrostowy do każdej linii w zaznaczeniu lub dopasowaniu

10

Mam problem. Mogę wymyślić dwa ogólne podejścia do rozwiązania, ale nie znam szczegółów dla żadnego z nich.

...
Level 1:    cũng    also
Level 1:    và      and
Level 1:    như     like; such as
Level 2:    các     plural marker
Level 2:    của     belonging to
...

Do każdej linii rozpoczynającej się od „Poziomu n” chcę wstawić liczbę rozpoczynającą się od „01”. Dla uproszczenia wstawmy liczbę.

Podejście 1: Ręcznie wybierz wszystkie linie o tym samym poziomie. Wywołaj magię, której wkrótce się nauczę.

Podejście 2: Napisz wyszukiwanie i zamień, które pasują do wszystkich wierszy o danym poziomie, który przy każdym dopasowaniu zawiera liczbę w tekście zamiany, która zwiększa się o jeden z każdym dopasowaniem.

Znalazłem podobne pytania na StackOverflow lub na innych stronach Vima, ale wydaje się, że każdy z nich ma jeden lub więcej z następujących problemów:

  1. Chodzi o wstawienie bieżącego numeru linii, a nie o dowolnym, ale rosnącym numerze.
  2. Nie wprowadza zerowej liczby.
  3. W rzeczywistości nie działa w przypadku wyborów w moim Vimie 7.4 działającym w systemie Windows 7. (Te powodują błąd E481: No range allowed).

Korzystam z gVim w systemie Windows z mswin.vim, ale najlepsze może być rozwiązanie, które działa na wszystkich instalacjach waniliowych Vim bez konieczności dostosowywania instalacji.

hippietrail
źródło
Najlepsze tagi, jakie mogłem wymyślić dla tego pytania, nie istnieją: wybór i zakres, więc możesz ponownie otagować lub utworzyć jeden z tych tagów.
hippietrail
1
Ta wtyczka nie jest kompletnym rozwiązaniem twojego problemu, ale jest niezwykle przydatna do dodawania kolumn liczb: VisIncr . Dokumenty tutaj . FWIW.
lcd047,

Odpowiedzi:

17

Podobnie do odpowiedzi na https://vi.stackexchange.com/a/818/227 , możesz użyć polecenia globalnego.

Za jego pomocą możesz poinstruować vima, aby szukał linii pasujących do wzorca, a następnie wykonywał na nim polecenia.

W twoim przypadku chcesz wstawić tekst do linii zaczynających się od „Poziomu N:”, aby nasze globalne polecenie mogło być

:g/^Level \d:/{COMMANDS}

Użycie polecenia zastępczego (zastąpienie wyrażenia regularnego) dla polecenia

Polecenia są zabawniejsze. Zwykle lubię zastępować wyrażenia regularne takimi rzeczami, ponieważ zmienne są łatwe w użyciu.

Przykład twojego pytania

:let i = 1 | g/^Level \d:/s/^/\=printf("%02d ", i)/ | let i = i+1

Jak to działa

W sekcji zastępowania polecenia podstawienia może znajdować się wyrażenie.

Pierwszą rzeczą, jaką zrobimy, jest ustawienie zmiennej ijako liczby początkowej. Wybrałem 1, ale wystarczy dowolna liczba.let i = 1

Następnie uruchamiamy nasze globalne polecenie, które ustawia nas do działania na dopasowanych liniach. g/^Level \d:/

Nasze globalne polecenie wstawi wartość i zwiększy licznik za pomocą polecenia podstawienia i polecenia let.s/^/\=printf("%02d ", i)/ | let i = i+1

Wyrażenie regularne polecenia podstawienia znajduje początek wiersza ^i zastępuje go wyrażeniem, a nasze wyrażenie będzie wynikiem sformatowanego wydruku. Podobnie jak w języku C, printf vima przyjmuje parametry formatowania. %02doznacza konwersję argumentu tak, jakby był liczbą dziesiętną d, zajmując co najmniej 2 spacje 2i pad z 0 0. Aby uzyskać szczegółowe informacje i inne opcje konwersji (w tym formatowanie zmiennoprzecinkowe), zobacz :help printf. Dajemy printf naszą zmienną zliczającą ii daje nam 01to pierwszy raz, 02drugi raz itp. Przydaje się to komendzie podstawienia, aby zastąpić początek linii, skutecznie wstawiając wynik printf na początku.

Zauważ, że mogę umieścić spację po d: "%02d ". Nie pytałeś o to w pytaniu (i nie widziałem przykładowego wyniku), ale podejrzewałem, że chcesz oddzielić liczbę od słowa „Poziom”. Usuń spację z ciągu podanego printf, aby wstawić liczbę tuż obok L na poziomie.

Na koniec let i = i + 1zwiększa to nasz licznik po każdej zmianie.

Można to ogólnie zastosować do zastąpienia części linii, które są zgodne innymi kryteriami, dowolnymi danymi funkcjonalnymi.

Używanie połączonych normalnych poleceń

Jest to dobre w przypadku prostych wstawek lub złożonej edycji. Podobnie jak w przypadku podstawiania, użyjemy globalnego, aby dopasować, ale zamiast podstawienia wyrażeń regularnych wykonamy serię operacji, jakby zostały wpisane przez użytkownika.

Przykład twojego pytania

:let i = 1 | g/^Level \d:/execute "normal! I" . printf("%02d ", i) | let i = i+1

Jak to działa

Użyte wartości są bardzo podobne do zamiennika (nadal używamy printf do sformatowania naszego numeru, aby był uzupełniony o 2 cyfry), ale operacja jest inna.

Tutaj używamy polecenia execute, które pobiera ciąg i uruchamia go jako polecenie ex ( :help :exe). Konstruujemy ciąg, który łączy „normalne! Ja” z naszymi danymi, które będą „normalne! I01” za pierwszym razem i „normalne! I02” za drugim razem itp.

Te normaloperacje Wykonuje polecenie jak w trybie normalnym. W tym przykładzie naszym normalnym poleceniem jest I, które wstawia na początku linii. Gdybyśmy go użyli dd, usunąłby linię, ootworzyłby nową linię po dopasowanej linii. To tak, jakbyś wpisał I(lub jakąkolwiek inną operację) w trybie normalnym. używamy !po, normalaby upewnić się, że żadne mapowania nie staną na naszej drodze. Zobaczyć :help :normal.

To, co jest wstawiane, to wartość naszego printf, jak w pierwszym przykładzie użycia substytutu.

Ta metoda może być bardziej wyrafinowana niż regex, ponieważ możesz robić takie rzeczy, jak execute "normal! ^2wy" . i . "th$p"przejście do początku tekstu ^, przesuwanie do przodu o 2 słowa 2w, szarpanie do i-tego znaku „h” y" . i . "th, przejście do końca wiersza $i wklejanie p.

To prawie jak uruchamianie makra, ale tak naprawdę nie zużywa rejestru i może łączyć ciągi z dowolnych wyrażeń. Uważam to za bardzo potężne.

Podejdź, gdzie każdy poziom ma swój własny licznik

Możesz chcieć, aby każdy poziom miał swój własny licznik. Jeśli znasz maksymalną liczbę poziomów z wyprzedzeniem, możesz wykonać następujące czynności (dodanie dodatkowego kodu w celu znalezienia największego poziomu może nie być zbyt trudne, ale spowodowałoby, że ta odpowiedź byłaby zbyt długa. Jest coraz dłuższa).

Po pierwsze, wypuśćmy ja, na wypadek, gdybyśmy użyli go już jako liczby całkowitej. Nie możemy przekonwertować i na listę, musimy ją w ten sposób utworzyć.

:unlet! i

Następnie ustawmy i jako listę zawierającą liczbę poziomów. W swoim pytaniu pokazałeś 2, ale załóżmy 10 dla zabawy. Ponieważ indeksowanie list jest oparte na 0, a nie chcę zawracać sobie głowy poprawianiem opartego na 1, tak jak twoja lista, utworzymy po prostu wystarczającą liczbę elementów (11) i nigdy nie używamy indeksu 0.

:let j = 0
:let i = []
:while j < 11 | let i += [1] | let j += 1 | endwhile

Następnie potrzebujemy sposobu na uzyskanie numeru poziomu. Na szczęście substytut jest również dostępny jako funkcja, więc podamy mu naszą linię i wyodrębnimy numer poziomusubstitute(getline("."), "^Level \\(\\d\\):.*", "\\=submatch(1)", "")

Ponieważ i jest teraz listą 11 1s (każdy indeks jest licznikiem naszego poziomu), możemy teraz dostosować jeden z powyższych przykładów, aby wykorzystać wynik tego podstawienia:

Za pomocą polecenia zastępczego:

:unlet! i | unlet! j | let j = 0 | let i = [] | while j < 11 | let i += [1] | let j += 1 | endwhile
:g/^Level \d:/let ind=str2nr(substitute(getline("."), "^Level \\(\\d\\):.*", "\\=submatch(1)", "")) | s/^/\=printf("%02d ", i[ind])/ | let i[ind] += 1

Za pomocą normalnego polecenia:

:unlet! i | unlet! j | let j = 0 | let i = [] | while j < 11 | let i += [1] | let j += 1 | endwhile
:g/^Level \d:/let ind=str2nr(substitute(getline("."), "^Level \\(\\d\\):.*", "\\=submatch(1)", "")) | execute "normal! I" . printf("%02d ", i[ind]) | let i[ind] += 1

Przykładowe dane wejściowe:

Level 1: stuff

Level 1: Stuff

Some text
Level 3: Other

Level 1: Meh

Level 2: More

Przykładowe dane wyjściowe:

01 Level 1: stuff

02 Level 1: Stuff

Some text
01 Level 3: Other

03 Level 1: Meh

01 Level 2: More
John O'M.
źródło
Wow, bardzo encyklopedyczny. Przeczytałem tylko pierwszą część i mam wrażenie, że nauczyłem się więcej o Vimie właśnie z tego, niż przez ostatnie kilka lat. Jest to rodzaj odpowiedzi, która sprawia, że ​​Stack Exchange jest lepszy niż przeciętne strony z pytaniami i odpowiedziami, a także pokazuje zalety posiadania SE specyficznej dla Vima.
hippietrail
3

Możesz budować z https://stackoverflow.com/a/4224454/15934, aby zerować swoje liczby.

" A reminder: how to start numbers at the first line
:'<,'>s/^\s*\zs/\=(line('.') - line("'<")+1).'. '

Ale aby uprościć akcję uzupełniania liczb, wybrałbym parę funkcji i polecenia:

command!  -range=% -nargs=? PrependNumbers <line1>,<line2>call s:Prepend(<args>)

function! s:ReplExpr(nb_digits, number)
  return repeat('0', a:nb_digits - strlen(a:number)).a:number
endfunction

function! s:Prepend(...) range
  let pattern = a:0 > 0 ? '\ze'. a:1 : '^'
  let nb_values = (a:lastline - a:firstline) + 1
  let nb_digits = strlen(nb_values)
  exe ':'.a:firstline.','a:lastline.'s#'.pattern.'#\=s:ReplExpr(nb_digits, 1+ line(".")-'.a:firstline.')." "#'
endfunction

Następnie wybierz swoje linie i wpisz :PrependNumber(zobaczysz :'<,'>PrependNumber. [Uwaga: Polecenie przyjmuje opcjonalny parametr: wzór, przed którym liczba zostanie wstawiona]

Luc Hermitte
źródło
Ale to nie line(".")znaczy „użyj bieżącego numeru linii”? To był mój problem 1. z poprzednimi odpowiedziami, które mogłem znaleźć.
hippietrail
1
Tak to jest. Dlatego odejmuję numer pierwszej linii w zakresie.
Luc Hermitte
Ach OK, jeszcze go nie testowałem, bo nawet nie pamiętam, jak wpisać skrypt w Vimie ... najpierw muszę przeczytać o tym (-:
hippietrail
1
W systemie Windows skopiuj kod do $HOME/vimfiles/plugin/whatevernameyouwish.vim. Lub nawet $HOME/_vimrc(nazwa pliku systemu Windows), jeśli wolisz (po raz pierwszy). Jeśli nie masz pewności, co $ HOME znajduje się na twoim komputerze, zapytaj vima, co on myśli ->:echo $HOME
Luc Hermitte
1
Nie potrzebujesz nawet, :sourcejeśli skrypt vim jest poprawnie zainstalowany we właściwym katalogu ({rtp} / plugin lub {rtp} / ftplugin / {filetype} / ftplugin dla wtyczek specyficznych dla rodzaju pliku). :sourcez tym musieliśmy grać ponad półtorej dekady temu.
Luc Hermitte
2

Możesz do niego podejść, rejestrując makro. Zaczynając od oryginalnego pliku, dodaj pierwszą instancję z numerem.

01 Level 1:    cũng    also
Level 1:    và      and
Level 1:    như     like; such as
Level 2:    các     plural marker
Level 2:    của     belonging to

Przesuń kursor do 01, wybierz i pociągnij go yiw. Teraz chcesz zarejestrować swoje działania od tego momentu.

qq/^Level 1<CR>P<C-A>A<space><esc>0yiwq

  • qq Uruchom makro w rejestrze q
  • /^Level 1<CR> Wyszukaj wiersz rozpoczynający się od „Poziomu 1”
  • P wklej przed kursorem (zawiera twój numer)
  • <C-A> zwiększa liczbę
  • A<space><esc> Wstaw spację po numerze
  • 0 Przejdź na początek
  • yiw wybierz bieżący numer
  • q Zakończ makro

Następnie powtórz to makro za pomocą @q.

jecxjo
źródło
1
Czy <C-A>kontrola + a? To zaznacza wszystko w Vimie na Windowsie.
hippietrail
To kontrola + a. W vimie powinno się zwiększać liczbę. Jeśli zmieniłeś przypisania klawiszy, aby wykonywać czynności systemu Windows zamiast działań Vima, nie będziesz w stanie wykonywać większości rzeczy, które ludzie tutaj zamieszczają.
jecxjo
Nie. Wersja Vima dla Windows ma różne powiązania z powodu nieskończonej mądrości. Po prostu używam waniliowych gotowych wiązań. Nigdy nie zmieniałem wiążącego klucza Vima od ponad dwudziestu lat, które pamiętam (-:
hippietrail
Nie powinno tak być, moja instalacja systemu Windows ma normalne powiązania vim. Czy to możliwe, że zainstalowałeś czyjąś wersję vima? A może działasz w vimie w trybie „Easy”? wierzę, że Windows instaluje ikony pulpitu z opcjami trybu normalnego i łatwego.
jecxjo
3
Nie, ponieważ domyślna instalacja w systemie Windows ładuje plik mswin.vim. Jeśli utworzysz własny vimrc i nie załadujesz mswin.vim, otrzymasz normalne powiązania vim. Trzymam jeden vimrc dla wszystkich moich instalacji (Linux, Mac i Windows) i nigdy nie mam do czynienia z mswin.vim. Aby uzyskać więcej informacji na ten temat, zobacz stackoverflow.com/questions/289681/…
jecxjo,