Bagaimana cara menulis lipatan-expr?

10

Saya telah membaca halaman bantuan tentang fold-expr ( :h fold-expr) tetapi tidak menjelaskan apa yang digunakan sintaksis dalam ekspresi.

Ada empat contoh:

  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

Saya mengerti bahwa itu v:lnumadalah garis yang membutuhkan tingkat indentasi, dan ekspresi dua adalah panggilan ke fungsi.

bagaimana dengan ekspresi 1,3 dan 4? Bisakah seseorang tolong jelaskan kepada saya?

elyashiv
sumber
Pemahaman saya adalah bahwa ekspresi harus mengembalikan angka, dan angka itu akan digunakan untuk menentukan pada level mana garis yang diberikan akan dilipat. 0 tidak terlipat, 1 adalah lipatan terluar, 2 adalah lipatan bersarang di dalam lipatan level 1, dan seterusnya
tommcdo

Jawaban:

12

Dari :help 'foldexpr':

Itu dievaluasi untuk setiap baris untuk mendapatkan tingkat lipatannya

The foldexprdievaluasi, sehingga perlu kode VimL; tidak disebutkan "sintaks khusus" atau sejenisnya. Hasil evaluasi ini mengontrol apa yang dianggap Vim sebagai lipatan atau tidak.

Nilai yang mungkin adalah

  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

Ini bukan daftar lengkap; hanya yang digunakan dalam contoh di pertanyaan Anda. Lihat :help foldexprdaftar lengkapnya.


Pertama

Yang pertama cukup sederhana setelah kita menambahkan beberapa spasi dan menghapus garis miring terbalik yang kita perlukan agar ini berfungsi dalam :setperintah:

getline(v:lnum)[0] == "\t"
  1. getline(v:lnum) mendapat seluruh baris.
  2. [0] mendapat karakter pertama dari itu
  3. dan == "\t"memeriksa apakah itu karakter tab.
  4. VimL tidak memiliki "true" atau "false", ia hanya menggunakan "0" untuk false, dan "1" untuk true. Jadi jika baris ini dimulai dengan sebuah tab, itu dilipat di foldlevel 1. Jika tidak, itu tidak dalam flip (0).

Jika Anda ingin memperluas ini untuk menghitung jumlah tab Anda akan memiliki lipatan berbasis lekukan (setidaknya, ketika expandtabtidak diaktifkan).


Ketiga

Yang ketiga benar-benar tidak jauh lebih rumit dari yang pertama; seperti contoh pertama, pertama-tama kita ingin membuatnya lebih mudah dibaca:

getline(v:lnum) =~ '^\s*$' && getline(v:lnum + 1) =~ '\S' ? '<1' : 1
  1. Kami mendapat seluruh garis dengan getline(v:lnum)
  2. Kami cocok bahwa sebagai regexp dengan =~untuk '^\s*$'; ^jangkar ke awal, \sberarti karakter spasi putih, *berarti mengulangi nol sebelumnya atau lebih banyak kali, dan $jangkar ke akhir. Jadi regexp ini cocok (mengembalikan true) untuk baris kosong atau baris dengan spasi kosong saja .
  3. getline(v:lnum + 1)mendapat baris berikutnya .
  4. Kami mencocokkan ini dengan \S, yang cocok dengan karakter non-spasi putih mana pun di baris ini.
  5. Jika 2 kondisi ini benar, kami mengevaluasi <1, jika tidak 1,. Hal ini dilakukan dengan "terner" ifdiketahui dari C dan beberapa bahasa lain: condition ? return_if_true : return_if_false.
  6. <1berarti lipatan berakhir pada garis ini, dan 1berarti lipatan tingkat satu.

Jadi, Jika kita mengakhiri lipatan jika garis kosong dan baris berikutnya tidak kosong. Kalau tidak, kita berada di foldlevel 1. Atau, seperti yang :h foldexprdikatakan:

Ini akan membuat lipatan paragraf dipisahkan oleh garis kosong


Keempat

Yang keempat berperilaku sama dengan yang ketiga, tetapi melakukannya dengan cara yang sedikit berbeda. Diperluas, ini:

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

Jika garis sebelumnya adalah garis kosong, dan garis saat ini adalah garis tidak-kosong, kita mulai lipatan pada garis ini ( >1), jika tidak, kita mengatur lipatan tingkat ke 1.


Kata penutup

Jadi logika pada ketiga contoh ini sangat sederhana. Sebagian besar kesulitan datang dalam kurangnya ruang dan beberapa penggunaan backslash.

Saya menduga bahwa memanggil fungsi memiliki beberapa overhead, dan karena ini dievaluasi untuk setiap baris Anda ingin memiliki kinerja yang layak. Saya tidak tahu seberapa besar perbedaannya pada mesin-mesin modern, dan saya sarankan Anda menggunakan fungsi (seperti pada contoh ke-2) kecuali Anda memiliki masalah kinerja. Remember The Knuth: "optimasi prematur adalah akar dari semua kejahatan" .

Pertanyaan ini juga ada di StackOverflow , yang memiliki jawaban yang sedikit berbeda. Tapi milikku tentu saja lebih baik ;-)

Martin Tournoij
sumber
3

Anda pada dasarnya bertanya apa unsur-unsur lain dalam ekspresi ini, yang dapat ditemukan dengan memanggil :helpsalah satu dari mereka secara bergantian:

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>

Saya telah memecah ekspresi ini dengan bagian-bagian mereka di bawah untuk membantu mengilustrasikan artinya:

1 Akan mengembalikan 1 untuk semua baris dimulai dengan tab dan 0 untuk baris lain:

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 Berakhir dengan garis kosong setelah paragraf:

 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 Mulai lipatan pada baris kosong paragraf awal:

(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 

Arti dari <1, >1, dll tepat di bawah ekspresi ini di:help fold-expr

Matt Boehm
sumber
1

Sengaja mengeposkan jawaban saya sebagai komentar dan mengirimkannya lebih awal. Sial mobile.

Pemahaman saya adalah bahwa ekspresi harus mengembalikan angka, dan angka itu akan digunakan untuk menentukan pada level mana garis yang diberikan akan dilipat. 0 tidak terlipat, 1 adalah lipatan terluar, 2 adalah lipatan bersarang di dalam lipatan level 1, dan seterusnya.

Ekspresi dalam contoh terlihat seperti mereka akan mengevaluasi benar atau salah. VimScript tidak memiliki tipe Boolean yang tepat, jadi ini akan benar-benar 1 atau 0, yang merupakan level lipatan yang valid.

Anda bisa menulis ekspresi Anda sendiri menggunakan VimScript yang semudah mengembalikan 1 atau 0, atau lebih rumit, memungkinkan untuk lipatan bersarang.

tommcdo
sumber
Menggunakan angka yang adil akan berhasil, tetapi perlu dicatat bahwa foldexpr dapat mengevaluasi nilai-nilai khusus lainnya, seperti =, a1, s1,> 1, <1, -1
Matt Boehm