updated for version 7.0187
diff --git a/runtime/autoload/pycomplete.vim b/runtime/autoload/pycomplete.vim
index 8cd5a5f..56a6b4e 100644
--- a/runtime/autoload/pycomplete.vim
+++ b/runtime/autoload/pycomplete.vim
@@ -1,10 +1,20 @@
 "pycomplete.vim - Omni Completion for python
 " Maintainer: Aaron Griffin
-" Version: 0.2
-" Last Updated: 5 January 2006
+" Version: 0.3
+" Last Updated: 23 January 2006
+"
+"   v0.3 Changes:
+"       added top level def parsing
+"       for safety, call returns are not evaluated
+"       handful of parsing changes
+"       trailing ( and . characters
+"       argument completion on open parens
+"       stop parsing at current line - ++performance, local var resolution
 "
 "   TODO
-"   * local variables *inside* class members
+"       RExec subclass
+"       Code cleanup + make class
+"       use internal dict, not globals()
 
 if !has('python')
     echo "Error: Required vim compiled with +python"
@@ -20,10 +30,10 @@
             let idx -= 1
             let c = line[idx-1]
             if c =~ '\w'
-				continue
-			elseif ! c =~ '\.'
+                continue
+            elseif ! c =~ '\.'
                 idx = -1
-				break
+                break
             else
                 break
             endif
@@ -39,79 +49,222 @@
 
 function! s:DefPython()
 python << PYTHONEOF
-import vim
-import sys
+import vim, sys, types
 import __builtin__
+import tokenize, keyword, cStringIO
 
 LOCALDEFS = \
 	['LOCALDEFS', 'clean_up','eval_source_code', \
 	 'get_completions', '__builtin__', '__builtins__', \
-	 'dbg', '__name__', 'vim', 'sys']
-#comment/uncomment one line at a time to enable/disable debugging
-def dbg(msg):
-    pass
-#    print(msg)
+	 'dbg', '__name__', 'vim', 'sys', 'parse_to_end', \
+     'parse_statement', 'tokenize', 'keyword', 'cStringIO', \
+     'debug_level', 'safe_eval', '_ctor', 'get_arguments', \
+     'strip_calls', 'types', 'parse_block']
 
-#it seems that by this point, vim has already stripped the base
-#  matched in the findstart=1 section, so we will create the
-#  statement from scratch
-def get_completions(base):
-    stmt = vim.eval('expand("<cWORD>")')+base
-    dbg("parsed statement => %s" % stmt)
-    eval_source_code()
+def dbg(level,msg):
+    debug_level = 1
     try:
-        dbg("eval: %s" % stmt)
-        if len(stmt.split('.')) == 1:
-            all = globals().keys() + dir(__builtin__)
-            match = stmt
-        else:
-            rindex= stmt.rfind('.')
-            all = dir(eval(stmt[:rindex]))
-            match = stmt[rindex+1:]
+        debug_level = vim.eval("g:pycomplete_debug_level")
+    except:
+        pass
+    if level <= debug_level: print(msg)
 
+def strip_calls(stmt):
+    parsed=''
+    level = 0
+    for c in stmt:
+        if c in ['[','(']:
+            level += 1
+        elif c in [')',']']:
+            level -= 1
+        elif level == 0:
+            parsed += c
+    ##dbg(10,"stripped: %s" % parsed)
+    return parsed
+
+def get_completions(base):
+    stmt = vim.eval('expand("<cWORD>")')
+    #dbg(1,"statement: %s - %s" % (stmt, base))
+    stmt = stmt+base
+    eval_source_code()
+
+    try:
+        ridx = stmt.rfind('.')
+        if stmt[-1] == '(':
+            match = ""
+            stmt = strip_calls(stmt[:len(stmt)-1])
+            all = get_arguments(eval(stmt))
+        elif ridx == -1:
+            match = stmt
+            all = globals() + __builtin__.__dict__
+        else:
+            match = stmt[ridx+1:]
+            stmt = strip_calls(stmt[:ridx])
+            all = eval(stmt).__dict__
+
+        #dbg(15,"completions for: %s, match=%s" % (stmt,match))
         completions = []
