Jak napisać fold-expr?

10

Przeczytałem stronę pomocy dotyczącą fold-expr ( :h fold-expr), ale nie wyjaśniło to, jakiej składni użyto w wyrażeniu.

Były cztery przykłady:

  1. :set foldexpr=getline(v:lnum)[0]==\"\\t\"
  2. :set foldexpr=MyFoldLevel(v:lnum)
  3. :set foldexpr=getline(v:lnum)=~'^\\s*$'&&getline(v:lnum+1)=~'\\S'?'<1':1
  4. :set foldexpr=getline(v:lnum-1)=~'^\\s*$'&&getline(v:lnum)=~'\\S'?'>1':1

Zrozumiałem, że v:lnumjest to linia, która wymaga poziomu wcięcia, a to wyrażenie drugie jest wywołaniem funkcji.

co z wyrażeniami 1,3 i 4? Czy ktoś może mi je wyjaśnić?

elyashiv
źródło
Rozumiem, że wyrażenie powinno zwracać liczbę i ta liczba zostanie wykorzystana do ustalenia, na którym poziomie dana linia zostanie zwinięta. 0 nie jest złożone, 1 jest najbardziej zewnętrznym foldem, 2 jest
foldem

Odpowiedzi:

12

Od :help 'foldexpr':

Jest oceniany dla każdej linii w celu uzyskania poziomu zagięcia

foldexprJest oceniany, więc to musi być kod VimL; nie ma wzmianki o „specjalnej składni” lub podobnej. Wynik tej oceny kontroluje to, co Vim uważa za pasowanie, czy nie.

Możliwe wartości to

  0                     the line is not in a fold
  1, 2, ..              the line is in a fold with this level
  "<1", "<2", ..        a fold with this level ends at this line
  ">1", ">2", ..        a fold with this level starts at this line

To nie jest pełna lista; tylko te użyte w przykładach w twoim pytaniu. Zobacz :help foldexprpełną listę.


Pierwszy

Pierwszy z nich jest dość prosty po dodaniu spacji i usunięciu ukośników odwrotnych potrzebnych do uruchomienia tego :setpolecenia:

getline(v:lnum)[0] == "\t"
  1. getline(v:lnum) pobiera całą linię.
  2. [0] dostaje pierwszą tego postać
  3. i == "\t"sprawdza, czy jest to znak tabulacji.
  4. VimL nie ma „true” ani „false”, po prostu używa „0” dla false i „1” dla true. Więc jeśli ta linia zaczyna się od tabulatora, jest składana na foldlevel 1. Jeśli nie, to nie jest ona składana (0).

Jeśli rozwinąłbyś to, by policzyć liczbę zakładek, miałbyś składanie oparte na wcięciach (przynajmniej wtedy, gdy expandtabnie jest włączone).


Trzeci

Trzeci nie jest tak naprawdę bardziej skomplikowany niż pierwszy; tak jak w pierwszym przykładzie, najpierw chcemy, aby był bardziej czytelny:

getline(v:lnum) =~ '^\s*$' && getline(v:lnum + 1) =~ '\S' ? '<1' : 1
  1. Otrzymujemy całą linię getline(v:lnum)
  2. Dopasowujemy to jako wyrażenie regularne =~do do '^\s*$'; ^zakotwiczenia na początku, \soznacza dowolny znak spacji, *oznacza powtórzenie poprzedniego zero lub więcej razy i $zakotwiczenie na końcu. Tak więc wyrażenie regularne dopasowuje (zwraca true) dla pustych linii lub linii tylko z białymi spacjami.
  3. getline(v:lnum + 1)dostaje następną linię.
  4. \SDopasowujemy to do , co odpowiada dowolnemu znakowi spoza dowolnego miejsca w tym wierszu.
  5. Jeśli te 2 warunki są prawdziwe, oceniamy <1, w przeciwnym razie 1. Odbywa się to z „trójskładnikowego” ifznanego z C oraz niektórych innych językach: condition ? return_if_true : return_if_false.
  6. <1oznacza, 1że zakładka kończy się na tej linii, i oznacza zakładkę pierwszego poziomu.

Więc jeśli zakończymy pas, jeśli linia jest pusta, a następna linia nie będzie pusta. W przeciwnym razie jesteśmy na foldlevel 1. Lub, jak :h foldexprto mówi:

Spowoduje to, że składanie akapitów zostanie oddzielone pustymi liniami


