Reputation: 2981
Does anybody know of a method, or perhaps a plugin, that will automatically fold long docstrings in Python? I have docstrings in my code that span several pages, so it is troublesome to keep paging through them. The other tricky part is that there is embedded python testing code in the docstrings, so that might make parsing them difficult. Note that I only need to automatically fold the entire docstring, regardless of what is in it.
Upvotes: 9
Views: 3059
Reputation: 43
I think I've found a slight improvement to the answer of @too_much_php by trying to figure out how to do the same thing without root access. Copying the syntax region definitions from $VIMRUNTIME/syntax/python.vim
to ~/.vim/after/syntax/python.vim
and editing them solved the problem for me. Here's my code for ~/.vim/after/syntax/python.vim
:
syn region docString1
\ start=+[uU]\=\z('''\|"""\)+ end="\z1" keepend transparent fold
syn region docString2
\ start=+[uU]\=[rR]\z('''\|"""\)+ end="\z1" keepend transparent fold
Now I can run :set foldmethod=syntax
to fold docstrings.
PS: Credits to @too_much_php for the initial idea
Upvotes: 1
Reputation: 4804
My solution involves using SimpylFold. After installing it using Vundle, I put this in my vimrc
file:
autocmd FileType python setlocal foldlevel=2
Which makes all docstrings folded by default when you open a python file, which is super awesome.
Upvotes: 0
Reputation: 21113
I wrote a vimscript plugin many years ago to do exactly this, but never got around to publishing it. I've put it up as a gist, but have also pasted the source below. I've also replicated its functionality for Sublime Text here.
"
" Fold multi-line Python comments into one line.
"
" Also maps the "-" key to toggle expansion and <C-f> to toggle all folding.
"
setlocal foldmethod=syntax
setlocal foldtext=FoldText()
setlocal fillchars=
map <buffer> - za
map <buffer> <C-f> :call ToggleFold()<CR>
let b:folded = 1
hi Folded gui=bold cterm=bold guifg=cyan ctermfg=cyan guibg=NONE ctermbg=NONE
function! ToggleFold()
if b:folded == 0
exec "normal! zM"
let b:folded = 1
else
exec "normal! zR"
let b:folded = 0
endif
endfunction
function! s:Strip(string)
return substitute(a:string, '^[[:space:][:return:][:cntrl:]]\+\|[[:space:][:return:][:cntrl:]]\+$', '', '')
endfunction
" Extract the first line of a multi-line comment to use as the fold snippet
function! FoldText()
let l:snippet = getline(v:foldstart)
if len(s:Strip(l:snippet)) == 3
let l:snippet = strpart(l:snippet, 1) . s:Strip(getline(v:foldstart + 1))
endif
return '+' . l:snippet . ' ...'
endfunction
Upvotes: 0
Reputation: 37461
You can do this with :set foldmethod=marker foldmarker=""","""
, I think. I haven't tested it, but that should do the trick. The arguments to foldmarker are start and end markers.
Upvotes: 0
Reputation: 91068
This is a bit of a dirty hack, but you can go through the python syntax file (:sp $VIMRUNTIME/syntax/python.vim
) and find all the syntax regions for triple-quoted strings (search for '''
and """
) and add the fold
keyword to the end of those statements. Then just set foldmethod=syntax
for python files and the comments should be folded.
Upvotes: 10
Reputation: 946
I'm not sure about a plugin or automation, but if you type zf/
you can then search for something and it will fold up to the next instance of it. So in a document like the following (where [] is the cursor):
def foo():
"""[]
Some long docstring
that takes up many
lines
"""
pass
Look at edit2 first for the updated search string!
If you use the command zf/"""[ENTER]
, it should fold everything from the current line (the beginning of the docstring) to the next occurrence of """
which should be the end of the docstring.
I know this isn't automation, but perhaps it will help in the interim, or lead you down the right path to automating it. See edit2 for a better search function, although I still don't know how to automate.
Hope this helps.
Edit: in a corollary, you can search for any docstring with /"""\_.\{-}"""
, although this will also return the code within the docstring. To search for a function definition followed by a docstring, you can use /def\_.\{-}"""\_.\{-}"""
, although this breaks on a def inside the docstring.
Edit2: Actually, some more playing with regexs led me to this: /def.\{-}):\_s*"""\_.\{-}"""
which should find any function followed by a docstring. It searches for def
followed by any characters, then ):
followed by a newline and/or whitespace followed by """
followed by any number of lines than the next """
, but always ensures the 2nd triple quote is the one immediately following the first.
Upvotes: 2
Reputation: 33864
In your .vimrc add:
" folding
set foldmethod=indent
This will auto-fold at every indentation, which, in python, translates to docstrings. It works VERY VERY nice. Give it a try. The above answer is correct, but requires a bunch of keystrokes (blah!)
Upvotes: 1