|
|
" 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
|
|
|
|