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