-        dbg("match == %s" % match)
-        for m in all:
-            #TODO: remove private (_foo) functions?
-            if m.find('__') != 0 and \
-               m.find(match) == 0 and \
-			   m not in LOCALDEFS:
-                dbg("matched... %s, %s" % (m, m.find(match)))
-                completions.append(m)
-        dbg("all completions: %s" % completions)
+        if type(all) == types.DictType:
+            for m in all:
+                if m.find('_') != 0 and m.find(match) == 0 and \
+			       m not in LOCALDEFS:
+                    #dbg(25,"matched... %s, %s" % (m, m.find(match)))
+                    typestr = str(all[m])
+                    if "function" in typestr: m += '('
+                    elif "method" in typestr: m += '('
+                    elif "module" in typestr: m += '.'
+                    elif "class" in typestr: m += '('
+                    completions.append(m)
+            completions.sort()
+        else:
+            completions.append(all)
+        #dbg(10,"all completions: %s" % completions)
         vim.command("let g:pycomplete_completions = %s" % completions)
     except:
-        dbg("exception: %s" % sys.exc_info()[1])
         vim.command("let g:pycomplete_completions = []")
+        #dbg(1,"exception: %s" % sys.exc_info()[1])
     clean_up()
 
-#yes, this is a quasi-functional python lexer
+def get_arguments(func_obj):
+    def _ctor(obj):
+        try:
+            return class_ob.__init__.im_func
+        except AttributeError:
+            for base in class_ob.__bases__:
+                rc = _find_constructor(base)
+                if rc is not None: return rc
+        return None
+
+    arg_offset = 1
+    if type(func_obj) == types.ClassType: func_obj = _ctor(func_obj)
+    elif type(func_obj) == types.MethodType: func_obj = func_obj.im_func
+    else: arg_offset = 0
+    
+    #dbg(20,"%s, offset=%s" % (str(func_obj), arg_offset))
+
+    arg_text = ''
+    if type(func_obj) in [types.FunctionType, types.LambdaType]:
+        try:
+            cd = func_obj.func_code
+            real_args = cd.co_varnames[arg_offset:cd.co_argcount]
+            defaults = func_obj.func_defaults or []
+            defaults = list(map(lambda name: "=%s" % name, defaults))
+            defaults = [""] * (len(real_args)-len(defaults)) + defaults
+            items = map(lambda a,d: a+d, real_args, defaults)
+            if func_obj.func_code.co_flags & 0x4:
+                items.append("...")
+            if func_obj.func_code.co_flags & 0x8:
+                items.append("***")
+            arg_text = ", ".join(items) + ')'
+
+        except:
+            #dbg(1,"exception: %s" % sys.exc_info()[1])
+            pass
+    if len(arg_text) == 0:
+        # The doc string sometimes contains the function signature
+        #  this works for alot of C modules that are part of the
+        #  standard library
+        doc = getattr(func_obj, '__doc__', '')
+        if doc:
+            doc = doc.lstrip()
+            pos = doc.find('\n')
+            if pos > 0:
+                sigline = doc[:pos]
+                lidx = sigline.find('(')
+                ridx = sigline.find(')')
+                retidx = sigline.find('->')
+                ret = sigline[retidx+2:].strip()
+                if lidx > 0 and ridx > 0:
+                    arg_text = sigline[lidx+1:ridx] + ')'
+                    if len(ret) > 0: arg_text += ' #returns %s' % ret
+    #dbg(15,"argument completion: %s" % arg_text)
+    return arg_text
+
+def parse_to_end(gen):
+    stmt=''
+    level = 0
+    for type, str, begin, end, line in gen:
+        if line == vim.eval('getline(\'.\')'): break
+        elif str == '\\': continue
+        elif str == ';':
+            break
+        elif type == tokenize.NEWLINE and level == 0:
+            break
+        elif str in ['[','(']:
+            level += 1
+        elif str in [')',']']:
+            level -= 1
+        elif level == 0:
+            stmt += str
+        #dbg(10,"current statement: %s" % stmt)
+    return stmt
+
+def parse_block(gen):
+    lines = []
+    level = 0
+    for type, str, begin, end, line in gen:
+        if line.replace('\n','') == vim.eval('getline(\'.\')'): break
+        elif type == tokenize.INDENT:
+            level += 1
+        elif type == tokenize.DEDENT:
+            level -= 1
+            if level == 0: break;
+        else:
+            stmt = parse_statement(gen,str)
+            if len(stmt) > 0: lines.append(stmt)
+    return lines
+
+def parse_statement(gen,curstr=''):
+    var = curstr
+    type, str, begin, end, line = gen.next()
+    if str == '=':
+        type, str, begin, end, line = gen.next()
+        if type == tokenize.NEWLINE:
+            return ''
+        elif type == tokenize.STRING or str == 'str':  
+            return '%s = str' % var
+        elif str == '[' or str == 'list':
+            return '%s= list' % var
+        elif str == '{' or str == 'dict':
+            return '%s = dict' % var
+        elif type == tokenize.NUMBER:
+            return '%s = 0' % var
+        elif str == 'Set': 
+            return '%s = Set' % var
+        elif str == 'open' or str == 'file':
+            return '%s = file' % var
+        else:
+            inst = str + parse_to_end(gen)
+            if len(inst) > 0:
+                #dbg(5,"found [%s = %s]" % (var, inst))
+                return '%s = %s' % (var, inst)
+    return ''
+
 def eval_source_code():
