ipy.vim
343 lines
| 10.7 KiB
| text/x-vim
|
VimLexer
Paul Ivanov
|
r4409 | " Vim integration with IPython 0.11+ | ||
" | ||||
" A two-way integration between Vim and IPython. | ||||
" | ||||
" Using this plugin, you can send lines or whole files for IPython to execute, | ||||
" and also get back object introspection and word completions in Vim, like | ||||
" what you get with: object?<enter> object.<tab> in IPython | ||||
" | ||||
" ----------------- | ||||
" Quickstart Guide: | ||||
" ----------------- | ||||
" Start ipython qtconsole and copy the connection string. | ||||
" Source this file, which provides new IPython command | ||||
" :source ipy.vim | ||||
" :IPythonClipboard | ||||
" (or :IPythonXSelection if you're using X11 without having to copy) | ||||
" | ||||
" written by Paul Ivanov (http://pirsquared.org) | ||||
python << EOF | ||||
import time | ||||
import vim | ||||
import sys | ||||
try: | ||||
sys.stdout.flush | ||||
except AttributeError: | ||||
# IPython complains if stderr and stdout don't have flush | ||||
# this is fixed in newer version of Vim | ||||
class WithFlush(object): | ||||
def __init__(self,noflush): | ||||
self.write=noflush.write | ||||
self.writelines=noflush.writelines | ||||
def flush(self):pass | ||||
sys.stdout = WithFlush(sys.stdout) | ||||
sys.stderr = WithFlush(sys.stderr) | ||||
from IPython.zmq.blockingkernelmanager import BlockingKernelManager | ||||
from IPython.config.loader import KeyValueConfigLoader | ||||
from IPython.zmq.kernelapp import kernel_aliases | ||||
ip = '127.0.0.1' | ||||
try: | ||||
km | ||||
except NameError: | ||||
km = None | ||||
def km_from_string(s): | ||||
"""create kernel manager from IPKernelApp string | ||||
such as '--shell=47378 --iopub=39859 --stdin=36778 --hb=52668' | ||||
""" | ||||
global km,send | ||||
# vim interface currently only deals with existing kernels | ||||
s = s.replace('--existing','') | ||||
loader = KeyValueConfigLoader(s.split(), aliases=kernel_aliases) | ||||
cfg = loader.load_config()['KernelApp'] | ||||
try: | ||||
km = BlockingKernelManager( | ||||
shell_address=(ip, cfg['shell_port']), | ||||
sub_address=(ip, cfg['iopub_port']), | ||||
stdin_address=(ip, cfg['stdin_port']), | ||||
hb_address=(ip, cfg['hb_port'])) | ||||
except KeyError,e: | ||||
echo(":IPython " +s + " failed", "Info") | ||||
echo("^-- failed --"+e.message.replace('_port','')+" not specified", "Error") | ||||
return | ||||
km.start_channels() | ||||
send = km.shell_channel.execute | ||||
return km | ||||
reselect = False | ||||
show_id= True | ||||
run_flags= "-i" | ||||
def echo(arg,style="Question"): | ||||
try: | ||||
vim.command("echohl %s" % style) | ||||
vim.command("echom \"%s\"" % arg.replace('\"','\\\"')) | ||||
vim.command("echohl None") | ||||
except vim.error: | ||||
print "-- %s" % arg | ||||
def disconnect(): | ||||
"disconnect kernel manager" | ||||
# XXX: make a prompt here if this km owns the kernel | ||||
pass | ||||
def get_doc(word): | ||||
msg_id = km.shell_channel.object_info(word) | ||||
time.sleep(.1) | ||||
doc = get_doc_msg(msg_id) | ||||
#if len(doc): | ||||
# echo(word, 'Special') | ||||
return doc | ||||
import re | ||||
# from http://serverfault.com/questions/71285/in-centos-4-4-how-can-i-strip-escape-sequences-from-a-text-file | ||||
strip = re.compile('\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]') | ||||
def strip_color_escapes(s): | ||||
return strip.sub('',s) | ||||
def get_doc_msg(msg_id): | ||||
content = get_child_msgs(msg_id)[0]['content'] | ||||
n = 13 # longest field name | ||||
##XXX: debug (print the whole message) | ||||
b=[] | ||||
if not content['found']: | ||||
return b | ||||
## debugging the whole message | ||||
#for k in content: | ||||
# if isinstance(content[k],str) and content[k].find('\n')!=-1: | ||||
# b.append(k.ljust(n)+":") | ||||
# b.append(content[k].splitlines()) | ||||
# else: | ||||
# b.append(k.ljust(n)+":"+str(content[k])) | ||||
for field in ['type_name','base_class','string_form','namespace', | ||||
'file','length','definition','source','docstring']: | ||||
# XXX: strip the 'definition' rich formatting | ||||
c = content.get(field,None) | ||||
if c: | ||||
if field in ['definition']: | ||||
c = strip_color_escapes(c).rstrip() | ||||
s = field.replace('_',' ').title()+':' | ||||
s = s.ljust(n) | ||||
if c.find('\n')==-1: | ||||
b.append(s+c) | ||||
else: | ||||
b.append(s) | ||||
b.extend(c.splitlines()) | ||||
return b | ||||
def get_doc_buffer(level=0): | ||||
word = vim.eval('expand("<cfile>")') | ||||
doc = get_doc(word) | ||||
if len(doc) ==0: | ||||
echo(word+" not found","Error") | ||||
return | ||||
vim.command('pcl') | ||||
vim.command('new '+word) | ||||
vim.command('setlocal pvw modifiable noro') | ||||
vim.command('map <buffer> q :q<CR>') | ||||
vim.command('map <buffer> :q<CR>') | ||||
#vim.command('pedit '+docbuf.name) | ||||
b = vim.current.buffer | ||||
#b.append(doc) | ||||
b[:] = doc | ||||
#b.append(doc) | ||||
vim.command('setlocal nomodified bufhidden=wipe') | ||||
#vim.command('setlocal previewwindow nomodifiable nomodified ro') | ||||
#vim.command('set previewheight=%d'%len(b))# go to previous window | ||||
vim.command('resize %d'%len(b)) | ||||
#vim.command('pcl') | ||||
#vim.command('pedit doc') | ||||
#vim.command('normal ') # go to previous window | ||||
def get_child_msgs(msg_id): | ||||
# XXX: message handling should be split into its own process in the future | ||||
msgs= km.shell_channel.get_msgs() | ||||
children = [m for m in msgs if m['parent_header']['msg_id'] == msg_id] | ||||
return children | ||||
def run_this_file(): | ||||
send('run %s %s' % (run_flags, vim.current.buffer.name,)) | ||||
echo("In[]: run %s %s" % (run_flags, vim.current.buffer.name)) | ||||
def print_prompt(prompt,msg_id=None): | ||||
global show_id | ||||
if show_id and msg_id: | ||||
time.sleep(.1) # wait to get message back from kernel | ||||
children = get_child_msgs(msg_id) | ||||
if len(children): | ||||
count = children[0]['content']['execution_count'] | ||||
echo("In[%d]: %s" %(count,prompt)) | ||||
else: | ||||
echo("In[]: %s (no reply from kernel)" % prompt) | ||||
else: | ||||
echo("In[]: %s" % prompt) | ||||
def run_this_line(): | ||||
msg_id = send(vim.current.line) | ||||
print_prompt(vim.current.line, msg_id) | ||||
def run_these_lines(): | ||||
r = vim.current.range | ||||
lines = "\n".join(vim.current.buffer[r.start:r.end+1]) | ||||
msg_id = send(lines) | ||||
#alternative way of doing this in more recent versions of ipython | ||||
#but %paste only works on the local machine | ||||
#vim.command("\"*yy") | ||||
#send("'%paste')") | ||||
#reselect the previously highlighted block | ||||
vim.command("normal gv") | ||||
if not reselect: | ||||
vim.command("normal ") | ||||
#vim lines start with 1 | ||||
#print "lines %d-%d sent to ipython"% (r.start+1,r.end+1) | ||||
prompt = "lines %d-%d "% (r.start+1,r.end+1) | ||||
print_prompt(prompt,msg_id) | ||||
def dedent_run_this_line(): | ||||
vim.command("left") | ||||
run_this_line() | ||||
vim.command("undo") | ||||
def dedent_run_these_lines(): | ||||
vim.command("'<,'>left") | ||||
run_these_lines() | ||||
vim.command("undo") | ||||
#def set_this_line(): | ||||
# # not sure if there's a way to do this, since we have multiple clients | ||||
# send("_ip.IP.rl_next_input= \'%s\'" % vim.current.line.replace("\'","\\\'")) | ||||
# #print "line \'%s\' set at ipython prompt"% vim.current.line | ||||
# echo("line \'%s\' set at ipython prompt"% vim.current.line,'Statement') | ||||
def toggle_reselect(): | ||||
global reselect | ||||
reselect=not reselect | ||||
print "F9 will%sreselect lines after sending to ipython"% (reselect and " " or " not ") | ||||
#def set_breakpoint(): | ||||
# send("__IP.InteractiveTB.pdb.set_break('%s',%d)" % (vim.current.buffer.name, | ||||
# vim.current.window.cursor[0])) | ||||
# print "set breakpoint in %s:%d"% (vim.current.buffer.name, | ||||
# vim.current.window.cursor[0]) | ||||
# | ||||
#def clear_breakpoint(): | ||||
# send("__IP.InteractiveTB.pdb.clear_break('%s',%d)" % (vim.current.buffer.name, | ||||
# vim.current.window.cursor[0])) | ||||
# print "clearing breakpoint in %s:%d" % (vim.current.buffer.name, | ||||
# vim.current.window.cursor[0]) | ||||
# | ||||
#def clear_all_breakpoints(): | ||||
# send("__IP.InteractiveTB.pdb.clear_all_breaks()"); | ||||
# print "clearing all breakpoints" | ||||
# | ||||
#def run_this_file_pdb(): | ||||
# send(' __IP.InteractiveTB.pdb.run(\'execfile("%s")\')' % (vim.current.buffer.name,)) | ||||
# #send('run -d %s' % (vim.current.buffer.name,)) | ||||
# echo("In[]: run -d %s (using pdb)" % vim.current.buffer.name) | ||||
EOF | ||||
fun! <SID>toggle_send_on_save() | ||||
if exists("s:ssos") && s:ssos == 0 | ||||
let s:ssos = 1 | ||||
au BufWritePost *.py :py run_this_file() | ||||
echo "Autosend On" | ||||
else | ||||
let s:ssos = 0 | ||||
au! BufWritePost *.py | ||||
echo "Autosend Off" | ||||
endif | ||||
endfun | ||||
map <silent> <F5> :python run_this_file()<CR> | ||||
map <silent> <S-F5> :python run_this_line()<CR> | ||||
map <silent> <F9> :python run_these_lines()<CR> | ||||
map <leader>d :py get_doc_buffer()<CR> | ||||
map <silent> <S-F9> :python toggle_reselect()<CR> | ||||
"map <silent> <C-F6> :python send('%pdb')<CR> | ||||
"map <silent> <F6> :python set_breakpoint()<CR> | ||||
"map <silent> <s-F6> :python clear_breakpoint()<CR> | ||||
"map <silent> <F7> :python run_this_file_pdb()<CR> | ||||
"map <silent> <s-F7> :python clear_all_breaks()<CR> | ||||
imap <C-F5> <C-O><F5> | ||||
imap <S-F5> <C-O><S-F5> | ||||
imap <silent> <F5> <C-O><F5> | ||||
map <C-F5> :call <SID>toggle_send_on_save()<CR> | ||||
"pi custom | ||||
map <silent> <C-Return> :python run_this_file()<CR> | ||||
map <silent> <C-s> :python run_this_line()<CR> | ||||
map <silent> <M-s> :python dedent_run_this_line()<CR> | ||||
vmap <silent> <C-S> :python run_these_lines()<CR> | ||||
vmap <silent> <M-s> :python dedent_run_these_lines()<CR> | ||||
"map <silent> <C-p> :python set_this_line()<CR> | ||||
map <silent> <M-c> I#<ESC> | ||||
vmap <silent> <M-c> I#<ESC> | ||||
map <silent> <M-C> :s/^\([ \t]*\)#/\1/<CR> | ||||
vmap <silent> <M-C> :s/^\([ \t]*\)#/\1/<CR> | ||||
command! -nargs=+ IPython :py km_from_string("<args>") | ||||
command! -nargs=0 IPythonClipboard :py km_from_string(vim.eval('@+')) | ||||
command! -nargs=0 IPythonXSelection :py km_from_string(vim.eval('@*')) | ||||
function! IPythonBalloonExpr() | ||||
python << endpython | ||||
word = vim.eval('v:beval_text') | ||||
reply = get_doc(word) | ||||
#reply = reply[:40] | ||||
vim.command("let l:doc = %s"% reply) | ||||
endpython | ||||
return l:doc | ||||
endfunction | ||||
set bexpr=IPythonBalloonExpr() | ||||
set ballooneval | ||||
fun! CompleteIPython(findstart, base) | ||||
if a:findstart | ||||
" locate the start of the word | ||||
let line = getline('.') | ||||
let start = col('.') - 1 | ||||
while start > 0 && line[start-1] =~ '\k\|\.' "keyword | ||||
let start -= 1 | ||||
endwhile | ||||
echo start | ||||
return start | ||||
else | ||||
" find months matching with "a:base" | ||||
let res = [] | ||||
python << endpython | ||||
base = vim.eval("a:base") | ||||
findstart = vim.eval("a:findstart") | ||||
msg_id = km.shell_channel.complete(base, vim.current.line, vim.eval("col('.')")) | ||||
time.sleep(.1) | ||||
m = get_child_msgs(msg_id)[0] | ||||
matches = m['content']['matches'] | ||||
#end = len(base) | ||||
#completions = [m[end:]+findstart+base for m in matches] | ||||
matches.insert(0,base) # the "no completion" version | ||||
completions = matches | ||||
vim.command("let l:completions = %s"% completions) | ||||
endpython | ||||
for m in l:completions | ||||
"if m =~ '^' . a:base | ||||
call add(res, m) | ||||
"endif | ||||
endfor | ||||
return res | ||||
endif | ||||
endfun | ||||
set completefunc=CompleteIPython | ||||