"pycomplete.vim - Omni Completion for python " Maintainer: Aaron Griffin " Version: 0.2 " Last Updated: 5 January 2006 " " TODO " * local variables *inside* class members if !has('python') echo "Error: Required vim compiled with +python" finish endif function! pycomplete#Complete(findstart, base) "findstart = 1 when we need to get the text length if a:findstart let line = getline('.') let idx = col('.') while idx > 0 let idx -= 1 let c = line[idx-1] if c =~ '\w' continue elseif ! c =~ '\.' idx = -1 break else break endif endwhile return idx "findstart = 0 when we need to return the list of completions else execute "python get_completions('" . a:base . "')" return g:pycomplete_completions endif endfunction function! s:DefPython() python << PYTHONEOF import vim import sys import __builtin__ LOCALDEFS = \ ['LOCALDEFS', 'clean_up','eval_source_code', \ 'get_completions', '__builtin__', '__builtins__', \ 'dbg', '__name__', 'vim', 'sys'] #comment/uncomment one line at a time to enable/disable debugging def dbg(msg): pass # print(msg) #it seems that by this point, vim has already stripped the base # matched in the findstart=1 section, so we will create the # statement from scratch def get_completions(base): stmt = vim.eval('expand("")')+base dbg("parsed statement => %s" % stmt) eval_source_code() try: dbg("eval: %s" % stmt) if len(stmt.split('.')) == 1: all = globals().keys() + dir(__builtin__) match = stmt else: rindex= stmt.rfind('.') all = dir(eval(stmt[:rindex])) match = stmt[rindex+1:] completions = [] dbg("match == %s" % match) for m in all: #TODO: remove private (_foo) functions? if m.find('__') != 0 and \ m.find(match) == 0 and \ m not in LOCALDEFS: dbg("matched... %s, %s" % (m, m.find(match))) completions.append(m) dbg("all completions: %s" % completions) vim.command("let g:pycomplete_completions = %s" % completions) except: dbg("exception: %s" % sys.exc_info()[1]) vim.command("let g:pycomplete_completions = []") clean_up() #yes, this is a quasi-functional python lexer def eval_source_code(): import tokenize import keyword import StringIO s = StringIO.StringIO('\n'.join(vim.current.buffer[:]) + '\n') g = tokenize.generate_tokens(s.readline) stmts = [] lineNo = 0 try: for type, str, begin, end, line in g: if begin[0] == lineNo: continue #junk elif type == tokenize.INDENT or \ type == tokenize.DEDENT or \ type == tokenize.ERRORTOKEN or \ type == tokenize.ENDMARKER or \ type == tokenize.NEWLINE: continue #import statement elif str == 'import': for type, str, begin, end, line in g: if str == ';' or type == tokenize.NEWLINE: break dbg("found [import %s]" % str) stmts.append("import %s" % str) #import from statement elif str == 'from': type, str, begin, end, line = g.next() mod = str type, str, begin, end, line = g.next() if str != "import": break mem = '' for type, str, begin, end, line in g: if str == ';' or type == tokenize.NEWLINE: break mem += (str + ',') if len(mem) > 0: dbg("found [from %s import %s]" % (mod, mem[:-1])) stmts.append("from %s import %s" % (mod, mem[:-1])) #class declaration elif str == 'class': type, str, begin, end, line = g.next() classname = str dbg("found [class %s]" % classname) level = 0 members = [] #we don't care about the meat of the members, # only the signatures, so we'll replace the bodies # with 'pass' for evaluation for type, str, begin, end, line in g: if type == tokenize.INDENT: level += 1 elif type == tokenize.DEDENT: level -= 1 if level == 0: break; elif str == 'def': #TODO: if name begins with '_', keep private memberstr = '' for type, str, begin, end, line in g: if str == ':': break memberstr += str dbg(" member [%s]" % memberstr) members.append(memberstr) #TODO parse self.blah = something lines #elif str == "self" && next && str == "." ...blah... classstr = 'class %s:' % classname for m in members: classstr += ("\n def %s:\n pass" % m) stmts.append("%s\n" % classstr) elif keyword.iskeyword(str) or str in globals(): dbg("keyword = %s" % str) lineNo = begin[0] else: if line.find("=") == -1: continue var = str type, str, begin, end, line = g.next() dbg('next = %s' % str) if str != '=': continue type, str, begin, end, line = g.next() if type == tokenize.NEWLINE: continue elif type == tokenize.STRING or str == 'str': stmts.append('%s = str' % var) elif str == '[' or str == 'list': stmts.append('%s= list' % var) elif str == '{' or str == 'dict': stmts.append('%s = dict' % var) elif type == tokenize.NUMBER: continue elif str == 'Set': stmts.append('%s = Set' % var) elif str == 'open' or str == 'file': stmts.append('%s = file' % var) else: inst = str for type, str, begin, end, line in g: if type == tokenize.NEWLINE: break inst += str if len(inst) > 0: dbg("found [%s = %s]" % (var, inst)) stmts.append('%s = %s' % (var, inst)) lineNo = begin[0] for s in stmts: try: dbg("evaluating: %s\n" % s) exec(s) in globals() except: pass except: dbg("exception: %s" % sys.exc_info()[1]) def clean_up(): for o in globals().keys(): if o not in LOCALDEFS: try: exec('del %s' % o) in globals() except: pass sys.path.extend(['.','..']) PYTHONEOF endfunction call s:DefPython() " vim: set et ts=4: