1
- k " Python-mode folding function2
2
1
" Notice that folding is based on single line so complex regular expressions
3
2
" that take previous line into consideration are not fit for the job.
4
3
@@ -47,25 +46,44 @@ endfunction "}}}
47
46
48
47
fun ! pymode#folding#expr (lnum)" {{{
49
48
49
+ return pymode#folding#foldcase (a: lnum )[' foldlevel' ]
50
+
51
+ endfunction " }}}
52
+
53
+ fun ! pymode#folding#foldcase (lnum)" {{{
54
+ " Return a dictionary with a brief description of the foldcase and the
55
+ " evaluated foldlevel: {'foldcase': 'case description', 'foldlevel': 1}.
56
+
57
+ let l: foldcase= ' general'
58
+ let l: foldlevel= 0
59
+
50
60
let line = getline (a: lnum )
51
61
let indent = indent (a: lnum )
52
62
let prev_line= getline (a: lnum- 1 )
53
63
let next_line= getline (a: lnum+ 1 )
54
64
55
65
" Decorators {{{
56
66
if line = ~s: decorator_regex
57
- return " >" .(indent / &shiftwidth + 1 )
67
+ let l: foldcase= ' decorator declaration'
68
+ let l: foldlevel= ' >' .(indent / &shiftwidth + 1 )
69
+ return {' foldcase' :l: foldcase ,' foldlevel' :l: foldlevel }
58
70
endif " }}}
59
71
60
72
" Definition {{{
61
73
if line = ~s: def_regex
74
+
75
+ " TODO: obscure case.
62
76
" If indent of this line is greater or equal than line below
63
77
" and previous non blank line does not end with : (that is, is not a
64
78
" definition)
65
79
" Keep the same indentation
66
- if indent (a: lnum )>= indent (a: lnum+ 1 )&& getline (prevnonblank (a: lnum ))!~ ' :\s*$'
67
- return ' ='
68
- endif
80
+ " xxx " if indent(a:lnum) >= indent(a:lnum+1)
81
+ " xxx " \ && getline(prevnonblank(a:lnum)) !~ ':\s*$'
82
+ " xxx " let l:foldcase = 'definition'
83
+ " xxx " let l:foldlevel = '='
84
+ " xxx " return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel}
85
+ " xxx " endif
86
+
69
87
" Check if last decorator is before the last def
70
88
let decorated= 0
71
89
let lnum= a: lnum- 1
@@ -79,14 +97,13 @@ fun! pymode#folding#expr(lnum) "{{{
79
97
let lnum-= 1
80
98
endwhile
81
99
if decorated
82
- return ' ='
100
+ let l: foldcase= ' decorated function declaration'
101
+ let l: foldlevel= ' ='
83
102
else
84
- " The line below may improve folding.
85
- return " >" .(indent / &shiftwidth + 1 )
86
- " This was the previous rule. It grouped classes definitions
87
- " together (undesired).
88
- " return indent / &shiftwidth + 1
103
+ let l: foldcase= ' function declaration'
104
+ let l: foldlevel= ' >' .(indent / &shiftwidth + 1 )
89
105
endif
106
+ return {' foldcase' :l: foldcase ,' foldlevel' :l: foldlevel }
90
107
endif " }}}
91
108
92
109
" Docstrings {{{
@@ -98,60 +115,105 @@ fun! pymode#folding#expr(lnum) "{{{
98
115
" Notice that an effect of this is that other docstring matches will not
99
116
" be one liners.
100
117
if line = ~s: docstring_line_regex
101
- return " ="
118
+ let l: foldcase= ' one-liner docstring'
119
+ let l: foldlevel= ' ='
120
+ return {' foldcase' :l: foldcase ,' foldlevel' :l: foldlevel }
102
121
endif
103
-
104
122
if line = ~s: docstring_begin_regex
105
123
if s: Is_opening_folding (a: lnum )
106
- return " >" .(indent / &shiftwidth + 1 )
124
+ let l: foldcase= ' open multiline docstring'
125
+ let l: foldlevel= ' a1'
107
126
endif
127
+ return {' foldcase' :l: foldcase ,' foldlevel' :l: foldlevel }
108
128
endif
109
129
if line = ~s: docstring_end_regex
110
130
if ! s: Is_opening_folding (a: lnum )
111
- return " <" .(indent / &shiftwidth + 1 )
131
+ let l: foldcase= ' close multiline docstring'
132
+ let l: foldlevel= ' s1'
112
133
endif
134
+ return {' foldcase' :l: foldcase ,' foldlevel' :l: foldlevel }
113
135
endif " }}}
114
136
115
137
" Blocks. {{{
138
+ let line_block_start= s: BlockStart (a: lnum )
139
+ let line_block_end= s: BlockEnd (a: lnum )
140
+ let prev_line_block_start= s: BlockStart (a: lnum- 1 )
116
141
let save_cursor= getcurpos ()
117
142
if line !~ s: blank_regex
118
- let line_block_start= s: BlockStart (a: lnum )
119
- let prev_line_block_start= s: BlockStart (a: lnum- 1 )
120
143
call setpos (' .' , save_cursor)
121
- if line_block_start== prev_line_block_start|| a: lnum- line_block_start== 1
122
- return ' ='
144
+ if line_block_start== prev_line_block_start
145
+ \ || a: lnum- line_block_start== 1
146
+ let l: foldcase= ' non blank line; first line of block or part of it'
147
+ let l: foldlevel= ' ='
123
148
elseif indent < indent (prevnonblank (a: lnum- 1 ))
124
- return indent (line_block_start) / &shiftwidth + 1
125
- else
149
+ if indent == 0
150
+ let l: foldcase= ' non blank line; zero indent'
151
+ let l: foldlevel= 0
152
+ else
153
+ let l: foldcase= ' non blank line; non zero indent'
154
+ let l: foldlevel= indent (line_block_start) / &shiftwidth + 1
155
+ endif
126
156
endif
157
+ return {' foldcase' :l: foldcase ,' foldlevel' :l: foldlevel }
127
158
endif
128
159
" endif " }}}
129
160
130
161
" Blank Line {{{
162
+ " Comments: cases of blank lines:
163
+ " 1. After non blank line: gets folded with previous line.
164
+ " 1. Just after a block; in this case it gets folded with the block.
165
+ " 1. Between docstrings and imports.
166
+ " 1. Inside docstrings.
167
+ " 2. Inside functions/methods.
168
+ " 3. Between functions/methods.
131
169
if line = ~s: blank_regex
132
- if prev_line= ~s: blank_regex
133
- if indent (a: lnum+ 1 )== 0 && next_line!~ s: blank_regex&& next_line!~ s: docstring_general_regex
134
- if s: Is_opening_folding (a: lnum )
135
- return " ="
136
- else
137
- return 0
138
- endif
139
- endif
140
- return -1
141
- else
142
- return ' ='
170
+ if prev_line!~ s: blank_regex
171
+ let l: foldcase= ' blank line after non blank line'
172
+ let l: foldlevel= ' ='
173
+ return {' foldcase' :l: foldcase ,' foldlevel' :l: foldlevel }
174
+ elseif a: lnum > line_block_start&& a: lnum < line_block_end
175
+ let l: foldcase= ' blank line inside block'
176
+ let l: foldlevel= ' ='
177
+ return {' foldcase' :l: foldcase ,' foldlevel' :l: foldlevel }
143
178
endif
179
+ " if prev_line =~ s:blank_regex
180
+ " if indent(a:lnum + 1) == 0 && next_line !~ s:blank_regex && next_line !~ s:docstring_general_regex
181
+ " if s:Is_opening_folding(a:lnum)
182
+ " let l:foldcase = 'case 1'
183
+ " let l:foldlevel = '='
184
+ " return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel}
185
+ " else
186
+ " let l:foldcase = 'case 2'
187
+ " let l:foldlevel = 0
188
+ " return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel}
189
+ " endif
190
+ " endif
191
+ " let l:foldcase = 'case 3'
192
+ " let l:foldlevel = -1
193
+ " return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel}
194
+ " else
195
+ " let l:foldcase = 'case 4'
196
+ " let l:foldlevel = '='
197
+ " return {'foldcase': l:foldcase, 'foldlevel': l:foldlevel}
198
+ " endif
144
199
endif " }}}
145
200
146
- return ' = '
201
+ return { ' foldcase ' : l: foldcase , ' foldlevel ' : l: foldlevel }
147
202
148
203
endfunction " }}}
149
204
150
- fun ! s: BlockStart (line_number )" {{{
205
+ fun ! s: BlockStart (lnum )" {{{
151
206
" Returns the definition statement which encloses the current line.
152
207
208
+ let line = getline (a: lnum )
209
+ if line !~ s: blank_regex
210
+ let l: inferred_indent= indent (a: lnum )
211
+ else
212
+ let l: inferred_indent= prevnonblank (a: lnum )
213
+ endif
214
+
153
215
" Note: Make sure to reset cursor position after using this function.
154
- call cursor (a: line_number ,0 )
216
+ call cursor (a: lnum ,0 )
155
217
156
218
" In case the end of the block is indented to a higher level than the def
157
219
" statement plus one shiftwidth, we need to find the indent level at the
@@ -162,7 +224,9 @@ fun! s:BlockStart(line_number) "{{{
162
224
" W: don't Wrap around the end of the file
163
225
let previous_definition= searchpos (s: def_regex ,' bnW' )
164
226
if previous_definition!= [0 ,0 ]
165
- while previous_definition!= [0 ,0 ]&& indent (previous_definition[0 ])>= indent (a: line_number )
227
+ " Lines that are blank have zero indent.
228
+ while previous_definition!= [0 ,0 ]
229
+ \ && indent (previous_definition[0 ])>= l: inferred_indent
166
230
let previous_definition= searchpos (s: def_regex ,' bnW' )
167
231
call cursor (previous_definition[0 ]- 1 ,0 )
168
232
endwhile
@@ -179,13 +243,15 @@ fun! s:BlockStart(line_number) "{{{
179
243
180
244
" Now find the class/def one shiftwidth lower than the start of the
181
245
" aforementioned indent block.
182
- if next_stmt_at_def_indent&& next_stmt_at_def_indent < a: line_number
246
+ if next_stmt_at_def_indent&& ( next_stmt_at_def_indent < a: lnum )
183
247
let max_indent= max ([indent (next_stmt_at_def_indent)- &shiftwidth ,0 ])
184
248
else
185
- let max_indent= max ([indent (prevnonblank (a: line_number ))- &shiftwidth ,0 ])
249
+ let max_indent= max ([indent (prevnonblank (a: lnum ))- &shiftwidth ,0 ])
186
250
endif
187
251
188
- return searchpos (' \v^\s{,' .max_indent.' }(def |class )\w' ,' bcnW' )[0 ]
252
+ let result= searchpos (' \v^\s{,' .max_indent.' }(def |class )\w' ,' bcnW' )[0 ]
253
+
254
+ return result
189
255
190
256
endfunction " }}}
191
257
function ! Blockstart (x )
@@ -206,56 +272,55 @@ function! Blockend(lnum)
206
272
endfunction
207
273
208
274
function ! s: Is_opening_folding (lnum)" {{{
209
- " Helper function to see if docstring is opening or closing
275
+ " Helper function to see ifmulti line docstring is opening or closing.
210
276
211
- " Cache the result so the loop runs only once per change
277
+ " Cache the result so the loop runs only once per change.
212
278
if get (b: ,' fold_changenr' ,-1 )== changenr ()
213
- return b: fold_cache [a: lnum ]" If odd then it is an opening
279
+ return b: fold_cache [a: lnum- 1 ]" If odd then it is an opening
214
280
else
215
281
let b: fold_changenr= changenr ()
216
282
let b: fold_cache= []
217
283
endif
218
284
219
- let number_of_folding= 0 " To be analized if odd/even to inform if it is opening or closing.
220
- let has_open_docstring= 0 " To inform is already has an open docstring.
221
- let extra_docstrings= 0 " To help skipping ''' and" " " which are not docstrings
285
+ " To be analized if odd/even to inform if it is opening or closing.
286
+ let fold_odd_even= 0
287
+ " To inform is already has an open docstring.
288
+ let has_open_docstring= 0
289
+ " To help skipping ''' and """ which are not docstrings.
290
+ let extra_docstrings= 0
222
291
223
292
" The idea of this part of the function is to identify real docstrings and
224
293
" not just triple quotes (that could be a regular string).
225
- "
294
+
226
295
" Iterater over all lines from the start until current line (inclusive)
227
296
for i in range (1 ,line (' $' ))
228
- call add (b: fold_cache , number_of_folding% 2 )
229
297
230
298
let i_line= getline (i )
231
299
232
- if i_line= ~s: docstring_line_regex
233
- continue
234
- endif
235
-
236
300
if i_line= ~s: docstring_begin_regex&& ! has_open_docstring
237
301
" This causes the loop to continue if there is a triple quote which
238
302
" is not a docstring.
239
303
if extra_docstrings > 0
240
304
let extra_docstrings= extra_docstrings- 1
241
- continue
242
305
else
243
306
let has_open_docstring= 1
244
- let number_of_folding = number_of_folding + 1
307
+ let fold_odd_even = fold_odd_even + 1
245
308
endif
246
309
" If it is an end doc and has an open docstring.
247
310
elseif i_line= ~s: docstring_end_regex&& has_open_docstring
248
311
let has_open_docstring= 0
249
- let number_of_folding = number_of_folding + 1
312
+ let fold_odd_even = fold_odd_even + 1
250
313
251
314
elseif i_line= ~s: docstring_general_regex
252
315
let extra_docstrings= extra_docstrings+ 1
253
316
endif
254
- endfor
255
317
256
- call add (b: fold_cache , number_of_folding% 2 )
318
+ call add (b: fold_cache , fold_odd_even% 2 )
319
+
320
+ endfor
257
321
258
322
return b: fold_cache [a: lnum ]
323
+
259
324
endfunction " }}}
260
325
261
326
" vim:fdm = marker:fdl = 0