diff options
| author | Adam Stankiewicz <sheerun@sher.pl> | 2017-09-27 20:43:42 +0200 | 
|---|---|---|
| committer | Adam Stankiewicz <sheerun@sher.pl> | 2017-09-27 20:43:42 +0200 | 
| commit | 5b77877888162f4e415fe9a7b8c5e9fb5dfb6ee1 (patch) | |
| tree | 965ae5128797f3d42d78d02692e62d24a4596e19 /autoload/python3complete.vim | |
| parent | 8148255ef1c416f414c3a78405eff08fe149d16e (diff) | |
| download | vim-polyglot-5b77877888162f4e415fe9a7b8c5e9fb5dfb6ee1.tar.gz vim-polyglot-5b77877888162f4e415fe9a7b8c5e9fb5dfb6ee1.zip | |
Add syntax files from upstream vim repository
Diffstat (limited to '')
| -rw-r--r-- | autoload/python3complete.vim | 611 | 
1 files changed, 611 insertions, 0 deletions
| diff --git a/autoload/python3complete.vim b/autoload/python3complete.vim new file mode 100644 index 00000000..7ed78a93 --- /dev/null +++ b/autoload/python3complete.vim @@ -0,0 +1,611 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'vim') == -1 +   +"python3complete.vim - Omni Completion for python +" Maintainer: Aaron Griffin <aaronmgriffin@gmail.com> +" Version: 0.9 +" Last Updated: 18 Jun 2009 (small fix 2015 Sep 14 from Debian) +" +" Roland Puntaier: this file contains adaptations for python3 and is parallel to pythoncomplete.vim +" +" Changes +" TODO: +" 'info' item output can use some formatting work +" Add an "unsafe eval" mode, to allow for return type evaluation +" Complete basic syntax along with import statements +"   i.e. "import url<c-x,c-o>" +" Continue parsing on invalid line?? +" +" v 0.9 +"   * Fixed docstring parsing for classes and functions +"   * Fixed parsing of *args and **kwargs type arguments +"   * Better function param parsing to handle things like tuples and +"     lambda defaults args +" +" v 0.8 +"   * Fixed an issue where the FIRST assignment was always used instead of +"   using a subsequent assignment for a variable +"   * Fixed a scoping issue when working inside a parameterless function +" +" +" v 0.7 +"   * Fixed function list sorting (_ and __ at the bottom) +"   * Removed newline removal from docs.  It appears vim handles these better in +"   recent patches +" +" v 0.6: +"   * Fixed argument completion +"   * Removed the 'kind' completions, as they are better indicated +"   with real syntax +"   * Added tuple assignment parsing (whoops, that was forgotten) +"   * Fixed import handling when flattening scope +" +" v 0.5: +" Yeah, I skipped a version number - 0.4 was never public. +"  It was a bugfix version on top of 0.3.  This is a complete +"  rewrite. +" + +if !has('python3') +    echo "Error: Required vim compiled with +python3" +    finish +endif + +function! python3complete#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 =~ '\.' +                let 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 "py3 vimpy3complete('" . cword . "', '" . a:base . "')" +        return g:python3complete_completions +    endif +endfunction + +function! s:DefPython() +py3 << PYTHONEOF +import sys, tokenize, io, types +from token import NAME, DEDENT, NEWLINE, STRING + +debugstmts=[] +def dbg(s): debugstmts.append(s) +def showdbg(): +    for d in debugstmts: print("DBG: %s " % d) + +def vimpy3complete(context,match): +    global debugstmts +    debugstmts = [] +    try: +        import vim +        cmpl = Completer() +        cmpl.evalsource('\n'.join(vim.current.buffer),vim.eval("line('.')")) +        all = cmpl.get_completions(context,match) +        all.sort(key=lambda x:x['abbr'].replace('_','z')) +        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:python3complete_completions = %s" % dictstr) +        #dbg("Completion dict:\n%s" % all) +    except vim.error: +        dbg("VIM Error: %s" % vim.error) + +class Completer(object): +    def __init__(self): +       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,self.compldict) +        except: dbg("parser: %s, %s" % (sys.exc_info()[0],sys.exc_info()[1])) +        for l in sc.locals: +            try: exec(l,self.compldict) +            except: dbg("locals: %s, %s [%s]" % (sys.exc_info()[0],sys.exc_info()[1],l)) + +    def _cleanstr(self,doc): +        return doc.replace('"',' ').replace("'",' ') + +    def get_arguments(self,func_obj): +        def _ctor(class_ob): +            try: return class_ob.__init__ +            except AttributeError: +                for base in class_ob.__bases__: +                    rc = _ctor(base) +                    if rc is not None: return rc +            return None + +        arg_offset = 1 +        if type(func_obj) == type: func_obj = _ctor(func_obj) +        elif type(func_obj) == types.MethodType: arg_offset = 1 +        else: arg_offset = 0 + +        arg_text='' +        if type(func_obj) in [types.FunctionType, types.LambdaType,types.MethodType]: +            try: +                cd = func_obj.__code__ +                real_args = cd.co_varnames[arg_offset:cd.co_argcount] +                defaults = func_obj.__defaults__ or [] +                defaults = ["=%s" % name for name in defaults] +                defaults = [""] * (len(real_args)-len(defaults)) + defaults +                items = [a+d for a,d in zip(real_args,defaults)] +                if func_obj.__code__.co_flags & 0x4: +                    items.append("...") +                if func_obj.__code__.co_flags & 0x8: +                    items.append("***") +                arg_text = (','.join(items)) + ')' +            except: +                dbg("arg completion: %s: %s" % (sys.exc_info()[0],sys.exc_info()[1])) +                pass +        if len(arg_text) == 0: +            # The doc string sometimes contains the function signature +            #  this works for alot of C modules that are part of the +            #  standard library +            doc = func_obj.__doc__ +            if doc: +                doc = doc.lstrip() +                pos = doc.find('\n') +                if pos > 0: +                    sigline = doc[:pos] +                    lidx = sigline.find('(') +                    ridx = sigline.find(')') +                    if lidx > 0 and ridx > 0: +                        arg_text = sigline[lidx+1:ridx] + ')' +        if len(arg_text) == 0: arg_text = ')' +        return arg_text + +    def get_completions(self,context,match): +        #dbg("get_completions('%s','%s')" % (context,match)) +        stmt = '' +        if context: stmt += str(context) +        if match: stmt += str(match) +        try: +            result = None +            all = {} +            ridx = stmt.rfind('.') +            if len(stmt) > 0 and stmt[-1] == '(': +                result = eval(_sanitize(stmt[:-1]), self.compldict) +                doc = result.__doc__ +                if doc is None: doc = '' +                args = self.get_arguments(result) +                return [{'word':self._cleanstr(args),'info':self._cleanstr(doc)}] +            elif ridx == -1: +                match = stmt +                all = self.compldict +            else: +                match = stmt[ridx+1:] +                stmt = _sanitize(stmt[:ridx]) +                result = eval(stmt, self.compldict) +                all = dir(result) + +            dbg("completing: stmt:%s" % stmt) +            completions = [] + +            try: maindoc = result.__doc__ +            except: maindoc = ' ' +            if maindoc is None: maindoc = ' ' +            for m in all: +                if m == "_PyCmplNoType": continue #this is internal +                try: +                    dbg('possible completion: %s' % m) +                    if m.find(match) == 0: +                        if result is None: inst = all[m] +                        else: inst = getattr(result,m) +                        try: doc = inst.__doc__ +                        except: doc = maindoc +                        typestr = str(inst) +                        if doc is None or doc == '': doc = maindoc + +                        wrd = m[len(match):] +                        c = {'word':wrd, 'abbr':m,  'info':self._cleanstr(doc)} +                        if "function" in typestr: +                            c['word'] += '(' +                            c['abbr'] += '(' + self._cleanstr(self.get_arguments(inst)) +                        elif "method" in typestr: +                            c['word'] += '(' +                            c['abbr'] += '(' + self._cleanstr(self.get_arguments(inst)) +                        elif "module" in typestr: +                            c['word'] += '.' +                        elif "type" in typestr: +                            c['word'] += '(' +                            c['abbr'] += '(' +                        completions.append(c) +                except: +                    i = sys.exc_info() +                    dbg("inner completion: %s,%s [stmt='%s']" % (i[0],i[1],stmt)) +            return completions +        except: +            i = sys.exc_info() +            dbg("completion: %s,%s [stmt='%s']" % (i[0],i[1],stmt)) +            return [] + +class Scope(object): +    def __init__(self,name,indent,docstr=''): +        self.subscopes = [] +        self.docstr = 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] +        dbg("Scope(%s)::docstr = %s" % (self,d)) +        self.docstr = d + +    def local(self,loc): +        self._checkexisting(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,self.docstr) + +    def _checkexisting(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(): +                    self.locals.remove(l) + +    def get_code(self): +        str = "" +        if len(self.docstr) > 0: str += '"""'+self.docstr+'"""\n' +        for l in self.locals: +            if l.startswith('import'): str += l+'\n' +        str += 'class _PyCmplNoType:\n    def __getattr__(self,name):\n        return None\n' +        for sub in self.subscopes: +            str += sub.get_code() +        for l in self.locals: +            if not l.startswith('import'): str += l+'\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, docstr=''): +        Scope.__init__(self,name,indent, docstr) +        self.supers = supers +    def copy_decl(self,indent=0): +        c = Class(self.name,self.supers,indent, self.docstr) +        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 str + + +class Function(Scope): +    def __init__(self, name, params, indent, docstr=''): +        Scope.__init__(self,name,indent, docstr) +        self.params = params +    def copy_decl(self,indent=0): +        return Function(self.name,self.params,indent, self.docstr) +    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 str + +class PyParser: +    def __init__(self): +        self.top = Scope('global',0) +        self.scope = self.top +        self.parserline = 0 + +    def _parsedotname(self,pre=None): +        #returns (dottedname, nexttoken) +        name = [] +        if pre is None: +            tokentype, token, indent = self.donext() +            if tokentype != NAME and token != '*': +                return ('', token) +        else: token = pre +        name.append(token) +        while True: +            tokentype, token, indent = self.donext() +            if token != '.': break +            tokentype, token, indent = self.donext() +            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.donext() +            if token != ",": break +        return imports + +    def _parenparse(self): +        name = '' +        names = [] +        level = 1 +        while True: +            tokentype, token, indent = self.donext() +            if token in (')', ',') and level == 1: +                if '=' not in name: name = name.replace(' ', '') +                names.append(name.strip()) +                name = '' +            if token == '(': +                level += 1 +                name += "(" +            elif token == ')': +                level -= 1 +                if level == 0: break +                else: name += ")" +            elif token == ',' and level == 1: +                pass +            else: +                name += "%s " % str(token) +        return names + +    def _parsefunction(self,indent): +        self.scope=self.scope.pop(indent) +        tokentype, fname, ind = self.donext() +        if tokentype != NAME: return None + +        tokentype, open, ind = self.donext() +        if open != '(': return None +        params=self._parenparse() + +        tokentype, colon, ind = self.donext() +        if colon != ':': return None + +        return Function(fname,params,indent) + +    def _parseclass(self,indent): +        self.scope=self.scope.pop(indent) +        tokentype, cname, ind = self.donext() +        if tokentype != NAME: return None + +        super = [] +        tokentype, thenext, ind = self.donext() +        if thenext == '(': +            super=self._parenparse() +        elif thenext != ':': return None + +        return Class(cname,super,indent) + +    def _parseassignment(self): +        assign='' +        tokentype, token, indent = self.donext() +        if tokentype == tokenize.STRING or token == 'str':   +            return '""' +        elif token == '(' or token == 'tuple': +            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.donext() +                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 donext(self): +        type, token, (lineno, indent), end, self.parserline = next(self.gen) +        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 +                    newscope.local('%s = %s' % (scp.params[0],scp.parent.name)) +                for p in scp.params[slice:]: +                    i = p.find('=') +                    if len(p) == 0: continue +                    pvar = '' +                    ptype = '' +                    if i == -1: +                        pvar = p +                        ptype = '_PyCmplNoType()' +                    else: +                        pvar = p[:i] +                        ptype = _sanitize(p[i+1:]) +                    if pvar.startswith('**'): +                        pvar = pvar[2:] +                        ptype = '{}' +                    elif pvar.startswith('*'): +                        pvar = pvar[1:] +                        ptype = '[]' + +                    newscope.local('%s = %s' % (pvar,ptype)) + +            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 = io.StringIO(''.join(text) + '\n') +        self.gen = tokenize.generate_tokens(buf.readline) +        self.currentscope = self.scope + +        try: +            freshscope=True +            while True: +                tokentype, token, indent = self.donext() +                #dbg( 'main: token=[%s] indent=[%s]' % (token,indent)) + +                if tokentype == DEDENT or token == "pass": +                    self.scope = self.scope.pop(indent) +                elif token == 'def': +                    func = self._parsefunction(indent) +                    if func is None: +                        print("function: syntax error...") +                        continue +                    dbg("new scope: function") +                    freshscope = True +                    self.scope = self.scope.add(func) +                elif token == 'class': +                    cls = self._parseclass(indent) +                    if cls is None: +                        print("class: syntax error...") +                        continue +                    freshscope = True +                    dbg("new scope: class") +                    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() +                        dbg("parseassignment: %s = %s" % (name, stmt)) +                        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 val + +sys.path.extend(['.','..']) +PYTHONEOF +endfunction + +call s:DefPython() + +endif | 
