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