folding.vim 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. " vim: set fdm=marker et ts=4 sw=4 sts=4:
  2. "
  3. " Init: {{{1
  4. function! pandoc#folding#Init() abort
  5. " set up defaults {{{2
  6. " Show foldcolum {{{3
  7. if !exists('g:pandoc#folding#fdc')
  8. let g:pandoc#folding#fdc = 1
  9. endif
  10. " Initial foldlevel {{{3
  11. if !exists('g:pandoc#folding#level')
  12. let g:pandoc#folding#level = &foldlevel
  13. endif
  14. " How to decide fold levels {{{3
  15. " 'syntax': Use syntax
  16. " 'relative': Count how many parents the header has
  17. if !exists('g:pandoc#folding#mode')
  18. let g:pandoc#folding#mode = 'syntax'
  19. endif
  20. " Fold the YAML frontmatter {{{3
  21. if !exists('g:pandoc#folding#fold_yaml')
  22. let g:pandoc#folding#fold_yaml = 0
  23. endif
  24. " Set YAML foldlevel {{{3
  25. if !exists('g:pandoc#folding#foldlevel_yaml')
  26. let g:pandoc#folding#foldlevel_yaml = 1
  27. endif
  28. " What <div> classes to fold {{{3
  29. if !exists('g:pandoc#folding#fold_div_classes')
  30. let g:pandoc#folding#fold_div_classes = ['notes']
  31. endif
  32. "}}}3
  33. " Fold vim markers (see help fold-marker) {{{3
  34. if !exists('g:pandoc#folding#fold_vim_markers')
  35. let g:pandoc#folding#fold_vim_markers = 1
  36. endif
  37. " Only fold vim markers inside comments {{{3
  38. if !exists('g:pandoc#folding#vim_markers_in_comments_only')
  39. let g:pandoc#folding#vim_markers_in_comments_only = 1
  40. endif
  41. " Fold fenced codeblocks? {{{3
  42. if !exists('g:pandoc#folding#fold_fenced_codeblocks')
  43. let g:pandoc#folding#fold_fenced_codeblocks = 0
  44. endif
  45. " Use custom foldtext? {{{3
  46. if !exists('g:pandoc#folding#use_foldtext')
  47. let g:pandoc#folding#use_foldtext = 1
  48. endif
  49. " Use fastfolds? {{{3
  50. if !exists('g:pandoc#folding#fastfolds')
  51. let g:pandoc#folding#fastfolds = 0
  52. endif
  53. " Use basic folding fot this buffer? {{{3
  54. if !exists('b:pandoc_folding_basic')
  55. let b:pandoc_folding_basic = 0
  56. endif
  57. " set up folding {{{2
  58. exe 'setlocal foldlevel='.g:pandoc#folding#level
  59. setlocal foldmethod=expr
  60. " might help with slowness while typing due to syntax checks
  61. if g:pandoc#folding#fastfolds
  62. augroup EnableFastFolds
  63. au!
  64. autocmd InsertEnter <buffer> setlocal foldmethod=manual
  65. autocmd InsertLeave <buffer> setlocal foldmethod=expr
  66. augroup end
  67. endif
  68. setlocal foldexpr=pandoc#folding#FoldExpr()
  69. if g:pandoc#folding#use_foldtext
  70. setlocal foldtext=pandoc#folding#FoldText()
  71. endif
  72. if g:pandoc#folding#fdc > 0
  73. let &l:foldcolumn = g:pandoc#folding#fdc
  74. endif
  75. "}}}
  76. " set up a command to change the folding mode on demand {{{2
  77. command! -buffer -nargs=1 -complete=custom,pandoc#folding#ModeCmdComplete PandocFolding call pandoc#folding#ModeCmd(<f-args>)
  78. " }}}2
  79. endfunction
  80. function! pandoc#folding#Disable() abort
  81. setlocal foldcolumn&
  82. setlocal foldlevel&
  83. setlocal foldexpr&
  84. if g:pandoc#folding#fastfolds
  85. au! VimPandoc InsertEnter
  86. au! VimPandoc InsertLeave
  87. endif
  88. if exists(':PandocFolding')
  89. delcommand PandocFolding
  90. endif
  91. setlocal foldmethod& " here because before deleting the autocmds, it might interfere
  92. endfunction
  93. " Change folding mode on demand {{{1
  94. function! pandoc#folding#ModeCmdComplete(...) abort
  95. return "syntax\nrelative\nstacked\nnone"
  96. endfunction
  97. function! pandoc#folding#ModeCmd(mode) abort
  98. if a:mode ==# 'none'
  99. setlocal foldmethod=manual
  100. normal! zE
  101. else
  102. exe 'let g:pandoc#folding#mode = "'.a:mode.'"'
  103. setlocal foldmethod=expr
  104. normal! zx
  105. endif
  106. endfunction
  107. " Main foldexpr function, includes support for common stuff. {{{1
  108. " Delegates to filetype specific functions.
  109. function! pandoc#folding#FoldExpr() abort
  110. " with multiple splits in the same buffer, the folding code can be called
  111. " way too many times too often, so it's best to disable it to keep good
  112. " performance. Only enable when using the built-in method of improving
  113. " performance of folds.
  114. if g:pandoc#folding#fastfolds == 1
  115. if count(map(range(1, winnr('$')), 'bufname(winbufnr(v:val))'), bufname('')) > 1
  116. return
  117. endif
  118. endif
  119. let vline = getline(v:lnum)
  120. " fold YAML headers
  121. if g:pandoc#folding#fold_yaml == 1
  122. if vline =~# '\(^---$\|^...$\)' && synIDattr(synID(v:lnum , 1, 1), 'name') =~? '\(delimiter\|yamldocumentstart\)'
  123. if vline =~# '^---$' && v:lnum == 1
  124. return '>' . g:pandoc#folding#foldlevel_yaml
  125. elseif synIDattr(synID(v:lnum - 1, 1, 1), 'name') ==# 'yamlkey'
  126. return '<' . g:pandoc#folding#foldlevel_yaml
  127. elseif synIDattr(synID(v:lnum - 1, 1, 1), 'name') ==# 'pandocYAMLHeader'
  128. return '<' . g:pandoc#folding#foldlevel_yaml
  129. elseif synIDattr(synID(v:lnum - 1, 1, 1), 'name') ==# 'yamlBlockMappingKey'
  130. return '<' . g:pandoc#folding#foldlevel_yaml
  131. else
  132. return '='
  133. endif
  134. endif
  135. endif
  136. " fold divs for special classes
  137. let div_classes_regex = '('.join(g:pandoc#folding#fold_div_classes, '|').')'
  138. if vline =~? '<div class=.'.div_classes_regex
  139. return 'a1'
  140. " the `endfold` attribute must be set, otherwise we can remove folds
  141. " incorrectly (see issue #32)
  142. " pandoc ignores this attribute, so this is safe.
  143. elseif vline =~? '</div endfold>'
  144. return 's1'
  145. endif
  146. " fold markers?
  147. if g:pandoc#folding#fold_vim_markers == 1
  148. if vline =~# '[{}]\{3}'
  149. if g:pandoc#folding#vim_markers_in_comments_only == 1
  150. let mark_head = '<!--.*'
  151. else
  152. let mark_head = ''
  153. endif
  154. if vline =~# mark_head.'{\{3}'
  155. let level = matchstr(vline, '\({\{3}\)\@<=\d')
  156. if level !=# ''
  157. return '>'.level
  158. else
  159. return 'a1'
  160. endif
  161. endif
  162. if vline =~# mark_head.'}\{3}'
  163. let level = matchstr(vline, '\(}\{3}\)\@<=\d')
  164. if level !=# ''
  165. return '<'.level
  166. else
  167. return 's1'
  168. endif
  169. endif
  170. endif
  171. endif
  172. " Delegate to filetype specific functions
  173. if &filetype =~# 'markdown' || &filetype ==# 'pandoc' || &filetype ==# 'rmd'
  174. " vim-pandoc-syntax sets this variable, so we can check if we can use
  175. " syntax assistance in our foldexpr function
  176. if exists('g:vim_pandoc_syntax_exists') && b:pandoc_folding_basic != 1
  177. return pandoc#folding#MarkdownLevelSA()
  178. " otherwise, we use a simple, but less featureful foldexpr
  179. else
  180. return pandoc#folding#MarkdownLevelBasic()
  181. endif
  182. elseif &filetype ==# 'textile'
  183. return pandoc#folding#TextileLevel()
  184. endif
  185. endfunction
  186. " Main foldtext function. Like ...FoldExpr() {{{1
  187. function! pandoc#folding#FoldText() abort
  188. " first line of the fold
  189. let f_line = getline(v:foldstart)
  190. " second line of the fold
  191. let n_line = getline(v:foldstart + 1)
  192. " count of lines in the fold
  193. let line_count = v:foldend - v:foldstart + 1
  194. let line_count_text = ' / ' . line_count . ' lines / '
  195. if n_line =~? 'title\s*:'
  196. return v:folddashes . ' [y] ' . matchstr(n_line, '\(title\s*:\s*\)\@<=\S.*') . line_count_text
  197. endif
  198. if f_line =~? 'fold-begin'
  199. return v:folddashes . ' [c] ' . matchstr(f_line, '\(<!-- \)\@<=.*\( fold-begin -->\)\@=') . line_count_text
  200. endif
  201. if f_line =~# '<!-- .*{{{'
  202. return v:folddashes . ' [m] ' . matchstr(f_line, '\(<!-- \)\@<=.*\( {{{.* -->\)\@=') . line_count_text
  203. endif
  204. if f_line =~? '<div class='
  205. return v:folddashes . ' ['. matchstr(f_line, "\\(class=[\"']\\)\\@<=.*[\"']\\@="). '] ' . n_line[:30] . '...' . line_count_text
  206. endif
  207. if &filetype =~# 'markdown' || &filetype ==# 'pandoc' || &filetype ==# 'rmd'
  208. return pandoc#folding#MarkdownFoldText() . line_count_text
  209. elseif &filetype ==# 'textile'
  210. return pandoc#folding#TextileFoldText() . line_count_text
  211. endif
  212. endfunction
  213. " Markdown: {{{1
  214. "
  215. " Originally taken from http://stackoverflow.com/questions/3828606
  216. "
  217. " Syntax assisted (SA) foldexpr {{{2
  218. function! pandoc#folding#MarkdownLevelSA() abort
  219. let vline = getline(v:lnum)
  220. let vline1 = getline(v:lnum + 1)
  221. if vline =~# '^#\{1,6}[^.]'
  222. if synIDattr(synID(v:lnum, 1, 1), 'name') =~# '^pandoc\(DelimitedCodeBlock$\)\@!'
  223. if g:pandoc#folding#mode ==# 'relative'
  224. return '>'. len(markdown#headers#CurrentHeaderAncestors(v:lnum))
  225. elseif g:pandoc#folding#mode ==# 'stacked'
  226. return '>1'
  227. else
  228. return '>'. len(matchstr(vline, '^#\{1,6}'))
  229. endif
  230. endif
  231. elseif vline =~# '^[^-=].\+$' && vline1 =~# '^=\+$'
  232. if synIDattr(synID(v:lnum, 1, 1), 'name') =~# '^pandoc\(DelimitedCodeBlock$\)\@!' &&
  233. \ synIDattr(synID(v:lnum + 1, 1, 1), 'name') ==# 'pandocSetexHeader'
  234. return '>1'
  235. endif
  236. elseif vline =~# '^[^-=].\+$' && vline1 =~# '^-\+$'
  237. if synIDattr(synID(v:lnum, 1, 1), 'name') =~# '^pandoc\(DelimitedCodeBlock$\)\@!' &&
  238. \ synIDattr(synID(v:lnum + 1, 1, 1), 'name') ==# 'pandocSetexHeader'
  239. if g:pandoc#folding#mode ==# 'relative'
  240. return '>'. len(markdown#headers#CurrentHeaderAncestors(v:lnum))
  241. elseif g:pandoc#folding#mode ==# 'stacked'
  242. return '>1'
  243. else
  244. return '>2'
  245. endif
  246. endif
  247. elseif vline =~? '^<!--.*fold-begin -->'
  248. return 'a1'
  249. elseif vline =~? '^<!--.*fold-end -->'
  250. return 's1'
  251. elseif vline =~# '^\s*[`~]\{3}'
  252. if g:pandoc#folding#fold_fenced_codeblocks == 1
  253. let synId = synIDattr(synID(v:lnum, match(vline, '[`~]') + 1, 1), 'name')
  254. if synId ==# 'pandocDelimitedCodeBlockStart'
  255. return 'a1'
  256. elseif synId =~# '^pandoc\(DelimitedCodeBlock$\)\@!'
  257. return 's1'
  258. endif
  259. endif
  260. endif
  261. return '='
  262. endfunction
  263. " Basic foldexpr {{{2
  264. function! pandoc#folding#MarkdownLevelBasic() abort
  265. if getline(v:lnum) =~# '^#\{1,6}' && getline(v:lnum-1) =~# '^\s*$'
  266. if g:pandoc#folding#mode ==# 'stacked'
  267. return '>1'
  268. else
  269. return '>'. len(matchstr(getline(v:lnum), '^#\{1,6}'))
  270. endif
  271. elseif getline(v:lnum) =~# '^[^-=].\+$' && getline(v:lnum+1) =~# '^=\+$'
  272. return '>1'
  273. elseif getline(v:lnum) =~# '^[^-=].\+$' && getline(v:lnum+1) =~# '^-\+$'
  274. if g:pandoc#folding#mode ==# 'stacked'
  275. return '>1'
  276. else
  277. return '>2'
  278. endif
  279. elseif getline(v:lnum) =~? '^<!--.*fold-begin -->'
  280. return 'a1'
  281. elseif getline(v:lnum) =~? '^<!--.*fold-end -->'
  282. return 's1'
  283. endif
  284. return '='
  285. endfunction
  286. " Markdown foldtext {{{2
  287. function! pandoc#folding#MarkdownFoldText() abort
  288. let c_line = getline(v:foldstart)
  289. let atx_title = match(c_line, '#') > -1
  290. if atx_title
  291. return '- '. c_line
  292. else
  293. if match(getline(v:foldstart+1), '=') != -1
  294. let level_mark = '#'
  295. else
  296. let level_mark = '##'
  297. endif
  298. return '- '. level_mark. ' '.c_line
  299. endif
  300. endfunction
  301. " Textile: {{{1
  302. "
  303. function! pandoc#folding#TextileLevel() abort
  304. let vline = getline(v:lnum)
  305. if vline =~# '^h[1-6]\.'
  306. if g:pandoc#folding#mode ==# 'stacked'
  307. return '>'
  308. else
  309. return '>' . matchstr(getline(v:lnum), 'h\@1<=[1-6]\.\=')
  310. endif
  311. elseif vline =~? '^.. .*fold-begin'
  312. return 'a1'
  313. elseif vline =~? '^.. .*fold end'
  314. return 's1'
  315. endif
  316. return '='
  317. endfunction
  318. function! pandoc#folding#TextileFoldText() abort
  319. return '- '. substitute(v:folddashes, '-', '#', 'g'). ' ' . matchstr(getline(v:foldstart), '\(h[1-6]\. \)\@4<=.*')
  320. endfunction