626
|
1 "pycomplete.vim - Omni Completion for python
|
|
2 " Maintainer: Aaron Griffin
|
644
|
3 " Version: 0.3
|
|
4 " Last Updated: 23 January 2006
|
|
5 "
|
|
6 " v0.3 Changes:
|
|
7 " added top level def parsing
|
|
8 " for safety, call returns are not evaluated
|
|
9 " handful of parsing changes
|
|
10 " trailing ( and . characters
|
|
11 " argument completion on open parens
|
|
12 " stop parsing at current line - ++performance, local var resolution
|
626
|
13 "
|
|
14 " TODO
|
644
|
15 " RExec subclass
|
|
16 " Code cleanup + make class
|
|
17 " use internal dict, not globals()
|
626
|
18
|
|
19 if !has('python')
|
|
20 echo "Error: Required vim compiled with +python"
|
|
21 finish
|
|
22 endif
|
|
23
|
|
24 function! pycomplete#Complete(findstart, base)
|
|
25 "findstart = 1 when we need to get the text length
|
|
26 if a:findstart
|
|
27 let line = getline('.')
|
|
28 let idx = col('.')
|
|
29 while idx > 0
|
|
30 let idx -= 1
|
|
31 let c = line[idx-1]
|
|
32 if c =~ '\w'
|
644
|
33 continue
|
|
34 elseif ! c =~ '\.'
|
626
|
35 idx = -1
|
644
|
36 break
|
626
|
37 else
|
|
38 break
|
|
39 endif
|
|
40 endwhile
|
|
41
|
|
42 return idx
|
|
43 "findstart = 0 when we need to return the list of completions
|
|
44 else
|
|
45 execute "python get_completions('" . a:base . "')"
|
|
46 return g:pycomplete_completions
|
|
47 endif
|
|
48 endfunction
|
|
49
|
|
50 function! s:DefPython()
|
|
51 python << PYTHONEOF
|
644
|
52 import vim, sys, types
|
626
|
53 import __builtin__
|
644
|
54 import tokenize, keyword, cStringIO
|
626
|
55
|
|
56 LOCALDEFS = \
|
|
57 ['LOCALDEFS', 'clean_up','eval_source_code', \
|
|
58 'get_completions', '__builtin__', '__builtins__', \
|
644
|
59 'dbg', '__name__', 'vim', 'sys', 'parse_to_end', \
|
|
60 'parse_statement', 'tokenize', 'keyword', 'cStringIO', \
|
|
61 'debug_level', 'safe_eval', '_ctor', 'get_arguments', \
|
|
62 'strip_calls', 'types', 'parse_block']
|
626
|
63
|
644
|
64 def dbg(level,msg):
|
|
65 debug_level = 1
|
626
|
66 try:
|
644
|
67 debug_level = vim.eval("g:pycomplete_debug_level")
|
|
68 except:
|
|
69 pass
|
|
70 if level <= debug_level: print(msg)
|
|
71
|
|
72 def strip_calls(stmt):
|
|
73 parsed=''
|
|
74 level = 0
|
|
75 for c in stmt:
|
|
76 if c in ['[','(']:
|
|
77 level += 1
|
|
78 elif c in [')',']']:
|
|
79 level -= 1
|
|
80 elif level == 0:
|
|
81 parsed += c
|
|
82 ##dbg(10,"stripped: %s" % parsed)
|
|
83 return parsed
|
|
84
|
|
85 def get_completions(base):
|
|
86 stmt = vim.eval('expand("<cWORD>")')
|
|
87 #dbg(1,"statement: %s - %s" % (stmt, base))
|
|
88 stmt = stmt+base
|
|
89 eval_source_code()
|
|
90
|
|
91 try:
|
|
92 ridx = stmt.rfind('.')
|
|
93 if stmt[-1] == '(':
|
|
94 match = ""
|
|
95 stmt = strip_calls(stmt[:len(stmt)-1])
|
|
96 all = get_arguments(eval(stmt))
|
|
97 elif ridx == -1:
|
626
|
98 match = stmt
|
644
|
99 all = globals() + __builtin__.__dict__
|
626
|
100 else:
|
644
|
101 match = stmt[ridx+1:]
|
|
102 stmt = strip_calls(stmt[:ridx])
|
|
103 all = eval(stmt).__dict__
|
626
|
104
|
644
|
105 #dbg(15,"completions for: %s, match=%s" % (stmt,match))
|
626
|
106 completions = []
|
644
|
107 if type(all) == types.DictType:
|
|
108 for m in all:
|
|
109 if m.find('_') != 0 and m.find(match) == 0 and \
|
|
110 m not in LOCALDEFS:
|
|
111 #dbg(25,"matched... %s, %s" % (m, m.find(match)))
|
|
112 typestr = str(all[m])
|
|
113 if "function" in typestr: m += '('
|
|
114 elif "method" in typestr: m += '('
|
|
115 elif "module" in typestr: m += '.'
|
|
116 elif "class" in typestr: m += '('
|
|
117 completions.append(m)
|
|
118 completions.sort()
|
|
119 else:
|
|
120 completions.append(all)
|
|
121 #dbg(10,"all completions: %s" % completions)
|
626
|
122 vim.command("let g:pycomplete_completions = %s" % completions)
|
|
123 except:
|
|
124 vim.command("let g:pycomplete_completions = []")
|
644
|
125 #dbg(1,"exception: %s" % sys.exc_info()[1])
|
626
|
126 clean_up()
|
|
127
|
644
|
128 def get_arguments(func_obj):
|
|
129 def _ctor(obj):
|
|
130 try:
|
|
131 return class_ob.__init__.im_func
|
|
132 except AttributeError:
|
|
133 for base in class_ob.__bases__:
|
|
134 rc = _find_constructor(base)
|
|
135 if rc is not None: return rc
|
|
136 return None
|
|
137
|
|
138 arg_offset = 1
|
|
139 if type(func_obj) == types.ClassType: func_obj = _ctor(func_obj)
|
|
140 elif type(func_obj) == types.MethodType: func_obj = func_obj.im_func
|
|
141 else: arg_offset = 0
|
|
142
|
|
143 #dbg(20,"%s, offset=%s" % (str(func_obj), arg_offset))
|
|
144
|
|
145 arg_text = ''
|
|
146 if type(func_obj) in [types.FunctionType, types.LambdaType]:
|
|
147 try:
|
|
148 cd = func_obj.func_code
|
|
149 real_args = cd.co_varnames[arg_offset:cd.co_argcount]
|
|
150 defaults = func_obj.func_defaults or []
|
|
151 defaults = list(map(lambda name: "=%s" % name, defaults))
|
|
152 defaults = [""] * (len(real_args)-len(defaults)) + defaults
|
|
153 items = map(lambda a,d: a+d, real_args, defaults)
|
|
154 if func_obj.func_code.co_flags & 0x4:
|
|
155 items.append("...")
|
|
156 if func_obj.func_code.co_flags & 0x8:
|
|
157 items.append("***")
|
|
158 arg_text = ", ".join(items) + ')'
|
|
159
|
|
160 except:
|
|
161 #dbg(1,"exception: %s" % sys.exc_info()[1])
|
|
162 pass
|
|
163 if len(arg_text) == 0:
|
|
164 # The doc string sometimes contains the function signature
|
|
165 # this works for alot of C modules that are part of the
|
|
166 # standard library
|
|
167 doc = getattr(func_obj, '__doc__', '')
|
|
168 if doc:
|
|
169 doc = doc.lstrip()
|
|
170 pos = doc.find('\n')
|
|
171 if pos > 0:
|
|
172 sigline = doc[:pos]
|
|
173 lidx = sigline.find('(')
|
|
174 ridx = sigline.find(')')
|
|
175 retidx = sigline.find('->')
|
|
176 ret = sigline[retidx+2:].strip()
|
|
177 if lidx > 0 and ridx > 0:
|
|
178 arg_text = sigline[lidx+1:ridx] + ')'
|
|
179 if len(ret) > 0: arg_text += ' #returns %s' % ret
|
|
180 #dbg(15,"argument completion: %s" % arg_text)
|
|
181 return arg_text
|
|
182
|
|
183 def parse_to_end(gen):
|
|
184 stmt=''
|
|
185 level = 0
|
|
186 for type, str, begin, end, line in gen:
|
|
187 if line == vim.eval('getline(\'.\')'): break
|
|
188 elif str == '\\': continue
|
|
189 elif str == ';':
|
|
190 break
|
|
191 elif type == tokenize.NEWLINE and level == 0:
|
|
192 break
|
|
193 elif str in ['[','(']:
|
|
194 level += 1
|
|
195 elif str in [')',']']:
|
|
196 level -= 1
|
|
197 elif level == 0:
|
|
198 stmt += str
|
|
199 #dbg(10,"current statement: %s" % stmt)
|
|
200 return stmt
|
|
201
|
|
202 def parse_block(gen):
|
|
203 lines = []
|
|
204 level = 0
|
|
205 for type, str, begin, end, line in gen:
|
|
206 if line.replace('\n','') == vim.eval('getline(\'.\')'): break
|
|
207 elif type == tokenize.INDENT:
|
|
208 level += 1
|
|
209 elif type == tokenize.DEDENT:
|
|
210 level -= 1
|
|
211 if level == 0: break;
|
|
212 else:
|
|
213 stmt = parse_statement(gen,str)
|
|
214 if len(stmt) > 0: lines.append(stmt)
|
|
215 return lines
|
|
216
|
|
217 def parse_statement(gen,curstr=''):
|
|
218 var = curstr
|
|
219 type, str, begin, end, line = gen.next()
|
|
220 if str == '=':
|
|
221 type, str, begin, end, line = gen.next()
|
|
222 if type == tokenize.NEWLINE:
|
|
223 return ''
|
|
224 elif type == tokenize.STRING or str == 'str':
|
|
225 return '%s = str' % var
|
|
226 elif str == '[' or str == 'list':
|
|
227 return '%s= list' % var
|
|
228 elif str == '{' or str == 'dict':
|
|
229 return '%s = dict' % var
|
|
230 elif type == tokenize.NUMBER:
|
|
231 return '%s = 0' % var
|
|
232 elif str == 'Set':
|
|
233 return '%s = Set' % var
|
|
234 elif str == 'open' or str == 'file':
|
|
235 return '%s = file' % var
|
|
236 else:
|
|
237 inst = str + parse_to_end(gen)
|
|
238 if len(inst) > 0:
|
|
239 #dbg(5,"found [%s = %s]" % (var, inst))
|
|
240 return '%s = %s' % (var, inst)
|
|
241 return ''
|
|
242
|
626
|
243 def eval_source_code():
|
644
|
244 LINE=vim.eval('getline(\'.\')')
|
|
245 s = cStringIO.StringIO('\n'.join(vim.current.buffer[:]) + '\n')
|
626
|
246 g = tokenize.generate_tokens(s.readline)
|
|
247
|
|
248 stmts = []
|
|
249 lineNo = 0
|
|
250 try:
|
|
251 for type, str, begin, end, line in g:
|
644
|
252 if line.replace('\n','') == vim.eval('getline(\'.\')'): break
|
|
253 elif begin[0] == lineNo: continue
|
626
|
254 #junk
|
|
255 elif type == tokenize.INDENT or \
|
|
256 type == tokenize.DEDENT or \
|
|
257 type == tokenize.ERRORTOKEN or \
|
|
258 type == tokenize.ENDMARKER or \
|
644
|
259 type == tokenize.NEWLINE or \
|
|
260 type == tokenize.COMMENT:
|
626
|
261 continue
|
|
262 #import statement
|
|
263 elif str == 'import':
|
644
|
264 import_stmt=parse_to_end(g)
|
|
265 if len(import_stmt) > 0:
|
|
266 #dbg(5,"found [import %s]" % import_stmt)
|
|
267 stmts.append("import %s" % import_stmt)
|
626
|
268 #import from statement
|
|
269 elif str == 'from':
|
|
270 type, str, begin, end, line = g.next()
|
|
271 mod = str
|
|
272
|
|
273 type, str, begin, end, line = g.next()
|
|
274 if str != "import": break
|
644
|
275 from_stmt=parse_to_end(g)
|
|
276 if len(from_stmt) > 0:
|
|
277 #dbg(5,"found [from %s import %s]" % (mod, from_stmt))
|
|
278 stmts.append("from %s import %s" % (mod, from_stmt))
|
|
279 #def statement
|
|
280 elif str == 'def':
|
|
281 funcstr = ''
|
626
|
282 for type, str, begin, end, line in g:
|
644
|
283 if line.replace('\n','') == vim.eval('getline(\'.\')'): break
|
|
284 elif str == ':':
|
|
285 stmts += parse_block(g)
|
|
286 break
|
|
287 funcstr += str
|
|
288 if len(funcstr) > 0:
|
|
289 #dbg(5,"found [def %s]" % funcstr)
|
|
290 stmts.append("def %s:\n pass" % funcstr)
|
626
|
291 #class declaration
|
|
292 elif str == 'class':
|
|
293 type, str, begin, end, line = g.next()
|
|
294 classname = str
|
644
|
295 #dbg(5,"found [class %s]" % classname)
|
626
|
296
|
|
297 level = 0
|
|
298 members = []
|
|
299 for type, str, begin, end, line in g:
|
644
|
300 if line.replace('\n','') == vim.eval('getline(\'.\')'): break
|
|
301 elif type == tokenize.INDENT:
|
626
|
302 level += 1
|
|
303 elif type == tokenize.DEDENT:
|
|
304 level -= 1
|
|
305 if level == 0: break;
|
|
306 elif str == 'def':
|
|
307 memberstr = ''
|
|
308 for type, str, begin, end, line in g:
|
644
|
309 if line.replace('\n','') == vim.eval('getline(\'.\')'): break
|
|
310 elif str == ':':
|
|
311 stmts += parse_block(g)
|
|
312 break
|
626
|
313 memberstr += str
|
644
|
314 #dbg(5," member [%s]" % memberstr)
|
626
|
315 members.append(memberstr)
|
|
316 classstr = 'class %s:' % classname
|
|
317 for m in members:
|
|
318 classstr += ("\n def %s:\n pass" % m)
|
|
319 stmts.append("%s\n" % classstr)
|
|
320 elif keyword.iskeyword(str) or str in globals():
|
644
|
321 #dbg(5,"keyword = %s" % str)
|
626
|
322 lineNo = begin[0]
|
|
323 else:
|
644
|
324 assign = parse_statement(g,str)
|
|
325 if len(assign) > 0: stmts.append(assign)
|
|
326
|
626
|
327 for s in stmts:
|
|
328 try:
|
644
|
329 #dbg(15,"evaluating: %s\n" % s)
|
626
|
330 exec(s) in globals()
|
|
331 except:
|
644
|
332 #dbg(1,"exception: %s" % sys.exc_info()[1])
|
626
|
333 pass
|
|
334 except:
|
644
|
335 #dbg(1,"exception: %s" % sys.exc_info()[1])
|
|
336 pass
|
626
|
337
|
|
338 def clean_up():
|
|
339 for o in globals().keys():
|
|
340 if o not in LOCALDEFS:
|
|
341 try:
|
|
342 exec('del %s' % o) in globals()
|
|
343 except: pass
|
|
344
|
|
345 sys.path.extend(['.','..'])
|
|
346 PYTHONEOF
|
|
347 endfunction
|
|
348
|
644
|
349 let g:pycomplete_debug_level = 0
|
626
|
350 call s:DefPython()
|
|
351 " vim: set et ts=4:
|