118144c84SBram Moolenaar"pythoncomplete.vim - Omni Completion for python
2*fc1421ebSBram Moolenaar" Maintainer: Aaron Griffin <[email protected]>
3*fc1421ebSBram Moolenaar" Version: 0.5
4*fc1421ebSBram Moolenaar" Last Updated: 19 April 2006
518144c84SBram Moolenaar"
6*fc1421ebSBram Moolenaar" Yeah, I skipped a version number - 0.4 was never public.
7*fc1421ebSBram Moolenaar"  It was a bugfix version on top of 0.3.  This is a complete
8*fc1421ebSBram Moolenaar"  rewrite.
918144c84SBram Moolenaar"
10*fc1421ebSBram Moolenaar" TODO:
11*fc1421ebSBram Moolenaar" User defined docstrings aren't handled right...
12*fc1421ebSBram Moolenaar" 'info' item output can use some formatting work
13*fc1421ebSBram Moolenaar" Add an "unsafe eval" mode, to allow for return type evaluation
1418144c84SBram Moolenaar
1518144c84SBram Moolenaarif !has('python')
1618144c84SBram Moolenaar    echo "Error: Required vim compiled with +python"
1718144c84SBram Moolenaar    finish
1818144c84SBram Moolenaarendif
1918144c84SBram Moolenaar
2018144c84SBram Moolenaarfunction! pythoncomplete#Complete(findstart, base)
2118144c84SBram Moolenaar    "findstart = 1 when we need to get the text length
22*fc1421ebSBram Moolenaar    if a:findstart == 1
2318144c84SBram Moolenaar        let line = getline('.')
2418144c84SBram Moolenaar        let idx = col('.')
2518144c84SBram Moolenaar        while idx > 0
2618144c84SBram Moolenaar            let idx -= 1
27*fc1421ebSBram Moolenaar            let c = line[idx]
2818144c84SBram Moolenaar            if c =~ '\w'
2918144c84SBram Moolenaar                continue
3018144c84SBram Moolenaar            elseif ! c =~ '\.'
3118144c84SBram Moolenaar                idx = -1
3218144c84SBram Moolenaar                break
3318144c84SBram Moolenaar            else
3418144c84SBram Moolenaar                break
3518144c84SBram Moolenaar            endif
3618144c84SBram Moolenaar        endwhile
3718144c84SBram Moolenaar
3818144c84SBram Moolenaar        return idx
3918144c84SBram Moolenaar    "findstart = 0 when we need to return the list of completions
4018144c84SBram Moolenaar    else
41*fc1421ebSBram Moolenaar        "vim no longer moves the cursor upon completion... fix that
42*fc1421ebSBram Moolenaar        let line = getline('.')
43*fc1421ebSBram Moolenaar        let idx = col('.')
44*fc1421ebSBram Moolenaar        let cword = ''
45*fc1421ebSBram Moolenaar        while idx > 0
46*fc1421ebSBram Moolenaar            let idx -= 1
47*fc1421ebSBram Moolenaar            let c = line[idx]
48*fc1421ebSBram Moolenaar            if c =~ '\w' || c =~ '\.'
49*fc1421ebSBram Moolenaar                let cword = c . cword
50*fc1421ebSBram Moolenaar                continue
51*fc1421ebSBram Moolenaar            elseif strlen(cword) > 0 || idx == 0
52*fc1421ebSBram Moolenaar                break
53*fc1421ebSBram Moolenaar            endif
54*fc1421ebSBram Moolenaar        endwhile
55*fc1421ebSBram Moolenaar        execute "python vimcomplete('" . cword . "', '" . a:base . "')"
5618144c84SBram Moolenaar        return g:pythoncomplete_completions
5718144c84SBram Moolenaar    endif
5818144c84SBram Moolenaarendfunction
5918144c84SBram Moolenaar
6018144c84SBram Moolenaarfunction! s:DefPython()
6118144c84SBram Moolenaarpython << PYTHONEOF
62*fc1421ebSBram Moolenaarimport sys, tokenize, cStringIO, types
63*fc1421ebSBram Moolenaarfrom token import NAME, DEDENT, NEWLINE, STRING
6418144c84SBram Moolenaar
65*fc1421ebSBram Moolenaardebugstmts=[]
66*fc1421ebSBram Moolenaardef dbg(s): debugstmts.append(s)
67*fc1421ebSBram Moolenaardef showdbg():
68*fc1421ebSBram Moolenaar    for d in debugstmts: print "DBG: %s " % d
6918144c84SBram Moolenaar
70*fc1421ebSBram Moolenaardef vimcomplete(context,match):
71*fc1421ebSBram Moolenaar    global debugstmts
72*fc1421ebSBram Moolenaar    debugstmts = []
7318144c84SBram Moolenaar    try:
74*fc1421ebSBram Moolenaar        import vim
75*fc1421ebSBram Moolenaar        def complsort(x,y):
76*fc1421ebSBram Moolenaar            return x['abbr'] > y['abbr']
77*fc1421ebSBram Moolenaar        cmpl = Completer()
78*fc1421ebSBram Moolenaar        cmpl.evalsource('\n'.join(vim.current.buffer),vim.eval("line('.')"))
79*fc1421ebSBram Moolenaar        all = cmpl.get_completions(context,match)
80*fc1421ebSBram Moolenaar        all.sort(complsort)
81*fc1421ebSBram Moolenaar        dictstr = '['
82*fc1421ebSBram Moolenaar        # have to do this for double quoting
83*fc1421ebSBram Moolenaar        for cmpl in all:
84*fc1421ebSBram Moolenaar            dictstr += '{'
85*fc1421ebSBram Moolenaar            for x in cmpl: dictstr += '"%s":"%s",' % (x,cmpl[x])
86*fc1421ebSBram Moolenaar            dictstr += '"icase":0},'
87*fc1421ebSBram Moolenaar        if dictstr[-1] == ',': dictstr = dictstr[:-1]
88*fc1421ebSBram Moolenaar        dictstr += ']'
89*fc1421ebSBram Moolenaar        dbg("dict: %s" % dictstr)
90*fc1421ebSBram Moolenaar        vim.command("silent let g:pythoncomplete_completions = %s" % dictstr)
91*fc1421ebSBram Moolenaar        #dbg("Completion dict:\n%s" % all)
92*fc1421ebSBram Moolenaar    except vim.error:
93*fc1421ebSBram Moolenaar        dbg("VIM Error: %s" % vim.error)
9418144c84SBram Moolenaar
95*fc1421ebSBram Moolenaarclass Completer(object):
96*fc1421ebSBram Moolenaar    def __init__(self):
97*fc1421ebSBram Moolenaar       self.compldict = {}
98*fc1421ebSBram Moolenaar       self.parser = PyParser()
9918144c84SBram Moolenaar
100*fc1421ebSBram Moolenaar    def evalsource(self,text,line=0):
101*fc1421ebSBram Moolenaar        sc = self.parser.parse(text,line)
102*fc1421ebSBram Moolenaar        src = sc.get_code()
103*fc1421ebSBram Moolenaar        dbg("source: %s" % src)
104*fc1421ebSBram Moolenaar        try: exec(src) in self.compldict
105*fc1421ebSBram Moolenaar        except: dbg("parser: %s, %s" % (sys.exc_info()[0],sys.exc_info()[1]))
106*fc1421ebSBram Moolenaar        for l in sc.locals:
107*fc1421ebSBram Moolenaar            try: exec(l) in self.compldict
108*fc1421ebSBram Moolenaar            except: dbg("locals: %s, %s [%s]" % (sys.exc_info()[0],sys.exc_info()[1],l))
10918144c84SBram Moolenaar
110*fc1421ebSBram Moolenaar    def _cleanstr(self,doc):
111*fc1421ebSBram Moolenaar        return doc.replace('"',' ')\
112*fc1421ebSBram Moolenaar                  .replace("'",' ')\
113*fc1421ebSBram Moolenaar                  .replace('\n',' ')\
114*fc1421ebSBram Moolenaar                  .replace('\r',' ')\
115*fc1421ebSBram Moolenaar                  .replace('
11618144c84SBram Moolenaar',' ')
117*fc1421ebSBram Moolenaar
11818144c84SBram Moolenaar    def get_arguments(self,func_obj):
119*fc1421ebSBram Moolenaar        def _ctor(obj):
12018144c84SBram Moolenaar            try: return class_ob.__init__.im_func
12118144c84SBram Moolenaar            except AttributeError:
12218144c84SBram Moolenaar                for base in class_ob.__bases__:
12318144c84SBram Moolenaar                    rc = _find_constructor(base)
12418144c84SBram Moolenaar                    if rc is not None: return rc
12518144c84SBram Moolenaar            return None
12618144c84SBram Moolenaar
12718144c84SBram Moolenaar        arg_offset = 1
12818144c84SBram Moolenaar        if type(func_obj) == types.ClassType: func_obj = _ctor(func_obj)
12918144c84SBram Moolenaar        elif type(func_obj) == types.MethodType: func_obj = func_obj.im_func
13018144c84SBram Moolenaar        else: arg_offset = 0
131*fc1421ebSBram Moolenaar
13218144c84SBram Moolenaar        arg_text = ')'
13318144c84SBram Moolenaar        if type(func_obj) in [types.FunctionType, types.LambdaType]:
13418144c84SBram Moolenaar            try:
13518144c84SBram Moolenaar                cd = func_obj.func_code
13618144c84SBram Moolenaar                real_args = cd.co_varnames[arg_offset:cd.co_argcount]
137*fc1421ebSBram Moolenaar                defaults = func_obj.func_defaults or []
13818144c84SBram Moolenaar                defaults = [map(lambda name: "=%s" % name, defaults)]
13918144c84SBram Moolenaar                defaults = [""] * (len(real_args)-len(defaults)) + defaults
14018144c84SBram Moolenaar                items = map(lambda a,d: a+d, real_args, defaults)
14118144c84SBram Moolenaar                if func_obj.func_code.co_flags & 0x4:
14218144c84SBram Moolenaar                    items.append("...")
14318144c84SBram Moolenaar                if func_obj.func_code.co_flags & 0x8:
14418144c84SBram Moolenaar                    items.append("***")
14518144c84SBram Moolenaar                arg_text = ", ".join(items) + ')'
14618144c84SBram Moolenaar
147*fc1421ebSBram Moolenaar            except:
14818144c84SBram Moolenaar                dbg("completion: %s: %s" % (sys.exc_info()[0],sys.exc_info()[1]))
14918144c84SBram Moolenaar                pass
15018144c84SBram Moolenaar        if len(arg_text) == 0:
15118144c84SBram Moolenaar            # The doc string sometimes contains the function signature
15218144c84SBram Moolenaar            #  this works for alot of C modules that are part of the
153*fc1421ebSBram Moolenaar            #  standard library
15418144c84SBram Moolenaar            doc = func_obj.__doc__
15518144c84SBram Moolenaar            if doc:
15618144c84SBram Moolenaar                doc = doc.lstrip()
15718144c84SBram Moolenaar                pos = doc.find('\n')
15818144c84SBram Moolenaar                if pos > 0:
15918144c84SBram Moolenaar                    sigline = doc[:pos]
16018144c84SBram Moolenaar                    lidx = sigline.find('(')
16118144c84SBram Moolenaar                    ridx = sigline.find(')')
16218144c84SBram Moolenaar                    if lidx > 0 and ridx > 0:
16318144c84SBram Moolenaar                        arg_text = sigline[lidx+1:ridx] + ')'
16418144c84SBram Moolenaar        return arg_text
165*fc1421ebSBram Moolenaar
166*fc1421ebSBram Moolenaar    def get_completions(self,context,match):
16718144c84SBram Moolenaar        dbg("get_completions('%s','%s')" % (context,match))
168*fc1421ebSBram Moolenaar        stmt = ''
169*fc1421ebSBram Moolenaar        if context: stmt += str(context)
170*fc1421ebSBram Moolenaar        if match: stmt += str(match)
171*fc1421ebSBram Moolenaar        try:
172*fc1421ebSBram Moolenaar            result = None
173*fc1421ebSBram Moolenaar            all = {}
174*fc1421ebSBram Moolenaar            ridx = stmt.rfind('.')
175*fc1421ebSBram Moolenaar            if len(stmt) > 0 and stmt[-1] == '(':
176*fc1421ebSBram Moolenaar                #TODO
177*fc1421ebSBram Moolenaar                result = eval(_sanitize(stmt[:-1]), self.compldict)
178*fc1421ebSBram Moolenaar                doc = result.__doc__
179*fc1421ebSBram Moolenaar                if doc == None: doc = ''
180*fc1421ebSBram Moolenaar                args = self.get_arguments(res)
181*fc1421ebSBram Moolenaar                return [{'word':self._cleanstr(args),'info':self._cleanstr(doc),'kind':'p'}]
182*fc1421ebSBram Moolenaar            elif ridx == -1:
183*fc1421ebSBram Moolenaar                match = stmt
184*fc1421ebSBram Moolenaar                all = self.compldict
185*fc1421ebSBram Moolenaar            else:
186*fc1421ebSBram Moolenaar                match = stmt[ridx+1:]
187*fc1421ebSBram Moolenaar                stmt = _sanitize(stmt[:ridx])
188*fc1421ebSBram Moolenaar                result = eval(stmt, self.compldict)
189*fc1421ebSBram Moolenaar                all = dir(result)
190*fc1421ebSBram Moolenaar
191*fc1421ebSBram Moolenaar            dbg("completing: stmt:%s" % stmt)
192*fc1421ebSBram Moolenaar            completions = []
193*fc1421ebSBram Moolenaar
194*fc1421ebSBram Moolenaar            try: maindoc = result.__doc__
195*fc1421ebSBram Moolenaar            except: maindoc = ' '
196*fc1421ebSBram Moolenaar            if maindoc == None: maindoc = ' '
197*fc1421ebSBram Moolenaar            for m in all:
198*fc1421ebSBram Moolenaar                if m == "_PyCmplNoType": continue #this is internal
199*fc1421ebSBram Moolenaar                try:
200*fc1421ebSBram Moolenaar                    dbg('possible completion: %s' % m)
201*fc1421ebSBram Moolenaar                    if m.find(match) == 0:
202*fc1421ebSBram Moolenaar                        if result == None: inst = all[m]
203*fc1421ebSBram Moolenaar                        else: inst = getattr(result,m)
204*fc1421ebSBram Moolenaar                        try: doc = inst.__doc__
205*fc1421ebSBram Moolenaar                        except: doc = maindoc
206*fc1421ebSBram Moolenaar                        typestr = str(inst)
207*fc1421ebSBram Moolenaar                        if doc == None or doc == '': doc = maindoc
208*fc1421ebSBram Moolenaar
209*fc1421ebSBram Moolenaar                        wrd = m[len(match):]
210*fc1421ebSBram Moolenaar                        c = {'word':wrd, 'abbr':m,  'info':self._cleanstr(doc),'kind':'m'}
211*fc1421ebSBram Moolenaar                        if "function" in typestr:
212*fc1421ebSBram Moolenaar                            c['word'] += '('
213*fc1421ebSBram Moolenaar                            c['abbr'] += '(' + self._cleanstr(self.get_arguments(inst))
214*fc1421ebSBram Moolenaar                            c['kind'] = 'f'
215*fc1421ebSBram Moolenaar                        elif "method" in typestr:
216*fc1421ebSBram Moolenaar                            c['word'] += '('
217*fc1421ebSBram Moolenaar                            c['abbr'] += '(' + self._cleanstr(self.get_arguments(inst))
218*fc1421ebSBram Moolenaar                            c['kind'] = 'f'
219*fc1421ebSBram Moolenaar                        elif "module" in typestr:
220*fc1421ebSBram Moolenaar                            c['word'] += '.'
221*fc1421ebSBram Moolenaar                            c['kind'] = 'm'
222*fc1421ebSBram Moolenaar                        elif "class" in typestr:
223*fc1421ebSBram Moolenaar                            c['word'] += '('
224*fc1421ebSBram Moolenaar                            c['abbr'] += '('
225*fc1421ebSBram Moolenaar                            c['kind']='c'
226*fc1421ebSBram Moolenaar                        completions.append(c)
227*fc1421ebSBram Moolenaar                except:
228*fc1421ebSBram Moolenaar                    i = sys.exc_info()
229*fc1421ebSBram Moolenaar                    dbg("inner completion: %s,%s [stmt='%s']" % (i[0],i[1],stmt))
230*fc1421ebSBram Moolenaar            return completions
231*fc1421ebSBram Moolenaar        except:
232*fc1421ebSBram Moolenaar            i = sys.exc_info()
233*fc1421ebSBram Moolenaar            dbg("completion: %s,%s [stmt='%s']" % (i[0],i[1],stmt))
234*fc1421ebSBram Moolenaar            return []
235*fc1421ebSBram Moolenaar
236*fc1421ebSBram Moolenaarclass Scope(object):
237*fc1421ebSBram Moolenaar    def __init__(self,name,indent):
238*fc1421ebSBram Moolenaar        self.subscopes = []
239*fc1421ebSBram Moolenaar        self.docstr = ''
240*fc1421ebSBram Moolenaar        self.locals = []
241*fc1421ebSBram Moolenaar        self.parent = None
242*fc1421ebSBram Moolenaar        self.name = name
243*fc1421ebSBram Moolenaar        self.indent = indent
244*fc1421ebSBram Moolenaar
245*fc1421ebSBram Moolenaar    def add(self,sub):
246*fc1421ebSBram Moolenaar        #print 'push scope: [%s@%s]' % (sub.name,sub.indent)
247*fc1421ebSBram Moolenaar        sub.parent = self
248*fc1421ebSBram Moolenaar        self.subscopes.append(sub)
249*fc1421ebSBram Moolenaar        return sub
250*fc1421ebSBram Moolenaar
251*fc1421ebSBram Moolenaar    def doc(self,str):
252*fc1421ebSBram Moolenaar        """ Clean up a docstring """
253*fc1421ebSBram Moolenaar        d = str.replace('\n',' ')
254*fc1421ebSBram Moolenaar        d = d.replace('\t',' ')
255*fc1421ebSBram Moolenaar        while d.find('  ') > -1: d = d.replace('  ',' ')
256*fc1421ebSBram Moolenaar        while d[0] in '"\'\t ': d = d[1:]
257*fc1421ebSBram Moolenaar        while d[-1] in '"\'\t ': d = d[:-1]
258*fc1421ebSBram Moolenaar        self.docstr = d
259*fc1421ebSBram Moolenaar
260*fc1421ebSBram Moolenaar    def local(self,loc):
261*fc1421ebSBram Moolenaar        if not self._hasvaralready(loc):
262*fc1421ebSBram Moolenaar            self.locals.append(loc)
263*fc1421ebSBram Moolenaar
264*fc1421ebSBram Moolenaar    def copy_decl(self,indent=0):
265*fc1421ebSBram Moolenaar        """ Copy a scope's declaration only, at the specified indent level - not local variables """
266*fc1421ebSBram Moolenaar        return Scope(self.name,indent)
267*fc1421ebSBram Moolenaar
268*fc1421ebSBram Moolenaar    def _hasvaralready(self,test):
269*fc1421ebSBram Moolenaar        "Convienance function... keep out duplicates"
270*fc1421ebSBram Moolenaar        if test.find('=') > -1:
271*fc1421ebSBram Moolenaar            var = test.split('=')[0].strip()
272*fc1421ebSBram Moolenaar            for l in self.locals:
273*fc1421ebSBram Moolenaar                if l.find('=') > -1 and var == l.split('=')[0].strip():
274*fc1421ebSBram Moolenaar                    return True
275*fc1421ebSBram Moolenaar        return False
276*fc1421ebSBram Moolenaar
277*fc1421ebSBram Moolenaar    def get_code(self):
278*fc1421ebSBram Moolenaar        # we need to start with this, to fix up broken completions
279*fc1421ebSBram Moolenaar        # hopefully this name is unique enough...
280*fc1421ebSBram Moolenaar        str = '"""'+self.docstr+'"""\n'
281*fc1421ebSBram Moolenaar        str += 'class _PyCmplNoType:\n    def __getattr__(self,name):\n        return None\n'
282*fc1421ebSBram Moolenaar        for sub in self.subscopes:
283*fc1421ebSBram Moolenaar            str += sub.get_code()
284*fc1421ebSBram Moolenaar        #str += '\n'.join(self.locals)+'\n'
285*fc1421ebSBram Moolenaar
286*fc1421ebSBram Moolenaar        return str
287*fc1421ebSBram Moolenaar
288*fc1421ebSBram Moolenaar    def pop(self,indent):
289*fc1421ebSBram Moolenaar        #print 'pop scope: [%s] to [%s]' % (self.indent,indent)
290*fc1421ebSBram Moolenaar        outer = self
291*fc1421ebSBram Moolenaar        while outer.parent != None and outer.indent >= indent:
292*fc1421ebSBram Moolenaar            outer = outer.parent
293*fc1421ebSBram Moolenaar        return outer
294*fc1421ebSBram Moolenaar
295*fc1421ebSBram Moolenaar    def currentindent(self):
296*fc1421ebSBram Moolenaar        #print 'parse current indent: %s' % self.indent
297*fc1421ebSBram Moolenaar        return '    '*self.indent
298*fc1421ebSBram Moolenaar
299*fc1421ebSBram Moolenaar    def childindent(self):
300*fc1421ebSBram Moolenaar        #print 'parse child indent: [%s]' % (self.indent+1)
301*fc1421ebSBram Moolenaar        return '    '*(self.indent+1)
302*fc1421ebSBram Moolenaar
303*fc1421ebSBram Moolenaarclass Class(Scope):
304*fc1421ebSBram Moolenaar    def __init__(self, name, supers, indent):
305*fc1421ebSBram Moolenaar        Scope.__init__(self,name,indent)
306*fc1421ebSBram Moolenaar        self.supers = supers
307*fc1421ebSBram Moolenaar    def copy_decl(self,indent=0):
308*fc1421ebSBram Moolenaar        c = Class(self.name,self.supers,indent)
309*fc1421ebSBram Moolenaar        for s in self.subscopes:
310*fc1421ebSBram Moolenaar            c.add(s.copy_decl(indent+1))
311*fc1421ebSBram Moolenaar        return c
312*fc1421ebSBram Moolenaar    def get_code(self):
313*fc1421ebSBram Moolenaar        str = '%sclass %s' % (self.currentindent(),self.name)
314*fc1421ebSBram Moolenaar        if len(self.supers) > 0: str += '(%s)' % ','.join(self.supers)
315*fc1421ebSBram Moolenaar        str += ':\n'
316*fc1421ebSBram Moolenaar        if len(self.docstr) > 0: str += self.childindent()+'"""'+self.docstr+'"""\n'
317*fc1421ebSBram Moolenaar        if len(self.subscopes) > 0:
318*fc1421ebSBram Moolenaar            for s in self.subscopes: str += s.get_code()
319*fc1421ebSBram Moolenaar        else:
320*fc1421ebSBram Moolenaar            str += '%spass\n' % self.childindent()
321*fc1421ebSBram Moolenaar        return str
322*fc1421ebSBram Moolenaar
323*fc1421ebSBram Moolenaar
324*fc1421ebSBram Moolenaarclass Function(Scope):
325*fc1421ebSBram Moolenaar    def __init__(self, name, params, indent):
326*fc1421ebSBram Moolenaar        Scope.__init__(self,name,indent)
327*fc1421ebSBram Moolenaar        self.params = params
328*fc1421ebSBram Moolenaar    def copy_decl(self,indent=0):
329*fc1421ebSBram Moolenaar        return Function(self.name,self.params,indent)
330*fc1421ebSBram Moolenaar    def get_code(self):
331*fc1421ebSBram Moolenaar        str = "%sdef %s(%s):\n" % \
332*fc1421ebSBram Moolenaar            (self.currentindent(),self.name,','.join(self.params))
333*fc1421ebSBram Moolenaar        if len(self.docstr) > 0: str += self.childindent()+'"""'+self.docstr+'"""\n'
334*fc1421ebSBram Moolenaar        str += "%spass\n" % self.childindent()
335*fc1421ebSBram Moolenaar        return str
336*fc1421ebSBram Moolenaar
337*fc1421ebSBram Moolenaarclass PyParser:
338*fc1421ebSBram Moolenaar    def __init__(self):
339*fc1421ebSBram Moolenaar        self.top = Scope('global',0)
340*fc1421ebSBram Moolenaar        self.scope = self.top
341*fc1421ebSBram Moolenaar
342*fc1421ebSBram Moolenaar    def _parsedotname(self,pre=None):
343*fc1421ebSBram Moolenaar        #returns (dottedname, nexttoken)
344*fc1421ebSBram Moolenaar        name = []
345*fc1421ebSBram Moolenaar        if pre == None:
346*fc1421ebSBram Moolenaar            tokentype, token, indent = self.next()
347*fc1421ebSBram Moolenaar            if tokentype != NAME and token != '*':
348*fc1421ebSBram Moolenaar                return ('', token)
349*fc1421ebSBram Moolenaar        else: token = pre
350*fc1421ebSBram Moolenaar        name.append(token)
351*fc1421ebSBram Moolenaar        while True:
352*fc1421ebSBram Moolenaar            tokentype, token, indent = self.next()
353*fc1421ebSBram Moolenaar            if token != '.': break
354*fc1421ebSBram Moolenaar            tokentype, token, indent = self.next()
355*fc1421ebSBram Moolenaar            if tokentype != NAME: break
356*fc1421ebSBram Moolenaar            name.append(token)
357*fc1421ebSBram Moolenaar        return (".".join(name), token)
358*fc1421ebSBram Moolenaar
359*fc1421ebSBram Moolenaar    def _parseimportlist(self):
360*fc1421ebSBram Moolenaar        imports = []
361*fc1421ebSBram Moolenaar        while True:
362*fc1421ebSBram Moolenaar            name, token = self._parsedotname()
363*fc1421ebSBram Moolenaar            if not name: break
364*fc1421ebSBram Moolenaar            name2 = ''
365*fc1421ebSBram Moolenaar            if token == 'as': name2, token = self._parsedotname()
366*fc1421ebSBram Moolenaar            imports.append((name, name2))
367*fc1421ebSBram Moolenaar            while token != "," and "\n" not in token:
368*fc1421ebSBram Moolenaar                tokentype, token, indent = self.next()
369*fc1421ebSBram Moolenaar            if token != ",": break
370*fc1421ebSBram Moolenaar        return imports
371*fc1421ebSBram Moolenaar
372*fc1421ebSBram Moolenaar    def _parenparse(self):
373*fc1421ebSBram Moolenaar        name = ''
374*fc1421ebSBram Moolenaar        names = []
375*fc1421ebSBram Moolenaar        level = 1
376*fc1421ebSBram Moolenaar        while True:
377*fc1421ebSBram Moolenaar            tokentype, token, indent = self.next()
378*fc1421ebSBram Moolenaar            if token in (')', ',') and level == 1:
379*fc1421ebSBram Moolenaar                names.append(name)
380*fc1421ebSBram Moolenaar                name = ''
38118144c84SBram Moolenaar            if token == '(':
382*fc1421ebSBram Moolenaar                level += 1
383*fc1421ebSBram Moolenaar            elif token == ')':
384*fc1421ebSBram Moolenaar                level -= 1
385*fc1421ebSBram Moolenaar                if level == 0: break
386*fc1421ebSBram Moolenaar            elif token == ',' and level == 1:
387*fc1421ebSBram Moolenaar                pass
388*fc1421ebSBram Moolenaar            else:
389*fc1421ebSBram Moolenaar                name += str(token)
390*fc1421ebSBram Moolenaar        return names
391*fc1421ebSBram Moolenaar
392*fc1421ebSBram Moolenaar    def _parsefunction(self,indent):
393*fc1421ebSBram Moolenaar        self.scope=self.scope.pop(indent)
394*fc1421ebSBram Moolenaar        tokentype, fname, ind = self.next()
395*fc1421ebSBram Moolenaar        if tokentype != NAME: return None
396*fc1421ebSBram Moolenaar
397*fc1421ebSBram Moolenaar        tokentype, open, ind = self.next()
398*fc1421ebSBram Moolenaar        if open != '(': return None
399*fc1421ebSBram Moolenaar        params=self._parenparse()
400*fc1421ebSBram Moolenaar
401*fc1421ebSBram Moolenaar        tokentype, colon, ind = self.next()
402*fc1421ebSBram Moolenaar        if colon != ':': return None
403*fc1421ebSBram Moolenaar
404*fc1421ebSBram Moolenaar        return Function(fname,params,indent)
405*fc1421ebSBram Moolenaar
406*fc1421ebSBram Moolenaar    def _parseclass(self,indent):
407*fc1421ebSBram Moolenaar        self.scope=self.scope.pop(indent)
408*fc1421ebSBram Moolenaar        tokentype, cname, ind = self.next()
409*fc1421ebSBram Moolenaar        if tokentype != NAME: return None
410*fc1421ebSBram Moolenaar
411*fc1421ebSBram Moolenaar        super = []
412*fc1421ebSBram Moolenaar        tokentype, next, ind = self.next()
413*fc1421ebSBram Moolenaar        if next == '(':
414*fc1421ebSBram Moolenaar            super=self._parenparse()
415*fc1421ebSBram Moolenaar        elif next != ':': return None
416*fc1421ebSBram Moolenaar
417*fc1421ebSBram Moolenaar        return Class(cname,super,indent)
418*fc1421ebSBram Moolenaar
419*fc1421ebSBram Moolenaar    def _parseassignment(self):
420*fc1421ebSBram Moolenaar        assign=''
421*fc1421ebSBram Moolenaar        tokentype, token, indent = self.next()
422*fc1421ebSBram Moolenaar        if tokentype == tokenize.STRING or token == 'str':
423*fc1421ebSBram Moolenaar            return '""'
424*fc1421ebSBram Moolenaar        elif token == '[' or token == 'list':
425*fc1421ebSBram Moolenaar            return '[]'
426*fc1421ebSBram Moolenaar        elif token == '{' or token == 'dict':
427*fc1421ebSBram Moolenaar            return '{}'
428*fc1421ebSBram Moolenaar        elif tokentype == tokenize.NUMBER:
429*fc1421ebSBram Moolenaar            return '0'
430*fc1421ebSBram Moolenaar        elif token == 'open' or token == 'file':
431*fc1421ebSBram Moolenaar            return 'file'
432*fc1421ebSBram Moolenaar        elif token == 'None':
433*fc1421ebSBram Moolenaar            return '_PyCmplNoType()'
434*fc1421ebSBram Moolenaar        elif token == 'type':
435*fc1421ebSBram Moolenaar            return 'type(_PyCmplNoType)' #only for method resolution
436*fc1421ebSBram Moolenaar        else:
437*fc1421ebSBram Moolenaar            assign += token
438*fc1421ebSBram Moolenaar            level = 0
439*fc1421ebSBram Moolenaar            while True:
440*fc1421ebSBram Moolenaar                tokentype, token, indent = self.next()
441*fc1421ebSBram Moolenaar                if token in ('(','{','['):
442*fc1421ebSBram Moolenaar                    level += 1
443*fc1421ebSBram Moolenaar                elif token in (']','}',')'):
444*fc1421ebSBram Moolenaar                    level -= 1
445*fc1421ebSBram Moolenaar                    if level == 0: break
446*fc1421ebSBram Moolenaar                elif level == 0:
447*fc1421ebSBram Moolenaar                    if token in (';','\n'): break
448*fc1421ebSBram Moolenaar                    assign += token
449*fc1421ebSBram Moolenaar        return "%s" % assign
450*fc1421ebSBram Moolenaar
451*fc1421ebSBram Moolenaar    def next(self):
452*fc1421ebSBram Moolenaar        type, token, (lineno, indent), end, self.parserline = self.gen.next()
453*fc1421ebSBram Moolenaar        if lineno == self.curline:
454*fc1421ebSBram Moolenaar            #print 'line found [%s] scope=%s' % (line.replace('\n',''),self.scope.name)
455*fc1421ebSBram Moolenaar            self.currentscope = self.scope
456*fc1421ebSBram Moolenaar        return (type, token, indent)
457*fc1421ebSBram Moolenaar
458*fc1421ebSBram Moolenaar    def _adjustvisibility(self):
459*fc1421ebSBram Moolenaar        newscope = Scope('result',0)
460*fc1421ebSBram Moolenaar        scp = self.currentscope
461*fc1421ebSBram Moolenaar        while scp != None:
462*fc1421ebSBram Moolenaar            if type(scp) == Function:
463*fc1421ebSBram Moolenaar                slice = 0
464*fc1421ebSBram Moolenaar                #Handle 'self' params
465*fc1421ebSBram Moolenaar                if scp.parent != None and type(scp.parent) == Class:
466*fc1421ebSBram Moolenaar                    slice = 1
467*fc1421ebSBram Moolenaar                    p = scp.params[0]
468*fc1421ebSBram Moolenaar                    i = p.find('=')
469*fc1421ebSBram Moolenaar                    if i != -1: p = p[:i]
470*fc1421ebSBram Moolenaar                    newscope.local('%s = %s' % (scp.params[0],scp.parent.name))
471*fc1421ebSBram Moolenaar                for p in scp.params[slice:]:
472*fc1421ebSBram Moolenaar                    i = p.find('=')
473*fc1421ebSBram Moolenaar                    if i == -1:
474*fc1421ebSBram Moolenaar                        newscope.local('%s = _PyCmplNoType()' % p)
475*fc1421ebSBram Moolenaar                    else:
476*fc1421ebSBram Moolenaar                        newscope.local('%s = %s' % (p[:i],_sanitize(p[i+1])))
477*fc1421ebSBram Moolenaar
478*fc1421ebSBram Moolenaar            for s in scp.subscopes:
479*fc1421ebSBram Moolenaar                ns = s.copy_decl(0)
480*fc1421ebSBram Moolenaar                newscope.add(ns)
481*fc1421ebSBram Moolenaar            for l in scp.locals: newscope.local(l)
482*fc1421ebSBram Moolenaar            scp = scp.parent
483*fc1421ebSBram Moolenaar
484*fc1421ebSBram Moolenaar        self.currentscope = newscope
485*fc1421ebSBram Moolenaar        return self.currentscope
486*fc1421ebSBram Moolenaar
487*fc1421ebSBram Moolenaar    #p.parse(vim.current.buffer[:],vim.eval("line('.')"))
488*fc1421ebSBram Moolenaar    def parse(self,text,curline=0):
489*fc1421ebSBram Moolenaar        self.curline = int(curline)
490*fc1421ebSBram Moolenaar        buf = cStringIO.StringIO(''.join(text) + '\n')
491*fc1421ebSBram Moolenaar        self.gen = tokenize.generate_tokens(buf.readline)
492*fc1421ebSBram Moolenaar        self.currentscope = self.scope
493*fc1421ebSBram Moolenaar
494*fc1421ebSBram Moolenaar        try:
495*fc1421ebSBram Moolenaar            freshscope=True
496*fc1421ebSBram Moolenaar            while True:
497*fc1421ebSBram Moolenaar                tokentype, token, indent = self.next()
498*fc1421ebSBram Moolenaar                #print 'main: token=[%s] indent=[%s]' % (token,indent)
499*fc1421ebSBram Moolenaar
500*fc1421ebSBram Moolenaar                if tokentype == DEDENT:
501*fc1421ebSBram Moolenaar                    self.scope = self.scope.pop(indent)
502*fc1421ebSBram Moolenaar                elif token == 'def':
503*fc1421ebSBram Moolenaar                    func = self._parsefunction(indent)
504*fc1421ebSBram Moolenaar                    if func == None:
505*fc1421ebSBram Moolenaar                        print "function: syntax error..."
506*fc1421ebSBram Moolenaar                        continue
507*fc1421ebSBram Moolenaar                    freshscope = True
508*fc1421ebSBram Moolenaar                    self.scope = self.scope.add(func)
509*fc1421ebSBram Moolenaar                elif token == 'class':
510*fc1421ebSBram Moolenaar                    cls = self._parseclass(indent)
511*fc1421ebSBram Moolenaar                    if cls == None:
512*fc1421ebSBram Moolenaar                        print "class: syntax error..."
513*fc1421ebSBram Moolenaar                        continue
514*fc1421ebSBram Moolenaar                    freshscope = True
515*fc1421ebSBram Moolenaar                    self.scope = self.scope.add(cls)
516*fc1421ebSBram Moolenaar
517*fc1421ebSBram Moolenaar                elif token == 'import':
518*fc1421ebSBram Moolenaar                    imports = self._parseimportlist()
519*fc1421ebSBram Moolenaar                    for mod, alias in imports:
520*fc1421ebSBram Moolenaar                        loc = "import %s" % mod
521*fc1421ebSBram Moolenaar                        if len(alias) > 0: loc += " as %s" % alias
522*fc1421ebSBram Moolenaar                        self.scope.local(loc)
523*fc1421ebSBram Moolenaar                    freshscope = False
524*fc1421ebSBram Moolenaar                elif token == 'from':
525*fc1421ebSBram Moolenaar                    mod, token = self._parsedotname()
526*fc1421ebSBram Moolenaar                    if not mod or token != "import":
527*fc1421ebSBram Moolenaar                        print "from: syntax error..."
528*fc1421ebSBram Moolenaar                        continue
529*fc1421ebSBram Moolenaar                    names = self._parseimportlist()
530*fc1421ebSBram Moolenaar                    for name, alias in names:
531*fc1421ebSBram Moolenaar                        loc = "from %s import %s" % (mod,name)
532*fc1421ebSBram Moolenaar                        if len(alias) > 0: loc += " as %s" % alias
533*fc1421ebSBram Moolenaar                        self.scope.local(loc)
534*fc1421ebSBram Moolenaar                    freshscope = False
535*fc1421ebSBram Moolenaar                elif tokentype == STRING:
536*fc1421ebSBram Moolenaar                    if freshscope: self.scope.doc(token)
537*fc1421ebSBram Moolenaar                elif tokentype == NAME:
538*fc1421ebSBram Moolenaar                    name,token = self._parsedotname(token)
539*fc1421ebSBram Moolenaar                    if token == '=':
540*fc1421ebSBram Moolenaar                        stmt = self._parseassignment()
541*fc1421ebSBram Moolenaar                        if stmt != None:
542*fc1421ebSBram Moolenaar                            self.scope.local("%s = %s" % (name,stmt))
543*fc1421ebSBram Moolenaar                    freshscope = False
544*fc1421ebSBram Moolenaar        except StopIteration: #thrown on EOF
545*fc1421ebSBram Moolenaar            pass
546*fc1421ebSBram Moolenaar        except:
547*fc1421ebSBram Moolenaar            dbg("parse error: %s, %s @ %s" %
548*fc1421ebSBram Moolenaar                (sys.exc_info()[0], sys.exc_info()[1], self.parserline))
549*fc1421ebSBram Moolenaar        return self._adjustvisibility()
550*fc1421ebSBram Moolenaar
551*fc1421ebSBram Moolenaardef _sanitize(str):
552*fc1421ebSBram Moolenaar    val = ''
553*fc1421ebSBram Moolenaar    level = 0
554*fc1421ebSBram Moolenaar    for c in str:
555*fc1421ebSBram Moolenaar        if c in ('(','{','['):
556*fc1421ebSBram Moolenaar            level += 1
55718144c84SBram Moolenaar        elif c in (']','}',')'):
55818144c84SBram Moolenaar            level -= 1
559*fc1421ebSBram Moolenaar        elif level == 0:
560*fc1421ebSBram Moolenaar            val += c
56118144c84SBram Moolenaar    return val
56218144c84SBram Moolenaar
56318144c84SBram Moolenaarsys.path.extend(['.','..'])
56418144c84SBram MoolenaarPYTHONEOF
56518144c84SBram Moolenaarendfunction
56618144c84SBram Moolenaar
56718144c84SBram Moolenaarcall s:DefPython()
568" vim: set et ts=4:
569