Como escrever uma dobra-expr?


10

Eu li a página de ajuda sobre fold-expr ( :h fold-expr), mas não explicou qual é a sintaxe usada na expressão.

Havia quatro exemplos:

  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

Entendi que v:lnumé a linha que precisa de um nível de indentação e que a expressão dois é uma chamada para uma função.

e as expressões 1,3 e 4? Alguém por favor pode me explicar?


Meu entendimento é que a expressão deve retornar um número, e esse número será usado para determinar em que nível a linha especificada será dobrada. 0 não é dobrado, 1 é a dobra mais externa, 2 é uma dobra aninhada dentro de uma dobra de nível 1 e assim por diante
tommcdo

Respostas:


12

De :help 'foldexpr':

É avaliado para cada linha para obter seu nível de dobra

O foldexpré avaliado, portanto, ele precisa ser código VimL; não há menção a "sintaxe especial" ou algo parecido. O resultado dessa avaliação controla o que o Vim considera uma dobra ou não.

Valores possíveis são

  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 não é a lista completa; apenas os usados ​​nos exemplos da sua pergunta. Veja :help foldexpra lista completa.


Primeiro

O primeiro é bastante simples quando adicionamos alguns espaços e removemos as barras invertidas necessárias para que isso funcione em um :setcomando:

getline(v:lnum)[0] == "\t"
  1. getline(v:lnum) recebe a linha inteira.
  2. [0] recebe o primeiro personagem disso
  3. e == "\t"verifica se esse é um caractere de tabulação.
  4. O VimL não possui "true" ou "false", apenas usa "0" para false e "1" para true. Portanto, se essa linha começar com uma guia, ela será dobrada no nível da dobra 1. Caso contrário, ela não estará na dobra (0).

Se você expandir isso para contar o número de guias, terá dobras baseadas em recuo (pelo menos quando expandtabnão estiver ativado).


Terceiro

O terceiro, na verdade, não é muito mais complicado que o primeiro; como no primeiro exemplo, primeiro queremos torná-lo mais legível:

getline(v:lnum) =~ '^\s*$' && getline(v:lnum + 1) =~ '\S' ? '<1' : 1
  1. Temos toda a linha com getline(v:lnum)
  2. Nós combinamos isso como uma expressão regular com =~to '^\s*$'; ^âncoras no início, \ssignifica qualquer caractere de espaço em branco, *significa repetir o zero anterior ou mais vezes e $ancora no final. Portanto, esse regexp corresponde (retorna true) para linhas em branco ou linhas com apenas espaço em branco.
  3. getline(v:lnum + 1)obtém a próxima linha.
  4. Nós combinamos isso com \S, que corresponde a qualquer caractere que não seja um espaço em branco em qualquer lugar desta linha.
  5. Se essas 2 condições forem verdadeiras, avaliamos como <1, caso contrário 1,. Isso é feito com o "ternário" ifconhecido a partir de C e outras linguagens: condition ? return_if_true : return_if_false.
  6. <1significa que uma dobra termina nessa linha e significa uma dobra no 1nível.

Portanto, se encerrarmos uma dobra, se a linha estiver em branco e a próxima linha não estiver em branco. Caso contrário, estamos no nível de dobra 1. Ou, como :h foldexprdiz:

Isso fará uma dobra de parágrafos separados por linhas em branco


Quarto

O quarto se comporta da mesma forma que o terceiro, mas de uma maneira ligeiramente diferente. Expandido, é:

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

Se a linha anterior for uma linha em branco e a linha atual for uma linha não em branco, iniciaremos uma dobra nessa linha ( >1); caso contrário, definiremos o nível de dobra como 1.


Posfácio

Portanto, a lógica dos três exemplos é realmente bastante simples. A maior parte da dificuldade está na falta de espaços e em parte do uso da barra invertida.

Suspeito que chamar uma função tenha alguma sobrecarga, e como isso é avaliado para cada linha que você deseja ter um desempenho decente. Porém, não sei quão grande é a diferença nas máquinas modernas e recomendo que você use uma função (como no segundo exemplo), a menos que tenha problemas de desempenho. Lembre-se de Knuth: "a otimização prematura é a raiz de todo mal" .

Esta pergunta também está no StackOverflow , que tem uma resposta um pouco diferente. Mas o meu é obviamente melhor ;-)


3

Você está basicamente perguntando quais são os outros elementos nessas expressões, que podem ser encontrados chamando- :helpos por 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>

Dividi essas expressões pelas partes abaixo para ajudar a ilustrar seu significado:

1 Retornará 1 para todas as linhas que começam com uma guia e 0 para outras linhas:

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 dobras em linhas em branco após parágrafos:

 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 Inicia dobras em linhas em branco começando com parágrafos:

(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 

Os significados de <1, >1etc. estão logo abaixo dessas expressões em:help fold-expr


1

Colocou acidentalmente minha resposta como comentário e a enviei cedo. Darn mobile.

Meu entendimento é que a expressão deve retornar um número, e esse número será usado para determinar em que nível a linha especificada será dobrada. 0 não é dobrado, 1 é a dobra mais externa, 2 é uma dobra aninhada dentro de uma dobra de nível 1 e assim por diante.

As expressões nos exemplos parecem avaliadas como verdadeiras ou falsas. O VimScript não possui um tipo booleano adequado; portanto, será realmente 1 ou 0, que são níveis de dobra válidos.

Você pode escrever sua própria expressão usando o VimScript, que é tão simples quanto retornar 1 ou 0, ou mais complicado, permitindo dobras aninhadas.


Utilizando números apenas irá funcionar, mas vale a pena notar que é foldexpr pode avaliar a outros valores especiais, como =, a1, s1,> 1, <1, -1
Matt Boehm
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.