mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-01-23 13:30:05 +08:00
242 lines
6.3 KiB
VimL
242 lines
6.3 KiB
VimL
" 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
|