Come scrivere un fold-expr?


10

Ho letto la pagina di aiuto su fold-expr ( :h fold-expr) ma non ha spiegato quale sia la sintassi utilizzata nell'espressione.

C'erano quattro esempi:

  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

Ho capito che v:lnumè la linea che ha bisogno di un livello di rientro e che l'espressione due è una chiamata a una funzione.

che dire delle espressioni 1,3 e 4? Qualcuno può spiegarmelo?


La mia comprensione è che l'espressione dovrebbe restituire un numero e quel numero verrà utilizzato per determinare a quale livello verrà piegata la riga specificata. 0 non è piegato, 1 è la piega più esterna, 2 è una piega nidificata all'interno di una piega di livello 1 e così via
tommcdo

Risposte:


12

Da :help 'foldexpr':

Viene valutato per ogni riga per ottenere il suo livello di piega

La foldexprviene valutata, quindi deve essere il codice VimL; non si fa menzione di "sintassi speciale" o simili. Il risultato di questa valutazione controlla ciò che Vim considera o meno una piega.

I valori possibili sono

  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

Questo non è l'elenco completo; solo quelli usati negli esempi della tua domanda. Vedi :help foldexprl'elenco completo.


Primo

Il primo è abbastanza semplice una volta aggiunti degli spazi e rimosso le barre rovesciate, dobbiamo farlo funzionare in un :setcomando:

getline(v:lnum)[0] == "\t"
  1. getline(v:lnum) ottiene l'intera riga.
  2. [0] ne ottiene il primo personaggio
  3. e == "\t"controlla se si tratta di un carattere di tabulazione.
  4. VimL non ha "true" o "false", usa solo "0" per false e "1" per true. Quindi, se questa linea inizia con una scheda, viene piegata a livello di piega 1. In caso contrario, non è piegata (0).

Se lo espandessi per contare il numero di schede avresti una piegatura basata sul rientro (almeno, quando expandtabnon è abilitata).


Terzo

Il terzo non è in realtà molto più complicato del primo; come nel primo esempio, vogliamo prima renderlo più leggibile:

getline(v:lnum) =~ '^\s*$' && getline(v:lnum + 1) =~ '\S' ? '<1' : 1
  1. Otteniamo l'intera riga con getline(v:lnum)
  2. Lo abbiniamo come regexp con =~to '^\s*$'; ^ancore all'inizio, \sindica qualsiasi carattere di spazio bianco, *significa ripetere lo zero precedente o più volte e $ancorare fino alla fine. Quindi questa regexp corrisponde (restituisce vero) per righe vuote o righe con solo spazi bianchi.
  3. getline(v:lnum + 1)ottiene la riga successiva .
  4. Abbiniamo questo a \S, che corrisponde a qualsiasi carattere non bianco in qualsiasi punto di questa linea.
  5. Se queste 2 condizioni sono vere, valutiamo a <1, in caso contrario, 1. Questo viene fatto con il "ternario" ifconosciuto da C e alcune altre lingue: condition ? return_if_true : return_if_false.
  6. <1significa che una piega termina su questa linea e 1significa una piega a livello.

Quindi, se si finisce una piega se la linea è vuota e la riga successiva è non vuoto. Altrimenti, siamo al livello 1 o, come :h foldexprdice:

Questo renderà un pieghevole di paragrafi separati da linee vuote


Il quarto

Il quarto si comporta come il terzo, ma lo fa in modo leggermente diverso. Espanso, è:

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

Se la riga precedente è una riga vuota e la riga corrente è una riga non vuota, iniziamo una piega su questa riga ( >1), in caso contrario, impostiamo il livello di piegatura su 1.


Epilogo

Quindi la logica di tutti e 3 gli esempi è davvero abbastanza semplice. La maggior parte della difficoltà deriva dalla mancanza di spazi e dall'uso di una barra rovesciata.

Sospetto che chiamare una funzione abbia un certo sovraccarico, e poiché questo viene valutato per ogni linea che si desidera avere prestazioni decenti. Non so quanto sia grande la differenza sulle macchine moderne e consiglierei di usare una funzione (come nel secondo esempio) a meno che tu non abbia problemi di prestazioni. Ricorda The Knuth: "l'ottimizzazione prematura è la radice di tutti i mali" .

Questa domanda è anche su StackOverflow , che ha una risposta leggermente diversa. Ma il mio è ovviamente migliore ;-)


3

Stai essenzialmente chiedendo quali sono gli altri elementi di queste espressioni, che puoi trovare chiamando :helpuno di essi a turno:

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>

Ho scomposto queste espressioni per le loro parti in basso per aiutare a illustrare il loro significato:

1 Restituirà 1 per tutte le righe che iniziano con una scheda e 0 per altre righe:

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 le pieghe su righe vuote dopo i paragrafi:

 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 Inizia le pieghe su righe vuote che iniziano con i paragrafi:

(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 

I significati di <1, >1ecc. Sono proprio sotto queste espressioni in:help fold-expr


1

Ho accidentalmente pubblicato la mia risposta come commento e l'ho inviata in anticipo. Maledettamente mobile.

La mia comprensione è che l'espressione dovrebbe restituire un numero e quel numero verrà utilizzato per determinare a quale livello verrà piegata la riga specificata. 0 non è piegato, 1 è la piega più esterna, 2 è una piega nidificata all'interno di una piega di livello 1 e così via.

Le espressioni negli esempi sembrano valutate come vere o false. VimScript non ha un tipo booleano appropriato, quindi sarà davvero 1 o 0, che sono livelli di piega validi.

Puoi scrivere la tua espressione usando VimScript che è semplice come restituire 1 o 0, o più complicato, consentendo pieghe nidificate.


Usare solo numeri funzionerà, ma vale la pena notare che foldexpr può valutare altri valori speciali, come =, a1, s1,> 1, <1, -1
Matt Boehm
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.