118144c84SBram Moolenaar"pythoncomplete.vim - Omni Completion for python 2*4f4d51a9SBram Moolenaar" Maintainer: <vacancy> 3*4f4d51a9SBram Moolenaar" Previous Maintainer: Aaron Griffin <[email protected]> 4b52073acSBram Moolenaar" Version: 0.9 5*4f4d51a9SBram Moolenaar" Last Updated: 2020 Oct 9 618144c84SBram Moolenaar" 79964e468SBram Moolenaar" Changes 8fc1421ebSBram Moolenaar" TODO: 9fc1421ebSBram Moolenaar" 'info' item output can use some formatting work 10fc1421ebSBram Moolenaar" Add an "unsafe eval" mode, to allow for return type evaluation 119964e468SBram Moolenaar" Complete basic syntax along with import statements 129964e468SBram Moolenaar" i.e. "import url<c-x,c-o>" 139964e468SBram Moolenaar" Continue parsing on invalid line?? 149964e468SBram Moolenaar" 15b52073acSBram Moolenaar" v 0.9 16b52073acSBram Moolenaar" * Fixed docstring parsing for classes and functions 17b52073acSBram Moolenaar" * Fixed parsing of *args and **kwargs type arguments 18b52073acSBram Moolenaar" * Better function param parsing to handle things like tuples and 19b52073acSBram Moolenaar" lambda defaults args 20b52073acSBram Moolenaar" 21b52073acSBram Moolenaar" v 0.8 22b52073acSBram Moolenaar" * Fixed an issue where the FIRST assignment was always used instead of 23b52073acSBram Moolenaar" using a subsequent assignment for a variable 24b52073acSBram Moolenaar" * Fixed a scoping issue when working inside a parameterless function 25b52073acSBram Moolenaar" 26b52073acSBram Moolenaar" 279964e468SBram Moolenaar" v 0.7 289964e468SBram Moolenaar" * Fixed function list sorting (_ and __ at the bottom) 299964e468SBram Moolenaar" * Removed newline removal from docs. It appears vim handles these better in 309964e468SBram Moolenaar" recent patches 319964e468SBram Moolenaar" 329964e468SBram Moolenaar" v 0.6: 339964e468SBram Moolenaar" * Fixed argument completion 349964e468SBram Moolenaar" * Removed the 'kind' completions, as they are better indicated 359964e468SBram Moolenaar" with real syntax 369964e468SBram Moolenaar" * Added tuple assignment parsing (whoops, that was forgotten) 379964e468SBram Moolenaar" * Fixed import handling when flattening scope 389964e468SBram Moolenaar" 399964e468SBram Moolenaar" v 0.5: 409964e468SBram Moolenaar" Yeah, I skipped a version number - 0.4 was never public. 419964e468SBram Moolenaar" It was a bugfix version on top of 0.3. This is a complete 429964e468SBram Moolenaar" rewrite. 439964e468SBram Moolenaar" 4418144c84SBram Moolenaar 4518144c84SBram Moolenaarif !has('python') 4618144c84SBram Moolenaar echo "Error: Required vim compiled with +python" 4718144c84SBram Moolenaar finish 4818144c84SBram Moolenaarendif 4918144c84SBram Moolenaar 5018144c84SBram Moolenaarfunction! pythoncomplete#Complete(findstart, base) 5118144c84SBram Moolenaar "findstart = 1 when we need to get the text length 52fc1421ebSBram Moolenaar if a:findstart == 1 5318144c84SBram Moolenaar let line = getline('.') 5418144c84SBram Moolenaar let idx = col('.') 5518144c84SBram Moolenaar while idx > 0 5618144c84SBram Moolenaar let idx -= 1 57fc1421ebSBram Moolenaar let c = line[idx] 5818144c84SBram Moolenaar if c =~ '\w' 5918144c84SBram Moolenaar continue 6018144c84SBram Moolenaar elseif ! c =~ '\.' 619964e468SBram Moolenaar let idx = -1 6218144c84SBram Moolenaar break 6318144c84SBram Moolenaar else 6418144c84SBram Moolenaar break 6518144c84SBram Moolenaar endif 6618144c84SBram Moolenaar endwhile 6718144c84SBram Moolenaar 6818144c84SBram Moolenaar return idx 6918144c84SBram Moolenaar "findstart = 0 when we need to return the list of completions 7018144c84SBram Moolenaar else 71fc1421ebSBram Moolenaar "vim no longer moves the cursor upon completion... fix that 72fc1421ebSBram Moolenaar let line = getline('.') 73fc1421ebSBram Moolenaar let idx = col('.') 74fc1421ebSBram Moolenaar let cword = '' 75fc1421ebSBram Moolenaar while idx > 0 76fc1421ebSBram Moolenaar let idx -= 1 77fc1421ebSBram Moolenaar let c = line[idx] 78b52073acSBram Moolenaar if c =~ '\w' || c =~ '\.' 79fc1421ebSBram Moolenaar let cword = c . cword 80fc1421ebSBram Moolenaar continue 81fc1421ebSBram Moolenaar elseif strlen(cword) > 0 || idx == 0 82fc1421ebSBram Moolenaar break 83fc1421ebSBram Moolenaar endif 84fc1421ebSBram Moolenaar endwhile 85*4f4d51a9SBram Moolenaar execute "python vimcomplete('" . escape(cword, "'") . "', '" . escape(a:base, "'") . "')" 8618144c84SBram Moolenaar return g:pythoncomplete_completions 8718144c84SBram Moolenaar endif 8818144c84SBram Moolenaarendfunction 8918144c84SBram Moolenaar 9018144c84SBram Moolenaarfunction! s:DefPython() 9118144c84SBram Moolenaarpython << PYTHONEOF 92fc1421ebSBram Moolenaarimport sys, tokenize, cStringIO, types 93fc1421ebSBram Moolenaarfrom token import NAME, DEDENT, NEWLINE, STRING 9418144c84SBram Moolenaar 95fc1421ebSBram Moolenaardebugstmts=[] 96fc1421ebSBram Moolenaardef dbg(s): debugstmts.append(s) 97fc1421ebSBram Moolenaardef showdbg(): 98fc1421ebSBram Moolenaar for d in debugstmts: print "DBG: %s " % d 9918144c84SBram Moolenaar 100fc1421ebSBram Moolenaardef vimcomplete(context,match): 101fc1421ebSBram Moolenaar global debugstmts 102fc1421ebSBram Moolenaar debugstmts = [] 10318144c84SBram Moolenaar try: 104fc1421ebSBram Moolenaar import vim 105fc1421ebSBram Moolenaar def complsort(x,y): 1069964e468SBram Moolenaar try: 1079964e468SBram Moolenaar xa = x['abbr'] 1089964e468SBram Moolenaar ya = y['abbr'] 1099964e468SBram Moolenaar if xa[0] == '_': 1109964e468SBram Moolenaar if xa[1] == '_' and ya[0:2] == '__': 1119964e468SBram Moolenaar return xa > ya 1129964e468SBram Moolenaar elif ya[0:2] == '__': 1139964e468SBram Moolenaar return -1 1149964e468SBram Moolenaar elif y[0] == '_': 1159964e468SBram Moolenaar return xa > ya 1169964e468SBram Moolenaar else: 1179964e468SBram Moolenaar return 1 1189964e468SBram Moolenaar elif ya[0] == '_': 1199964e468SBram Moolenaar return -1 1209964e468SBram Moolenaar else: 1219964e468SBram Moolenaar return xa > ya 1229964e468SBram Moolenaar except: 1239964e468SBram Moolenaar return 0 124fc1421ebSBram Moolenaar cmpl = Completer() 125fc1421ebSBram Moolenaar cmpl.evalsource('\n'.join(vim.current.buffer),vim.eval("line('.')")) 126fc1421ebSBram Moolenaar all = cmpl.get_completions(context,match) 127fc1421ebSBram Moolenaar all.sort(complsort) 128fc1421ebSBram Moolenaar dictstr = '[' 129fc1421ebSBram Moolenaar # have to do this for double quoting 130fc1421ebSBram Moolenaar for cmpl in all: 131fc1421ebSBram Moolenaar dictstr += '{' 132fc1421ebSBram Moolenaar for x in cmpl: dictstr += '"%s":"%s",' % (x,cmpl[x]) 133fc1421ebSBram Moolenaar dictstr += '"icase":0},' 134fc1421ebSBram Moolenaar if dictstr[-1] == ',': dictstr = dictstr[:-1] 135fc1421ebSBram Moolenaar dictstr += ']' 1369964e468SBram Moolenaar #dbg("dict: %s" % dictstr) 137fc1421ebSBram Moolenaar vim.command("silent let g:pythoncomplete_completions = %s" % dictstr) 138fc1421ebSBram Moolenaar #dbg("Completion dict:\n%s" % all) 139fc1421ebSBram Moolenaar except vim.error: 140fc1421ebSBram Moolenaar dbg("VIM Error: %s" % vim.error) 14118144c84SBram Moolenaar 142fc1421ebSBram Moolenaarclass Completer(object): 143fc1421ebSBram Moolenaar def __init__(self): 144fc1421ebSBram Moolenaar self.compldict = {} 145fc1421ebSBram Moolenaar self.parser = PyParser() 14618144c84SBram Moolenaar 147fc1421ebSBram Moolenaar def evalsource(self,text,line=0): 148fc1421ebSBram Moolenaar sc = self.parser.parse(text,line) 149fc1421ebSBram Moolenaar src = sc.get_code() 150fc1421ebSBram Moolenaar dbg("source: %s" % src) 151fc1421ebSBram Moolenaar try: exec(src) in self.compldict 152fc1421ebSBram Moolenaar except: dbg("parser: %s, %s" % (sys.exc_info()[0],sys.exc_info()[1])) 153fc1421ebSBram Moolenaar for l in sc.locals: 154fc1421ebSBram Moolenaar try: exec(l) in self.compldict 155fc1421ebSBram Moolenaar except: dbg("locals: %s, %s [%s]" % (sys.exc_info()[0],sys.exc_info()[1],l)) 15618144c84SBram Moolenaar 157fc1421ebSBram Moolenaar def _cleanstr(self,doc): 1589964e468SBram Moolenaar return doc.replace('"',' ').replace("'",' ') 15918144c84SBram Moolenaar 160fc1421ebSBram Moolenaar def get_arguments(self,func_obj): 16118144c84SBram Moolenaar def _ctor(obj): 162fc1421ebSBram Moolenaar try: return class_ob.__init__.im_func 16318144c84SBram Moolenaar except AttributeError: 16418144c84SBram Moolenaar for base in class_ob.__bases__: 16518144c84SBram Moolenaar rc = _find_constructor(base) 16618144c84SBram Moolenaar if rc is not None: return rc 16718144c84SBram Moolenaar return None 16818144c84SBram Moolenaar 16918144c84SBram Moolenaar arg_offset = 1 17018144c84SBram Moolenaar if type(func_obj) == types.ClassType: func_obj = _ctor(func_obj) 17118144c84SBram Moolenaar elif type(func_obj) == types.MethodType: func_obj = func_obj.im_func 17218144c84SBram Moolenaar else: arg_offset = 0 17318144c84SBram Moolenaar 1749964e468SBram Moolenaar arg_text='' 17518144c84SBram Moolenaar if type(func_obj) in [types.FunctionType, types.LambdaType]: 17618144c84SBram Moolenaar try: 17718144c84SBram Moolenaar cd = func_obj.func_code 17818144c84SBram Moolenaar real_args = cd.co_varnames[arg_offset:cd.co_argcount] 1799964e468SBram Moolenaar defaults = func_obj.func_defaults or '' 1809964e468SBram Moolenaar defaults = map(lambda name: "=%s" % name, defaults) 18118144c84SBram Moolenaar defaults = [""] * (len(real_args)-len(defaults)) + defaults 18218144c84SBram Moolenaar items = map(lambda a,d: a+d, real_args, defaults) 18318144c84SBram Moolenaar if func_obj.func_code.co_flags & 0x4: 18418144c84SBram Moolenaar items.append("...") 18518144c84SBram Moolenaar if func_obj.func_code.co_flags & 0x8: 18618144c84SBram Moolenaar items.append("***") 1879964e468SBram Moolenaar arg_text = (','.join(items)) + ')' 18818144c84SBram Moolenaar 18918144c84SBram Moolenaar except: 1909964e468SBram Moolenaar dbg("arg completion: %s: %s" % (sys.exc_info()[0],sys.exc_info()[1])) 19118144c84SBram Moolenaar pass 19218144c84SBram Moolenaar if len(arg_text) == 0: 19318144c84SBram Moolenaar # The doc string sometimes contains the function signature 19418144c84SBram Moolenaar # this works for a lot of C modules that are part of the 19518144c84SBram Moolenaar # standard library 196fc1421ebSBram Moolenaar doc = func_obj.__doc__ 19718144c84SBram Moolenaar if doc: 19818144c84SBram Moolenaar doc = doc.lstrip() 19918144c84SBram Moolenaar pos = doc.find('\n') 20018144c84SBram Moolenaar if pos > 0: 20118144c84SBram Moolenaar sigline = doc[:pos] 20218144c84SBram Moolenaar lidx = sigline.find('(') 20318144c84SBram Moolenaar ridx = sigline.find(')') 20418144c84SBram Moolenaar if lidx > 0 and ridx > 0: 20518144c84SBram Moolenaar arg_text = sigline[lidx+1:ridx] + ')' 2069964e468SBram Moolenaar if len(arg_text) == 0: arg_text = ')' 20718144c84SBram Moolenaar return arg_text 20818144c84SBram Moolenaar 209fc1421ebSBram Moolenaar def get_completions(self,context,match): 210fc1421ebSBram Moolenaar dbg("get_completions('%s','%s')" % (context,match)) 21118144c84SBram Moolenaar stmt = '' 212fc1421ebSBram Moolenaar if context: stmt += str(context) 213fc1421ebSBram Moolenaar if match: stmt += str(match) 214fc1421ebSBram Moolenaar try: 215fc1421ebSBram Moolenaar result = None 216fc1421ebSBram Moolenaar all = {} 217fc1421ebSBram Moolenaar ridx = stmt.rfind('.') 218fc1421ebSBram Moolenaar if len(stmt) > 0 and stmt[-1] == '(': 219fc1421ebSBram Moolenaar result = eval(_sanitize(stmt[:-1]), self.compldict) 220fc1421ebSBram Moolenaar doc = result.__doc__ 221b52073acSBram Moolenaar if doc is None: doc = '' 2229964e468SBram Moolenaar args = self.get_arguments(result) 2239964e468SBram Moolenaar return [{'word':self._cleanstr(args),'info':self._cleanstr(doc)}] 224fc1421ebSBram Moolenaar elif ridx == -1: 225fc1421ebSBram Moolenaar match = stmt 226fc1421ebSBram Moolenaar all = self.compldict 227fc1421ebSBram Moolenaar else: 228fc1421ebSBram Moolenaar match = stmt[ridx+1:] 229fc1421ebSBram Moolenaar stmt = _sanitize(stmt[:ridx]) 230fc1421ebSBram Moolenaar result = eval(stmt, self.compldict) 231fc1421ebSBram Moolenaar all = dir(result) 232fc1421ebSBram Moolenaar 233fc1421ebSBram Moolenaar dbg("completing: stmt:%s" % stmt) 234fc1421ebSBram Moolenaar completions = [] 235fc1421ebSBram Moolenaar 236fc1421ebSBram Moolenaar try: maindoc = result.__doc__ 237fc1421ebSBram Moolenaar except: maindoc = ' ' 238b52073acSBram Moolenaar if maindoc is None: maindoc = ' ' 239fc1421ebSBram Moolenaar for m in all: 240fc1421ebSBram Moolenaar if m == "_PyCmplNoType": continue #this is internal 241fc1421ebSBram Moolenaar try: 242fc1421ebSBram Moolenaar dbg('possible completion: %s' % m) 243fc1421ebSBram Moolenaar if m.find(match) == 0: 244b52073acSBram Moolenaar if result is None: inst = all[m] 245fc1421ebSBram Moolenaar else: inst = getattr(result,m) 246fc1421ebSBram Moolenaar try: doc = inst.__doc__ 247fc1421ebSBram Moolenaar except: doc = maindoc 248fc1421ebSBram Moolenaar typestr = str(inst) 249b52073acSBram Moolenaar if doc is None or doc == '': doc = maindoc 250fc1421ebSBram Moolenaar 251fc1421ebSBram Moolenaar wrd = m[len(match):] 2529964e468SBram Moolenaar c = {'word':wrd, 'abbr':m, 'info':self._cleanstr(doc)} 253fc1421ebSBram Moolenaar if "function" in typestr: 254fc1421ebSBram Moolenaar c['word'] += '(' 255fc1421ebSBram Moolenaar c['abbr'] += '(' + self._cleanstr(self.get_arguments(inst)) 256fc1421ebSBram Moolenaar elif "method" in typestr: 257fc1421ebSBram Moolenaar c['word'] += '(' 258fc1421ebSBram Moolenaar c['abbr'] += '(' + self._cleanstr(self.get_arguments(inst)) 259fc1421ebSBram Moolenaar elif "module" in typestr: 260fc1421ebSBram Moolenaar c['word'] += '.' 261fc1421ebSBram Moolenaar elif "class" in typestr: 262fc1421ebSBram Moolenaar c['word'] += '(' 263fc1421ebSBram Moolenaar c['abbr'] += '(' 264fc1421ebSBram Moolenaar completions.append(c) 265fc1421ebSBram Moolenaar except: 266fc1421ebSBram Moolenaar i = sys.exc_info() 267fc1421ebSBram Moolenaar dbg("inner completion: %s,%s [stmt='%s']" % (i[0],i[1],stmt)) 268fc1421ebSBram Moolenaar return completions 269fc1421ebSBram Moolenaar except: 270fc1421ebSBram Moolenaar i = sys.exc_info() 271fc1421ebSBram Moolenaar dbg("completion: %s,%s [stmt='%s']" % (i[0],i[1],stmt)) 272fc1421ebSBram Moolenaar return [] 273fc1421ebSBram Moolenaar 274fc1421ebSBram Moolenaarclass Scope(object): 275b52073acSBram Moolenaar def __init__(self,name,indent,docstr=''): 276fc1421ebSBram Moolenaar self.subscopes = [] 277b52073acSBram Moolenaar self.docstr = docstr 278fc1421ebSBram Moolenaar self.locals = [] 279fc1421ebSBram Moolenaar self.parent = None 280fc1421ebSBram Moolenaar self.name = name 281fc1421ebSBram Moolenaar self.indent = indent 282fc1421ebSBram Moolenaar 283fc1421ebSBram Moolenaar def add(self,sub): 284fc1421ebSBram Moolenaar #print 'push scope: [%s@%s]' % (sub.name,sub.indent) 285fc1421ebSBram Moolenaar sub.parent = self 286fc1421ebSBram Moolenaar self.subscopes.append(sub) 287fc1421ebSBram Moolenaar return sub 288fc1421ebSBram Moolenaar 289fc1421ebSBram Moolenaar def doc(self,str): 290fc1421ebSBram Moolenaar """ Clean up a docstring """ 291fc1421ebSBram Moolenaar d = str.replace('\n',' ') 292fc1421ebSBram Moolenaar d = d.replace('\t',' ') 293fc1421ebSBram Moolenaar while d.find(' ') > -1: d = d.replace(' ',' ') 294fc1421ebSBram Moolenaar while d[0] in '"\'\t ': d = d[1:] 295fc1421ebSBram Moolenaar while d[-1] in '"\'\t ': d = d[:-1] 296b52073acSBram Moolenaar dbg("Scope(%s)::docstr = %s" % (self,d)) 297fc1421ebSBram Moolenaar self.docstr = d 298fc1421ebSBram Moolenaar 299fc1421ebSBram Moolenaar def local(self,loc): 300b52073acSBram Moolenaar self._checkexisting(loc) 301fc1421ebSBram Moolenaar self.locals.append(loc) 302fc1421ebSBram Moolenaar 303fc1421ebSBram Moolenaar def copy_decl(self,indent=0): 304fc1421ebSBram Moolenaar """ Copy a scope's declaration only, at the specified indent level - not local variables """ 305b52073acSBram Moolenaar return Scope(self.name,indent,self.docstr) 306fc1421ebSBram Moolenaar 307b52073acSBram Moolenaar def _checkexisting(self,test): 308fc1421ebSBram Moolenaar "Convienance function... keep out duplicates" 309fc1421ebSBram Moolenaar if test.find('=') > -1: 310fc1421ebSBram Moolenaar var = test.split('=')[0].strip() 311fc1421ebSBram Moolenaar for l in self.locals: 312fc1421ebSBram Moolenaar if l.find('=') > -1 and var == l.split('=')[0].strip(): 313b52073acSBram Moolenaar self.locals.remove(l) 314fc1421ebSBram Moolenaar 315fc1421ebSBram Moolenaar def get_code(self): 316b52073acSBram Moolenaar str = "" 317b52073acSBram Moolenaar if len(self.docstr) > 0: str += '"""'+self.docstr+'"""\n' 3189964e468SBram Moolenaar for l in self.locals: 3199964e468SBram Moolenaar if l.startswith('import'): str += l+'\n' 320fc1421ebSBram Moolenaar str += 'class _PyCmplNoType:\n def __getattr__(self,name):\n return None\n' 321fc1421ebSBram Moolenaar for sub in self.subscopes: 322fc1421ebSBram Moolenaar str += sub.get_code() 3239964e468SBram Moolenaar for l in self.locals: 3249964e468SBram Moolenaar if not l.startswith('import'): str += l+'\n' 325fc1421ebSBram Moolenaar 326fc1421ebSBram Moolenaar return str 327fc1421ebSBram Moolenaar 328fc1421ebSBram Moolenaar def pop(self,indent): 329fc1421ebSBram Moolenaar #print 'pop scope: [%s] to [%s]' % (self.indent,indent) 330fc1421ebSBram Moolenaar outer = self 331fc1421ebSBram Moolenaar while outer.parent != None and outer.indent >= indent: 332fc1421ebSBram Moolenaar outer = outer.parent 333fc1421ebSBram Moolenaar return outer 334fc1421ebSBram Moolenaar 335fc1421ebSBram Moolenaar def currentindent(self): 336fc1421ebSBram Moolenaar #print 'parse current indent: %s' % self.indent 337fc1421ebSBram Moolenaar return ' '*self.indent 338fc1421ebSBram Moolenaar 339fc1421ebSBram Moolenaar def childindent(self): 340fc1421ebSBram Moolenaar #print 'parse child indent: [%s]' % (self.indent+1) 341fc1421ebSBram Moolenaar return ' '*(self.indent+1) 342fc1421ebSBram Moolenaar 343fc1421ebSBram Moolenaarclass Class(Scope): 344b52073acSBram Moolenaar def __init__(self, name, supers, indent, docstr=''): 345b52073acSBram Moolenaar Scope.__init__(self,name,indent, docstr) 346fc1421ebSBram Moolenaar self.supers = supers 347fc1421ebSBram Moolenaar def copy_decl(self,indent=0): 348b52073acSBram Moolenaar c = Class(self.name,self.supers,indent, self.docstr) 349fc1421ebSBram Moolenaar for s in self.subscopes: 350fc1421ebSBram Moolenaar c.add(s.copy_decl(indent+1)) 351fc1421ebSBram Moolenaar return c 352fc1421ebSBram Moolenaar def get_code(self): 353fc1421ebSBram Moolenaar str = '%sclass %s' % (self.currentindent(),self.name) 354fc1421ebSBram Moolenaar if len(self.supers) > 0: str += '(%s)' % ','.join(self.supers) 355fc1421ebSBram Moolenaar str += ':\n' 356fc1421ebSBram Moolenaar if len(self.docstr) > 0: str += self.childindent()+'"""'+self.docstr+'"""\n' 357fc1421ebSBram Moolenaar if len(self.subscopes) > 0: 358fc1421ebSBram Moolenaar for s in self.subscopes: str += s.get_code() 359fc1421ebSBram Moolenaar else: 360fc1421ebSBram Moolenaar str += '%spass\n' % self.childindent() 361fc1421ebSBram Moolenaar return str 362fc1421ebSBram Moolenaar 363fc1421ebSBram Moolenaar 364fc1421ebSBram Moolenaarclass Function(Scope): 365b52073acSBram Moolenaar def __init__(self, name, params, indent, docstr=''): 366b52073acSBram Moolenaar Scope.__init__(self,name,indent, docstr) 367fc1421ebSBram Moolenaar self.params = params 368fc1421ebSBram Moolenaar def copy_decl(self,indent=0): 369b52073acSBram Moolenaar return Function(self.name,self.params,indent, self.docstr) 370fc1421ebSBram Moolenaar def get_code(self): 371fc1421ebSBram Moolenaar str = "%sdef %s(%s):\n" % \ 372fc1421ebSBram Moolenaar (self.currentindent(),self.name,','.join(self.params)) 373fc1421ebSBram Moolenaar if len(self.docstr) > 0: str += self.childindent()+'"""'+self.docstr+'"""\n' 374fc1421ebSBram Moolenaar str += "%spass\n" % self.childindent() 375fc1421ebSBram Moolenaar return str 376fc1421ebSBram Moolenaar 377fc1421ebSBram Moolenaarclass PyParser: 378fc1421ebSBram Moolenaar def __init__(self): 379fc1421ebSBram Moolenaar self.top = Scope('global',0) 380fc1421ebSBram Moolenaar self.scope = self.top 381a0f849eeSBram Moolenaar self.parserline = 0 382fc1421ebSBram Moolenaar 383fc1421ebSBram Moolenaar def _parsedotname(self,pre=None): 384fc1421ebSBram Moolenaar #returns (dottedname, nexttoken) 385fc1421ebSBram Moolenaar name = [] 386b52073acSBram Moolenaar if pre is None: 387fc1421ebSBram Moolenaar tokentype, token, indent = self.next() 388fc1421ebSBram Moolenaar if tokentype != NAME and token != '*': 389fc1421ebSBram Moolenaar return ('', token) 390fc1421ebSBram Moolenaar else: token = pre 391fc1421ebSBram Moolenaar name.append(token) 392fc1421ebSBram Moolenaar while True: 393fc1421ebSBram Moolenaar tokentype, token, indent = self.next() 394fc1421ebSBram Moolenaar if token != '.': break 395fc1421ebSBram Moolenaar tokentype, token, indent = self.next() 396fc1421ebSBram Moolenaar if tokentype != NAME: break 397fc1421ebSBram Moolenaar name.append(token) 398fc1421ebSBram Moolenaar return (".".join(name), token) 399fc1421ebSBram Moolenaar 400fc1421ebSBram Moolenaar def _parseimportlist(self): 401fc1421ebSBram Moolenaar imports = [] 402fc1421ebSBram Moolenaar while True: 403fc1421ebSBram Moolenaar name, token = self._parsedotname() 404fc1421ebSBram Moolenaar if not name: break 405fc1421ebSBram Moolenaar name2 = '' 406fc1421ebSBram Moolenaar if token == 'as': name2, token = self._parsedotname() 407fc1421ebSBram Moolenaar imports.append((name, name2)) 408fc1421ebSBram Moolenaar while token != "," and "\n" not in token: 409fc1421ebSBram Moolenaar tokentype, token, indent = self.next() 410fc1421ebSBram Moolenaar if token != ",": break 411fc1421ebSBram Moolenaar return imports 412fc1421ebSBram Moolenaar 413fc1421ebSBram Moolenaar def _parenparse(self): 414fc1421ebSBram Moolenaar name = '' 415fc1421ebSBram Moolenaar names = [] 416fc1421ebSBram Moolenaar level = 1 417fc1421ebSBram Moolenaar while True: 418fc1421ebSBram Moolenaar tokentype, token, indent = self.next() 419fc1421ebSBram Moolenaar if token in (')', ',') and level == 1: 420b52073acSBram Moolenaar if '=' not in name: name = name.replace(' ', '') 421b52073acSBram Moolenaar names.append(name.strip()) 422fc1421ebSBram Moolenaar name = '' 423fc1421ebSBram Moolenaar if token == '(': 42418144c84SBram Moolenaar level += 1 425b52073acSBram Moolenaar name += "(" 426fc1421ebSBram Moolenaar elif token == ')': 427fc1421ebSBram Moolenaar level -= 1 428fc1421ebSBram Moolenaar if level == 0: break 429b52073acSBram Moolenaar else: name += ")" 430fc1421ebSBram Moolenaar elif token == ',' and level == 1: 431fc1421ebSBram Moolenaar pass 432fc1421ebSBram Moolenaar else: 433b52073acSBram Moolenaar name += "%s " % str(token) 434fc1421ebSBram Moolenaar return names 435fc1421ebSBram Moolenaar 436fc1421ebSBram Moolenaar def _parsefunction(self,indent): 437fc1421ebSBram Moolenaar self.scope=self.scope.pop(indent) 438fc1421ebSBram Moolenaar tokentype, fname, ind = self.next() 439fc1421ebSBram Moolenaar if tokentype != NAME: return None 440fc1421ebSBram Moolenaar 441fc1421ebSBram Moolenaar tokentype, open, ind = self.next() 442fc1421ebSBram Moolenaar if open != '(': return None 443fc1421ebSBram Moolenaar params=self._parenparse() 444fc1421ebSBram Moolenaar 445fc1421ebSBram Moolenaar tokentype, colon, ind = self.next() 446fc1421ebSBram Moolenaar if colon != ':': return None 447fc1421ebSBram Moolenaar 448fc1421ebSBram Moolenaar return Function(fname,params,indent) 449fc1421ebSBram Moolenaar 450fc1421ebSBram Moolenaar def _parseclass(self,indent): 451fc1421ebSBram Moolenaar self.scope=self.scope.pop(indent) 452fc1421ebSBram Moolenaar tokentype, cname, ind = self.next() 453fc1421ebSBram Moolenaar if tokentype != NAME: return None 454fc1421ebSBram Moolenaar 455fc1421ebSBram Moolenaar super = [] 456fc1421ebSBram Moolenaar tokentype, next, ind = self.next() 457fc1421ebSBram Moolenaar if next == '(': 458fc1421ebSBram Moolenaar super=self._parenparse() 459fc1421ebSBram Moolenaar elif next != ':': return None 460fc1421ebSBram Moolenaar 461fc1421ebSBram Moolenaar return Class(cname,super,indent) 462fc1421ebSBram Moolenaar 463fc1421ebSBram Moolenaar def _parseassignment(self): 464fc1421ebSBram Moolenaar assign='' 465fc1421ebSBram Moolenaar tokentype, token, indent = self.next() 466fc1421ebSBram Moolenaar if tokentype == tokenize.STRING or token == 'str': 467fc1421ebSBram Moolenaar return '""' 4689964e468SBram Moolenaar elif token == '(' or token == 'tuple': 4699964e468SBram Moolenaar return '()' 470fc1421ebSBram Moolenaar elif token == '[' or token == 'list': 471fc1421ebSBram Moolenaar return '[]' 472fc1421ebSBram Moolenaar elif token == '{' or token == 'dict': 473fc1421ebSBram Moolenaar return '{}' 474fc1421ebSBram Moolenaar elif tokentype == tokenize.NUMBER: 475fc1421ebSBram Moolenaar return '0' 476fc1421ebSBram Moolenaar elif token == 'open' or token == 'file': 477fc1421ebSBram Moolenaar return 'file' 478fc1421ebSBram Moolenaar elif token == 'None': 479fc1421ebSBram Moolenaar return '_PyCmplNoType()' 480fc1421ebSBram Moolenaar elif token == 'type': 481fc1421ebSBram Moolenaar return 'type(_PyCmplNoType)' #only for method resolution 482fc1421ebSBram Moolenaar else: 483fc1421ebSBram Moolenaar assign += token 484fc1421ebSBram Moolenaar level = 0 485fc1421ebSBram Moolenaar while True: 486fc1421ebSBram Moolenaar tokentype, token, indent = self.next() 487fc1421ebSBram Moolenaar if token in ('(','{','['): 488fc1421ebSBram Moolenaar level += 1 489fc1421ebSBram Moolenaar elif token in (']','}',')'): 490fc1421ebSBram Moolenaar level -= 1 491fc1421ebSBram Moolenaar if level == 0: break 492fc1421ebSBram Moolenaar elif level == 0: 493fc1421ebSBram Moolenaar if token in (';','\n'): break 494fc1421ebSBram Moolenaar assign += token 495fc1421ebSBram Moolenaar return "%s" % assign 496fc1421ebSBram Moolenaar 497fc1421ebSBram Moolenaar def next(self): 498fc1421ebSBram Moolenaar type, token, (lineno, indent), end, self.parserline = self.gen.next() 499fc1421ebSBram Moolenaar if lineno == self.curline: 500fc1421ebSBram Moolenaar #print 'line found [%s] scope=%s' % (line.replace('\n',''),self.scope.name) 501fc1421ebSBram Moolenaar self.currentscope = self.scope 502fc1421ebSBram Moolenaar return (type, token, indent) 503fc1421ebSBram Moolenaar 504fc1421ebSBram Moolenaar def _adjustvisibility(self): 505fc1421ebSBram Moolenaar newscope = Scope('result',0) 506fc1421ebSBram Moolenaar scp = self.currentscope 507fc1421ebSBram Moolenaar while scp != None: 508fc1421ebSBram Moolenaar if type(scp) == Function: 509fc1421ebSBram Moolenaar slice = 0 510fc1421ebSBram Moolenaar #Handle 'self' params 511fc1421ebSBram Moolenaar if scp.parent != None and type(scp.parent) == Class: 512fc1421ebSBram Moolenaar slice = 1 513fc1421ebSBram Moolenaar newscope.local('%s = %s' % (scp.params[0],scp.parent.name)) 514fc1421ebSBram Moolenaar for p in scp.params[slice:]: 515fc1421ebSBram Moolenaar i = p.find('=') 516b52073acSBram Moolenaar if len(p) == 0: continue 517b52073acSBram Moolenaar pvar = '' 518b52073acSBram Moolenaar ptype = '' 519fc1421ebSBram Moolenaar if i == -1: 520b52073acSBram Moolenaar pvar = p 521b52073acSBram Moolenaar ptype = '_PyCmplNoType()' 522fc1421ebSBram Moolenaar else: 523b52073acSBram Moolenaar pvar = p[:i] 524b52073acSBram Moolenaar ptype = _sanitize(p[i+1:]) 525b52073acSBram Moolenaar if pvar.startswith('**'): 526b52073acSBram Moolenaar pvar = pvar[2:] 527b52073acSBram Moolenaar ptype = '{}' 528b52073acSBram Moolenaar elif pvar.startswith('*'): 529b52073acSBram Moolenaar pvar = pvar[1:] 530b52073acSBram Moolenaar ptype = '[]' 531b52073acSBram Moolenaar 532b52073acSBram Moolenaar newscope.local('%s = %s' % (pvar,ptype)) 533fc1421ebSBram Moolenaar 534fc1421ebSBram Moolenaar for s in scp.subscopes: 535fc1421ebSBram Moolenaar ns = s.copy_decl(0) 536fc1421ebSBram Moolenaar newscope.add(ns) 537fc1421ebSBram Moolenaar for l in scp.locals: newscope.local(l) 538fc1421ebSBram Moolenaar scp = scp.parent 539fc1421ebSBram Moolenaar 540fc1421ebSBram Moolenaar self.currentscope = newscope 541fc1421ebSBram Moolenaar return self.currentscope 542fc1421ebSBram Moolenaar 543fc1421ebSBram Moolenaar #p.parse(vim.current.buffer[:],vim.eval("line('.')")) 544fc1421ebSBram Moolenaar def parse(self,text,curline=0): 545fc1421ebSBram Moolenaar self.curline = int(curline) 546fc1421ebSBram Moolenaar buf = cStringIO.StringIO(''.join(text) + '\n') 547fc1421ebSBram Moolenaar self.gen = tokenize.generate_tokens(buf.readline) 548fc1421ebSBram Moolenaar self.currentscope = self.scope 549fc1421ebSBram Moolenaar 550fc1421ebSBram Moolenaar try: 551fc1421ebSBram Moolenaar freshscope=True 552fc1421ebSBram Moolenaar while True: 553fc1421ebSBram Moolenaar tokentype, token, indent = self.next() 5549964e468SBram Moolenaar #dbg( 'main: token=[%s] indent=[%s]' % (token,indent)) 555fc1421ebSBram Moolenaar 5569964e468SBram Moolenaar if tokentype == DEDENT or token == "pass": 557fc1421ebSBram Moolenaar self.scope = self.scope.pop(indent) 558fc1421ebSBram Moolenaar elif token == 'def': 559fc1421ebSBram Moolenaar func = self._parsefunction(indent) 560b52073acSBram Moolenaar if func is None: 561fc1421ebSBram Moolenaar print "function: syntax error..." 562fc1421ebSBram Moolenaar continue 563b52073acSBram Moolenaar dbg("new scope: function") 564fc1421ebSBram Moolenaar freshscope = True 565fc1421ebSBram Moolenaar self.scope = self.scope.add(func) 566fc1421ebSBram Moolenaar elif token == 'class': 567fc1421ebSBram Moolenaar cls = self._parseclass(indent) 568b52073acSBram Moolenaar if cls is None: 569fc1421ebSBram Moolenaar print "class: syntax error..." 570fc1421ebSBram Moolenaar continue 571fc1421ebSBram Moolenaar freshscope = True 572b52073acSBram Moolenaar dbg("new scope: class") 573fc1421ebSBram Moolenaar self.scope = self.scope.add(cls) 574fc1421ebSBram Moolenaar 575fc1421ebSBram Moolenaar elif token == 'import': 576fc1421ebSBram Moolenaar imports = self._parseimportlist() 577fc1421ebSBram Moolenaar for mod, alias in imports: 578fc1421ebSBram Moolenaar loc = "import %s" % mod 579fc1421ebSBram Moolenaar if len(alias) > 0: loc += " as %s" % alias 580fc1421ebSBram Moolenaar self.scope.local(loc) 581fc1421ebSBram Moolenaar freshscope = False 582fc1421ebSBram Moolenaar elif token == 'from': 583fc1421ebSBram Moolenaar mod, token = self._parsedotname() 584fc1421ebSBram Moolenaar if not mod or token != "import": 585fc1421ebSBram Moolenaar print "from: syntax error..." 586fc1421ebSBram Moolenaar continue 587fc1421ebSBram Moolenaar names = self._parseimportlist() 588fc1421ebSBram Moolenaar for name, alias in names: 589fc1421ebSBram Moolenaar loc = "from %s import %s" % (mod,name) 590fc1421ebSBram Moolenaar if len(alias) > 0: loc += " as %s" % alias 591fc1421ebSBram Moolenaar self.scope.local(loc) 592fc1421ebSBram Moolenaar freshscope = False 593fc1421ebSBram Moolenaar elif tokentype == STRING: 594fc1421ebSBram Moolenaar if freshscope: self.scope.doc(token) 595fc1421ebSBram Moolenaar elif tokentype == NAME: 596fc1421ebSBram Moolenaar name,token = self._parsedotname(token) 597fc1421ebSBram Moolenaar if token == '=': 598fc1421ebSBram Moolenaar stmt = self._parseassignment() 599b52073acSBram Moolenaar dbg("parseassignment: %s = %s" % (name, stmt)) 600fc1421ebSBram Moolenaar if stmt != None: 601fc1421ebSBram Moolenaar self.scope.local("%s = %s" % (name,stmt)) 602fc1421ebSBram Moolenaar freshscope = False 603fc1421ebSBram Moolenaar except StopIteration: #thrown on EOF 604fc1421ebSBram Moolenaar pass 605fc1421ebSBram Moolenaar except: 606fc1421ebSBram Moolenaar dbg("parse error: %s, %s @ %s" % 607fc1421ebSBram Moolenaar (sys.exc_info()[0], sys.exc_info()[1], self.parserline)) 608fc1421ebSBram Moolenaar return self._adjustvisibility() 609fc1421ebSBram Moolenaar 610fc1421ebSBram Moolenaardef _sanitize(str): 611fc1421ebSBram Moolenaar val = '' 612fc1421ebSBram Moolenaar level = 0 613fc1421ebSBram Moolenaar for c in str: 614fc1421ebSBram Moolenaar if c in ('(','{','['): 615fc1421ebSBram Moolenaar level += 1 616fc1421ebSBram Moolenaar elif c in (']','}',')'): 61718144c84SBram Moolenaar level -= 1 61818144c84SBram Moolenaar elif level == 0: 619fc1421ebSBram Moolenaar val += c 620fc1421ebSBram Moolenaar return val 62118144c84SBram Moolenaar 62218144c84SBram Moolenaarsys.path.extend(['.','..']) 62318144c84SBram MoolenaarPYTHONEOF 62418144c84SBram Moolenaarendfunction 62518144c84SBram Moolenaar 62618144c84SBram Moolenaarcall s:DefPython() 62718144c84SBram Moolenaar" vim: set et ts=4: 628