-    import tokenize
-    import keyword
-    import StringIO
-    s = StringIO.StringIO('\n'.join(vim.current.buffer[:]) + '\n')
+    LINE=vim.eval('getline(\'.\')')
+    s = cStringIO.StringIO('\n'.join(vim.current.buffer[:]) + '\n')
     g = tokenize.generate_tokens(s.readline)
 
     stmts = []
     lineNo = 0
     try:
         for type, str, begin, end, line in g:
-            if begin[0] == lineNo:
-                continue
+            if line.replace('\n','') == vim.eval('getline(\'.\')'): break
+            elif begin[0] == lineNo: continue
             #junk
             elif type == tokenize.INDENT or \
                  type == tokenize.DEDENT or \
                  type == tokenize.ERRORTOKEN or \
                  type == tokenize.ENDMARKER or \
-                 type == tokenize.NEWLINE:
+                 type == tokenize.NEWLINE or \
+                 type == tokenize.COMMENT:
                 continue
             #import statement
             elif str == 'import':
-                for type, str, begin, end, line in g:
-                    if str == ';' or type == tokenize.NEWLINE: break
-                    dbg("found [import %s]" % str)
-                    stmts.append("import %s" % str)
+                import_stmt=parse_to_end(g)
+                if len(import_stmt) > 0:
+                    #dbg(5,"found [import %s]" % import_stmt)
+                    stmts.append("import %s" % import_stmt)
             #import from statement
             elif str == 'from':
                 type, str, begin, end, line = g.next()
@@ -119,87 +272,68 @@
 
                 type, str, begin, end, line = g.next()
                 if str != "import": break
-                mem = ''
+                from_stmt=parse_to_end(g)
+                if len(from_stmt) > 0:
+                    #dbg(5,"found [from %s import %s]" % (mod, from_stmt))
+                    stmts.append("from %s import %s" % (mod, from_stmt))
+            #def statement
+            elif str == 'def':
+                funcstr = ''
                 for type, str, begin, end, line in g:
