-
Notifications
You must be signed in to change notification settings - Fork 52
/
Copy pathpydocstring.vim
241 lines (209 loc) · 6.25 KB
/
pydocstring.vim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
" 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