##// END OF EJS Templates
two-way vim-ipython integration
Paul Ivanov -
Show More
@@ -0,0 +1,107 b''
1 ###########
2 vim-ipython
3 ###########
4
5 A two-way integration between Vim and IPython 0.11+
6
7 author: Paul Ivanov (http://pirsquared.org)
8
9 github: http://github.com/ivanov/vim-ipython
10
11 demo: http://pirsquared.org/vim-ipython/
12
13 Using this plugin, you can send lines or whole files for IPython to
14 execute, and also get back object introspection and word completions in
15 Vim, like what you get with: ``object?<enter>`` and ``object.<tab>`` in
16 IPython.
17
18 The big change from previous versions of ``ipy.vim`` is that it no longer
19 the old requires the brittle ipy_vimserver.py instantiation, and since
20 it uses just vim and python, it is platform independent (i.e. should work
21 even on windows, unlike the previous \*nix only solution)
22
23
24 -----------------
25 Quickstart Guide:
26 -----------------
27 Start ``ipython qtconsole`` and copy the connection string.
28 Source ``ipy.vim`` file, which provides new IPython command::
29
30 :source ipy.vim
31 (or copy it to ~/.vim/ftplugin/python to load automatically)
32
33 :IPythonClipboard
34 (or :IPythonXSelection if you're using X11 without having to copy)
35
36 The :IPython command allows you to put the full string, e.g.::
37
38 :IPython --existing --shell=41882 --iopub=43286 --stdin=34987 --hb=36697
39
40 The ``:IPythonClipboard`` command just uses the ``+`` register to get the
41 connection string, whereas ``:IPythonXSelection`` uses the ``*`` register
42
43 ------------------------
44 Sending lines to IPython
45 ------------------------
46 Now type out a line and send it to IPython using ``<Ctrl-S>`` from Command mode::
47
48 import os
49
50 You should see a notification message confirming the line was sent, along
51 with the input number for the line, like so ``In[1]: import os``.
52
53 ``<Ctrl-S>`` also works from insert mode, but doesn't show notification
54
55 It also works blockwise in Visual Mode. Strip the leading double quotes and
56 send these lines using ``<Ctrl-S>``::
57
58 import this,math # secret decoder ring
59 a,b,c,d,e,f,g,h,i = range(1,10)
60 code =(c,a,d,a,e,i,)
61 msg = '...jrer nyy frag sebz Ivz.\nIvz+VClguba=%fyl '+this.s.split()[g]
62 decode=lambda x:"\n"+"".join([this.d.get(c,c) for c in x])+"!"
63 format=lambda x:'These lines:\n '+'\n '.join([l for l in x.splitlines()])
64 secret_decoder = lambda a,b: format(a)+decode(msg)%str(b)[:-1]
65 '%d'*len(code)%code == str(int(math.pi*1e5))
66
67 Then, go to the qtconsole and run this line::
68
69 print secret_decoder(_i,_)
70
71 You can also send whole files to IPython's ``%run`` magic using ``<F5>``.
72
73 -------------------------------
74 IPython's object? Functionality
75 -------------------------------
76
77 If you're using gvim, mouse-over a variable to see IPython's ? equivalent. If
78 you're using vim from a terminal, or want to copy something from the
79 docstring, type ``<leader>d``. ``<leader>`` is usually ``\`` (the backslash
80 key). This will open a quickpreview window, which can be closed by hitting
81 ``q`` or ``<escape>``.
82
83 --------------------------------------
84 IPython's tab-completion Functionality
85 --------------------------------------
86 vim-ipython activates a 'completefunc' that queries IPython.
87 A completefunc is activated using ``Ctrl-X Ctrl-U`` in Insert Mode (vim
88 default). You can combine this functionality with SuperTab to get tab
89 completion
90
91 ---------------
92 Current issues:
93 ---------------
94 For now, vim-ipython only connects to an ipython session in progress.
95
96 ipy.vim takes a while to load, I'll eventually move the python code to its
97 own file and do a lazy import (only when the IPython command is called)
98
99 The ipdb integration is not yet re-implemented.
100
101 Need to add more message handling for sub_channel messages from IPython
102 (i.e. notification of changes which were not sent from vim).
103
104 ------
105 Thanks
106 ------
107 @MinRK for guiding me through the IPython kernel manager protocol.
@@ -0,0 +1,343 b''
1 " Vim integration with IPython 0.11+
2 "
3 " A two-way integration between Vim and IPython.
4 "
5 " Using this plugin, you can send lines or whole files for IPython to execute,
6 " and also get back object introspection and word completions in Vim, like
7 " what you get with: object?<enter> object.<tab> in IPython
8 "
9 " -----------------
10 " Quickstart Guide:
11 " -----------------
12 " Start ipython qtconsole and copy the connection string.
13 " Source this file, which provides new IPython command
14 " :source ipy.vim
15 " :IPythonClipboard
16 " (or :IPythonXSelection if you're using X11 without having to copy)
17 "
18 " written by Paul Ivanov (http://pirsquared.org)
19 python << EOF
20 import time
21 import vim
22 import sys
23
24 try:
25 sys.stdout.flush
26 except AttributeError:
27 # IPython complains if stderr and stdout don't have flush
28 # this is fixed in newer version of Vim
29 class WithFlush(object):
30 def __init__(self,noflush):
31 self.write=noflush.write
32 self.writelines=noflush.writelines
33 def flush(self):pass
34 sys.stdout = WithFlush(sys.stdout)
35 sys.stderr = WithFlush(sys.stderr)
36
37 from IPython.zmq.blockingkernelmanager import BlockingKernelManager
38
39 from IPython.config.loader import KeyValueConfigLoader
40 from IPython.zmq.kernelapp import kernel_aliases
41
42
43 ip = '127.0.0.1'
44 try:
45 km
46 except NameError:
47 km = None
48
49 def km_from_string(s):
50 """create kernel manager from IPKernelApp string
51 such as '--shell=47378 --iopub=39859 --stdin=36778 --hb=52668'
52 """
53 global km,send
54 # vim interface currently only deals with existing kernels
55 s = s.replace('--existing','')
56 loader = KeyValueConfigLoader(s.split(), aliases=kernel_aliases)
57 cfg = loader.load_config()['KernelApp']
58 try:
59 km = BlockingKernelManager(
60 shell_address=(ip, cfg['shell_port']),
61 sub_address=(ip, cfg['iopub_port']),
62 stdin_address=(ip, cfg['stdin_port']),
63 hb_address=(ip, cfg['hb_port']))
64 except KeyError,e:
65 echo(":IPython " +s + " failed", "Info")
66 echo("^-- failed --"+e.message.replace('_port','')+" not specified", "Error")
67 return
68 km.start_channels()
69 send = km.shell_channel.execute
70 return km
71
72
73 reselect = False
74 show_id= True
75 run_flags= "-i"
76
77 def echo(arg,style="Question"):
78 try:
79 vim.command("echohl %s" % style)
80 vim.command("echom \"%s\"" % arg.replace('\"','\\\"'))
81 vim.command("echohl None")
82 except vim.error:
83 print "-- %s" % arg
84
85 def disconnect():
86 "disconnect kernel manager"
87 # XXX: make a prompt here if this km owns the kernel
88 pass
89
90 def get_doc(word):
91 msg_id = km.shell_channel.object_info(word)
92 time.sleep(.1)
93 doc = get_doc_msg(msg_id)
94 #if len(doc):
95 # echo(word, 'Special')
96 return doc
97
98 import re
99 # from http://serverfault.com/questions/71285/in-centos-4-4-how-can-i-strip-escape-sequences-from-a-text-file
100 strip = re.compile('\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]')
101 def strip_color_escapes(s):
102 return strip.sub('',s)
103
104 def get_doc_msg(msg_id):
105 content = get_child_msgs(msg_id)[0]['content']
106 n = 13 # longest field name
107 ##XXX: debug (print the whole message)
108 b=[]
109
110 if not content['found']:
111 return b
112
113 ## debugging the whole message
114 #for k in content:
115 # if isinstance(content[k],str) and content[k].find('\n')!=-1:
116 # b.append(k.ljust(n)+":")
117 # b.append(content[k].splitlines())
118 # else:
119 # b.append(k.ljust(n)+":"+str(content[k]))
120
121 for field in ['type_name','base_class','string_form','namespace',
122 'file','length','definition','source','docstring']:
123 # XXX: strip the 'definition' rich formatting
124 c = content.get(field,None)
125 if c:
126 if field in ['definition']:
127 c = strip_color_escapes(c).rstrip()
128 s = field.replace('_',' ').title()+':'
129 s = s.ljust(n)
130 if c.find('\n')==-1:
131 b.append(s+c)
132 else:
133 b.append(s)
134 b.extend(c.splitlines())
135 return b
136
137 def get_doc_buffer(level=0):
138 word = vim.eval('expand("<cfile>")')
139 doc = get_doc(word)
140 if len(doc) ==0:
141 echo(word+" not found","Error")
142 return
143 vim.command('pcl')
144 vim.command('new '+word)
145 vim.command('setlocal pvw modifiable noro')
146 vim.command('map <buffer> q :q<CR>')
147 vim.command('map <buffer>  :q<CR>')
148 #vim.command('pedit '+docbuf.name)
149 b = vim.current.buffer
150 #b.append(doc)
151 b[:] = doc
152 #b.append(doc)
153 vim.command('setlocal nomodified bufhidden=wipe')
154 #vim.command('setlocal previewwindow nomodifiable nomodified ro')
155 #vim.command('set previewheight=%d'%len(b))# go to previous window
156 vim.command('resize %d'%len(b))
157 #vim.command('pcl')
158 #vim.command('pedit doc')
159 #vim.command('normal ') # go to previous window
160
161 def get_child_msgs(msg_id):
162 # XXX: message handling should be split into its own process in the future
163 msgs= km.shell_channel.get_msgs()
164 children = [m for m in msgs if m['parent_header']['msg_id'] == msg_id]
165 return children
166
167
168
169 def run_this_file():
170 send('run %s %s' % (run_flags, vim.current.buffer.name,))
171 echo("In[]: run %s %s" % (run_flags, vim.current.buffer.name))
172
173 def print_prompt(prompt,msg_id=None):
174 global show_id
175 if show_id and msg_id:
176 time.sleep(.1) # wait to get message back from kernel
177 children = get_child_msgs(msg_id)
178 if len(children):
179 count = children[0]['content']['execution_count']
180 echo("In[%d]: %s" %(count,prompt))
181 else:
182 echo("In[]: %s (no reply from kernel)" % prompt)
183 else:
184 echo("In[]: %s" % prompt)
185
186 def run_this_line():
187 msg_id = send(vim.current.line)
188 print_prompt(vim.current.line, msg_id)
189
190 def run_these_lines():
191 r = vim.current.range
192 lines = "\n".join(vim.current.buffer[r.start:r.end+1])
193 msg_id = send(lines)
194 #alternative way of doing this in more recent versions of ipython
195 #but %paste only works on the local machine
196 #vim.command("\"*yy")
197 #send("'%paste')")
198 #reselect the previously highlighted block
199 vim.command("normal gv")
200 if not reselect:
201 vim.command("normal ")
202
203 #vim lines start with 1
204 #print "lines %d-%d sent to ipython"% (r.start+1,r.end+1)
205 prompt = "lines %d-%d "% (r.start+1,r.end+1)
206 print_prompt(prompt,msg_id)
207
208 def dedent_run_this_line():
209 vim.command("left")
210 run_this_line()
211 vim.command("undo")
212
213 def dedent_run_these_lines():
214 vim.command("'<,'>left")
215 run_these_lines()
216 vim.command("undo")
217
218 #def set_this_line():
219 # # not sure if there's a way to do this, since we have multiple clients
220 # send("_ip.IP.rl_next_input= \'%s\'" % vim.current.line.replace("\'","\\\'"))
221 # #print "line \'%s\' set at ipython prompt"% vim.current.line
222 # echo("line \'%s\' set at ipython prompt"% vim.current.line,'Statement')
223
224
225 def toggle_reselect():
226 global reselect
227 reselect=not reselect
228 print "F9 will%sreselect lines after sending to ipython"% (reselect and " " or " not ")
229
230 #def set_breakpoint():
231 # send("__IP.InteractiveTB.pdb.set_break('%s',%d)" % (vim.current.buffer.name,
232 # vim.current.window.cursor[0]))
233 # print "set breakpoint in %s:%d"% (vim.current.buffer.name,
234 # vim.current.window.cursor[0])
235 #
236 #def clear_breakpoint():
237 # send("__IP.InteractiveTB.pdb.clear_break('%s',%d)" % (vim.current.buffer.name,
238 # vim.current.window.cursor[0]))
239 # print "clearing breakpoint in %s:%d" % (vim.current.buffer.name,
240 # vim.current.window.cursor[0])
241 #
242 #def clear_all_breakpoints():
243 # send("__IP.InteractiveTB.pdb.clear_all_breaks()");
244 # print "clearing all breakpoints"
245 #
246 #def run_this_file_pdb():
247 # send(' __IP.InteractiveTB.pdb.run(\'execfile("%s")\')' % (vim.current.buffer.name,))
248 # #send('run -d %s' % (vim.current.buffer.name,))
249 # echo("In[]: run -d %s (using pdb)" % vim.current.buffer.name)
250
251 EOF
252
253 fun! <SID>toggle_send_on_save()
254 if exists("s:ssos") && s:ssos == 0
255 let s:ssos = 1
256 au BufWritePost *.py :py run_this_file()
257 echo "Autosend On"
258 else
259 let s:ssos = 0
260 au! BufWritePost *.py
261 echo "Autosend Off"
262 endif
263 endfun
264
265 map <silent> <F5> :python run_this_file()<CR>
266 map <silent> <S-F5> :python run_this_line()<CR>
267 map <silent> <F9> :python run_these_lines()<CR>
268 map <leader>d :py get_doc_buffer()<CR>
269 map <silent> <S-F9> :python toggle_reselect()<CR>
270 "map <silent> <C-F6> :python send('%pdb')<CR>
271 "map <silent> <F6> :python set_breakpoint()<CR>
272 "map <silent> <s-F6> :python clear_breakpoint()<CR>
273 "map <silent> <F7> :python run_this_file_pdb()<CR>
274 "map <silent> <s-F7> :python clear_all_breaks()<CR>
275 imap <C-F5> <C-O><F5>
276 imap <S-F5> <C-O><S-F5>
277 imap <silent> <F5> <C-O><F5>
278 map <C-F5> :call <SID>toggle_send_on_save()<CR>
279
280 "pi custom
281 map <silent> <C-Return> :python run_this_file()<CR>
282 map <silent> <C-s> :python run_this_line()<CR>
283 map <silent> <M-s> :python dedent_run_this_line()<CR>
284 vmap <silent> <C-S> :python run_these_lines()<CR>
285 vmap <silent> <M-s> :python dedent_run_these_lines()<CR>
286 "map <silent> <C-p> :python set_this_line()<CR>
287 map <silent> <M-c> I#<ESC>
288 vmap <silent> <M-c> I#<ESC>
289 map <silent> <M-C> :s/^\([ \t]*\)#/\1/<CR>
290 vmap <silent> <M-C> :s/^\([ \t]*\)#/\1/<CR>
291
292 command! -nargs=+ IPython :py km_from_string("<args>")
293 command! -nargs=0 IPythonClipboard :py km_from_string(vim.eval('@+'))
294 command! -nargs=0 IPythonXSelection :py km_from_string(vim.eval('@*'))
295
296 function! IPythonBalloonExpr()
297 python << endpython
298 word = vim.eval('v:beval_text')
299 reply = get_doc(word)
300 #reply = reply[:40]
301 vim.command("let l:doc = %s"% reply)
302 endpython
303 return l:doc
304 endfunction
305 set bexpr=IPythonBalloonExpr()
306 set ballooneval
307
308 fun! CompleteIPython(findstart, base)
309 if a:findstart
310 " locate the start of the word
311 let line = getline('.')
312 let start = col('.') - 1
313 while start > 0 && line[start-1] =~ '\k\|\.' "keyword
314 let start -= 1
315 endwhile
316 echo start
317 return start
318 else
319 " find months matching with "a:base"
320 let res = []
321 python << endpython
322 base = vim.eval("a:base")
323 findstart = vim.eval("a:findstart")
324 msg_id = km.shell_channel.complete(base, vim.current.line, vim.eval("col('.')"))
325 time.sleep(.1)
326 m = get_child_msgs(msg_id)[0]
327
328 matches = m['content']['matches']
329 #end = len(base)
330 #completions = [m[end:]+findstart+base for m in matches]
331 matches.insert(0,base) # the "no completion" version
332 completions = matches
333 vim.command("let l:completions = %s"% completions)
334 endpython
335 for m in l:completions
336 "if m =~ '^' . a:base
337 call add(res, m)
338 "endif
339 endfor
340 return res
341 endif
342 endfun
343 set completefunc=CompleteIPython
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now