11" Insert Docstring.
22" Author: Shinya Ohyanagi <sohyanagi@gmail.com>
3- " Version: 0.2 .0
3+ " Version: 0.3 .0
44" WebPage: http://github.com/heavenshell/vim-pydocstriong/
55" Description: Generate Python docstring to your Python script file.
66" License: BSD, see LICENSE for more details.
2525let s: regexs = {
2626 \ ' def' : ' ^def\s\|^\s*def\s' ,
2727 \ ' class' : ' ^class\s\|^\s*class\s' ,
28- \ ' async' : ' ^async\s*def\s\|^\s*async\sdef\s'
28+ \ ' async' : ' ^async\s*def\s\|^\s*async\sdef\s' ,
29+ \ ' typed_args' : ' \([0-9A-Za-z_.]\+:[0-9A-Za-z_.]\+\|[0-9A-Za-z_.]\+\)\(,\|$\)' ,
2930 \ }
3031
3132function ! s: readtmpl (type )
@@ -43,7 +44,7 @@ function! s:readtmpl(type)
4344 return tmpl
4445endfunction
4546
46- function ! s: parseClass (line )
47+ function ! s: parse_class (line )
4748 " For class definition, we just simply need to extract the class name. We can
4849 " do that by just delete every white spaces and the whole parenthesics if
4950 " existed.
@@ -52,22 +53,129 @@ function! s:parseClass(line)
5253 return parse
5354endfunction
5455
56+ function ! s: compare (lhs, rhs)
57+ return a: lhs [' start' ] - a: rhs [' start' ]
58+ endfunction
59+
60+ function ! s: parse_args (args_str)
61+ let primitive_pos = 0
62+ let nested = 0
63+ let start_pos = 0
64+ let end_pos = 0
65+ let args = []
66+ let primitives = []
67+
68+ " Extract none typed arguments or primitive arguments first.
69+ while 1
70+ let primitives = matchstrpos (a: args_str , s: regexs [' typed_args' ], primitive_pos)
71+ if primitives[1 ] == -1
72+ break
73+ endif
74+
75+ if match (primitives[0 ], ' [0-9A-Za-z_.]\+:[0-9A-Za-z_.]\+' ) == -1
76+ let separator_pos = strridx (a: args_str , ' :' , primitives[1 ])
77+ if a: args_str [primitives[1 ] - 1 : primitives[1 ] - 1 ] == ' ['
78+ " If `[` exist right before argument, current argument is inner of
79+ " braket. So this argument is not standalone argument.
80+ let primitive_pos = primitives[2 ]
81+ continue
82+ endif
83+ let braket_start_pos = stridx (a: args_str , ' [' , separator_pos)
84+ let next_separator_pos = stridx (a: args_str , ' :' , primitives[2 ])
85+ let braket_end_pos = strridx (a: args_str , ' ]' , next_separator_pos)
86+ if next_separator_pos == -1
87+ if braket_start_pos == -1 && braket_end_pos == -1
88+ \ && a: args_str [primitives[1 ] : ] == primitives[0 ]
89+ " Current argument is last argument.
90+ else
91+ " Arguments are still remains.
92+ let primitive_pos = primitives[2 ]
93+ continue
94+ endif
95+ endif
96+
97+ if braket_start_pos < primitives[1 ] && primitives[1 ] < braket_end_pos
98+ " Current argument is inner of braket,
99+ " such as `List[str, str, str]`'s second `str`.
100+ let primitive_pos = primitives[2 ]
101+ continue
102+ endif
103+ endif
104+
105+ " `[: -1] trims `,`.
106+ let arg = primitives[0 ][: -1 ]
107+ let arg = substitute (arg, ' ,$' , ' ' , ' ' )
108+
109+ call add (args , {' val' : arg, ' start' : primitives[1 ]})
110+ " Move current position.
111+ let primitive_pos = primitives[2 ]
112+ endwhile
113+
114+ " Parse nested typed args.
115+ while 1
116+ let start_idx = match (a: args_str , ' \[' , start_pos)
117+ let end_idx = match (a: args_str , ' \]' , end_pos)
118+
119+ if start_idx == -1 && end_idx == -1
120+ break
121+ endif
122+
123+ if end_pos > start_idx
124+ " For nested. e.g. `arg: List[List[List[int, int], List[List[int, int]]]`.
125+ let idx = strridx (a: args_str , ' ,' , nested)
126+ if idx == -1
127+ let idx = strridx (a: args_str , ' (' , nested)
128+ endif
129+ let arg = a: args_str [idx + 1 : end_idx]
130+ " Override previous arg by complete one.
131+ let args [-1 ] = {' val' : arg, ' start' : idx + 1 }
132+ else
133+ let idx = strridx (a: args_str , ' ,' , start_idx)
134+ if idx == -1
135+ let idx = strridx (a: args_str , ' (' , start_idx)
136+ endif
137+
138+ let arg = a: args_str [idx + 1 : end_idx]
139+ call add (args , {' val' : arg, ' start' : idx + 1 })
140+ let nested = start_idx
141+ endif
142+
143+ let start_pos = start_idx + 1
144+ let end_pos = end_idx + 1
145+ endwhile
146+
147+ " Sort by argument start position.
148+ call sort (args , ' s:compare' )
149+ return map (args , {i , v - > substitute (v [' val' ], ' ,' , ' , ' , ' g' )})
150+ endfunction
151+
55152function ! s: parse_func (type , line )
56153 let header = substitute (a: line , ' \s\|(.*\|:' , ' ' , ' g' )
57154
58155 let args_str = substitute (a: line , ' \s\|.*(\|).*' , ' ' , ' g' )
59- let args = split (args_str, ' ,' )
156+ if args_str = ~ ' :'
157+ let args = s: parse_args (args_str)
158+ else
159+ " No typed args.
160+ let args = split (args_str, ' ,' )
161+ endif
60162
61- let arrow_index = match (a: line , " ->" )
163+ let arrow_index = match (a: line , ' ->' )
164+ let return_type = ' '
62165 if arrow_index != -1
63166 let substring = strpart (a: line , arrow_index + 2 )
64167 " issue #28 `\W*` would deleted `.`.
65- let return_type = substitute (substring, ' [^0-9A-Za-z_.]*' , ' ' , ' g' )
66- else
67- let return_type = ' '
168+ let return_type = substitute (substring, ' [^0-9A-Za-z_.,\[\] ]*' , ' ' , ' g' )
169+ " Add space after `,` such as `List[int, str]`.
170+ let return_type = substitute (return_type, ' , ' , ' , ' , ' ' )
68171 endif
69172
70- let parse = {' type' : a: type , ' header' : header, ' args' : args , ' return_type' : return_type}
173+ let parse = {
174+ \ ' type' : a: type ,
175+ \ ' header' : header,
176+ \ ' args' : args ,
177+ \ ' return_type' : return_type
178+ \ }
71179 return parse
72180endfunction
73181
@@ -77,7 +185,7 @@ function! s:parse(line)
77185
78186 if str = ~ s: regexs [' class' ]
79187 let str = substitute (str, s: regexs [' class' ], ' ' , ' ' )
80- return s: parseClass (str)
188+ return s: parse_class (str)
81189 endif
82190
83191 if str = ~ s: regexs [' def' ]
@@ -136,7 +244,7 @@ function! s:should_use_one_line_docstring(type, args, return_type)
136244 return ! s: should_include_args (a: args )
137245endfunction
138246
139- function ! s: builddocstring (strs, indent , nested_indent)
247+ function ! s: build_docstring (strs, indent , nested_indent)
140248 let type = a: strs [' type' ]
141249 let prefix = a: strs [' header' ]
142250 let args = a: strs [' args' ]
@@ -242,7 +350,7 @@ function! pydocstring#insert()
242350 let indent = indent . space
243351 endif
244352 try
245- let result = s: builddocstring (docstring, indent , nested_indent)
353+ let result = s: build_docstring (docstring, indent , nested_indent)
246354 call s: insert (insertpos + 1 , result)
247355 catch /^Template/
248356 echomsg v: exception
@@ -257,9 +365,9 @@ function! s:insert(pos, docstring)
257365 let paste = &g: paste
258366 let &g: paste = 1
259367 silent ! execute ' normal! ' . a: pos . ' G$'
260- let currentpos = line (' .' )
368+ let current_pos = line (' .' )
261369 " If current position is bottom, add docstring below.
262- if a: pos == currentpos
370+ if a: pos == current_pos
263371 silent ! execute ' normal! O' . a: docstring
264372 else
265373 silent ! execute ' normal! o' . a: docstring
0 commit comments