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