11" Python-mode folding functions
22
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.
35
6+ " Regex definitions for correct folding
47let s: def_regex = g: pymode_folding_regex
58let 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]\=\%("""\|'''''' \)'
813let 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*$'
1018let s: symbol = matchstr (&fillchars , ' fold:\zs.' ) " handles multibyte characters
1119if s: symbol == ' '
1220 let s: symbol = ' '
1321endif
22+ " ''''''''
1423
1524
1625fun ! pymode#folding#text () " {{{
@@ -33,24 +42,29 @@ fun! pymode#folding#text() " {{{
3342 let line = substitute (line , ' \t' , onetab, ' g' )
3443
3544 let line = strpart (line , 0 , windowwidth - 2 - len (foldedlinecount))
36- let line = substitute (line , ' \%("""\|'''''' \)' , ' ' , ' ' )
45+ let line = substitute (line , ' [uU]\= \%("""\|'''''' \)' , ' ' , ' ' )
3746 let fillcharcount = windowwidth - len (line ) - len (foldedlinecount) + 1
3847 return line . ' ' . repeat (s: symbol , fillcharcount) . ' ' . foldedlinecount
3948endfunction " }}}
4049
41-
4250fun ! pymode#folding#expr (lnum) " {{{
4351
4452 let line = getline (a: lnum )
4553 let indent = indent (a: lnum )
4654 let prev_line = getline (a: lnum - 1 )
55+ let next_line = getline (a: lnum + 1 )
4756
57+ " Decorators {{{
4858 if line = ~ s: decorator_regex
4959 return " >" .(indent / &shiftwidth + 1 )
50- endif
60+ endif " }}}
5161
62+ " Definition {{{
5263 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
5468 if indent (a: lnum ) >= indent (a: lnum+ 1 ) && getline (prevnonblank (a: lnum )) !~ ' :\s*$'
5569 return ' ='
5670 endif
@@ -71,16 +85,35 @@ fun! pymode#folding#expr(lnum) "{{{
7185 else
7286 return " >" .(indent / &shiftwidth + 1 )
7387 endif
74- endif
88+ endif " }}}
7589
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 " ="
78100 endif
79101
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
82108 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 " }}}
83115
116+ " Nested Definitions {{{
84117 " Handle nested defs but only for files shorter than
85118 " g:pymode_folding_nest_limit lines due to performance concerns
86119 if line (' $' ) < g: pymode_folding_nest_limit && indent (prevnonblank (a: lnum ))
@@ -125,18 +158,25 @@ fun! pymode#folding#expr(lnum) "{{{
125158 finally
126159 call setpos (' .' , curpos)
127160 endtry
128- endif
161+ endif " }}}
129162
163+ " Blank Line {{{
130164 if line = ~ s: blank_regex
131165 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
134174 endif
135175 return -1
136176 else
137177 return ' ='
138178 endif
139- endif
179+ endif " }}}
140180
141181 return ' ='
142182
@@ -174,4 +214,52 @@ fun! s:BlockEnd(lnum) "{{{
174214 return searchpos (' \v^\s{,' .indent (' .' ).' }\S' , ' nW' )[0 ] - 1
175215endfunction " }}}
176216
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+
177265" vim: fdm = marker:fdl = 0
0 commit comments