summaryrefslogtreecommitdiffstats
path: root/ftplugin/svelte/fold.vim
blob: 2732660a656d0017aca99850e41fd67db5ca9ddc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
if polyglot#init#is_disabled(expand('<sfile>:p'), 'svelte', 'ftplugin/svelte/fold.vim')
  finish
endif

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"
" Config {{{
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
let s:use_foldexpr = exists("g:vim_svelte_plugin_use_foldexpr") 
      \ && g:vim_svelte_plugin_use_foldexpr == 1
"}}}

if !s:use_foldexpr | finish | endif

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"
" Settings {{{
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
setlocal foldmethod=expr
setlocal foldexpr=GetSvelteFold(v:lnum)
"}}}

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"
" Variables {{{
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
let s:empty_line = '\v^\s*$'
let s:block_end = '\v^\s*}|]|\)'
let s:svelte_tag_start = '\v^\<\w+' 
let s:svelte_tag_end = '\v^\<\/\w+' 
let s:svelte_internal_blocks = '\v:(else|then|catch)'
"}}}

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"
" Functions {{{
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"  see :h fold-expr
"  value			    meaning
"  0			        the line is not in a fold
"  1, 2, ..		    the line is in a fold with this level
"  -1			        the fold level is undefined, use the fold level of a
"			            line before or after this line, whichever is the
"			            lowest.
"  "="			      use fold level from the previous line
"  "a1", "a2", ..	add one, two, .. to the fold level of the previous
"			            line, use the result for the current line
"  "s1", "s2", ..	subtract one, two, .. from the fold level of the
"                 previous line, use the result for the next line
"  ">1", ">2", ..	a fold with this level starts at this line
"  "<1", "<2", ..	a fold with this level ends at this line
function! GetSvelteFold(lnum)
  let this_line = getline(a:lnum)
  let next_line = getline(a:lnum + 1)

  " Handle empty lines
  if this_line =~ s:empty_line
    return -1
  endif

  " Handle start/end tags
  if this_line =~ s:svelte_tag_start
    return '>1'
  endif
  if this_line =~ s:svelte_tag_end
    " If return '<1', fold will get incorrect with prev line
    return 1
  endif

  " Fold by indent
  if a:lnum > 1
    let prev_indent = s:IndentLevel(a:lnum - 1)
  else
    let prev_indent = 0
  endif
  let this_indent = s:IndentLevel(a:lnum)
  let next_indent = s:IndentLevel(s:NextNonBlankLine(a:lnum))

  " Fold separately on blocks
  if this_line =~ s:svelte_internal_blocks
    return '>'.next_indent
  endif

  if GetSvelteTag(a:lnum) == 'script'
    " Handle closing '}'
    if this_line =~ '\v^\s*},?\s*$'
      return '<'.prev_indent
    endif

    " --this
    " ----next
    if this_indent < next_indent
      return '>'.next_indent
    endif

    " ----this
    " --next
    if this_indent >= next_indent 
      return this_indent
    endif
  else
    " Template or style
    return this_indent
  endif
endfunction

function! s:IndentLevel(lnum)
  " Add 1 to indentLevel, so start/end tags can fold properly
  return indent(a:lnum) / &shiftwidth + 1
endfunction

function! s:NextNonBlankLine(lnum)
  let next_line = a:lnum + 1
  let last_line = line('$')

  while next_line <= last_line
    if getline(next_line) =~ '\v\S'
      return next_line
    endif

    let next_line += 1
  endwhile

  return 0
endfunction
"}}}
" vim: fdm=marker