Skip to content

Commit 59613fb

Browse files
committed
Merge pull request python-mode#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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy