11" File: jsdoc.vim
22" Author: NAKAMURA, Hisashi <https://github.com/sunvisor>
33" Modifyed: Shinya Ohyanagi <[email protected] >4- " Version: 0.9.1
4+ " Version: 0.10.0
55" WebPage: http://github.com/heavenshell/vim-jsdoc/
66" Description: Generate JSDoc to your JavaScript file.
77" License: BSD, see LICENSE for more details.
@@ -50,6 +50,10 @@ let g:jsdoc_tags = exists('g:jsdoc_tags')
5050" Fill in any missing ones with defaults, keeping user overrides
5151call extend (g: jsdoc_tags , s: jsdoc_default_tags , ' keep' )
5252
53+ " Add param types when `g:jsdoc_input_description=1` and has typed parameter,
54+ " such as TypeScript.
55+ let s: candidate_type = ' '
56+
5357" Return data types for argument type auto completion :)
5458function ! jsdoc#listDataTypes (A, L, P ) abort
5559 let l: types = [
@@ -71,6 +75,13 @@ function! jsdoc#listDataTypes(A, L, P) abort
7175 \ ' typedArray' , ' TypedArray' ,
7276 \ ' weakmap' , ' WeakMap' ,
7377 \ ' weakset' , ' WeakSet' ]
78+
79+ if s: candidate_type != ' '
80+ " Ignore Builtin types. Already defined at `l:types`.
81+ if index (l: types , s: candidate_type ) == -1
82+ call insert (l: types , s: candidate_type , 0 )
83+ endif
84+ endif
7485 return join (l: types , " \n " )
7586endfunction
7687
@@ -92,9 +103,46 @@ let s:regexs = {
92103 \ ' class' : ' ^.\{-}\s*class\s*\([a-zA-Z_$][a-zA-Z0-9_$]*\).*$' ,
93104 \ ' shorthand' : ' ^.\{-}\s*\([a-zA-Z_$][a-zA-Z0-9_$]*\)\s*(\s*\([^)]*\)\s*).*$' ,
94105 \ ' static' : ' ^.\{-}\s*static\s*\([a-zA-Z_$][a-zA-Z0-9_$]*\)\s*(\s*\([^)]*\)\s*).*$' ,
95- \ ' arrow' : ' ^.\{-}\s*\([a-zA-Z_$][a-zA-Z0-9_$]*\)\s*[:=]\s*(\s*\([^)]*\)\s*)\s*=>.*$'
106+ \ ' arrow' : ' ^.\{-}\s*\([a-zA-Z_$][a-zA-Z0-9_$]*\)\s*[:=]\s*(\s*\([^)]*\)\s*)\s*=>.*$' ,
107+ \ ' return_type' : ' )\(:\|:\s\|\s*:\s*\)\([a-zA-Z]\+\).*$' ,
108+ \ ' interface' : ' ^.\{-}\s*interface\s*\([a-zA-Z_$][a-zA-Z0-9_$]*\).*$' ,
109+ \ ' access' : ' ^\(public\|protected\|private\)' ,
110+ \ ' implements' : ' ^.\{-}\s*implements\s*\(\([^{]*\)\).*$' ,
111+ \ ' extends' : ' ^.\{-}\s*extends\s*\([^\s*]\)'
96112 \ }
97113
114+ function ! s: trim (value)
115+ return substitute (a: value , ' \s' , ' ' , ' ' )
116+ endfunction
117+
118+ " If someday Vim support lambda use lambda.
119+ function ! s: parse_type (args )
120+ let results = []
121+ for arg in a: args
122+ if arg = ~# ' :'
123+ let args = split (arg, ' :' )
124+ let val = args [0 ]
125+ if val = ~# s: regexs [' access' ]
126+ " let val = substitute(split(val, s:regexs['access'])[0], '\s', '', '')
127+ let val = s: trim (split (val, s: regexs [' access' ])[0 ])
128+ endif
129+
130+ " let type = substitute(args[1], '\s', '', '')
131+ let type = s: trim (args [1 ])
132+ " Split keywaord args.
133+ if type = ~# ' ='
134+ " let type = substitute(split(type, '=')[0], '\s', '', '')
135+ let type = s: trim (split (type , ' =' )[0 ])
136+ endif
137+ call add (results, {' val' : val, ' type' : type })
138+ else
139+ call add (results, {' val' : arg, ' type' : ' ' })
140+ endif
141+ endfor
142+
143+ return results
144+ endfunction
145+
98146function ! s: build_description (argType, arg) abort
99147 let l: description = ' '
100148 let l: override = 0
@@ -113,7 +161,7 @@ function! s:build_description(argType, arg) abort
113161
114162 " Prompt for description
115163 if l: override == 0
116- let l: inputDescription = input (' Argument "' . a: arg . ' " description: ' )
164+ let l: inputDescription = input (' Argument "' . a: arg[ ' val ' ] . ' " description: ' )
117165 if l: inputDescription !=# ' '
118166 let l: description = l: inputDescription
119167 endif
@@ -181,74 +229,121 @@ function! s:hookArgs(lines, space, arg, hook, argType, argDescription) abort
181229
182230endfunction
183231
184- function ! jsdoc#insert () abort
185- let l: line = getline (' .' )
186- let l: indentCharSpace = ' '
187- let l: indentCharTab = ' '
188- let l: autoexpandtab = &l: expandtab
189-
190- if l: autoexpandtab == 0 " noexpandtab
191- " tabs
192- let l: indent = indent (' .' ) / &l: tabstop
193- let l: indentChar = l: indentCharTab
194- elseif l: autoexpandtab == 1 " expandtab
195- " spaces
196- let l: indent = indent (' .' )
197- let l: indentChar = l: indentCharSpace
198- endif
232+ function ! s: determine_style (line )
233+ let l: is_class = 0
234+ let l: is_function = 0
235+ let l: is_named = 0
236+ let l: is_static = 0
237+ let l: is_interface = 0
238+ let l: regex = 0
199239
200- let l: space = repeat (l: indentChar , l: indent )
201-
202- " Determine function defintion style
203- let l: is_class = 0
204- let l: is_function = 0
205- let l: is_named = 0
206- let l: is_static = 0
207- if l: line = ~ s: regexs [' function_declaration' ]
240+ if a: line = ~ s: regexs [' function_declaration' ]
208241 let l: is_function = 1
209242 let l: is_named = 1
210243 let l: regex = s: regexs [' function_declaration' ]
211- elseif l : line = ~ s: regexs [' function_expression' ]
244+ elseif a : line = ~ s: regexs [' function_expression' ]
212245 let l: is_function = 1
213246 let l: is_named = 1
214247 let l: regex = s: regexs [' function_expression' ]
215- elseif l : line = ~ s: regexs [' anonymous_function' ]
248+ elseif a : line = ~ s: regexs [' anonymous_function' ]
216249 let l: is_function = 1
217250 let l: regex = s: regexs [' anonymous_function' ]
218- elseif g: jsdoc_enable_es6 == 1 && l : line = ~ s: regexs [' static' ]
251+ elseif g: jsdoc_enable_es6 == 1 && a : line = ~ s: regexs [' static' ]
219252 let l: is_function = 1
220253 let l: is_named = 1
221254 let l: is_static = 1
222255 let l: regex = s: regexs [' static' ]
223- elseif (g: jsdoc_allow_shorthand == 1 || g: jsdoc_enable_es6 == 1 ) && l: line = ~ s: regexs [' shorthand' ]
224- echomsg string (' shorthand' )
256+ elseif (g: jsdoc_allow_shorthand == 1 || g: jsdoc_enable_es6 == 1 ) && a: line = ~ s: regexs [' shorthand' ]
225257 let l: is_function = 1
226258 let l: is_named = 1
227259 let l: regex = s: regexs [' shorthand' ]
228- elseif g: jsdoc_enable_es6 == 1 && l : line = ~ s: regexs [' arrow' ]
260+ elseif g: jsdoc_enable_es6 == 1 && a : line = ~ s: regexs [' arrow' ]
229261 let l: is_function = 1
230262 let l: is_named = 1
231263 let l: regex = s: regexs [' arrow' ]
232- elseif g: jsdoc_enable_es6 == 1 && l : line = ~ s: regexs [' class_extend' ]
264+ elseif g: jsdoc_enable_es6 == 1 && a : line = ~ s: regexs [' class_extend' ]
233265 let l: is_class = 1
234266 let l: is_named = 1
235267 let l: regex = s: regexs [' class_extend' ]
236- elseif g: jsdoc_enable_es6 == 1 && l : line = ~ s: regexs [' class' ]
268+ elseif g: jsdoc_enable_es6 == 1 && a : line = ~ s: regexs [' class' ]
237269 let l: is_class = 1
238270 let l: is_named = 1
239271 let l: regex = s: regexs [' class' ]
272+ elseif a: line = ~ s: regexs [' interface' ]
273+ let l: is_interface = 1
274+ let l: is_named = 1
275+ let l: regex = s: regexs [' interface' ]
276+ endif
277+
278+ return {
279+ \ ' is_class' : l: is_class ,
280+ \ ' is_function' : l: is_function ,
281+ \ ' is_named' : l: is_named ,
282+ \ ' is_static' : l: is_static ,
283+ \ ' is_interface' : l: is_interface ,
284+ \ ' regex' : l: regex ,
285+ \ }
286+ endfunction
287+
288+ " Extract return type such as TypeScript.
289+ function ! s: extract_return_type (line )
290+ let l: return_type = ' '
291+ if a: line = ~ s: regexs [' return_type' ]
292+ let matched = matchstr (a: line , s: regexs [' return_type' ], 0 )
293+ if matched != ' '
294+ " FIXME:
295+ " If signature has union tyoe, such as `function foo(): string | number {`
296+ " Regex `\([a-zA-Z]\+\).*$` would extract like `number | string {`.
297+ " So delete `{` if exists.
298+ let l: return_type = matchstr (matched, ' \([a-zA-Z]\+\).*$' , 0 )
299+ let l: return_type = substitute (l: return_type , ' \(\s*{\|{\)' , ' ' , ' g' )
300+ endif
240301 endif
241302
303+ return l: return_type
304+ endfunction
305+
306+ function ! jsdoc#insert () abort
307+ let l: line = getline (' .' )
308+ let l: indentCharSpace = ' '
309+ let l: indentCharTab = ' '
310+ let l: autoexpandtab = &l: expandtab
311+
312+ if l: autoexpandtab == 0 " noexpandtab
313+ " tabs
314+ let l: indent = indent (' .' ) / &l: tabstop
315+ let l: indentChar = l: indentCharTab
316+ elseif l: autoexpandtab == 1 " expandtab
317+ " spaces
318+ let l: indent = indent (' .' )
319+ let l: indentChar = l: indentCharSpace
320+ endif
321+
322+ let l: space = repeat (l: indentChar , l: indent )
323+
324+ " Determine function defintion style
325+ let l: style = s: determine_style (l: line )
326+ let l: is_class = l: style [' is_class' ]
327+ let l: is_function = l: style [' is_function' ]
328+ let l: is_named = l: style [' is_named' ]
329+ let l: is_static = l: style [' is_static' ]
330+ let l: is_interface = l: style [' is_interface' ]
331+ let l: regex = l: style [' regex' ]
332+
242333 let l: lines = []
243334 let l: desc = g: jsdoc_input_description == 1 ? input (' Description: ' ) : ' '
244335 call add (l: lines , l: space . ' /**' )
245336 call add (l: lines , l: space . ' * ' . l: desc )
246- if ! l: is_class
337+ " Class and interface generate only typed name.
338+ if ! l: is_class && ! l: is_interface
247339 call add (l: lines , l: space . ' *' )
248340 endif
249341
250342 let l: funcName = ' '
251- if l: is_function || l: is_class
343+ let l: parent_class = ' '
344+ let l: implements = ' '
345+ let l: return_type = ' '
346+ if l: is_function || l: is_class || l: is_interface
252347
253348 " Parse function definition
254349 " @FIXME: Does not work if function is split over several lines...
@@ -266,13 +361,23 @@ function! jsdoc#insert() abort
266361 let l: argString = substitute (l: line , l: regex , ' \1' , ' g' )
267362 endif
268363 let l: args = []
269- let l: parent_class = ' '
270364 if l: is_class
271- let l: parent_class = substitute (l: argString , ' \s' , ' ' , ' ' )
365+ " let l:parent_class = substitute(l:argString, '\s', '', '')
366+ if l: argString = ~ s: regexs [' implements' ]
367+ let implements = s: trim (substitute (l: argString , s: regexs [' implements' ], ' \1' , ' g' ))
368+ endif
369+ let l: extends = substitute (l: argString , s: regexs [' extends' ], ' \1' , ' g' )
370+ let l: parent_class = matchstr (l: extends , ' ^\([a-zA-Z0-9-_$]*\)' )
272371 else
273372 let l: args = split (l: argString , ' \s*,\s*' )
274373 endif
275374
375+ " Return type for typed language such as TypeScript.
376+ let l: return_type = s: extract_return_type (l: line )
377+
378+ " Parse typed args. ex: TypeScript's argument .
379+ let l: args = s: parse_type (l: args )
380+
276381 if g: jsdoc_additional_descriptions == 1
277382 call add (l: lines , l: space . ' * @name ' . l: funcName )
278383 call add (l: lines , l: space . ' * @' . g: jsdoc_tags [' function' ])
@@ -298,39 +403,50 @@ function! jsdoc#insert() abort
298403
299404 let l: hook = keys (g: jsdoc_custom_args_hook )
300405 for l: arg in l: args
406+ let s: candidate_type = ' '
301407 if g: jsdoc_enable_es6 == 1
302408 " Remove `(` or `)` from args.
303- let l: arg = substitute (l: arg , ' \((\|)\)' , ' ' , ' ' )
409+ let l: arg[ ' val ' ] = substitute (l: arg[ ' val ' ] , ' \((\|)\)' , ' ' , ' ' )
304410 endif
305411
306412 if g: jsdoc_allow_input_prompt == 1
307- let l: argType = input (' Argument "' . l: arg . ' " type: ' , ' ' , ' custom,jsdoc#listDataTypes' )
413+ if l: arg [' type' ] != ' '
414+ let s: candidate_type = l: arg [' type' ]
415+ endif
416+ let l: argType = input (' Argument "' . l: arg [' val' ] . ' " type: ' , ' ' , ' custom,jsdoc#listDataTypes' )
417+ redraw | echo ' '
308418 let l: argDescription = s: build_description (l: argType , l: arg )
309419 if g: jsdoc_custom_args_hook == {}
310420 " Prepend separator to start of description only if it was provided
311421 if l: argDescription !=# ' '
312422 let l: argDescription = g: jsdoc_param_description_separator . l: argDescription
313423 endif
314- call add (l: lines , l: space . ' * @' . g: jsdoc_tags [' param' ] . ' {' . l: argType . ' } ' . l: arg . l: argDescription )
424+ call add (l: lines , l: space . ' * @' . g: jsdoc_tags [' param' ] . ' {' . l: argType . ' } ' . l: arg[ ' val ' ] . l: argDescription )
315425 else
316- let l: lines = s: hookArgs (l: lines , l: space , l: arg , l: hook , l: argType , l: argDescription )
426+ let l: lines = s: hookArgs (l: lines , l: space , l: arg[ ' val ' ] , l: hook , l: argType , l: argDescription )
317427 endif
318428 else
319429 " Hook args.
320- let l: lines = s: hookArgs (l: lines , l: space , l: arg , l: hook , ' ' , ' ' )
430+ let l: lines = s: hookArgs (l: lines , l: space , l: arg[ ' val ' ] , l: hook , l: arg [ ' type ' ] , ' ' )
321431 endif
322432 endfor
323433 endif
324434
325- if l: is_class
326- " Class does not need return description.
435+ if l: is_class || l: is_interface
436+ " Class and inteface does not need return description.
327437 if l: parent_class != ' '
328438 call add (l: lines , l: space . ' *' )
329- call add (l: lines , l: space . ' * @extends ' . l: parent_class )
439+ if l: implements != ' '
440+ call add (l: lines , l: space . ' * @implements ' . ' {' . l: implements . ' }' )
441+ endif
442+
443+ call add (l: lines , l: space . ' * @extends ' . ' {' . l: parent_class . ' }' )
330444 endif
331445 elseif g: jsdoc_return == 1
332446 if g: jsdoc_allow_input_prompt == 1
447+ let s: candidate_type = l: return_type
333448 let l: returnType = input (' Return type (blank for no @' . g: jsdoc_tags [' returns' ] . ' ): ' , ' ' , ' custom,jsdoc#listDataTypes' )
449+ redraw | echo ' '
334450 let l: returnDescription = ' '
335451 if l: returnType !=# ' '
336452 if g: jsdoc_return_description == 1
@@ -342,7 +458,11 @@ function! jsdoc#insert() abort
342458 call add (l: lines , l: space . ' * @' . g: jsdoc_tags [' returns' ] . ' {' . l: returnType . ' }' . l: returnDescription )
343459 endif
344460 else
345- call add (l: lines , l: space . ' * @' . g: jsdoc_tags [' returns' ] . ' {undefined}' )
461+ let l: return_type = l: return_type == ' '
462+ \ ? ' {undefined}'
463+ \ : printf (' {%s}' , l: return_type )
464+
465+ cal add (l: lines , l: space . ' * @' . g: jsdoc_tags [' returns' ] . l: return_type )
346466 endif
347467 endif
348468 call add (l: lines , l: space . ' */' )
0 commit comments