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