"pythoncomplete.vim"" dodac do .vimrc" autocmd FileType python set omnifunc=pythoncomplete#Complete" set completeopt=menu,longest,preview" autocmd FileType python set et sts=4 sw=4if !has('python') echo "Error: Required vim compiled with +python" finishendiffunction! pythoncomplete#Complete(findstart, base) "findstart = 1 when we need to get the text length if a:findstart == 1 let line = getline('.') let idx = col('.') while idx > 0 let idx -= 1 let c = line[idx] 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 "vim no longer moves the cursor upon completion... fix that let line = getline('.') let idx = col('.') let cword = '' while idx > 0 let idx -= 1 let c = line[idx] if c =~ '\w' || c =~ '\.' let cword = c . cword continue elseif strlen(cword) > 0 || idx == 0 break endif endwhile execute "python vimcomplete('" . cword . "', '" . a:base . "')" return g:pythoncomplete_completions endifendfunctionfunction! s:DefPython()python << PYTHONEOFimport sys, tokenize, cStringIO, typesfrom token import NAME, DEDENT, NEWLINE, STRINGdebugstmts=[]def dbg(s): debugstmts.append(s)def showdbg(): for d in debugstmts: print "DBG: %s " % ddef vimcomplete(context,match): global debugstmts debugstmts = [] try: import vim def complsort(x,y): return x['abbr'] > y['abbr'] #ocmpl = CompleterORIG() cmpl = Completer() #dbg("context: %s" % context) #import time #dbg("match: %s" % match) #time.sleep(4) cmpl.evalsource('\n'.join(vim.current.buffer),vim.eval("line('.')")) #all = ocmpl.get_completions(context,match) all = cmpl.all_completions(str(context)+str(match)) all.sort(complsort) #all = [] dictstr = '[' # have to do this for double quoting for cmpl in all: dictstr += '{' for x in cmpl: dictstr += '"%s":"%s",' % (x,cmpl[x]) dictstr += '"icase":0},' if dictstr[-1] == ',': dictstr = dictstr[:-1] dictstr += ']' dbg("dict: %s" % dictstr) vim.command("silent let g:pythoncomplete_completions = %s" % dictstr) #dbg("Completion dict:\n%s" % all) except vim.error: dbg("VIM Error: %s" % vim.error)#############################################################################import __builtin__import __main__import globimport keywordimport osimport reimport shleximport sysimport itertoolsimport types# Python 2.4 offers sets as a builtintry: set([1,2])except NameError: from sets import Set as set__all__ = ['Completer']def get_class_members(cls): ret = dir(cls) if hasattr(cls,'__bases__'): for base in cls.__bases__: ret.extend(get_class_members(base)) return retclass Completer: def __init__(self,namespace=None,global_namespace=None): """Create a new completer for the command line. Completer([namespace,global_namespace]) -> completer instance. If unspecified, the default namespace where completions are performed is __main__ (technically, __main__.__dict__). Namespaces should be given as dictionaries. An optional second namespace can be given. This allows the completer to handle cases where both the local and global scopes need to be distinguished. Completer instances should be used as the completion mechanism of readline via the set_completer() call: readline.set_completer(Completer(my_namespace).complete) """ # some minimal strict typechecks. For some core data structures, I # want actual basic python types, not just anything that looks like # one. This is especially true for namespaces. for ns in (namespace,global_namespace): if ns is not None and type(ns) != types.DictType: raise TypeError,'namespace must be a dictionary' # Don't bind to namespace quite yet, but flag whether the user wants a # specific namespace or to use __main__.__dict__. This will allow us # to bind to __main__.__dict__ at completion time, not now. self.namespace = __builtin__.__dict__ self.global_namespace = __builtin__.__dict__ if namespace is None: self.use_main_ns = 1 else: self.use_main_ns = 0 self.namespace = namespace # The global namespace, if given, can be bound directly if global_namespace is None: self.global_namespace = {} else: self.global_namespace = global_namespace self.compldict = {} self.parser = PyParser() def evalsource(self,text,line=0): sc = self.parser.parse(text,line) src = sc.get_code() dbg("source: %s" % src) try: exec(src) in self.namespace except: dbg("parser: %s, %s" % (sys.exc_info()[0],sys.exc_info()[1])) for l in sc.locals: try: exec(l) in self.global_namespace except: dbg("locals: %s, %s [%s]" % (sys.exc_info()[0],sys.exc_info()[1],l)) def complete(self, text, state): """Return the next possible completion for 'text'. This is called successively with state == 0, 1, 2, ... until it returns None. The completion should begin with 'text'. """ #if self.use_main_ns: # self.namespace = __main__.__dict__ if state == 0: if "." in text: self.matches = self.attr_matches(text) else: self.matches = self.global_matches(text) try: return self.matches[state] except IndexError: return None def all_completions(self, text): """Return all possible completions for the benefit of emacs.""" completions = [] comp_append = completions.append try: for i in xrange(sys.maxint): res = self.complete(text, i) if not res: break ############## aaa = res[len(text):] attr = None doc = '' try: attr = eval(res, self.namespace) except: try: attr = eval(res, self.global_namespace) except: if not self.is_keyword(res): continue c = {'word':str(aaa), 'abbr':res, 'info':'','kind':''} if self.is_keyword(res): c['word'] = aaa c['kind'] = 'keyword' elif self.is_type(attr): c['info'] = self.get_doc(attr) c['kind'] = 'type' elif self.is_function(attr): c['word'] = aaa + '()' c['abbr'] = res + '()' c['kind'] = 'function' elif self.is_method(attr): c['word'] = aaa + '()' c['abbr'] = res + '()' c['info'] = self.get_doc(attr) c['kind'] = 'method' elif self.is_class(attr): c['info'] = self.get_doc(attr) c['kind'] = 'class' elif self.is_module(attr): c['info'] = self.get_doc(attr) c['kind'] = 'module' elif self.is_list(attr): #c['word'] += aaa #c['abbr'] += res c['info'] = self.get_doc(attr) c['kind']='list' elif self.is_builtin_method(attr): c['word'] = aaa + '()' c['abbr'] = res + '()' c['info'] = self.get_doc(attr) c['kind']='builtinmethod' elif self.is_builtin_function(attr): c['word'] = aaa + '()' c['abbr'] = res + '()' c['info'] = self.get_doc(attr) c['kind']='builtinfunction' elif self.is_file(attr): c['info'] = self.get_doc(attr) c['kind'] = 'file' elif self.is_string(attr): c['info'] = self.get_doc(attr) c['kind']='string' elif self.is_dict(attr): c['info'] = self.get_doc(attr) c['kind']='dict' elif self.is_int(attr): c['info'] = self.get_doc(attr) c['kind']='int' elif self.is_boolean(attr): c['info'] = self.get_doc(attr) c['kind']='bool' elif self.is_tuple(attr): c['info'] = self.get_doc(attr) c['kind']='tuple' elif self.is_instance(attr): c['info'] = self.get_doc(attr) c['kind']='instance' elif self.is_none(attr): c['kind']='None' ############### comp_append(c) except NameError: pass return completions def get_attr(self, x): ret_attr = None at = x.split('.') at = ''.join(at[len(at)-1]) y = x[:-len(at)-1] ml = [x, y, __main__, __builtin__] for i in ml: #print "x:%s y:%s i:%s" % (x,y,i) try: ret_attr = getattr(i, at) except: continue break return ret_attr def get_doc(self, x): try: doc = x.__doc__ except: return '' if doc == None: return '' return doc.replace('"',' ')\ .replace("'",' ')\ .replace('\n',' ')\ .replace('\r',' ')\ .replace('',' ') def is_keyword(self, x): return keyword.iskeyword(x) def cmp_type(self, x, typ): try: if type(x) is typ: return True else: return False except: pass def is_boolean(self, x): return self.cmp_type(x, types.BooleanType) def is_buffer(self, x): return self.cmp_type(x, types.BufferType) def is_builtin_function(self, x): return self.cmp_type(x, types.BuiltinFunctionType) def is_builtin_method(self, x): return self.cmp_type(x, types.BuiltinMethodType) def is_class(self, x): return self.cmp_type(x, types.ClassType) def is_dict(self, x): return self.cmp_type(x, types.DictType) def is_file(self, x): return self.cmp_type(x, types.FileType) def is_int(self, x): return self.cmp_type(x, types.IntType) def is_instance(self, x): return self.cmp_type(x, types.InstanceType) def is_none(self, x): return self.cmp_type(x, types.NoneType) def is_string(self, x): return self.cmp_type(x, types.StringType) def is_type(self, x): return self.cmp_type(x, types.TypeType) def is_tuple(self, x): return self.cmp_type(x, types.TupleType) def is_function(self, x): return self.cmp_type(x, types.FunctionType) def is_method(self, x): return self.cmp_type(x, types.MethodType) def is_module(self, x): return self.cmp_type(x, types.ModuleType) def is_list(self, x): return self.cmp_type(x, types.ListType) def global_matches(self, text): """Compute matches when text is a simple name. Return a list of all keywords, built-in functions and names currently defined in self.namespace or self.global_namespace that match. """ matches = [] match_append = matches.append n = len(text) for lst in [keyword.kwlist, __builtin__.__dict__.keys(), self.namespace.keys(), self.global_namespace.keys()]: for word in lst: if word[:n] == text and word != "__builtins__": match_append(word) return matches def attr_matches(self, text): """Compute matches when text contains a dot. Assuming the text is of the form NAME.NAME....[NAME], and is evaluatable in self.namespace or self.global_namespace, it will be evaluated and its attributes (as revealed by dir()) are used as possible completions. (For class instances, class members are are also considered.) WARNING: this can still invoke arbitrary C code, if an object with a __getattr__ hook is evaluated. """ import re # Another option, seems to work great. Catches things like ''.<tab> m = re.match(r"(\S+(\.\w+)*)\.(\w*)$", text) if not m: return [] expr, attr = m.group(1, 3) try: object = eval(expr, self.namespace) except: try: object = eval(expr, self.global_namespace) except: return [] # Start building the attribute list via dir(), and then complete it # with a few extra special-purpose calls. words = dir(object) if hasattr(object,'__class__'): words.append('__class__') words.extend(get_class_members(object.__class__)) # this is the 'dir' function for objects with Enthought's traits if hasattr(object, 'trait_names'): try: words.extend(object.trait_names()) # eliminate possible duplicates, as some traits may also # appear as normal attributes in the dir() call. words = set(words) except TypeError: # This will happen if `object` is a class and not an instance. pass # Support for PyCrust-style _getAttributeNames magic method. if hasattr(object, '_getAttributeNames'): try: words.extend(object._getAttributeNames()) # Eliminate duplicates. words = set(words) except TypeError: # `object` is a class and not an instance. Ignore # this error. pass # filter out non-string attributes which may be stuffed by dir() calls # and poor coding in third-party modules words = [w for w in words if isinstance(w, basestring) and w != "__builtins__"] # Build match list to return n = len(attr) return ["%s.%s" % (expr, w) for w in words if w[:n] == attr ]class Scope(object): def __init__(self,name,indent): self.subscopes = [] self.docstr = '' self.locals = [] self.parent = None self.name = name self.indent = indent def add(self,sub): #print 'push scope: [%s@%s]' % (sub.name,sub.indent) sub.parent = self self.subscopes.append(sub) return sub def doc(self,str): """ Clean up a docstring """ d = str.replace('\n',' ') d = d.replace('\t',' ') while d.find(' ') > -1: d = d.replace(' ',' ') while d[0] in '"\'\t ': d = d[1:] while d[-1] in '"\'\t ': d = d[:-1] self.docstr = d def local(self,loc): if not self._hasvaralready(loc): self.locals.append(loc) def copy_decl(self,indent=0): """ Copy a scope's declaration only, at the specified indent level - not local variables """ return Scope(self.name,indent) def _hasvaralready(self,test): "Convienance function... keep out duplicates" if test.find('=') > -1: var = test.split('=')[0].strip() for l in self.locals: if l.find('=') > -1 and var == l.split('=')[0].strip(): return True return False def get_code(self): # we need to start with this, to fix up broken completions # hopefully this name is unique enough... str = '"""'+self.docstr+'"""\n' str += 'class _PyCmplNoType:\n def __getattr__(self,name):\n return None\n' for sub in self.subscopes: str += sub.get_code() #str += '\n'.join(self.locals)+'\n' return str def pop(self,indent): #print 'pop scope: [%s] to [%s]' % (self.indent,indent) outer = self while outer.parent != None and outer.indent >= indent: outer = outer.parent return outer def currentindent(self): #print 'parse current indent: %s' % self.indent return ' '*self.indent def childindent(self): #print 'parse child indent: [%s]' % (self.indent+1) return ' '*(self.indent+1)class Class(Scope): def __init__(self, name, supers, indent): Scope.__init__(self,name,indent) self.supers = supers def copy_decl(self,indent=0): c = Class(self.name,self.supers,indent) for s in self.subscopes: c.add(s.copy_decl(indent+1)) return c def get_code(self): str = '%sclass %s' % (self.currentindent(),self.name) if len(self.supers) > 0: str += '(%s)' % ','.join(self.supers) str += ':\n' if len(self.docstr) > 0: str += self.childindent()+'"""'+self.docstr+'"""\n' if len(self.subscopes) > 0: for s in self.subscopes: str += s.get_code() else: str += '%spass\n' % self.childindent() return strclass Function(Scope): def __init__(self, name, params, indent): Scope.__init__(self,name,indent) self.params = params def copy_decl(self,indent=0): return Function(self.name,self.params,indent) def get_code(self): str = "%sdef %s(%s):\n" % \ (self.currentindent(),self.name,','.join(self.params)) if len(self.docstr) > 0: str += self.childindent()+'"""'+self.docstr+'"""\n' str += "%spass\n" % self.childindent() return strclass PyParser: def __init__(self): self.top = Scope('global',0) self.scope = self.top def _parsedotname(self,pre=None): #returns (dottedname, nexttoken) name = [] if pre == None: tokentype, token, indent = self.next() if tokentype != NAME and token != '*': return ('', token) else: token = pre name.append(token) while True: tokentype, token, indent = self.next() if token != '.': break tokentype, token, indent = self.next() if tokentype != NAME: break name.append(token) return (".".join(name), token) def _parseimportlist(self): imports = [] while True: name, token = self._parsedotname() if not name: break name2 = '' if token == 'as': name2, token = self._parsedotname() imports.append((name, name2)) while token != "," and "\n" not in token: tokentype, token, indent = self.next() if token != ",": break return imports def _parenparse(self): name = '' names = [] level = 1 while True: tokentype, token, indent = self.next() if token in (')', ',') and level == 1: names.append(name) name = '' if token == '(': level += 1 elif token == ')': level -= 1 if level == 0: break elif token == ',' and level == 1: pass else: name += str(token) return names def _parsefunction(self,indent): self.scope=self.scope.pop(indent) tokentype, fname, ind = self.next() if tokentype != NAME: return None tokentype, open, ind = self.next() if open != '(': return None params=self._parenparse() tokentype, colon, ind = self.next() if colon != ':': return None return Function(fname,params,indent) def _parseclass(self,indent): self.scope=self.scope.pop(indent) tokentype, cname, ind = self.next() if tokentype != NAME: return None super = [] tokentype, next, ind = self.next() if next == '(': super=self._parenparse() elif next != ':': return None return Class(cname,super,indent) def _parseassignment(self): assign='' tokentype, token, indent = self.next() if tokentype == tokenize.STRING or token == 'str': return '""' elif token == '[' or token == 'list': return '[]' elif token == '{' or token == 'dict': return '{}' elif tokentype == tokenize.NUMBER: return '0' elif token == 'open' or token == 'file': return 'file' elif token == 'None': return '_PyCmplNoType()' elif token == 'type': return 'type(_PyCmplNoType)' #only for method resolution else: assign += token level = 0 while True: tokentype, token, indent = self.next() if token in ('(','{','['): level += 1 elif token in (']','}',')'): level -= 1 if level == 0: break elif level == 0: if token in (';','\n'): break assign += token return "%s" % assign def next(self): type, token, (lineno, indent), end, self.parserline = self.gen.next() if lineno == self.curline: #print 'line found [%s] scope=%s' % (line.replace('\n',''),self.scope.name) self.currentscope = self.scope return (type, token, indent) def _adjustvisibility(self): newscope = Scope('result',0) scp = self.currentscope while scp != None: if type(scp) == Function: slice = 0 #Handle 'self' params if scp.parent != None and type(scp.parent) == Class: slice = 1 p = scp.params[0] i = p.find('=') if i != -1: p = p[:i] newscope.local('%s = %s' % (scp.params[0],scp.parent.name)) for p in scp.params[slice:]: i = p.find('=') if i == -1: newscope.local('%s = _PyCmplNoType()' % p) else: newscope.local('%s = %s' % (p[:i],_sanitize(p[i+1]))) for s in scp.subscopes: ns = s.copy_decl(0) newscope.add(ns) for l in scp.locals: newscope.local(l) scp = scp.parent self.currentscope = newscope return self.currentscope #p.parse(vim.current.buffer[:],vim.eval("line('.')")) def parse(self,text,curline=0): self.curline = int(curline) buf = cStringIO.StringIO(''.join(text) + '\n') self.gen = tokenize.generate_tokens(buf.readline) self.currentscope = self.scope try: freshscope=True while True: tokentype, token, indent = self.next() #print 'main: token=[%s] indent=[%s]' % (token,indent) if tokentype == DEDENT: self.scope = self.scope.pop(indent) elif token == 'def': func = self._parsefunction(indent) if func == None: print "function: syntax error..." continue freshscope = True self.scope = self.scope.add(func) elif token == 'class': cls = self._parseclass(indent) if cls == None: print "class: syntax error..." continue freshscope = True self.scope = self.scope.add(cls) elif token == 'import': imports = self._parseimportlist() for mod, alias in imports: loc = "import %s" % mod if len(alias) > 0: loc += " as %s" % alias self.scope.local(loc) freshscope = False elif token == 'from': mod, token = self._parsedotname() if not mod or token != "import": print "from: syntax error..." continue names = self._parseimportlist() for name, alias in names: loc = "from %s import %s" % (mod,name) if len(alias) > 0: loc += " as %s" % alias self.scope.local(loc) freshscope = False elif tokentype == STRING: if freshscope: self.scope.doc(token) elif tokentype == NAME: name,token = self._parsedotname(token) if token == '=': stmt = self._parseassignment() if stmt != None: self.scope.local("%s = %s" % (name,stmt)) freshscope = False except StopIteration: #thrown on EOF pass except: dbg("parse error: %s, %s @ %s" % (sys.exc_info()[0], sys.exc_info()[1], self.parserline)) return self._adjustvisibility()def _sanitize(str): val = '' level = 0 for c in str: if c in ('(','{','['): level += 1 elif c in (']','}',')'): level -= 1 elif level == 0: val += c return valsys.path.extend(['.','..'])PYTHONEOFendfunctioncall s:DefPython()" vim: set et ts=4: