Comment écrire un fold-expr?

10

J'ai lu la page d'aide sur fold-expr ( :h fold-expr) mais cela n'explique pas quelle est la syntaxe utilisée dans l'expression.

Il y avait là quatre exemples:

  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

J'ai compris que v:lnumc'est la ligne qui a besoin d'un niveau d'indentation, et que l'expression deux est un appel à une fonction.

qu'en est-il des expressions 1,3 et 4? Quelqu'un peut-il me les expliquer?

elyashiv
la source
Ma compréhension est que l'expression doit renvoyer un nombre, et ce nombre sera utilisé pour déterminer à quel niveau la ligne donnée sera pliée. 0 n'est pas plié, 1 est le pli le plus à l'extérieur, 2 est un pli imbriqué à l'intérieur d'un pli de niveau 1, et ainsi de suite
tommcdo

Réponses:

12

De :help 'foldexpr':

Il est évalué pour chaque ligne pour obtenir son niveau de pli

Le foldexprest évalué, il doit donc être du code VimL; il n'est fait aucune mention de "syntaxe spéciale" ou similaire. Le résultat de cette évaluation contrôle ce que Vim considère comme un pli ou non.

Les valeurs possibles sont

  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

Ce n'est pas la liste complète; juste ceux utilisés dans les exemples de votre question. Voir :help foldexprpour la liste complète.


Première

Le premier est assez simple une fois que nous avons ajouté des espaces et supprimé les barres obliques inverses dont nous avons besoin pour que cela fonctionne dans une :setcommande:

getline(v:lnum)[0] == "\t"
  1. getline(v:lnum) obtient la ligne entière.
  2. [0] obtient le premier caractère de cette
  3. et == "\t"vérifie s'il s'agit d'un caractère de tabulation.
  4. VimL n'a pas "vrai" ou "faux", il utilise simplement "0" pour faux et "1" pour vrai. Donc, si cette ligne commence par une tabulation, elle est pliée au niveau de pliage 1. Si ce n'est pas le cas, elle n'est pas dans un pli (0).

Si vous étendez ceci pour compter le nombre d'onglets, vous auriez un pliage basé sur l'indentation (au moins, lorsqu'il expandtabn'est pas activé).


Troisième

Le troisième n'est vraiment pas beaucoup plus compliqué que le premier; comme avec le premier exemple, nous voulons d'abord le rendre plus lisible:

getline(v:lnum) =~ '^\s*$' && getline(v:lnum + 1) =~ '\S' ? '<1' : 1
  1. Nous obtenons toute la gamme avec getline(v:lnum)
  2. Nous associons cela comme une expression rationnelle avec =~à '^\s*$'; ^ancres au début, \ssignifie n'importe quel caractère d'espacement, *signifie répéter le zéro précédent ou plusieurs fois, et $ancres à la fin. Donc, cette expression régulière correspond (renvoie vrai) pour les lignes vides ou les lignes avec uniquement des espaces.
  3. getline(v:lnum + 1)obtient la ligne suivante .
  4. Nous faisons correspondre cela à \S, ce qui correspond à tout caractère non blanc n'importe où sur cette ligne.
  5. Si ces 2 conditions sont vraies, nous évaluons à <1, sinon 1. Cela se fait avec le "ternaire" ifconnu de C et d'autres langages:condition ? return_if_true : return_if_false .
  6. <1signifie qu'un pli se termine sur cette ligne et 1signifie un niveau de pli.

Donc, si nous terminons un pli si la ligne est vide et la ligne suivante n'est pas vide. Sinon, nous sommes au niveau 1. Ou, comme le :h foldexprdit:

Cela fera un repli des paragraphes séparés par des lignes vides


Quatrième

Le quatrième se comporte de la même manière que le troisième, mais le fait d'une manière légèrement différente. Développé, c'est:

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

Si la ligne précédente est une ligne vierge et que la ligne actuelle est une ligne non vide, nous commençons un pli sur cette ligne ( >1), sinon, nous mettons le niveau de pli à 1.


Épilogue

Donc, la logique sur les 3 exemples est vraiment assez simple. La majeure partie de la difficulté vient du manque d'espace et d'une partie de l'utilisation de la barre oblique inverse.

Je soupçonne que l'appel d'une fonction a une surcharge, et puisque cela est évalué pour chaque ligne, vous voulez avoir une performance décente. Je ne sais pas à quel point la différence est grande sur les machines modernes, et je recommanderais d'utiliser une fonction (comme dans le 2ème exemple), sauf si vous avez des problèmes de performances. Rappelez-vous The Knuth: "l'optimisation prématurée est la racine de tout mal" .

Cette question se trouve également sur StackOverflow , qui a une réponse légèrement différente. Mais le mien est bien sûr meilleur ;-)

Martin Tournoij
la source
3

Vous demandez essentiellement quels sont les autres éléments de ces expressions, qui peuvent être trouvés en appelant l' :helpun d'eux à son tour:

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>

J'ai décomposé ces expressions par leurs parties ci-dessous pour aider à illustrer leur signification:

1 Renvoie 1 pour toutes les lignes commençant par un onglet et 0 pour les autres lignes:

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 Se termine les plis sur les lignes vides après les paragraphes:

 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 Commence les plis sur les lignes vides commençant les paragraphes:

(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 

Les significations <1, >1etc. sont juste en dessous de ces expressions:help fold-expr

Matt Boehm
la source
1

J'ai accidentellement posté ma réponse en tant que commentaire et je l'ai soumise tôt. Darn mobile.

Ma compréhension est que l'expression doit renvoyer un nombre, et ce nombre sera utilisé pour déterminer à quel niveau la ligne donnée sera pliée. 0 n'est pas plié, 1 est le pli le plus à l'extérieur, 2 est un pli imbriqué à l'intérieur d'un pli de niveau 1, etc.

Les expressions dans les exemples semblent correspondre à vrai ou faux. VimScript n'a pas de type booléen approprié, ce sera donc vraiment 1 ou 0, qui sont des niveaux de pliage valides.

Vous pouvez écrire votre propre expression en utilisant VimScript qui est aussi simple que de renvoyer 1 ou 0, ou plus compliqué, permettant des replis imbriqués.

tommcdo
la source
Utiliser uniquement des nombres fonctionnera, mais il convient de noter que foldexpr peut évaluer d'autres valeurs spéciales, comme =, a1, s1,> 1, <1, -1
Matt Boehm