8000 Merge pull request #500 from wilywampa/nested_defs · python-mode/python-mode@59613fb · GitHub
[go: up one dir, main page]

Skip to content

Commit 59613fb

Browse files
committed
Merge pull request #500 from wilywampa/nested_defs
Handle folding of nested defs correctly
2 parents c26b4dc + f2a03ec commit 59613fb

File tree

2 files changed

+79
-0
lines changed

2 files changed

+79
-0
lines changed

autoload/pymode/folding.vim

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,52 @@ fun! pymode#folding#expr(lnum) "{{{
7676
return "<".(indent / &shiftwidth + 1)
7777
endif
7878

79+
" Handle nested defs but only for files shorter than
80+
" g:pymode_folding_nest_limit lines due to performance concerns
81+
if line('$') < g:pymode_folding_nest_limit && indent(prevnonblank(a:lnum))
82+
let curpos = getcurpos()
83+
try
84+
let last_block = s:BlockStart(a:lnum)
85+
let last_block_indent = indent(last_block)
86+
87+
" Check if last class/def is not indented and therefore can't be
88+
" nested.
89+
if last_block_indent
90+
call cursor(a:lnum, 0)
91+
let next_def = searchpos(s:def_regex, 'nW')[0]
92+
let next_def_indent = next_def ? indent(next_def) : -1
93+
let last_block_end = s:BlockEnd(last_block)
94+
95+
" If the next def has greater indent than the previous def, it
96+
" is nested one level deeper and will have its own fold. If
97+
" the class/def containing the current line is on the first
98+
" line it can't be nested, and if this block ends on the last
99+
" line, it contains no trailing code that should not be
100+
" folded. Finally, if the next non-blank line after the end of
101+
" the previous def is less indented than the previous def, it
102+
" is not part of the same fold as that def. Otherwise, we know
103+
" the current line is at the end of a nested def.
104+
if next_def_indent <= last_block_indent && last_block > 1 && last_block_end < line('$')
105+
\ && indent(nextnonblank(last_block_end)) >= last_block_indent
106+
107+
" Include up to one blank line in the fold
108+
if getline(last_block_end) =~ s:blank_regex
109+
let fold_end = min([prevnonblank(last_block_end - 1), last_block_end]) + 1
110+
else
111+
let fold_end = last_block_end
112+
endif
113+
if a:lnum == fold_end
114+
return 's1'
115+
else
116+
return '='
117+
endif
118+
endif
119+
endif
120+
finally
121+
call setpos('.', curpos)
122+
endtry
123+
endif
124+
79125
if line =~ s:blank_regex
80126
if prev_line =~ s:blank_regex
81127
if indent(a:lnum + 1) == 0 && getline(a:lnum + 1) !~ s:blank_regex
@@ -95,5 +141,36 @@ fun! pymode#folding#expr(lnum) "{{{
95141

96142
endfunction "}}}
97143

144+
fun! s:BlockStart(lnum) "{{{
145+
" Note: Make sure to reset cursor position after using this function.
146+
call cursor(a:lnum, 0)
147+
148+
" In case the end of the block is indented to a higher level than the def
149+
" statement plus one shiftwidth, we need to find the indent level at the
150+
" bottom of that if/for/try/while/etc. block.
151+
let last_def = searchpos(s:def_regex, 'bcnW')[0]
152+
if last_def
153+
let last_def_indent = indent(last_def)
154+
call cursor(last_def, 0)
155+
let next_stmt_at_def_indent = searchpos('\v^\s{'.last_def_indent.'}[^[:space:]#]', 'nW')[0]
156+
else
157+
let next_stmt_at_def_indent = -1
158+
endif
159+
160+
" Now find the class/def one shiftwidth lower than the start of the
161+
" aforementioned indent block.
162+
if next_stmt_at_def_indent && next_stmt_at_def_indent < a:lnum
163+
let max_indent = max([indent(next_stmt_at_def_indent) - &shiftwidth, 0])
164+
else
165+
let max_indent = max([indent(prevnonblank(a:lnum)) - &shiftwidth, 0])
166+
endif
167+
return searchpos('\v^\s{,'.max_indent.'}(def |class )\w', 'bcnW')[0]
168+
endfunction "}}}
169+
170+
fun! s:BlockEnd(lnum) "{{{
171+
" Note: Make sure to reset cursor position after using this function.
172+
call cursor(a:lnum, 0)
173+
return searchpos('\v^\s{,'.indent('.').'}\S', 'nW')[0] - 1
174+
endfunction "}}}
98175

99176
" vim: fdm=marker:fdl=0

plugin/pymode.vim

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ call pymode#default("g:pymode_indent", 1)
3636

3737
" Enable/disable pymode folding for pyfiles.
3838
call pymode#default("g:pymode_folding", 1)
39+
" Maximum file length to check for nested class/def statements
40+
call pymode#default("g:pymode_folding_nest_limit", 1000)
3941
" Change for folding customization (by example enable fold for 'if', 'for')
4042
call pymode#default("g:pymode_folding_regex", '^\s*\%(class\|def\) \w\+')
4143

0 commit comments

Comments
 (0)
0