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 19if !has('python') 20 echo "Error: Required vim compiled with +python" 21 finish 22endif 23 24function! 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 48endfunction 49 50function! s:DefPython() 51python << PYTHONEOF 52import vim, sys, types 53import __builtin__ 54import tokenize, keyword, cStringIO 55 56LOCALDEFS = \ 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 64def 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 72def 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 85def 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 128def 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 183def 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 202def 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 217def 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 243def 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 338def 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 345sys.path.extend(['.','..']) 346PYTHONEOF 347endfunction 348 349let g:pythoncomplete_debug_level = 0 350call s:DefPython() 351" vim: set et ts=4: 352