Czwarty

Czwarty zachowuje się tak samo jak trzeci, ale robi to w nieco inny sposób. Rozszerzony, to:

getline(v:lnum - 1) =~ '^\s*$' && getline(v:lnum) =~ '\S' ? '>1' : 1

Jeśli poprzednia linia jest pustą linią, a bieżąca linia jest >1niepustą linią, rozpoczynamy składanie na tej linii ( ), jeśli nie, ustawiamy poziom foldelowania na 1.


Posłowie

Logika wszystkich 3 przykładów jest naprawdę bardzo prosta. Większość trudności wynika z braku spacji i użycia odwrotnego ukośnika.

Podejrzewam, że wywołanie funkcji ma pewne obciążenie, a ponieważ jest to oceniane dla każdej linii, chcesz mieć przyzwoitą wydajność. Nie wiem jednak, jak duża jest różnica na nowoczesnych komputerach, i zaleciłbym użycie funkcji (jak w drugim przykładzie), chyba że masz problemy z wydajnością. Pamiętajmy o Knutcie: „przedwczesna optymalizacja jest źródłem wszelkiego zła” .

To pytanie dotyczy także StackOverflow , który ma nieco inną odpowiedź. Ale mój jest oczywiście lepszy ;-)

Martin Tournoij
źródło
3

Zasadniczo pytasz, jakie są inne elementy tych wyrażeń, które można znaleźć, wywołując kolejno :helpjeden z nich:

v:lnum: the line being evaluated
getline(): get the line of text for a line number
==: equals
=~: matches
<cond>?<if-true>:<if-false>: evaluates to <if-true> if <cond> is true, else to <if-false>

Podzieliłem te wyrażenia według ich części poniżej, aby pomóc zilustrować ich znaczenie:

1 Zwraca 1 dla wszystkich linii zaczynających się od tabulatora i 0 dla innych linii:

v:lnum                      the current line number
getline(v:lnum)             the text of the current line
getline(v:lnum)[0]          the first character of the current line
getline(v:lnum)[0]==\"\\t\" the first char of the current line is 'tab'

3 Kończy fałdy na pustych liniach po akapitach:

 getline(v:lnum)=~'^\\s*$'                                       current line is only spaces
                              getline(v:lnum+1)=~'\\S'           next line has non-space
(getline(v:lnum)=~'^\\s*$' && getline(v:lnum+1)=~'\\S') ? '<1'   if both of these: <1
                                                              :1 otherwise: 1
(getline(v:lnum)=~'^\\s*$' && getline(v:lnum+1)=~'\\S') ? '<1':1

4 Rozpoczyna fałdy na pustych liniach rozpoczynających akapity:

(getline(v:lnum-1)=~'^\\s*$'                                     previous line only spaces
                                getline(v:lnum)=~'\\S'           this line has non-space
(getline(v:lnum-1)=~'^\\s*$' && getline(v:lnum)=~'\\S') ? '>1'   if both of these: >1
                                                              :1 otherwise: 1
(getline(v:lnum-1)=~'^\\s*$' && getline(v:lnum)=~'\\S') ? '>1':1 

Znaczenia <1, >1itp są tuż poniżej tych wyrażeń w:help fold-expr

Matt Boehm
źródło
1

Przypadkowo opublikowałem moją odpowiedź jako komentarz i przesłałem ją wcześnie. Darn mobile.

Rozumiem, że wyrażenie powinno zwracać liczbę i ta liczba zostanie wykorzystana do ustalenia, na którym poziomie dana linia zostanie zwinięta. 0 nie jest złożone, 1 jest najbardziej zewnętrznym zagięciem, 2 jest zagięciem zagiętym wewnątrz zagięcia poziomu 1 i tak dalej.

Wyrażenia w przykładach wyglądają tak, jakby miały wartość prawda lub fałsz. VimScript nie ma odpowiedniego typu logicznego, więc tak naprawdę będzie to 1 lub 0, które są prawidłowymi poziomami foldowania.

Możesz napisać własne wyrażenie za pomocą VimScript, który jest tak prosty, jak zwracanie 1 lub 0, lub bardziej skomplikowany, pozwalając na zagnieżdżanie zagięć.

tommcdo
źródło
Używanie tylko liczb będzie działać, ale warto zauważyć, że foldexpr może oceniać na inne wartości specjalne, takie jak =, a1, s1,> 1, <1, -1
Matt Boehm