summaryrefslogtreecommitdiff
path: root/runtime/autoload/pycomplete.vim
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/autoload/pycomplete.vim')
-rw-r--r--runtime/autoload/pycomplete.vim329
1 files changed, 232 insertions, 97 deletions
diff --git a/runtime/autoload/pycomplete.vim b/runtime/autoload/pycomplete.vim
index 8cd5a5f6d..56a6b4e26 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 @@ function! pycomplete#Complete(findstart, base)
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 @@ endfunction
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)
-
-#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
+ 'dbg', '__name__', 'vim', 'sys', 'parse_to_end', \
+ 'parse_statement', 'tokenize', 'keyword', 'cStringIO', \
+ 'debug_level', 'safe_eval', '_ctor', 'get_arguments', \
+ 'strip_calls', 'types', 'parse_block']
+
+def dbg(level,msg):
+ debug_level = 1
+ try:
+ 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>")')+base
- dbg("parsed statement => %s" % stmt)
+ stmt = vim.eval('expand("<cWORD>")')
+ #dbg(1,"statement: %s - %s" % (stmt, base))
+ stmt = stmt+base
eval_source_code()
+
try:
- dbg("eval: %s" % stmt)
- if len(stmt.split('.')) == 1:
- all = globals().keys() + dir(__builtin__)
+ 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:
- rindex= stmt.rfind('.')
- all = dir(eval(stmt[:rindex]))
- match = stmt[rindex+1:]
+ 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 @@ def eval_source_code():
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 @@ sys.path.extend(['.','..'])
PYTHONEOF
endfunction
+let g:pycomplete_debug_level = 0
call s:DefPython()
" vim: set et ts=4: