forked from python-mode/python-mode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfolding.vim
More file actions
177 lines (151 loc) · 6.25 KB
/
folding.vim
File metadata and controls
177 lines (151 loc) · 6.25 KB
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
" Python-mode folding functions
let s:def_regex = g:pymode_folding_regex
let s:blank_regex = '^\s*$'
let s:decorator_regex = '^\s*@'
let s:doc_begin_regex = '^\s*\%("""\|''''''\)'
let s:doc_end_regex = '\%("""\|''''''\)\s*$'
let s:doc_line_regex = '^\s*\("""\|''''''\).\+\1\s*$'
let s:symbol = matchstr(&fillchars, 'fold:\zs.') " handles multibyte characters
if s:symbol == ''
let s:symbol = ' '
endif
fun! pymode#folding#text() " {{{
let fs = v:foldstart
while getline(fs) !~ s:def_regex && getline(fs) !~ s:doc_begin_regex
let fs = nextnonblank(fs + 1)
endwhile
if getline(fs) =~ s:doc_end_regex && getline(fs) =~ s:doc_begin_regex
let fs = nextnonblank(fs + 1)
endif
let line = getline(fs)
let has_numbers = &number || &relativenumber
let nucolwidth = &fdc + has_numbers * &numberwidth
let windowwidth = winwidth(0) - nucolwidth - 6
let foldedlinecount = v:foldend - v:foldstart
" expand tabs into spaces
let onetab = strpart(' ', 0, &tabstop)
let line = substitute(line, '\t', onetab, 'g')
let line = strpart(line, 0, windowwidth - 2 -len(foldedlinecount))
let line = substitute(line, '\%("""\|''''''\)', '', '')
let fillcharcount = windowwidth - len(line) - len(foldedlinecount) + 1
return line . ' ' . repeat(s:symbol, fillcharcount) . ' ' . foldedlinecount
endfunction "}}}
fun! pymode#folding#expr(lnum) "{{{
let line = getline(a:lnum)
let indent = indent(a:lnum)
let prev_line = getline(a:lnum - 1)
if line =~ s:decorator_regex
return ">".(indent / &shiftwidth + 1)
endif
if line =~ s:def_regex
" single line def
if indent(a:lnum) >= indent(a:lnum+1) && getline(prevnonblank(a:lnum)) !~ ':\s*$'
return '='
endif
" Check if last decorator is before the last def
let decorated = 0
let lnum = a:lnum - 1
while lnum > 0
if getline(lnum) =~ s:def_regex
break
elseif getline(lnum) =~ s:decorator_regex
let decorated = 1
break
endif
let lnum -= 1
endwhile
if decorated
return '='
else
return ">".(indent / &shiftwidth + 1)
endif
endif
if line =~ s:doc_begin_regex && line !~ s:doc_line_regex && prev_line =~ s:def_regex
return ">".(indent / &shiftwidth + 1)
endif
if line =~ s:doc_end_regex && line !~ s:doc_line_regex
return "<".(indent / &shiftwidth + 1)
endif
" Handle nested defs but only for files shorter than
" g:pymode_folding_nest_limit lines due to performance concerns
if line('$') < g:pymode_folding_nest_limit && indent(prevnonblank(a:lnum))
let curpos = getpos('.')
try
let last_block = s:BlockStart(a:lnum)
let last_block_indent = indent(last_block)
" Check if last class/def is not indented and therefore can't be
" nested.
if last_block_indent
call cursor(a:lnum, 0)
let next_def = searchpos(s:def_regex, 'nW')[0]
let next_def_indent = next_def ? indent(next_def) : -1
let last_block_end = s:BlockEnd(last_block)
" If the next def has greater indent than the previous def, it
" is nested one level deeper and will have its own fold. If
" the class/def containing the current line is on the first
" line it can't be nested, and if this block ends on the last
" line, it contains no trailing code that should not be
" folded. Finally, if the next non-blank line after the end of
" the previous def is less indented than the previous def, it
" is not part of the same fold as that def. Otherwise, we know
" the current line is at the end of a nested def.
if next_def_indent <= last_block_indent && last_block > 1 && last_block_end < line('$')
\ && indent(nextnonblank(last_block_end)) >= last_block_indent
" Include up to one blank line in the fold
if getline(last_block_end) =~ s:blank_regex
let fold_end = min([prevnonblank(last_block_end - 1), last_block_end]) + 1
else
let fold_end = last_block_end
endif
if a:lnum == fold_end
return 's1'
else
return '='
endif
endif
endif
finally
call setpos('.', curpos)
endtry
endif
if line =~ s:blank_regex
if prev_line =~ s:blank_regex
if indent(a:lnum + 1) == 0 && getline(a:lnum + 1) !~ s:blank_regex
return 0
endif
return -1
else
return '='
endif
endif
return '='
endfunction "}}}
fun! s:BlockStart(lnum) "{{{
" Note: Make sure to reset cursor position after using this function.
call cursor(a:lnum, 0)
" In case the end of the block is indented to a higher level than the def
" statement plus one shiftwidth, we need to find the indent level at the
" bottom of that if/for/try/while/etc. block.
let last_def = searchpos(s:def_regex, 'bcnW')[0]
if last_def
let last_def_indent = indent(last_def)
call cursor(last_def, 0)
let next_stmt_at_def_indent = searchpos('\v^\s{'.last_def_indent.'}[^[:space:]#]', 'nW')[0]
else
let next_stmt_at_def_indent = -1
endif
" Now find the class/def one shiftwidth lower than the start of the
" aforementioned indent block.
if next_stmt_at_def_indent && next_stmt_at_def_indent < a:lnum
let max_indent = max([indent(next_stmt_at_def_indent) - &shiftwidth, 0])
else
let max_indent = max([indent(prevnonblank(a:lnum)) - &shiftwidth, 0])
endif
return searchpos('\v^\s{,'.max_indent.'}(def |class )\w', 'bcnW')[0]
endfunction "}}}
fun! s:BlockEnd(lnum) "{{{
" Note: Make sure to reset cursor position after using this function.
call cursor(a:lnum, 0)
return searchpos('\v^\s{,'.indent('.').'}\S', 'nW')[0] - 1
endfunction "}}}
" vim: fdm=marker:fdl=0