1
1
" Python-mode folding functions
2
2
3
+ " Notice that folding is based on single line so complex regular expressions
4
+ " that take previous line into consideration are not fit for the job.
3
5
6
+ " Regex definitions for correct folding
4
7
let s: def_regex= g: pymode_folding_regex
5
8
let s: blank_regex= ' ^\s*$'
6
- let s: decorator_regex= ' ^\s*@'
7
- let s: doc_begin_regex= ' ^\s*\%("""\|'''''' \)'
9
+ " Spyder, a very popular IDE for python has a template which includes
10
+ " '@author:' ; thus the regex below.
11
+ let s: decorator_regex= ' ^\s*@\(author:\)\@!'
12
+ let s: doc_begin_regex= ' ^\s*[uU]\=\%("""\|'''''' \)'
8
13
let s: doc_end_regex= ' \%("""\|'''''' \)\s*$'
9
- let s: doc_line_regex= ' ^\s*\("""\|'''''' \).\+\1\s*$'
14
+ " This one is needed for the while loop to count for opening and closing
15
+ " docstrings.
16
+ let s: doc_general_regex= ' \%("""\|'''''' \)'
17
+ let s: doc_line_regex= ' ^\s*[uU]\=\("""\|'''''' \).\+\1\s*$'
10
18
let s: symbol= matchstr (&fillchars ,' fold:\zs.' )" handles multibyte characters
11
19
if s: symbol== ' '
12
20
let s: symbol= ' '
13
21
endif
22
+ " ''''''''
14
23
15
24
16
25
fun ! pymode#folding#text ()" {{{
@@ -33,24 +42,29 @@ fun! pymode#folding#text() " {{{
33
42
let line = substitute (line ,' \t' , onetab,' g' )
34
43
35
44
let line = strpart (line ,0 , windowwidth- 2 - len (foldedlinecount))
36
- let line = substitute (line ,' \%("""\|'''''' \)' ,' ' ,' ' )
45
+ let line = substitute (line ,' [uU]\= \%("""\|'''''' \)' ,' ' ,' ' )
37
46
let fillcharcount= windowwidth- len (line )- len (foldedlinecount)+ 1
38
47
return line .' ' .repeat (s: symbol , fillcharcount) .' ' . foldedlinecount
39
48
endfunction " }}}
40
49
41
-
42
50
fun ! pymode#folding#expr (lnum)" {{{
43
51
44
52
let line = getline (a: lnum )
45
53
let indent = indent (a: lnum )
46
54
let prev_line= getline (a: lnum- 1 )
55
+ let next_line= getline (a: lnum+ 1 )
47
56
57
+ " Decorators {{{
48
58
if line = ~s: decorator_regex
49
59
return " >" .(indent / &shiftwidth + 1 )
50
- endif
60
+ endif " }}}
51
61
62
+ " Definition {{{
52
63
if line = ~s: def_regex
53
- " single line def
64
+ " If indent of this line is greater or equal than line below
65
+ " and previous non blank line does not end with : (that is, is not a
66
+ " definition)
67
+ " Keep the same indentation
54
68
if indent (a: lnum )>= indent (a: lnum+ 1 )&& getline (prevnonblank (a: lnum ))!~ ' :\s*$'
55
69
return ' ='
56
70
endif
@@ -71,16 +85,35 @@ fun! pymode#folding#expr(lnum) "{{{
71
85
else
72
86
return " >" .(indent / &shiftwidth + 1 )
73
87
endif
74
- endif
88
+ endif " }}}
75
89
76
- if line = ~s: doc_begin_regex&& line !~ s: doc_line_regex&& prev_line= ~s: def_regex
77
- return " >" .(indent / &shiftwidth + 1 )
90
+ " Docstrings {{{
91
+
92
+ " TODO: A while loop now counts the number of open and closed folding in
93
+ " order to determine if it is a closing or opening folding.
94
+ " It is working but looks like it is an overkill.
95
+
96
+ " Notice that an effect of this is that other docstring matches will not
97
+ " be one liners.
98
+ if line = ~s: doc_line_regex
99
+ return " ="
78
100
endif
79
101
80
- if line = ~s: doc_end_regex&& line !~ s: doc_line_regex
81
- return " <" .(indent / &shiftwidth + 1 )
102
+ if line = ~s: doc_begin_regex
103
+ " echom 'just entering'
104
+ if s: Is_opening_folding (a: lnum )
105
+ " echom 'entering at line ' . a:lnum
106
+ return " >" .(indent / &shiftwidth + 1 )
107
+ endif
82
108
endif
109
+ if line = ~s: doc_end_regex
110
+ if ! s: Is_opening_folding (a: lnum )
111
+ " echom 'leaving at line ' . a:lnum
112
+ return " <" .(indent / &shiftwidth + 1 )
113
+ endif
114
+ endif " }}}
83
115
116
+ " Nested Definitions {{{
84
117
" Handle nested defs but only for files shorter than
85
118
" g:pymode_folding_nest_limit lines due to performance concerns
86
119
if line (' $' ) < g: pymode_folding_nest_limit&& indent (prevnonblank (a: lnum ))
@@ -125,18 +158,25 @@ fun! pymode#folding#expr(lnum) "{{{
125
158
finally
126
159
call setpos (' .' , curpos)
127
160
endtry
128
- endif
161
+ endif " }}}
129
162
163
+ " Blank Line {{{
130
164
if line = ~s: blank_regex
131
165
if prev_line= ~s: blank_regex
132
- if indent (a: lnum+ 1 )== 0 && getline (a: lnum+ 1 )!~ s: blank_regex
133
- return 0
166
+ if indent (a: lnum+ 1 )== 0 && next_line!~ s: blank_regex&& next_line!~ s: doc_general_regex
167
+ if s: Is_opening_folding (a: lnum )
168
+ " echom a:lnum
169
+ return " ="
170
+ else
171
+ " echom "not " . a:lnum
172
+ return 0
173
+ endif
134
174
endif
135
175
return -1
136
176
else
137
177
return ' ='
138
178
endif
139
- endif
179
+ endif " }}}
140
180
141
181
return ' ='
142
182
@@ -174,4 +214,52 @@ fun! s:BlockEnd(lnum) "{{{
174
214
return searchpos (' \v^\s{,' .indent (' .' ).' }\S' ,' nW' )[0 ]- 1
175
215
endfunction " }}}
176
216
217
+ function ! s: Is_opening_folding (lnum)" {{{
218
+ " Helper function to see if docstring is opening or closing
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
222
+
223
+ " The idea of this part of the function is to identify real docstrings and
224
+ " not just triple quotes (that could be a regular string).
225
+ "
226
+ " Iterater over all lines from the start until current line (inclusive)
227
+ for i in range (1 ,a: lnum )
228
+ let i_line= getline (i )
229
+
230
+ if i_line= ~s: doc_line_regex
231
+ " echom "case 00 on line " . i
232
+ continue
233
+ endif
234
+
235
+ if i_line= ~s: doc_begin_regex&& ! has_open_docstring
236
+ " echom "case 01 on line " . i
237
+ " This causes the loop to continue if there is a triple quote which
238
+ " is not a docstring.
239
+ if extra_docstrings > 0
240
+ let extra_docstrings= extra_docstrings- 1
241
+ continue
242
+ else
243
+ let has_open_docstring= 1
244
+ let number_of_folding= number_of_folding+ 1
245
+ endif
246
+ " If it is an end doc and has an open docstring.
247
+ elseif i_line= ~s: doc_end_regex&& has_open_docstring
248
+ " echom "case 02 on line " . i
249
+ let has_open_docstring= 0
250
+ let number_of_folding= number_of_folding+ 1
251
+
252
+ elseif i_line= ~s: doc_general_regex
253
+ " echom "extra docstrings on line " . i
254
+ let extra_docstrings= extra_docstrings+ 1
255
+ endif
256
+ endfor
257
+
258
+ if fmod (number_of_folding,2 )== 1 " If odd then it is an opening
259
+ return 1
260
+ else
261
+ return 0
262
+ endif
263
+ endfunction " }}}
264
+
177
265
" vim:fdm = marker:fdl = 0