Mercurial > vim
comparison runtime/autoload/pythoncomplete.vim @ 827:fd1b3406fd1c v7.0d02
updated for version 7.0d02
author | vimboss |
---|---|
date | Wed, 12 Apr 2006 21:52:12 +0000 |
parents | |
children | 8e5830943bff |
comparison
equal
deleted
inserted
replaced
826:1cdd2661f34c | 827:fd1b3406fd1c |
---|---|
1 "pythoncomplete.vim - Omni Completion for python | |
2 " Maintainer: Aaron Griffin | |
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 | |
13 " | |
14 " TODO | |
15 " RExec subclass | |
16 " Code cleanup + make class | |
17 " use internal dict, not globals() | |
18 | |
19 if !has('python') | |
20 echo "Error: Required vim compiled with +python" | |
21 finish | |
22 endif | |
23 | |
24 function! pythoncomplete#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' | |
33 continue | |
34 elseif ! c =~ '\.' | |
35 idx = -1 | |
36 break | |
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:pythoncomplete_completions | |
47 endif | |
48 endfunction | |
49 | |
50 function! s:DefPython() | |
51 python << PYTHONEOF | |
52 import vim, sys, types | |
53 import __builtin__ | |
54 import tokenize, keyword, cStringIO | |
55 | |
56 LOCALDEFS = \ | |
57 ['LOCALDEFS', 'clean_up','eval_source_code', \ | |
58 'get_completions', '__builtin__', '__builtins__', \ | |
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'] | |
63 | |
64 def dbg(level,msg): | |
65 debug_level = 1 | |
66 try: | |
67 debug_level = vim.eval("g:pythoncomplete_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: | |
98 match = stmt | |
99 all = globals() + __builtin__.__dict__ | |
100 else: | |
101 match = stmt[ridx+1:] | |
102 stmt = strip_calls(stmt[:ridx]) | |
103 all = eval(stmt).__dict__ | |
104 | |
105 #dbg(15,"completions for: %s, match=%s" % (stmt,match)) | |
106 completions = [] | |
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) | |
122 vim.command("let g:pythoncomplete_completions = %s" % completions) | |
123 except: | |
124 vim.command("let g:pythoncomplete_completions = []") | |
125 #dbg(1,"exception: %s" % sys.exc_info()[1]) | |
126 clean_up() | |
127 | |
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 | |
243 def eval_source_code(): | |
244 LINE=vim.eval('getline(\'.\')') | |
245 s = cStringIO.StringIO('\n'.join(vim.current.buffer[:]) + '\n') | |
246 g = tokenize.generate_tokens(s.readline) | |
247 | |
248 stmts = [] | |
249 lineNo = 0 | |
250 try: | |
251 for type, str, begin, end, line in g: | |
252 if line.replace('\n','') == vim.eval('getline(\'.\')'): break | |
253 elif begin[0] == lineNo: continue | |
254 #junk | |
255 elif type == tokenize.INDENT or \ | |
256 type == tokenize.DEDENT or \ | |
257 type == tokenize.ERRORTOKEN or \ | |
258 type == tokenize.ENDMARKER or \ | |
259 type == tokenize.NEWLINE or \ | |
260 type == tokenize.COMMENT: | |
261 continue | |
262 #import statement | |
263 elif str == 'import': | |
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) | |
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 | |
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 = '' | |
282 for type, str, begin, end, line in g: | |
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) | |
291 #class declaration | |
292 elif str == 'class': | |
293 type, str, begin, end, line = g.next() | |
294 classname = str | |
295 #dbg(5,"found [class %s]" % classname) | |
296 | |
297 level = 0 | |
298 members = [] | |
299 for type, str, begin, end, line in g: | |
300 if line.replace('\n','') == vim.eval('getline(\'.\')'): break | |
301 elif type == tokenize.INDENT: | |
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: | |
309 if line.replace('\n','') == vim.eval('getline(\'.\')'): break | |
310 elif str == ':': | |
311 stmts += parse_block(g) | |
312 break | |
313 memberstr += str | |
314 #dbg(5," member [%s]" % memberstr) | |
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(): | |
321 #dbg(5,"keyword = %s" % str) | |
322 lineNo = begin[0] | |
323 else: | |
324 assign = parse_statement(g,str) | |
325 if len(assign) > 0: stmts.append(assign) | |
326 | |
327 for s in stmts: | |
328 try: | |
329 #dbg(15,"evaluating: %s\n" % s) | |
330 exec(s) in globals() | |
331 except: | |
332 #dbg(1,"exception: %s" % sys.exc_info()[1]) | |
333 pass | |
334 except: | |
335 #dbg(1,"exception: %s" % sys.exc_info()[1]) | |
336 pass | |
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 | |
349 let g:pythoncomplete_debug_level = 0 | |
350 call s:DefPython() | |
351 " vim: set et ts=4: |