1*bd5e15fdSBram Moolenaar"python3complete.vim - Omni Completion for python 2*bd5e15fdSBram Moolenaar" Maintainer: Aaron Griffin <[email protected]> 3*bd5e15fdSBram Moolenaar" Version: 0.9 4*bd5e15fdSBram Moolenaar" Last Updated: 18 Jun 2009 5*bd5e15fdSBram Moolenaar" 6*bd5e15fdSBram Moolenaar" Roland Puntaier: this file contains adaptations for python3 and is parallel to pythoncomplete.vim 7*bd5e15fdSBram Moolenaar" 8*bd5e15fdSBram Moolenaar" Changes 9*bd5e15fdSBram Moolenaar" TODO: 10*bd5e15fdSBram Moolenaar" 'info' item output can use some formatting work 11*bd5e15fdSBram Moolenaar" Add an "unsafe eval" mode, to allow for return type evaluation 12*bd5e15fdSBram Moolenaar" Complete basic syntax along with import statements 13*bd5e15fdSBram Moolenaar" i.e. "import url<c-x,c-o>" 14*bd5e15fdSBram Moolenaar" Continue parsing on invalid line?? 15*bd5e15fdSBram Moolenaar" 16*bd5e15fdSBram Moolenaar" v 0.9 17*bd5e15fdSBram Moolenaar" * Fixed docstring parsing for classes and functions 18*bd5e15fdSBram Moolenaar" * Fixed parsing of *args and **kwargs type arguments 19*bd5e15fdSBram Moolenaar" * Better function param parsing to handle things like tuples and 20*bd5e15fdSBram Moolenaar" lambda defaults args 21*bd5e15fdSBram Moolenaar" 22*bd5e15fdSBram Moolenaar" v 0.8 23*bd5e15fdSBram Moolenaar" * Fixed an issue where the FIRST assignment was always used instead of 24*bd5e15fdSBram Moolenaar" using a subsequent assignment for a variable 25*bd5e15fdSBram Moolenaar" * Fixed a scoping issue when working inside a parameterless function 26*bd5e15fdSBram Moolenaar" 27*bd5e15fdSBram Moolenaar" 28*bd5e15fdSBram Moolenaar" v 0.7 29*bd5e15fdSBram Moolenaar" * Fixed function list sorting (_ and __ at the bottom) 30*bd5e15fdSBram Moolenaar" * Removed newline removal from docs. It appears vim handles these better in 31*bd5e15fdSBram Moolenaar" recent patches 32*bd5e15fdSBram Moolenaar" 33*bd5e15fdSBram Moolenaar" v 0.6: 34*bd5e15fdSBram Moolenaar" * Fixed argument completion 35*bd5e15fdSBram Moolenaar" * Removed the 'kind' completions, as they are better indicated 36*bd5e15fdSBram Moolenaar" with real syntax 37*bd5e15fdSBram Moolenaar" * Added tuple assignment parsing (whoops, that was forgotten) 38*bd5e15fdSBram Moolenaar" * Fixed import handling when flattening scope 39*bd5e15fdSBram Moolenaar" 40*bd5e15fdSBram Moolenaar" v 0.5: 41*bd5e15fdSBram Moolenaar" Yeah, I skipped a version number - 0.4 was never public. 42*bd5e15fdSBram Moolenaar" It was a bugfix version on top of 0.3. This is a complete 43*bd5e15fdSBram Moolenaar" rewrite. 44*bd5e15fdSBram Moolenaar" 45*bd5e15fdSBram Moolenaar 46*bd5e15fdSBram Moolenaarif !has('python3') 47*bd5e15fdSBram Moolenaar echo "Error: Required vim compiled with +python3" 48*bd5e15fdSBram Moolenaar finish 49*bd5e15fdSBram Moolenaarendif 50*bd5e15fdSBram Moolenaar 51*bd5e15fdSBram Moolenaarfunction! python3complete#Complete(findstart, base) 52*bd5e15fdSBram Moolenaar "findstart = 1 when we need to get the text length 53*bd5e15fdSBram Moolenaar if a:findstart == 1 54*bd5e15fdSBram Moolenaar let line = getline('.') 55*bd5e15fdSBram Moolenaar let idx = col('.') 56*bd5e15fdSBram Moolenaar while idx > 0 57*bd5e15fdSBram Moolenaar let idx -= 1 58*bd5e15fdSBram Moolenaar let c = line[idx] 59*bd5e15fdSBram Moolenaar if c =~ '\w' 60*bd5e15fdSBram Moolenaar continue 61*bd5e15fdSBram Moolenaar elseif ! c =~ '\.' 62*bd5e15fdSBram Moolenaar let idx = -1 63*bd5e15fdSBram Moolenaar break 64*bd5e15fdSBram Moolenaar else 65*bd5e15fdSBram Moolenaar break 66*bd5e15fdSBram Moolenaar endif 67*bd5e15fdSBram Moolenaar endwhile 68*bd5e15fdSBram Moolenaar 69*bd5e15fdSBram Moolenaar return idx 70*bd5e15fdSBram Moolenaar "findstart = 0 when we need to return the list of completions 71*bd5e15fdSBram Moolenaar else 72*bd5e15fdSBram Moolenaar "vim no longer moves the cursor upon completion... fix that 73*bd5e15fdSBram Moolenaar let line = getline('.') 74*bd5e15fdSBram Moolenaar let idx = col('.') 75*bd5e15fdSBram Moolenaar let cword = '' 76*bd5e15fdSBram Moolenaar while idx > 0 77*bd5e15fdSBram Moolenaar let idx -= 1 78*bd5e15fdSBram Moolenaar let c = line[idx] 79*bd5e15fdSBram Moolenaar if c =~ '\w' || c =~ '\.' 80*bd5e15fdSBram Moolenaar let cword = c . cword 81*bd5e15fdSBram Moolenaar continue 82*bd5e15fdSBram Moolenaar elseif strlen(cword) > 0 || idx == 0 83*bd5e15fdSBram Moolenaar break 84*bd5e15fdSBram Moolenaar endif 85*bd5e15fdSBram Moolenaar endwhile 86*bd5e15fdSBram Moolenaar execute "py3 vimpy3complete('" . cword . "', '" . a:base . "')" 87*bd5e15fdSBram Moolenaar return g:python3complete_completions 88*bd5e15fdSBram Moolenaar endif 89*bd5e15fdSBram Moolenaarendfunction 90*bd5e15fdSBram Moolenaar 91*bd5e15fdSBram Moolenaarfunction! s:DefPython() 92*bd5e15fdSBram Moolenaarpy3 << PYTHONEOF 93*bd5e15fdSBram Moolenaarimport sys, tokenize, io, types 94*bd5e15fdSBram Moolenaarfrom token import NAME, DEDENT, NEWLINE, STRING 95*bd5e15fdSBram Moolenaar 96*bd5e15fdSBram Moolenaardebugstmts=[] 97*bd5e15fdSBram Moolenaardef dbg(s): debugstmts.append(s) 98*bd5e15fdSBram Moolenaardef showdbg(): 99*bd5e15fdSBram Moolenaar for d in debugstmts: print("DBG: %s " % d) 100*bd5e15fdSBram Moolenaar 101*bd5e15fdSBram Moolenaardef vimpy3complete(context,match): 102*bd5e15fdSBram Moolenaar global debugstmts 103*bd5e15fdSBram Moolenaar debugstmts = [] 104*bd5e15fdSBram Moolenaar try: 105*bd5e15fdSBram Moolenaar import vim 106*bd5e15fdSBram Moolenaar cmpl = Completer() 107*bd5e15fdSBram Moolenaar cmpl.evalsource('\n'.join(vim.current.buffer),vim.eval("line('.')")) 108*bd5e15fdSBram Moolenaar all = cmpl.get_completions(context,match) 109*bd5e15fdSBram Moolenaar all.sort(key=lambda x:x['abbr'].replace('_','z')) 110*bd5e15fdSBram Moolenaar dictstr = '[' 111*bd5e15fdSBram Moolenaar # have to do this for double quoting 112*bd5e15fdSBram Moolenaar for cmpl in all: 113*bd5e15fdSBram Moolenaar dictstr += '{' 114*bd5e15fdSBram Moolenaar for x in cmpl: dictstr += '"%s":"%s",' % (x,cmpl[x]) 115*bd5e15fdSBram Moolenaar dictstr += '"icase":0},' 116*bd5e15fdSBram Moolenaar if dictstr[-1] == ',': dictstr = dictstr[:-1] 117*bd5e15fdSBram Moolenaar dictstr += ']' 118*bd5e15fdSBram Moolenaar #dbg("dict: %s" % dictstr) 119*bd5e15fdSBram Moolenaar vim.command("silent let g:python3complete_completions = %s" % dictstr) 120*bd5e15fdSBram Moolenaar #dbg("Completion dict:\n%s" % all) 121*bd5e15fdSBram Moolenaar except vim.error: 122*bd5e15fdSBram Moolenaar dbg("VIM Error: %s" % vim.error) 123*bd5e15fdSBram Moolenaar 124*bd5e15fdSBram Moolenaarclass Completer(object): 125*bd5e15fdSBram Moolenaar def __init__(self): 126*bd5e15fdSBram Moolenaar self.compldict = {} 127*bd5e15fdSBram Moolenaar self.parser = PyParser() 128*bd5e15fdSBram Moolenaar 129*bd5e15fdSBram Moolenaar def evalsource(self,text,line=0): 130*bd5e15fdSBram Moolenaar sc = self.parser.parse(text,line) 131*bd5e15fdSBram Moolenaar src = sc.get_code() 132*bd5e15fdSBram Moolenaar dbg("source: %s" % src) 133*bd5e15fdSBram Moolenaar try: exec(src,self.compldict) 134*bd5e15fdSBram Moolenaar except: dbg("parser: %s, %s" % (sys.exc_info()[0],sys.exc_info()[1])) 135*bd5e15fdSBram Moolenaar for l in sc.locals: 136*bd5e15fdSBram Moolenaar try: exec(l,self.compldict) 137*bd5e15fdSBram Moolenaar except: dbg("locals: %s, %s [%s]" % (sys.exc_info()[0],sys.exc_info()[1],l)) 138*bd5e15fdSBram Moolenaar 139*bd5e15fdSBram Moolenaar def _cleanstr(self,doc): 140*bd5e15fdSBram Moolenaar return doc.replace('"',' ').replace("'",' ') 141*bd5e15fdSBram Moolenaar 142*bd5e15fdSBram Moolenaar def get_arguments(self,func_obj): 143*bd5e15fdSBram Moolenaar def _ctor(class_ob): 144*bd5e15fdSBram Moolenaar try: return class_ob.__init__ 145*bd5e15fdSBram Moolenaar except AttributeError: 146*bd5e15fdSBram Moolenaar for base in class_ob.__bases__: 147*bd5e15fdSBram Moolenaar rc = _ctor(base) 148*bd5e15fdSBram Moolenaar if rc is not None: return rc 149*bd5e15fdSBram Moolenaar return None 150*bd5e15fdSBram Moolenaar 151*bd5e15fdSBram Moolenaar arg_offset = 1 152*bd5e15fdSBram Moolenaar if type(func_obj) == type: func_obj = _ctor(func_obj) 153*bd5e15fdSBram Moolenaar elif type(func_obj) == types.MethodType: arg_offset = 1 154*bd5e15fdSBram Moolenaar else: arg_offset = 0 155*bd5e15fdSBram Moolenaar 156*bd5e15fdSBram Moolenaar arg_text='' 157*bd5e15fdSBram Moolenaar if type(func_obj) in [types.FunctionType, types.LambdaType,types.MethodType]: 158*bd5e15fdSBram Moolenaar try: 159*bd5e15fdSBram Moolenaar cd = func_obj.__code__ 160*bd5e15fdSBram Moolenaar real_args = cd.co_varnames[arg_offset:cd.co_argcount] 161*bd5e15fdSBram Moolenaar defaults = func_obj.__defaults__ or [] 162*bd5e15fdSBram Moolenaar defaults = ["=%s" % name for name in defaults] 163*bd5e15fdSBram Moolenaar defaults = [""] * (len(real_args)-len(defaults)) + defaults 164*bd5e15fdSBram Moolenaar items = [a+d for a,d in zip(real_args,defaults)] 165*bd5e15fdSBram Moolenaar if func_obj.__code__.co_flags & 0x4: 166*bd5e15fdSBram Moolenaar items.append("...") 167*bd5e15fdSBram Moolenaar if func_obj.__code__.co_flags & 0x8: 168*bd5e15fdSBram Moolenaar items.append("***") 169*bd5e15fdSBram Moolenaar arg_text = (','.join(items)) + ')' 170*bd5e15fdSBram Moolenaar except: 171*bd5e15fdSBram Moolenaar dbg("arg completion: %s: %s" % (sys.exc_info()[0],sys.exc_info()[1])) 172*bd5e15fdSBram Moolenaar pass 173*bd5e15fdSBram Moolenaar if len(arg_text) == 0: 174*bd5e15fdSBram Moolenaar # The doc string sometimes contains the function signature 175*bd5e15fdSBram Moolenaar # this works for alot of C modules that are part of the 176*bd5e15fdSBram Moolenaar # standard library 177*bd5e15fdSBram Moolenaar doc = func_obj.__doc__ 178*bd5e15fdSBram Moolenaar if doc: 179*bd5e15fdSBram Moolenaar doc = doc.lstrip() 180*bd5e15fdSBram Moolenaar pos = doc.find('\n') 181*bd5e15fdSBram Moolenaar if pos > 0: 182*bd5e15fdSBram Moolenaar sigline = doc[:pos] 183*bd5e15fdSBram Moolenaar lidx = sigline.find('(') 184*bd5e15fdSBram Moolenaar ridx = sigline.find(')') 185*bd5e15fdSBram Moolenaar if lidx > 0 and ridx > 0: 186*bd5e15fdSBram Moolenaar arg_text = sigline[lidx+1:ridx] + ')' 187*bd5e15fdSBram Moolenaar if len(arg_text) == 0: arg_text = ')' 188*bd5e15fdSBram Moolenaar return arg_text 189*bd5e15fdSBram Moolenaar 190*bd5e15fdSBram Moolenaar def get_completions(self,context,match): 191*bd5e15fdSBram Moolenaar #dbg("get_completions('%s','%s')" % (context,match)) 192*bd5e15fdSBram Moolenaar stmt = '' 193*bd5e15fdSBram Moolenaar if context: stmt += str(context) 194*bd5e15fdSBram Moolenaar if match: stmt += str(match) 195*bd5e15fdSBram Moolenaar try: 196*bd5e15fdSBram Moolenaar result = None 197*bd5e15fdSBram Moolenaar all = {} 198*bd5e15fdSBram Moolenaar ridx = stmt.rfind('.') 199*bd5e15fdSBram Moolenaar if len(stmt) > 0 and stmt[-1] == '(': 200*bd5e15fdSBram Moolenaar result = eval(_sanitize(stmt[:-1]), self.compldict) 201*bd5e15fdSBram Moolenaar doc = result.__doc__ 202*bd5e15fdSBram Moolenaar if doc is None: doc = '' 203*bd5e15fdSBram Moolenaar args = self.get_arguments(result) 204*bd5e15fdSBram Moolenaar return [{'word':self._cleanstr(args),'info':self._cleanstr(doc)}] 205*bd5e15fdSBram Moolenaar elif ridx == -1: 206*bd5e15fdSBram Moolenaar match = stmt 207*bd5e15fdSBram Moolenaar all = self.compldict 208*bd5e15fdSBram Moolenaar else: 209*bd5e15fdSBram Moolenaar match = stmt[ridx+1:] 210*bd5e15fdSBram Moolenaar stmt = _sanitize(stmt[:ridx]) 211*bd5e15fdSBram Moolenaar result = eval(stmt, self.compldict) 212*bd5e15fdSBram Moolenaar all = dir(result) 213*bd5e15fdSBram Moolenaar 214*bd5e15fdSBram Moolenaar dbg("completing: stmt:%s" % stmt) 215*bd5e15fdSBram Moolenaar completions = [] 216*bd5e15fdSBram Moolenaar 217*bd5e15fdSBram Moolenaar try: maindoc = result.__doc__ 218*bd5e15fdSBram Moolenaar except: maindoc = ' ' 219*bd5e15fdSBram Moolenaar if maindoc is None: maindoc = ' ' 220*bd5e15fdSBram Moolenaar for m in all: 221*bd5e15fdSBram Moolenaar if m == "_PyCmplNoType": continue #this is internal 222*bd5e15fdSBram Moolenaar try: 223*bd5e15fdSBram Moolenaar dbg('possible completion: %s' % m) 224*bd5e15fdSBram Moolenaar if m.find(match) == 0: 225*bd5e15fdSBram Moolenaar if result is None: inst = all[m] 226*bd5e15fdSBram Moolenaar else: inst = getattr(result,m) 227*bd5e15fdSBram Moolenaar try: doc = inst.__doc__ 228*bd5e15fdSBram Moolenaar except: doc = maindoc 229*bd5e15fdSBram Moolenaar typestr = str(inst) 230*bd5e15fdSBram Moolenaar if doc is None or doc == '': doc = maindoc 231*bd5e15fdSBram Moolenaar 232*bd5e15fdSBram Moolenaar wrd = m[len(match):] 233*bd5e15fdSBram Moolenaar c = {'word':wrd, 'abbr':m, 'info':self._cleanstr(doc)} 234*bd5e15fdSBram Moolenaar if "function" in typestr: 235*bd5e15fdSBram Moolenaar c['word'] += '(' 236*bd5e15fdSBram Moolenaar c['abbr'] += '(' + self._cleanstr(self.get_arguments(inst)) 237*bd5e15fdSBram Moolenaar elif "method" in typestr: 238*bd5e15fdSBram Moolenaar c['word'] += '(' 239*bd5e15fdSBram Moolenaar c['abbr'] += '(' + self._cleanstr(self.get_arguments(inst)) 240*bd5e15fdSBram Moolenaar elif "module" in typestr: 241*bd5e15fdSBram Moolenaar c['word'] += '.' 242*bd5e15fdSBram Moolenaar elif "type" in typestr: 243*bd5e15fdSBram Moolenaar c['word'] += '(' 244*bd5e15fdSBram Moolenaar c['abbr'] += '(' 245*bd5e15fdSBram Moolenaar completions.append(c) 246*bd5e15fdSBram Moolenaar except: 247*bd5e15fdSBram Moolenaar i = sys.exc_info() 248*bd5e15fdSBram Moolenaar dbg("inner completion: %s,%s [stmt='%s']" % (i[0],i[1],stmt)) 249*bd5e15fdSBram Moolenaar return completions 250*bd5e15fdSBram Moolenaar except: 251*bd5e15fdSBram Moolenaar i = sys.exc_info() 252*bd5e15fdSBram Moolenaar dbg("completion: %s,%s [stmt='%s']" % (i[0],i[1],stmt)) 253*bd5e15fdSBram Moolenaar return [] 254*bd5e15fdSBram Moolenaar 255*bd5e15fdSBram Moolenaarclass Scope(object): 256*bd5e15fdSBram Moolenaar def __init__(self,name,indent,docstr=''): 257*bd5e15fdSBram Moolenaar self.subscopes = [] 258*bd5e15fdSBram Moolenaar self.docstr = docstr 259*bd5e15fdSBram Moolenaar self.locals = [] 260*bd5e15fdSBram Moolenaar self.parent = None 261*bd5e15fdSBram Moolenaar self.name = name 262*bd5e15fdSBram Moolenaar self.indent = indent 263*bd5e15fdSBram Moolenaar 264*bd5e15fdSBram Moolenaar def add(self,sub): 265*bd5e15fdSBram Moolenaar #print('push scope: [%s@%s]' % (sub.name,sub.indent)) 266*bd5e15fdSBram Moolenaar sub.parent = self 267*bd5e15fdSBram Moolenaar self.subscopes.append(sub) 268*bd5e15fdSBram Moolenaar return sub 269*bd5e15fdSBram Moolenaar 270*bd5e15fdSBram Moolenaar def doc(self,str): 271*bd5e15fdSBram Moolenaar """ Clean up a docstring """ 272*bd5e15fdSBram Moolenaar d = str.replace('\n',' ') 273*bd5e15fdSBram Moolenaar d = d.replace('\t',' ') 274*bd5e15fdSBram Moolenaar while d.find(' ') > -1: d = d.replace(' ',' ') 275*bd5e15fdSBram Moolenaar while d[0] in '"\'\t ': d = d[1:] 276*bd5e15fdSBram Moolenaar while d[-1] in '"\'\t ': d = d[:-1] 277*bd5e15fdSBram Moolenaar dbg("Scope(%s)::docstr = %s" % (self,d)) 278*bd5e15fdSBram Moolenaar self.docstr = d 279*bd5e15fdSBram Moolenaar 280*bd5e15fdSBram Moolenaar def local(self,loc): 281*bd5e15fdSBram Moolenaar self._checkexisting(loc) 282*bd5e15fdSBram Moolenaar self.locals.append(loc) 283*bd5e15fdSBram Moolenaar 284*bd5e15fdSBram Moolenaar def copy_decl(self,indent=0): 285*bd5e15fdSBram Moolenaar """ Copy a scope's declaration only, at the specified indent level - not local variables """ 286*bd5e15fdSBram Moolenaar return Scope(self.name,indent,self.docstr) 287*bd5e15fdSBram Moolenaar 288*bd5e15fdSBram Moolenaar def _checkexisting(self,test): 289*bd5e15fdSBram Moolenaar "Convienance function... keep out duplicates" 290*bd5e15fdSBram Moolenaar if test.find('=') > -1: 291*bd5e15fdSBram Moolenaar var = test.split('=')[0].strip() 292*bd5e15fdSBram Moolenaar for l in self.locals: 293*bd5e15fdSBram Moolenaar if l.find('=') > -1 and var == l.split('=')[0].strip(): 294*bd5e15fdSBram Moolenaar self.locals.remove(l) 295*bd5e15fdSBram Moolenaar 296*bd5e15fdSBram Moolenaar def get_code(self): 297*bd5e15fdSBram Moolenaar str = "" 298*bd5e15fdSBram Moolenaar if len(self.docstr) > 0: str += '"""'+self.docstr+'"""\n' 299*bd5e15fdSBram Moolenaar for l in self.locals: 300*bd5e15fdSBram Moolenaar if l.startswith('import'): str += l+'\n' 301*bd5e15fdSBram Moolenaar str += 'class _PyCmplNoType:\n def __getattr__(self,name):\n return None\n' 302*bd5e15fdSBram Moolenaar for sub in self.subscopes: 303*bd5e15fdSBram Moolenaar str += sub.get_code() 304*bd5e15fdSBram Moolenaar for l in self.locals: 305*bd5e15fdSBram Moolenaar if not l.startswith('import'): str += l+'\n' 306*bd5e15fdSBram Moolenaar 307*bd5e15fdSBram Moolenaar return str 308*bd5e15fdSBram Moolenaar 309*bd5e15fdSBram Moolenaar def pop(self,indent): 310*bd5e15fdSBram Moolenaar #print('pop scope: [%s] to [%s]' % (self.indent,indent)) 311*bd5e15fdSBram Moolenaar outer = self 312*bd5e15fdSBram Moolenaar while outer.parent != None and outer.indent >= indent: 313*bd5e15fdSBram Moolenaar outer = outer.parent 314*bd5e15fdSBram Moolenaar return outer 315*bd5e15fdSBram Moolenaar 316*bd5e15fdSBram Moolenaar def currentindent(self): 317*bd5e15fdSBram Moolenaar #print('parse current indent: %s' % self.indent) 318*bd5e15fdSBram Moolenaar return ' '*self.indent 319*bd5e15fdSBram Moolenaar 320*bd5e15fdSBram Moolenaar def childindent(self): 321*bd5e15fdSBram Moolenaar #print('parse child indent: [%s]' % (self.indent+1)) 322*bd5e15fdSBram Moolenaar return ' '*(self.indent+1) 323*bd5e15fdSBram Moolenaar 324*bd5e15fdSBram Moolenaarclass Class(Scope): 325*bd5e15fdSBram Moolenaar def __init__(self, name, supers, indent, docstr=''): 326*bd5e15fdSBram Moolenaar Scope.__init__(self,name,indent, docstr) 327*bd5e15fdSBram Moolenaar self.supers = supers 328*bd5e15fdSBram Moolenaar def copy_decl(self,indent=0): 329*bd5e15fdSBram Moolenaar c = Class(self.name,self.supers,indent, self.docstr) 330*bd5e15fdSBram Moolenaar for s in self.subscopes: 331*bd5e15fdSBram Moolenaar c.add(s.copy_decl(indent+1)) 332*bd5e15fdSBram Moolenaar return c 333*bd5e15fdSBram Moolenaar def get_code(self): 334*bd5e15fdSBram Moolenaar str = '%sclass %s' % (self.currentindent(),self.name) 335*bd5e15fdSBram Moolenaar if len(self.supers) > 0: str += '(%s)' % ','.join(self.supers) 336*bd5e15fdSBram Moolenaar str += ':\n' 337*bd5e15fdSBram Moolenaar if len(self.docstr) > 0: str += self.childindent()+'"""'+self.docstr+'"""\n' 338*bd5e15fdSBram Moolenaar if len(self.subscopes) > 0: 339*bd5e15fdSBram Moolenaar for s in self.subscopes: str += s.get_code() 340*bd5e15fdSBram Moolenaar else: 341*bd5e15fdSBram Moolenaar str += '%spass\n' % self.childindent() 342*bd5e15fdSBram Moolenaar return str 343*bd5e15fdSBram Moolenaar 344*bd5e15fdSBram Moolenaar 345*bd5e15fdSBram Moolenaarclass Function(Scope): 346*bd5e15fdSBram Moolenaar def __init__(self, name, params, indent, docstr=''): 347*bd5e15fdSBram Moolenaar Scope.__init__(self,name,indent, docstr) 348*bd5e15fdSBram Moolenaar self.params = params 349*bd5e15fdSBram Moolenaar def copy_decl(self,indent=0): 350*bd5e15fdSBram Moolenaar return Function(self.name,self.params,indent, self.docstr) 351*bd5e15fdSBram Moolenaar def get_code(self): 352*bd5e15fdSBram Moolenaar str = "%sdef %s(%s):\n" % \ 353*bd5e15fdSBram Moolenaar (self.currentindent(),self.name,','.join(self.params)) 354*bd5e15fdSBram Moolenaar if len(self.docstr) > 0: str += self.childindent()+'"""'+self.docstr+'"""\n' 355*bd5e15fdSBram Moolenaar str += "%spass\n" % self.childindent() 356*bd5e15fdSBram Moolenaar return str 357*bd5e15fdSBram Moolenaar 358*bd5e15fdSBram Moolenaarclass PyParser: 359*bd5e15fdSBram Moolenaar def __init__(self): 360*bd5e15fdSBram Moolenaar self.top = Scope('global',0) 361*bd5e15fdSBram Moolenaar self.scope = self.top 362*bd5e15fdSBram Moolenaar 363*bd5e15fdSBram Moolenaar def _parsedotname(self,pre=None): 364*bd5e15fdSBram Moolenaar #returns (dottedname, nexttoken) 365*bd5e15fdSBram Moolenaar name = [] 366*bd5e15fdSBram Moolenaar if pre is None: 367*bd5e15fdSBram Moolenaar tokentype, token, indent = self.donext() 368*bd5e15fdSBram Moolenaar if tokentype != NAME and token != '*': 369*bd5e15fdSBram Moolenaar return ('', token) 370*bd5e15fdSBram Moolenaar else: token = pre 371*bd5e15fdSBram Moolenaar name.append(token) 372*bd5e15fdSBram Moolenaar while True: 373*bd5e15fdSBram Moolenaar tokentype, token, indent = self.donext() 374*bd5e15fdSBram Moolenaar if token != '.': break 375*bd5e15fdSBram Moolenaar tokentype, token, indent = self.donext() 376*bd5e15fdSBram Moolenaar if tokentype != NAME: break 377*bd5e15fdSBram Moolenaar name.append(token) 378*bd5e15fdSBram Moolenaar return (".".join(name), token) 379*bd5e15fdSBram Moolenaar 380*bd5e15fdSBram Moolenaar def _parseimportlist(self): 381*bd5e15fdSBram Moolenaar imports = [] 382*bd5e15fdSBram Moolenaar while True: 383*bd5e15fdSBram Moolenaar name, token = self._parsedotname() 384*bd5e15fdSBram Moolenaar if not name: break 385*bd5e15fdSBram Moolenaar name2 = '' 386*bd5e15fdSBram Moolenaar if token == 'as': name2, token = self._parsedotname() 387*bd5e15fdSBram Moolenaar imports.append((name, name2)) 388*bd5e15fdSBram Moolenaar while token != "," and "\n" not in token: 389*bd5e15fdSBram Moolenaar tokentype, token, indent = self.donext() 390*bd5e15fdSBram Moolenaar if token != ",": break 391*bd5e15fdSBram Moolenaar return imports 392*bd5e15fdSBram Moolenaar 393*bd5e15fdSBram Moolenaar def _parenparse(self): 394*bd5e15fdSBram Moolenaar name = '' 395*bd5e15fdSBram Moolenaar names = [] 396*bd5e15fdSBram Moolenaar level = 1 397*bd5e15fdSBram Moolenaar while True: 398*bd5e15fdSBram Moolenaar tokentype, token, indent = self.donext() 399*bd5e15fdSBram Moolenaar if token in (')', ',') and level == 1: 400*bd5e15fdSBram Moolenaar if '=' not in name: name = name.replace(' ', '') 401*bd5e15fdSBram Moolenaar names.append(name.strip()) 402*bd5e15fdSBram Moolenaar name = '' 403*bd5e15fdSBram Moolenaar if token == '(': 404*bd5e15fdSBram Moolenaar level += 1 405*bd5e15fdSBram Moolenaar name += "(" 406*bd5e15fdSBram Moolenaar elif token == ')': 407*bd5e15fdSBram Moolenaar level -= 1 408*bd5e15fdSBram Moolenaar if level == 0: break 409*bd5e15fdSBram Moolenaar else: name += ")" 410*bd5e15fdSBram Moolenaar elif token == ',' and level == 1: 411*bd5e15fdSBram Moolenaar pass 412*bd5e15fdSBram Moolenaar else: 413*bd5e15fdSBram Moolenaar name += "%s " % str(token) 414*bd5e15fdSBram Moolenaar return names 415*bd5e15fdSBram Moolenaar 416*bd5e15fdSBram Moolenaar def _parsefunction(self,indent): 417*bd5e15fdSBram Moolenaar self.scope=self.scope.pop(indent) 418*bd5e15fdSBram Moolenaar tokentype, fname, ind = self.donext() 419*bd5e15fdSBram Moolenaar if tokentype != NAME: return None 420*bd5e15fdSBram Moolenaar 421*bd5e15fdSBram Moolenaar tokentype, open, ind = self.donext() 422*bd5e15fdSBram Moolenaar if open != '(': return None 423*bd5e15fdSBram Moolenaar params=self._parenparse() 424*bd5e15fdSBram Moolenaar 425*bd5e15fdSBram Moolenaar tokentype, colon, ind = self.donext() 426*bd5e15fdSBram Moolenaar if colon != ':': return None 427*bd5e15fdSBram Moolenaar 428*bd5e15fdSBram Moolenaar return Function(fname,params,indent) 429*bd5e15fdSBram Moolenaar 430*bd5e15fdSBram Moolenaar def _parseclass(self,indent): 431*bd5e15fdSBram Moolenaar self.scope=self.scope.pop(indent) 432*bd5e15fdSBram Moolenaar tokentype, cname, ind = self.donext() 433*bd5e15fdSBram Moolenaar if tokentype != NAME: return None 434*bd5e15fdSBram Moolenaar 435*bd5e15fdSBram Moolenaar super = [] 436*bd5e15fdSBram Moolenaar tokentype, thenext, ind = self.donext() 437*bd5e15fdSBram Moolenaar if thenext == '(': 438*bd5e15fdSBram Moolenaar super=self._parenparse() 439*bd5e15fdSBram Moolenaar elif thenext != ':': return None 440*bd5e15fdSBram Moolenaar 441*bd5e15fdSBram Moolenaar return Class(cname,super,indent) 442*bd5e15fdSBram Moolenaar 443*bd5e15fdSBram Moolenaar def _parseassignment(self): 444*bd5e15fdSBram Moolenaar assign='' 445*bd5e15fdSBram Moolenaar tokentype, token, indent = self.donext() 446*bd5e15fdSBram Moolenaar if tokentype == tokenize.STRING or token == 'str': 447*bd5e15fdSBram Moolenaar return '""' 448*bd5e15fdSBram Moolenaar elif token == '(' or token == 'tuple': 449*bd5e15fdSBram Moolenaar return '()' 450*bd5e15fdSBram Moolenaar elif token == '[' or token == 'list': 451*bd5e15fdSBram Moolenaar return '[]' 452*bd5e15fdSBram Moolenaar elif token == '{' or token == 'dict': 453*bd5e15fdSBram Moolenaar return '{}' 454*bd5e15fdSBram Moolenaar elif tokentype == tokenize.NUMBER: 455*bd5e15fdSBram Moolenaar return '0' 456*bd5e15fdSBram Moolenaar elif token == 'open' or token == 'file': 457*bd5e15fdSBram Moolenaar return 'file' 458*bd5e15fdSBram Moolenaar elif token == 'None': 459*bd5e15fdSBram Moolenaar return '_PyCmplNoType()' 460*bd5e15fdSBram Moolenaar elif token == 'type': 461*bd5e15fdSBram Moolenaar return 'type(_PyCmplNoType)' #only for method resolution 462*bd5e15fdSBram Moolenaar else: 463*bd5e15fdSBram Moolenaar assign += token 464*bd5e15fdSBram Moolenaar level = 0 465*bd5e15fdSBram Moolenaar while True: 466*bd5e15fdSBram Moolenaar tokentype, token, indent = self.donext() 467*bd5e15fdSBram Moolenaar if token in ('(','{','['): 468*bd5e15fdSBram Moolenaar level += 1 469*bd5e15fdSBram Moolenaar elif token in (']','}',')'): 470*bd5e15fdSBram Moolenaar level -= 1 471*bd5e15fdSBram Moolenaar if level == 0: break 472*bd5e15fdSBram Moolenaar elif level == 0: 473*bd5e15fdSBram Moolenaar if token in (';','\n'): break 474*bd5e15fdSBram Moolenaar assign += token 475*bd5e15fdSBram Moolenaar return "%s" % assign 476*bd5e15fdSBram Moolenaar 477*bd5e15fdSBram Moolenaar def donext(self): 478*bd5e15fdSBram Moolenaar type, token, (lineno, indent), end, self.parserline = next(self.gen) 479*bd5e15fdSBram Moolenaar if lineno == self.curline: 480*bd5e15fdSBram Moolenaar #print('line found [%s] scope=%s' % (line.replace('\n',''),self.scope.name)) 481*bd5e15fdSBram Moolenaar self.currentscope = self.scope 482*bd5e15fdSBram Moolenaar return (type, token, indent) 483*bd5e15fdSBram Moolenaar 484*bd5e15fdSBram Moolenaar def _adjustvisibility(self): 485*bd5e15fdSBram Moolenaar newscope = Scope('result',0) 486*bd5e15fdSBram Moolenaar scp = self.currentscope 487*bd5e15fdSBram Moolenaar while scp != None: 488*bd5e15fdSBram Moolenaar if type(scp) == Function: 489*bd5e15fdSBram Moolenaar slice = 0 490*bd5e15fdSBram Moolenaar #Handle 'self' params 491*bd5e15fdSBram Moolenaar if scp.parent != None and type(scp.parent) == Class: 492*bd5e15fdSBram Moolenaar slice = 1 493*bd5e15fdSBram Moolenaar newscope.local('%s = %s' % (scp.params[0],scp.parent.name)) 494*bd5e15fdSBram Moolenaar for p in scp.params[slice:]: 495*bd5e15fdSBram Moolenaar i = p.find('=') 496*bd5e15fdSBram Moolenaar if len(p) == 0: continue 497*bd5e15fdSBram Moolenaar pvar = '' 498*bd5e15fdSBram Moolenaar ptype = '' 499*bd5e15fdSBram Moolenaar if i == -1: 500*bd5e15fdSBram Moolenaar pvar = p 501*bd5e15fdSBram Moolenaar ptype = '_PyCmplNoType()' 502*bd5e15fdSBram Moolenaar else: 503*bd5e15fdSBram Moolenaar pvar = p[:i] 504*bd5e15fdSBram Moolenaar ptype = _sanitize(p[i+1:]) 505*bd5e15fdSBram Moolenaar if pvar.startswith('**'): 506*bd5e15fdSBram Moolenaar pvar = pvar[2:] 507*bd5e15fdSBram Moolenaar ptype = '{}' 508*bd5e15fdSBram Moolenaar elif pvar.startswith('*'): 509*bd5e15fdSBram Moolenaar pvar = pvar[1:] 510*bd5e15fdSBram Moolenaar ptype = '[]' 511*bd5e15fdSBram Moolenaar 512*bd5e15fdSBram Moolenaar newscope.local('%s = %s' % (pvar,ptype)) 513*bd5e15fdSBram Moolenaar 514*bd5e15fdSBram Moolenaar for s in scp.subscopes: 515*bd5e15fdSBram Moolenaar ns = s.copy_decl(0) 516*bd5e15fdSBram Moolenaar newscope.add(ns) 517*bd5e15fdSBram Moolenaar for l in scp.locals: newscope.local(l) 518*bd5e15fdSBram Moolenaar scp = scp.parent 519*bd5e15fdSBram Moolenaar 520*bd5e15fdSBram Moolenaar self.currentscope = newscope 521*bd5e15fdSBram Moolenaar return self.currentscope 522*bd5e15fdSBram Moolenaar 523*bd5e15fdSBram Moolenaar #p.parse(vim.current.buffer[:],vim.eval("line('.')")) 524*bd5e15fdSBram Moolenaar def parse(self,text,curline=0): 525*bd5e15fdSBram Moolenaar self.curline = int(curline) 526*bd5e15fdSBram Moolenaar buf = io.StringIO(''.join(text) + '\n') 527*bd5e15fdSBram Moolenaar self.gen = tokenize.generate_tokens(buf.readline) 528*bd5e15fdSBram Moolenaar self.currentscope = self.scope 529*bd5e15fdSBram Moolenaar 530*bd5e15fdSBram Moolenaar try: 531*bd5e15fdSBram Moolenaar freshscope=True 532*bd5e15fdSBram Moolenaar while True: 533*bd5e15fdSBram Moolenaar tokentype, token, indent = self.donext() 534*bd5e15fdSBram Moolenaar #dbg( 'main: token=[%s] indent=[%s]' % (token,indent)) 535*bd5e15fdSBram Moolenaar 536*bd5e15fdSBram Moolenaar if tokentype == DEDENT or token == "pass": 537*bd5e15fdSBram Moolenaar self.scope = self.scope.pop(indent) 538*bd5e15fdSBram Moolenaar elif token == 'def': 539*bd5e15fdSBram Moolenaar func = self._parsefunction(indent) 540*bd5e15fdSBram Moolenaar if func is None: 541*bd5e15fdSBram Moolenaar print("function: syntax error...") 542*bd5e15fdSBram Moolenaar continue 543*bd5e15fdSBram Moolenaar dbg("new scope: function") 544*bd5e15fdSBram Moolenaar freshscope = True 545*bd5e15fdSBram Moolenaar self.scope = self.scope.add(func) 546*bd5e15fdSBram Moolenaar elif token == 'class': 547*bd5e15fdSBram Moolenaar cls = self._parseclass(indent) 548*bd5e15fdSBram Moolenaar if cls is None: 549*bd5e15fdSBram Moolenaar print("class: syntax error...") 550*bd5e15fdSBram Moolenaar continue 551*bd5e15fdSBram Moolenaar freshscope = True 552*bd5e15fdSBram Moolenaar dbg("new scope: class") 553*bd5e15fdSBram Moolenaar self.scope = self.scope.add(cls) 554*bd5e15fdSBram Moolenaar 555*bd5e15fdSBram Moolenaar elif token == 'import': 556*bd5e15fdSBram Moolenaar imports = self._parseimportlist() 557*bd5e15fdSBram Moolenaar for mod, alias in imports: 558*bd5e15fdSBram Moolenaar loc = "import %s" % mod 559*bd5e15fdSBram Moolenaar if len(alias) > 0: loc += " as %s" % alias 560*bd5e15fdSBram Moolenaar self.scope.local(loc) 561*bd5e15fdSBram Moolenaar freshscope = False 562*bd5e15fdSBram Moolenaar elif token == 'from': 563*bd5e15fdSBram Moolenaar mod, token = self._parsedotname() 564*bd5e15fdSBram Moolenaar if not mod or token != "import": 565*bd5e15fdSBram Moolenaar print("from: syntax error...") 566*bd5e15fdSBram Moolenaar continue 567*bd5e15fdSBram Moolenaar names = self._parseimportlist() 568*bd5e15fdSBram Moolenaar for name, alias in names: 569*bd5e15fdSBram Moolenaar loc = "from %s import %s" % (mod,name) 570*bd5e15fdSBram Moolenaar if len(alias) > 0: loc += " as %s" % alias 571*bd5e15fdSBram Moolenaar self.scope.local(loc) 572*bd5e15fdSBram Moolenaar freshscope = False 573*bd5e15fdSBram Moolenaar elif tokentype == STRING: 574*bd5e15fdSBram Moolenaar if freshscope: self.scope.doc(token) 575*bd5e15fdSBram Moolenaar elif tokentype == NAME: 576*bd5e15fdSBram Moolenaar name,token = self._parsedotname(token) 577*bd5e15fdSBram Moolenaar if token == '=': 578*bd5e15fdSBram Moolenaar stmt = self._parseassignment() 579*bd5e15fdSBram Moolenaar dbg("parseassignment: %s = %s" % (name, stmt)) 580*bd5e15fdSBram Moolenaar if stmt != None: 581*bd5e15fdSBram Moolenaar self.scope.local("%s = %s" % (name,stmt)) 582*bd5e15fdSBram Moolenaar freshscope = False 583*bd5e15fdSBram Moolenaar except StopIteration: #thrown on EOF 584*bd5e15fdSBram Moolenaar pass 585*bd5e15fdSBram Moolenaar except: 586*bd5e15fdSBram Moolenaar dbg("parse error: %s, %s @ %s" % 587*bd5e15fdSBram Moolenaar (sys.exc_info()[0], sys.exc_info()[1], self.parserline)) 588*bd5e15fdSBram Moolenaar return self._adjustvisibility() 589*bd5e15fdSBram Moolenaar 590*bd5e15fdSBram Moolenaardef _sanitize(str): 591*bd5e15fdSBram Moolenaar val = '' 592*bd5e15fdSBram Moolenaar level = 0 593*bd5e15fdSBram Moolenaar for c in str: 594*bd5e15fdSBram Moolenaar if c in ('(','{','['): 595*bd5e15fdSBram Moolenaar level += 1 596*bd5e15fdSBram Moolenaar elif c in (']','}',')'): 597*bd5e15fdSBram Moolenaar level -= 1 598*bd5e15fdSBram Moolenaar elif level == 0: 599*bd5e15fdSBram Moolenaar val += c 600*bd5e15fdSBram Moolenaar return val 601*bd5e15fdSBram Moolenaar 602*bd5e15fdSBram Moolenaarsys.path.extend(['.','..']) 603*bd5e15fdSBram MoolenaarPYTHONEOF 604*bd5e15fdSBram Moolenaarendfunction 605*bd5e15fdSBram Moolenaar 606*bd5e15fdSBram Moolenaarcall s:DefPython() 607