Як написати fold-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. Отже, якщо цей рядок починається з вкладки, він складається в foldlevel 1. Якщо це не так, він не знаходиться в складці (0).

Якщо ви розширите це для підрахунку кількості вкладок, у вас було б складання на основі відступу (принаймні, коли expandtabце не ввімкнено).


Третя

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

getline(v:lnum) =~ '^\s*$' && getline(v:lnum + 1) =~ '\S' ? '<1' : 1
  1. Ми отримуємо весь рядок с getline(v:lnum)
  2. Ми збігаються як регулярний вираз з =~до '^\s*$'; ^якір до початку, \sозначає будь-який символ пробілу, *означає повторити попередній нуль або більше разів і $прив’язувати до кінця. Таким чином, це регулярне вираження збігається (повертає істину) для порожніх рядків або рядків з лише пробілом.
  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.


Післямова

Тож логіка на всіх 3-х прикладах насправді досить проста. Більшість труднощів виникає у відсутності пробілів та деякому використанні зворотньої косої риски.

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

Це питання також є у 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, і так далі.

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

Ви можете написати власний вираз за допомогою VimScript, який є таким же простим, як повернення 1 або 0, або більш складним, що дозволяє вкладати складки.


Використання лише чисел спрацює, але варто відзначити, що foldexpr може оцінювати інші спеціальні значення, такі як =, a1, s1,> 1, <1, -1
Метт Боем
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.