mirror of
https://github.com/vim/vim.git
synced 2025-07-04 23:07:33 -04:00
217 lines
7.2 KiB
VimL
217 lines
7.2 KiB
VimL
"pycomplete.vim - Omni Completion for python
|
|
" Maintainer: Aaron Griffin
|
|
" Version: 0.2
|
|
" Last Updated: 5 January 2006
|
|
"
|
|
" TODO
|
|
" * local variables *inside* class members
|
|
|
|
if !has('python')
|
|
echo "Error: Required vim compiled with +python"
|
|
finish
|
|
endif
|
|
|
|
function! pycomplete#Complete(findstart, base)
|
|
"findstart = 1 when we need to get the text length
|
|
if a:findstart
|
|
let line = getline('.')
|
|
let idx = col('.')
|
|
while idx > 0
|
|
let idx -= 1
|
|
let c = line[idx-1]
|
|
if c =~ '\w'
|
|
continue
|
|
elseif ! c =~ '\.'
|
|
idx = -1
|
|
break
|
|
else
|
|
break
|
|
endif
|
|
endwhile
|
|
|
|
return idx
|
|
"findstart = 0 when we need to return the list of completions
|
|
else
|
|
execute "python get_completions('" . a:base . "')"
|
|
return g:pycomplete_completions
|
|
endif
|
|
endfunction
|
|
|
|
function! s:DefPython()
|
|
python << PYTHONEOF
|
|
import vim
|
|
import sys
|
|
import __builtin__
|
|
|
|
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
|
|
def get_completions(base):
|
|
stmt = vim.eval('expand("<cWORD>")')+base
|
|
dbg("parsed statement => %s" % stmt)
|
|
eval_source_code()
|
|
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:]
|
|
|
|
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)
|
|
vim.command("let g:pycomplete_completions = %s" % completions)
|
|
except:
|
|
dbg("exception: %s" % sys.exc_info()[1])
|
|
vim.command("let g:pycomplete_completions = []")
|
|
clean_up()
|
|
|
|
#yes, this is a quasi-functional python lexer
|
|
def eval_source_code():
|
|
import tokenize
|
|
import keyword
|
|
import StringIO
|
|
s = StringIO.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
|
|
#junk
|
|
elif type == tokenize.INDENT or \
|
|
type == tokenize.DEDENT or \
|
|
type == tokenize.ERRORTOKEN or \
|
|
type == tokenize.ENDMARKER or \
|
|
type == tokenize.NEWLINE:
|
|
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 from statement
|
|
elif str == 'from':
|
|
type, str, begin, end, line = g.next()
|
|
mod = str
|
|
|
|
type, str, begin, end, line = g.next()
|
|
if str != "import": break
|
|
mem = ''
|
|
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]))
|
|
#class declaration
|
|
elif str == 'class':
|
|
type, str, begin, end, line = g.next()
|
|
classname = str
|
|
dbg("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:
|
|
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
|
|
memberstr += str
|
|
dbg(" 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)
|
|
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]
|
|
for s in stmts:
|
|
try:
|
|
dbg("evaluating: %s\n" % s)
|
|
exec(s) in globals()
|
|
except:
|
|
pass
|
|
except:
|
|
dbg("exception: %s" % sys.exc_info()[1])
|
|
|
|
def clean_up():
|
|
for o in globals().keys():
|
|
if o not in LOCALDEFS:
|
|
try:
|
|
exec('del %s' % o) in globals()
|
|
except: pass
|
|
|
|
sys.path.extend(['.','..'])
|
|
PYTHONEOF
|
|
endfunction
|
|
|
|
call s:DefPython()
|
|
" vim: set et ts=4:
|