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