Как написать фолд-expr?

10

Я прочитал страницу справки о fold-expr ( :h fold-expr), но он не объяснил, какой синтаксис используется в выражении.

Там было четыре примера:

  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

Я понял, что v:lnumэто строка, которая требует уровня отступа, и что выражение два - это вызов функции.

как насчет выражений 1,3 и 4? Может кто-нибудь, пожалуйста, объясните мне их?

Эльяшив
источник
Насколько я понимаю, выражение должно возвращать число, и это число будет использоваться для определения, на каком уровне будет сложена данная строка. 0 не сложен, 1 - самая внешняя складка, 2 - складка, вложенная в складку уровня 1, и так далее
tommcdo

Ответы:

12

От :help 'foldexpr':

Он оценивается для каждой строки, чтобы получить ее уровень сгиба

foldexprОценивается, поэтому она должна быть VIML код; нет никакого упоминания о «специальном синтаксисе» или тому подобном. Результат этой оценки контролирует то, что Vim считает фолдом или нет.

Возможные значения

  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

Это не полный список; только те, которые используются в примерах в вашем вопросе. Смотрите :help foldexprполный список.


Первый

Первый довольно прост, когда мы добавляем несколько пробелов и удаляем обратную косую черту, нам нужно, чтобы это работало в :setкоманде:

getline(v:lnum)[0] == "\t"
  1. getline(v:lnum) получает всю линию.
  2. [0] получает первый персонаж этого
  3. и == "\t"проверяет, является ли это символом табуляции.
  4. VimL не имеет "true" или "false", он просто использует "0" для false и "1" для true. Поэтому, если эта строка начинается с вкладки, она складывается на уровне 1 сгиба. Если нет, она не в сгибе (0).

Если бы вы расширили это, чтобы подсчитать количество вкладок, у вас будет сворачивание на основе отступов (по крайней мере, когда expandtabне включено).


В третьих

Третий действительно не намного сложнее, чем первый; как и в первом примере, мы сначала хотим сделать его более читабельным:

getline(v:lnum) =~ '^\s*$' && getline(v:lnum + 1) =~ '\S' ? '<1' : 1
  1. Мы получаем всю строку с getline(v:lnum)
  2. Мы сопоставляем это как регулярное выражение с =~to '^\s*$'; ^привязки к началу, \sозначает любой символ пробела, *означает повторение предыдущего нуля или более раз и $привязку к концу. Таким образом, это регулярное выражение соответствует (возвращает true) для пустых строк или строк только с пробелами .
  3. getline(v:lnum + 1)получает следующую строку.
  4. Мы сопоставляем это с \S, что соответствует любому непробельному символу в любом месте этой строки.
  5. Если эти 2 условия выполняются, мы оцениваем <1иначе 1. Это делается с «тройным» , ifизвестной из C и некоторых других языках: condition ? return_if_true : return_if_false.
  6. <1означает сгиб заканчивается на этой линии, а 1означает сгиб один.

Итак, если мы завершим сгиб, если строка пуста, а следующая строка не пуста. В противном случае мы находимся на уровне 1. Или, как :h foldexprговорится:

Это сделает складывание абзацев разделенными пустыми строками


четвертый

Четвертый ведет себя так же, как третий, но делает это немного по-другому. Расширено, это:

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

Если предыдущая строка является пустой строкой, а текущая строка является непустой строкой, мы начинаем сгиб на этой строке ( >1), если нет, мы устанавливаем уровень сгиба равным 1.


Послесловие

Таким образом, логика на всех трех примерах действительно проста. Большая часть трудностей заключается в недостатке пробелов и использовании обратной косой черты.

Я подозреваю, что вызов функции имеет некоторые издержки, и, поскольку он оценивается для каждой строки, вы хотите иметь достойную производительность. Я не знаю, насколько велика разница на современных машинах, и рекомендую использовать функцию (как во втором примере), если у вас нет проблем с производительностью. Помните Кнута: «преждевременная оптимизация - корень всего зла» .

Этот вопрос также на StackOverflow , который имеет немного другой ответ. Но мой, конечно, лучше ;-)

Мартин Турной
источник
3

По сути, вы спрашиваете, каковы другие элементы в этих выражениях, что можно найти, вызвав :helpлюбое из них по очереди:

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>

Я разбил эти выражения по частям ниже, чтобы проиллюстрировать их значение:

1 вернет 1 для всех строк, начинающихся с табуляции, и 0 для других строк:

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 Концы сгиба на пустых строках после абзацев:

 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 Запускает сгибы на пустых строках, начиная с абзацев:

(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 

Значения <1, >1и т.д., прямо под этими выражениями:help fold-expr

Мэтт Бём
источник
1

Случайно разместил мой ответ как комментарий и отправил его рано. Чертовски мобильный.

Насколько я понимаю, выражение должно возвращать число, и это число будет использоваться для определения, на каком уровне будет сложена данная строка. 0 не сложен, 1 - самая внешняя складка, 2 - складка, вложенная в складку уровня 1, и так далее.

Выражения в примерах выглядят так, как будто они имеют значение true или false. VimScript не имеет правильного логического типа, поэтому в действительности это будет 1 или 0, которые являются допустимыми уровнями сгиба.

Вы можете написать свое собственное выражение, используя VimScript, который так же прост, как возврат 1 или 0, или более сложный, с учетом вложенных сгибов.

tommcdo
источник
Использование только чисел будет работать, но стоит отметить, что foldexpr может оценивать другие специальные значения, такие как =, a1, s1,> 1, <1, -1
Мэтт Бём