¿Cómo escribir un fold-expr?

10

He leído la página de ayuda sobre fold-expr ( :h fold-expr) pero no explica cuál es la sintaxis utilizada en la expresión.

Había cuatro ejemplos:

  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

Comprendí que esa v:lnumes la línea que necesita un nivel de sangría, y que la expresión dos es una llamada a una función.

¿Qué pasa con las expresiones 1,3 y 4? ¿Puede alguien por favor explicármelos?

elyashiv
fuente
Entiendo que la expresión debería devolver un número, y ese número se usará para determinar en qué nivel se doblará la línea dada. 0 no está plegado, 1 es el pliegue más externo, 2 es un pliegue anidado dentro de un pliegue de nivel 1, y así sucesivamente
tommcdo

Respuestas:

12

De :help 'foldexpr':

Se evalúa para cada línea para obtener su nivel de plegado

Se foldexprevalúa, por lo que debe ser un código VimL; no se menciona la "sintaxis especial" o similar. El resultado de esta evaluación controla lo que Vim considera un pliegue o no.

Los valores posibles son

  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

Esta no es la lista completa; solo los utilizados en los ejemplos en su pregunta. Ver :help foldexprpara la lista completa.


primero

El primero es bastante simple una vez que agregamos algunos espacios y eliminamos las barras diagonales inversas que necesitamos para que esto funcione en un :setcomando:

getline(v:lnum)[0] == "\t"
  1. getline(v:lnum) Obtiene toda la línea.
  2. [0] obtiene el primer personaje de eso
  3. y == "\t"comprueba si ese es un carácter de tabulación.
  4. VimL no tiene "verdadero" o "falso", solo usa "0" para falso y "1" para verdadero. Entonces, si esta línea comienza con una pestaña, se pliega en el nivel de plegado 1. Si no lo hace, no está en un pliegue (0).

Si expandiera esto para contar el número de pestañas, tendría un plegado basado en sangría (al menos, cuando expandtabno esté habilitado).


Tercero

El tercero no es realmente mucho más complicado que el primero; Como con el primer ejemplo, primero queremos que sea más legible:

getline(v:lnum) =~ '^\s*$' && getline(v:lnum + 1) =~ '\S' ? '<1' : 1
  1. Obtenemos toda la línea con getline(v:lnum)
  2. Hacemos coincidir eso como una expresión regular con =~to '^\s*$'; ^ancla al inicio, \ssignifica cualquier carácter de espacio en blanco, *significa repetir el cero anterior o más veces y $ancla al final. Entonces, esta expresión regular coincide (devuelve verdadero) para líneas en blanco o líneas con solo espacios en blanco.
  3. getline(v:lnum + 1)Obtiene la siguiente línea.
  4. Hacemos coincidir esto con \S, que coincide con cualquier carácter que no sea un espacio en blanco en cualquier lugar de esta línea.
  5. Si se cumplen estas condiciones 2, evaluamos a <1, de lo contrario, 1. Esto se hace con la "ternaria" ifconocido de C y algunos otros idiomas: condition ? return_if_true : return_if_false.
  6. <1significa que un pliegue termina en esta línea, y 1significa un pliegue de nivel uno.

Así pues, si nos terminamos un pliegue si la línea está en blanco y la siguiente línea es no blanco. De lo contrario, estamos en el nivel 1. o, como :h foldexprdice:

Esto hará un pliegue de párrafos separados por líneas en blanco


Cuarto

El cuarto se comporta igual que el tercero, pero lo hace de una manera ligeramente diferente. Ampliado, es:

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

Si la línea anterior es una línea en blanco, y la línea actual es una línea no en blanco, comenzamos un pliegue en esta línea ( >1), si no, estableceremos el nivel de pliegue en 1.


Epílogo

Entonces la lógica en los 3 ejemplos es realmente bastante simple. La mayor parte de la dificultad viene por la falta de espacios y parte del uso de la barra invertida.

Sospecho que llamar a una función tiene algo de sobrecarga, y dado que esto se evalúa para cada línea que desea tener un rendimiento decente. Sin embargo, no sé cuán grande es la diferencia en las máquinas modernas, y recomendaría que use una función (como en el segundo ejemplo) a menos que tenga problemas de rendimiento. Recuerde The Knuth: "la optimización prematura es la raíz de todo mal" .

Esta pregunta también está en StackOverflow , que tiene una respuesta ligeramente diferente. Pero el mío es, por supuesto, mejor ;-)

Martin Tournoij
fuente
3

Básicamente, se pregunta cuáles son los otros elementos en estas expresiones, que se pueden encontrar llamando :helpa uno de ellos a su vez:

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>

He desglosado estas expresiones por sus partes a continuación para ayudar a ilustrar su significado:

1 Devuelve 1 para todas las líneas que comienzan con una pestaña y 0 para otras líneas:

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 Termina los pliegues en líneas en blanco después de los párrafos:

 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 Comienza pliegues en líneas en blanco que comienzan párrafos:

(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 

Los significados de <1, >1, etc., son justo debajo de estas expresiones en:help fold-expr

Matt Boehm
fuente
1

Accidentalmente publiqué mi respuesta como comentario y la envié temprano. Maldito móvil.

Entiendo que la expresión debería devolver un número, y ese número se usará para determinar en qué nivel se doblará la línea dada. 0 no está plegado, 1 es el pliegue más externo, 2 es un pliegue anidado dentro de un pliegue de nivel 1, y así sucesivamente.

Las expresiones en los ejemplos parecen evaluarse como verdaderas o falsas. VimScript no tiene un tipo booleano adecuado, por lo que realmente será 1 o 0, que son niveles de plegado válidos.

Puede escribir su propia expresión usando VimScript que es tan simple como devolver 1 o 0, o más complicado, permitiendo pliegues anidados.

tommcdo
fuente
Usar solo números funcionará, pero vale la pena señalar que foldexpr puede evaluar otros valores especiales, como =, a1, s1,> 1, <1, -1
Matt Boehm