Wie schreibe ich einen Fold-Expr?

10

Ich habe die Hilfeseite zu fold-expr ( :h fold-expr) gelesen, aber sie hat nicht erklärt, welche Syntax im Ausdruck verwendet wird.

Es gab dort vier Beispiele:

  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

Ich habe verstanden, dass dies v:lnumdie Zeile ist, die eine Einrückungsstufe benötigt, und dass Ausdruck zwei ein Aufruf einer Funktion ist.

Was ist mit den Ausdrücken 1,3 und 4? Kann mir bitte jemand das erklären?

elyashiv
quelle
Nach meinem Verständnis sollte der Ausdruck eine Zahl zurückgeben, und diese Zahl wird verwendet, um zu bestimmen, auf welcher Ebene die angegebene Linie gefaltet wird. 0 ist nicht gefaltet, 1 ist die äußerste Falte, 2 ist eine Falte, die in einer Stufe 1-Falte verschachtelt ist, und so weiter
tommcdo

Antworten:

12

Von :help 'foldexpr':

Es wird für jede Linie ausgewertet, um ihre Falzstufe zu erhalten

Das foldexprwird ausgewertet, daher muss es sich um VimL-Code handeln. "spezielle Syntax" oder ähnliches wird nicht erwähnt. Das Ergebnis dieser Bewertung steuert, was Vim als Falte ansieht oder nicht.

Mögliche Werte sind

  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

Dies ist nicht die vollständige Liste; nur die in den Beispielen in Ihrer Frage verwendeten. Siehe :help foldexprfür die vollständige Liste.


Zuerst

Der erste ist ziemlich einfach, wenn wir einige Leerzeichen hinzufügen und die Backslashes entfernen, die wir benötigen, damit dies in einem :setBefehl funktioniert :

getline(v:lnum)[0] == "\t"
  1. getline(v:lnum) bekommt die ganze Zeile.
  2. [0] bekommt den ersten Charakter davon
  3. und == "\t"prüft, ob dies ein Tabulatorzeichen ist.
  4. VimL hat nicht "true" oder "false", sondern verwendet nur "0" für "false" und "1" für "true". Wenn diese Zeile also mit einem Tab beginnt, wird sie auf Faltebene 1 gefaltet. Wenn dies nicht der Fall ist, befindet sie sich nicht in einer Falte (0).

Wenn Sie dies erweitern würden, um die Anzahl der Registerkarten zu zählen , hätten Sie einrückungsbasiertes Falten (zumindest, wenn expandtabes nicht aktiviert ist).


Dritte

Der dritte ist wirklich nicht viel komplizierter als der erste; Wie beim ersten Beispiel möchten wir es zunächst lesbarer machen:

getline(v:lnum) =~ '^\s*$' && getline(v:lnum + 1) =~ '\S' ? '<1' : 1
  1. Wir bekommen die ganze Linie mit getline(v:lnum)
  2. Wir stimmen das als regulären Ausdruck mit =~zu '^\s*$'; ^Anker am Anfang, \sbedeutet ein beliebiges Leerzeichen, *bedeutet, dass die vorherigen null oder mehrmals wiederholt werden, und $Anker am Ende. Dieser reguläre Ausdruck entspricht also (gibt true zurück) für leere Zeilen oder Zeilen mit nur Leerzeichen.
  3. getline(v:lnum + 1)bekommt die nächste Zeile.
  4. Wir passen dies an \S, was mit jedem Nicht-Leerzeichen irgendwo in dieser Zeile übereinstimmt.
  5. Wenn diese beiden Bedingungen zutreffen, bewerten wir <1andernfalls 1. Dies geschieht mit dem ifaus C und einigen anderen Sprachen bekannten "ternären" : condition ? return_if_true : return_if_false.
  6. <1bedeutet, dass eine Falte auf dieser Linie endet und 1eine Faltebene bedeutet.

Wenn wir also eine Falte beenden, wenn die Zeile leer ist und die nächste Zeile nicht leer ist. Ansonsten sind wir auf Foldlevel 1. Oder, wie :h foldexpres heißt:

Dadurch werden Absätze gefaltet, die durch Leerzeilen getrennt sind


Vierte

Der vierte verhält sich genauso wie der dritte, macht es aber etwas anders. Erweitert ist es:

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

Wenn die vorherige Zeile eine leere Zeile ist und die aktuelle Zeile eine nicht leere Zeile ist, beginnen wir eine Falte in dieser Zeile ( >1). Wenn nicht, setzen wir die Falzstufe auf 1.


Nachwort

Die Logik aller drei Beispiele ist also sehr einfach. Die größte Schwierigkeit ergibt sich aus dem Platzmangel und der Verwendung von Backslashs.

Ich vermute, dass das Aufrufen einer Funktion einen gewissen Overhead hat, und da dies für jede Zeile ausgewertet wird, möchten Sie eine anständige Leistung erzielen. Ich weiß jedoch nicht, wie groß der Unterschied bei modernen Maschinen ist, und würde empfehlen, dass Sie eine Funktion (wie im zweiten Beispiel) verwenden, es sei denn, Sie haben Leistungsprobleme. Denken Sie an The Knuth: "Vorzeitige Optimierung ist die Wurzel allen Übels" .

Diese Frage bezieht sich auch auf StackOverflow , das eine etwas andere Antwort hat. Aber meins ist natürlich besser ;-)

Martin Tournoij
quelle
3

Sie fragen sich im Wesentlichen, was die anderen Elemente in diesen Ausdrücken sind, die Sie finden können, indem :helpSie nacheinander eines von ihnen aufrufen :

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>

Ich habe diese Ausdrücke nach ihren Teilen unten aufgeschlüsselt, um ihre Bedeutung zu veranschaulichen:

1 Gibt 1 für alle Zeilen zurück, die mit einer Registerkarte beginnen, und 0 für andere Zeilen:

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 Endet Falten in Leerzeilen nach Absätzen:

 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 Startet Falten in Leerzeilen, beginnend mit Absätzen:

(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 

Die Bedeutungen <1, >1usw. sind direkt unter dieser Ausdrücke in:help fold-expr

Matt Boehm
quelle
1

Ich habe meine Antwort versehentlich als Kommentar gepostet und sie frühzeitig eingereicht. Verdammt mobil.

Nach meinem Verständnis sollte der Ausdruck eine Zahl zurückgeben, und diese Zahl wird verwendet, um zu bestimmen, auf welcher Ebene die angegebene Linie gefaltet wird. 0 ist nicht gefaltet, 1 ist die äußerste Falte, 2 ist eine Falte, die in einer Falte der Ebene 1 verschachtelt ist, und so weiter.

Die Ausdrücke in den Beispielen sehen so aus, als würden sie als wahr oder falsch bewertet. VimScript hat keinen richtigen Booleschen Typ, daher ist dies wirklich 1 oder 0, was gültige Falzstufen sind.

Sie können Ihren eigenen Ausdruck mit VimScript schreiben, das so einfach wie die Rückgabe von 1 oder 0 oder komplizierter ist und verschachtelte Falten zulässt.

tommcdo
quelle
Nur Zahlen zu verwenden wird funktionieren, aber es ist erwähnenswert, dass foldexprs andere spezielle Werte wie =, a1, s1,> 1, <1, -1
Matt Boehm