-                    if str == ';' or type == tokenize.NEWLINE: break
-                    mem += (str + ',')
-                if len(mem) > 0:
-                    dbg("found [from %s import %s]" % (mod, mem[:-1]))
-                    stmts.append("from %s import %s" % (mod, mem[:-1]))
+                    if line.replace('\n','') == vim.eval('getline(\'.\')'): break
+                    elif str == ':':
+                        stmts += parse_block(g)
+                        break
+                    funcstr += str
+                if len(funcstr) > 0:
+                    #dbg(5,"found [def %s]" % funcstr)
+                    stmts.append("def %s:\n   pass" % funcstr)
             #class declaration
             elif str == 'class':
                 type, str, begin, end, line = g.next()
                 classname = str
-                dbg("found [class %s]" % classname)
+                #dbg(5,"found [class %s]" % classname)
 
                 level = 0
                 members = []
-                #we don't care about the meat of the members,
-                # only the signatures, so we'll replace the bodies
-                # with 'pass' for evaluation
                 for type, str, begin, end, line in g:
-                    if type == tokenize.INDENT:
+                    if line.replace('\n','') == vim.eval('getline(\'.\')'): break
+                    elif type == tokenize.INDENT:
                         level += 1
                     elif type == tokenize.DEDENT:
                         level -= 1
                         if level == 0: break;
                     elif str == 'def':
-                        #TODO: if name begins with '_', keep private
                         memberstr = ''
                         for type, str, begin, end, line in g:
-                            if str == ':': break
+                            if line.replace('\n','') == vim.eval('getline(\'.\')'): break
+                            elif str == ':':
+                                stmts += parse_block(g)
+                                break
                             memberstr += str
-                        dbg("   member [%s]" % memberstr)
+                        #dbg(5,"   member [%s]" % memberstr)
                         members.append(memberstr)
-                    #TODO parse self.blah = something lines
-                    #elif str == "self" && next && str == "." ...blah...
                 classstr = 'class %s:' % classname
                 for m in members:
                     classstr += ("\n   def %s:\n      pass" % m)
                 stmts.append("%s\n" % classstr)
             elif keyword.iskeyword(str) or str in globals():
-                dbg("keyword = %s" % str)
+                #dbg(5,"keyword = %s" % str)
                 lineNo = begin[0]
             else:
-                if line.find("=") == -1: continue
-                var = str
-                type, str, begin, end, line = g.next()
-                dbg('next = %s' % str)
-                if str != '=': continue
-
-                type, str, begin, end, line = g.next()
-                if type == tokenize.NEWLINE:
-                    continue
-                elif type == tokenize.STRING or str == 'str':  
-                    stmts.append('%s = str' % var)
-                elif str == '[' or str == 'list':
-                    stmts.append('%s= list' % var)
-                elif str == '{' or str == 'dict':
-                    stmts.append('%s = dict' % var)
-                elif type == tokenize.NUMBER:
-                    continue
-                elif str == 'Set': 
-                    stmts.append('%s = Set' % var)
-                elif str == 'open' or str == 'file':
-                    stmts.append('%s = file' % var)
-                else:
-                    inst = str
-                    for type, str, begin, end, line in g:
-                        if type == tokenize.NEWLINE:
-                            break
-                        inst += str
-                    if len(inst) > 0:
-                        dbg("found [%s = %s]" % (var, inst))
-                        stmts.append('%s = %s' % (var, inst))
-                lineNo = begin[0]
+                assign = parse_statement(g,str)
+                if len(assign) > 0: stmts.append(assign)
+                
         for s in stmts:
             try:
-                dbg("evaluating: %s\n" % s)
+                #dbg(15,"evaluating: %s\n" % s)
                 exec(s) in globals()
             except:
+                #dbg(1,"exception: %s" % sys.exc_info()[1])
                 pass
     except:
-        dbg("exception: %s" % sys.exc_info()[1])
+        #dbg(1,"exception: %s" % sys.exc_info()[1])
+        pass
 
 def clean_up():
     for o in globals().keys():
@@ -212,5 +346,6 @@
 PYTHONEOF
 endfunction
 
+let g:pycomplete_debug_level = 0
 call s:DefPython()
 " vim: set et ts=4: