1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-01-24 06:30:03 +08:00
SpaceVim/bundle/vim-pydocstring/autoload/pydocstring.vim

242 lines
6.3 KiB
VimL
Raw Normal View History

" Insert Docstring.
" Author: Shinya Ohyanagi <sohyanagi@gmail.com>
" WebPage: http://github.com/heavenshell/vim-pydocstriong/
" Description: Generate Python docstring to your Python script file.
" License: BSD, see LICENSE for more details.
" NOTE: This module is heavily inspired by php-doc.vim and
" sonictemplate.vim
let s:save_cpo = &cpo
set cpo&vim
let g:pydocstring_templates_path = get(g:, 'pydocstring_templates_path', '')
let g:pydocstring_formatter = get(g:, 'pydocstring_formatter', 'sphinx')
let g:pydocstring_doq_path = get(
\ g:,
\ 'pydocstring_doq_path',
\ printf('%s/lib/doq', expand('<sfile>:p:h:h'))
\ )
let g:pydocstring_ignore_init = get(g:, 'pydocstring_ignore_init', 0)
let s:results = []
function! s:get_indent_width() abort
" Get indentation width
return &smarttab ? &shiftwidth : &softtabstop
endfunction
function! s:get_range() abort
" Get visual mode selection.
let mode = visualmode(1)
if mode == 'v' || mode == 'V' || mode == ''
let start_lineno = line("'<")
let end_lineno = line("'>")
return {'start_lineno': start_lineno, 'end_lineno': end_lineno}
endif
let current = line('.')
return {'start_lineno': 0, 'end_lineno': '$'}
endfunction
function! s:insert_docstring(docstrings, end_lineno) abort
let paste = &g:paste
let &g:paste = 1
silent! execute 'normal! ' . a:end_lineno . 'G$'
let current_lineno = line('.')
" If current position is bottom, add docstring below.
if a:end_lineno == current_lineno
silent! execute 'normal! O' . a:docstrings['docstring']
else
silent! execute 'normal! o' . a:docstrings['docstring']
endif
let &g:paste = paste
silent! execute 'normal! ' . a:end_lineno . 'G$'
endfunction
function! s:callback(msg, indent, start_lineno) abort
let msg = join(a:msg, '')
" Check needed for Neovim
if len(msg) == 0
return
endif
let docstrings = reverse(json_decode(msg))
silent! execute 'normal! 0'
let length = len(docstrings)
for docstring in docstrings
let lineno = 0
if length > 1
call cursor(a:start_lineno + docstring['start_lineno'] - 1, 1)
let lineno = search('\:\(\s*#.*\)*$', 'n') + 1
else
let lineno = search('\:\(\s*#.*\)*$', 'n') + 1
endif
call s:insert_docstring(docstring, lineno)
endfor
endfunction
function! s:format_callback(msg, indent, start_lineno) abort
call extend(s:results, a:msg)
endfunction
function! s:exit_callback(msg) abort
unlet s:job " Needed for Neovim
let length = len(s:results)
if length
if length == 1 && s:results[0] == ''
let s:results = []
return
endif
let view = winsaveview()
silent execute '% delete'
" Hack for Neovima PydocstringFormat
" Neovim add blank line to the end of list
if has('nvim') && s:results[-1] == ''
call remove(s:results, -1)
endif
call setline(1, s:results)
call winrestview(view)
let s:results = []
endif
endfunction
function! s:execute(cmd, lines, indent, start_lineno, cb, ex_cb) abort
if !executable(expand(g:pydocstring_doq_path))
redraw
echohl Error
echo '`doq` not found. Install `doq`.'
echohl None
return
endif
let s:results = []
if has('nvim')
if exists('s:job')
call jobstop(s:job)
endif
let s:job = jobstart(a:cmd, {
\ 'on_stdout': {_c, m, _e -> a:cb(m, a:indent, a:start_lineno)},
\ 'on_exit': {_c, m, _e -> a:ex_cb(m)},
\ 'stdout_buffered': v:true,
\ })
if exists('*chansend')
" Neovim >0.3.0
call chansend(s:job, a:lines)
call chanclose(s:job, 'stdin')
else
" Legacy API
call jobsend(s:job, a:lines)
call jobclose(s:job, 'stdin')
endif
else
if exists('s:job') && job_status(s:job) != 'stop'
call job_stop(s:job)
endif
let s:job = job_start(a:cmd, {
\ 'callback': {_, m -> a:cb([m], a:indent, a:start_lineno)},
\ 'exit_cb': {_, m -> a:ex_cb([m])},
\ 'in_mode': 'nl',
\ })
let channel = job_getchannel(s:job)
if ch_status(channel) ==# 'open'
call ch_sendraw(channel, a:lines)
call ch_close_in(channel)
endif
endif
endfunction
function! s:create_cmd(style, omissions) abort
if a:omissions ==# ''
let cmd = printf(
\ '%s --style=%s --formatter=%s --indent=%s',
\ expand(g:pydocstring_doq_path),
\ a:style,
\ g:pydocstring_formatter,
\ s:get_indent_width()
\ )
else
let cmd = printf(
\ '%s --style=%s --formatter=%s --omit=%s --indent=%s',
\ expand(g:pydocstring_doq_path),
\ a:style,
\ g:pydocstring_formatter,
\ a:omissions,
\ s:get_indent_width()
\ )
endif
if g:pydocstring_templates_path !=# ''
let cmd = printf('%s --template_path=%s', cmd, expand(g:pydocstring_templates_path))
endif
return cmd
endfunction
function! pydocstring#format() abort
let lines = printf("%s\n", join(getbufline(bufnr('%'), 1, '$'), "\n"))
let cmd = s:create_cmd('string', '')
let cmd = g:pydocstring_ignore_init ? printf('%s --ignore_init', cmd) : cmd
let indent = s:get_indent_width()
let end_lineno = line('.')
call s:execute(
\ cmd,
\ lines,
\ indent,
\ end_lineno,
\ function('s:format_callback'),
\ function('s:exit_callback')
\ )
endfunction
function! pydocstring#insert(...) abort
let range = s:get_range()
let pos = getpos('.')
let line = getline('.')
let indent = matchstr(line, '^\(\s*\)')
let space = repeat(' ', s:get_indent_width())
let indent = indent . space
if len(indent) == 0
let indent = space
endif
silent! execute 'normal! 0'
let is_not_range = range['start_lineno'] == 0 && range['end_lineno'] == '$'
if is_not_range
let start_lineno = line('.')
let end_lineno = search('\:\(\s*#.*\)*$')
else
let start_lineno = range['start_lineno']
let end_lineno = range['end_lineno']
endif
call setpos('.', pos)
let cmd = s:create_cmd('json', 'self,cls')
let lines = join(getline(start_lineno, end_lineno), "\n")
if is_not_range
let lines = printf("%s\n%s%s", lines, indent, 'pass')
let cmd = printf('%s --ignore_exception', cmd)
endif
call s:execute(
\ cmd,
\ lines,
\ indent,
\ start_lineno,
\ function('s:callback'),
\ function('s:exit_callback')
\ )
endfunction
let &cpo = s:save_cpo
unlet s:save_cpo