Show More
@@ -4,11 +4,10 b' vim-ipython' | |||||
4 |
|
4 | |||
5 | A two-way integration between Vim and IPython 0.11+ |
|
5 | A two-way integration between Vim and IPython 0.11+ | |
6 |
|
6 | |||
7 | author: Paul Ivanov (http://pirsquared.org) |
|
7 | * author: Paul Ivanov (http://pirsquared.org) | |
8 |
|
8 | * github: http://github.com/ivanov/vim-ipython | ||
9 | github: http://github.com/ivanov/vim-ipython |
|
9 | * demos: http://pirsquared.org/vim-ipython/ | |
10 |
|
10 | * blogpost: http://pirsquared.org/blog/2011/07/28/vim-ipython/ | ||
11 | demo: http://pirsquared.org/vim-ipython/ |
|
|||
12 |
|
11 | |||
13 | Using this plugin, you can send lines or whole files for IPython to |
|
12 | 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 |
|
13 | execute, and also get back object introspection and word completions in | |
@@ -16,30 +15,46 b' Vim, like what you get with: ``object?<enter>`` and ``object.<tab>`` in' | |||||
16 | IPython. |
|
15 | IPython. | |
17 |
|
16 | |||
18 | The big change from previous versions of ``ipy.vim`` is that it no longer |
|
17 | The big change from previous versions of ``ipy.vim`` is that it no longer | |
19 |
requires the brittle ipy_vimserver.py instantiation, and since |
|
18 | requires the old brittle ``ipy_vimserver.py`` instantiation, and since | |
20 |
just vim and python, it is platform independent (i.e. should work |
|
19 | it uses just vim and python, it is platform independent (i.e. should work | |
21 | on windows, unlike the previous \*nix only solution) |
|
20 | even on windows, unlike the previous \*nix only solution). The requirements | |
|
21 | are IPython 0.11+ with zeromq capabilities, vim compiled with +python. | |||
22 |
|
22 | |||
|
23 | If you can launch ``ipython qtconsole`` or ``ipython kernel``, and | |||
|
24 | ``:echo has('python')`` returns 1 in vim, you should be good to go. | |||
23 |
|
25 | |||
24 | ----------------- |
|
26 | ----------------- | |
25 | Quickstart Guide: |
|
27 | Quickstart Guide: | |
26 | ----------------- |
|
28 | ----------------- | |
27 | Start ``ipython qtconsole`` and copy the connection string. |
|
29 | Start ``ipython qtconsole`` [*]_ and copy the connection string. | |
28 | Source ``ipy.vim`` file, which provides new IPython command:: |
|
30 | Source ``ipy.vim`` file, which provides new IPython command:: | |
29 |
|
31 | |||
30 |
:source ipy.vim |
|
32 | :source ipy.vim | |
31 | (or copy it to ~/.vim/ftplugin/python to load automatically) |
|
33 | (or copy it to ~/.vim/ftplugin/python to load automatically) | |
32 |
|
34 | |||
33 |
:IPythonClipboard |
|
35 | :IPythonClipboard | |
34 | (or :IPythonXSelection if you're using X11 without having to copy) |
|
36 | (or :IPythonXSelection if you're using X11 without having to copy) | |
35 |
|
37 | |||
36 |
The :IPython command allows you to put the full string |
|
38 | The :IPython command allows you to put the full connection string. For IPython | |
|
39 | 0.11, it would look like this:: | |||
37 |
|
40 | |||
38 | :IPython --existing --shell=41882 --iopub=43286 --stdin=34987 --hb=36697 |
|
41 | :IPython --existing --shell=41882 --iopub=43286 --stdin=34987 --hb=36697 | |
39 |
|
42 | |||
|
43 | and for IPython 0.12, like this:: | |||
|
44 | ||||
|
45 | :IPython --existing kernel-85997.json | |||
|
46 | ||||
40 | The ``:IPythonClipboard`` command just uses the ``+`` register to get the |
|
47 | The ``:IPythonClipboard`` command just uses the ``+`` register to get the | |
41 | connection string, whereas ``:IPythonXSelection`` uses the ``*`` register |
|
48 | connection string, whereas ``:IPythonXSelection`` uses the ``*`` register | |
42 |
|
49 | |||
|
50 | .. [*] Though the demos above use ``qtconsole``, it is not required | |||
|
51 | for this workflow, it's just that it was the easiest way to show how to | |||
|
52 | make use of the new functionality in 0.11 release. In the current git | |||
|
53 | trunk of IPython, you can use ``ipython kernel`` to create a kernel and | |||
|
54 | get the connection string to use for any frontend (including vim-ipython). | |||
|
55 | If you are still using 0.11, you can launch a regular kernel using | |||
|
56 | ``python -c "from IPython.zmq.ipkernel import main; main()"`` | |||
|
57 | ||||
43 | ------------------------ |
|
58 | ------------------------ | |
44 | Sending lines to IPython |
|
59 | Sending lines to IPython | |
45 | ------------------------ |
|
60 | ------------------------ | |
@@ -50,10 +65,12 b' Now type out a line and send it to IPython using ``<Ctrl-S>`` from Command mode:' | |||||
50 | You should see a notification message confirming the line was sent, along |
|
65 | 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``. |
|
66 | with the input number for the line, like so ``In[1]: import os``. | |
52 |
|
67 | |||
53 | ``<Ctrl-S>`` also works from insert mode, but doesn't show notification |
|
68 | ``<Ctrl-S>`` also works from insert mode, but doesn't show notification, | |
|
69 | unless ``monitor_subchannel`` is set to ``True`` (see `vim-ipython 'shell'`_, | |||
|
70 | below) | |||
54 |
|
71 | |||
55 |
It also works blockwise in Visual Mode. S |
|
72 | It also works blockwise in Visual Mode. Select and send these lines using | |
56 | send these lines using ``<Ctrl-S>``:: |
|
73 | ``<Ctrl-S>``:: | |
57 |
|
74 | |||
58 | import this,math # secret decoder ring |
|
75 | import this,math # secret decoder ring | |
59 | a,b,c,d,e,f,g,h,i = range(1,10) |
|
76 | a,b,c,d,e,f,g,h,i = range(1,10) | |
@@ -65,7 +82,7 b' send these lines using ``<Ctrl-S>``::' | |||||
65 | '%d'*len(code)%code == str(int(math.pi*1e5)) |
|
82 | '%d'*len(code)%code == str(int(math.pi*1e5)) | |
66 |
|
83 | |||
67 | Then, go to the qtconsole and run this line:: |
|
84 | Then, go to the qtconsole and run this line:: | |
68 |
|
85 | |||
69 | print secret_decoder(_i,_) |
|
86 | print secret_decoder(_i,_) | |
70 |
|
87 | |||
71 | You can also send whole files to IPython's ``%run`` magic using ``<F5>``. |
|
88 | You can also send whole files to IPython's ``%run`` magic using ``<F5>``. | |
@@ -86,22 +103,87 b" IPython's tab-completion Functionality" | |||||
86 | vim-ipython activates a 'completefunc' that queries IPython. |
|
103 | vim-ipython activates a 'completefunc' that queries IPython. | |
87 | A completefunc is activated using ``Ctrl-X Ctrl-U`` in Insert Mode (vim |
|
104 | 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 |
|
105 | default). You can combine this functionality with SuperTab to get tab | |
89 |
completion |
|
106 | completion. | |
90 |
|
107 | |||
91 | --------------- |
|
108 | ------------------- | |
92 | Current issues: |
|
109 | vim-ipython 'shell' | |
93 | --------------- |
|
110 | ------------------- | |
94 | For now, vim-ipython only connects to an ipython session in progress. |
|
111 | **NEW since IPython 0.11**! | |
|
112 | ||||
|
113 | By monitoring km.sub_channel, we can recreate what messages were sent to | |||
|
114 | IPython, and what IPython sends back in response. | |||
|
115 | ||||
|
116 | ``monitor_subchannel`` is a parameter that sets whether this 'shell' should | |||
|
117 | updated on every sent command (default: True). | |||
|
118 | ||||
|
119 | If at any later time you wish to bring this shell up, including if you've set | |||
|
120 | ``monitor_subchannel=False``, hit ``<leader>s``. | |||
95 |
|
121 | |||
96 | ipy.vim takes a while to load, I'll eventually move the python code to its |
|
122 | ------- | |
97 | own file and do a lazy import (only when the IPython command is called) |
|
123 | Options | |
|
124 | ------- | |||
|
125 | You can change these at the top of the ipy.vim:: | |||
98 |
|
|
126 | ||
99 | The ipdb integration is not yet re-implemented. |
|
127 | reselect = False # reselect lines after sending from Visual mode | |
|
128 | show_execution_count = True # wait to get numbers for In[43]: feedback? | |||
|
129 | monitor_subchannel = True # update vim-ipython 'shell' on every send? | |||
|
130 | run_flags= "-i" # flags to for IPython's run magic when using <F5> | |||
100 |
|
|
131 | ||
101 | Need to add more message handling for sub_channel messages from IPython |
|
132 | **Disabling default mappings** | |
102 | (i.e. notification of changes which were not sent from vim). |
|
133 | In your own ``.vimrc``, if you don't like the mappings provided by default, | |
|
134 | you can define a variable ``let g:ipy_perform_mappings=0`` which will prevent | |||
|
135 | vim-ipython from defining any of the default mappings. | |||
103 |
|
136 | |||
104 | ------ |
|
137 | --------------- | |
105 | Thanks |
|
138 | Current issues: | |
106 | ------ |
|
139 | --------------- | |
107 | @MinRK for guiding me through the IPython kernel manager protocol. |
|
140 | - For now, vim-ipython only connects to an ipython session in progress. | |
|
141 | - The ipdb integration is not yet re-implemented. | |||
|
142 | - If you're running inside ``screen``, read about the ``<CTRL-S>`` issue `here | |||
|
143 | <http://munkymorgy.blogspot.com/2008/07/screen-ctrl-s-bug.html>`_, and add | |||
|
144 | this line to your ``.bashrc`` to fix it:: | |||
|
145 | ||||
|
146 | stty stop undef # to unmap ctrl-s | |||
|
147 | ||||
|
148 | - In vim, if you're getting ``ImportError: No module named | |||
|
149 | IPython.zmq.blockingkernelmanager`` but are able to import it in regular | |||
|
150 | python, **either** | |||
|
151 | ||||
|
152 | 1. your ``sys.path`` in vim differs from the ``sys.path`` in regular python. | |||
|
153 | Try running these two lines, and comparing their output files:: | |||
|
154 | ||||
|
155 | $ vim -c 'py import vim, sys; vim.current.buffer.append(sys.path)' -c ':wq vim_syspath' | |||
|
156 | $ python -c "import sys; f=file('python_syspath','w'); f.write('\n'.join(sys.path)); f.close()" | |||
|
157 | ||||
|
158 | **or** | |||
|
159 | ||||
|
160 | 2. your vim is compiled against a different python than you are launching. See | |||
|
161 | if there's a difference between :: | |||
|
162 | ||||
|
163 | $ vim -c ':py import os; print os.__file__' -c ':q' | |||
|
164 | $ python -c ':py import os; print os.__file__' | |||
|
165 | ||||
|
166 | - For vim inside a terminal, using the arrow keys won't work inside a | |||
|
167 | documentation buffer, because the mapping for ``<Esc>`` overlaps with | |||
|
168 | ``^[OA`` and so on, and we use ``<Esc>`` as a quick way of closing the | |||
|
169 | documentation preview window. If you want go without this quick close | |||
|
170 | functionality and want to use the arrow keys instead, look for instructions | |||
|
171 | starting with "Known issue: to enable the use of arrow keys..." in the | |||
|
172 | ``get_doc_buffer`` function. | |||
|
173 | ||||
|
174 | - @fholgado's update to ``minibufexpl.vim`` that is up on GitHub will always | |||
|
175 | put the cursor in the minibuf after sending a command when | |||
|
176 | ``monitor_subchannel`` is set. This is a bug in minibufexpl.vim and the workaround | |||
|
177 | is described in vim-ipython issue #7. | |||
|
178 | ||||
|
179 | ---------------------------- | |||
|
180 | Thanks and Bug Participation | |||
|
181 | ---------------------------- | |||
|
182 | * @MinRK for guiding me through the IPython kernel manager protocol. | |||
|
183 | * @nakamuray and @tcheneau for reporting and providing a fix for when vim is compiled without a gui (#1) | |||
|
184 | * @unpingco for reporting Windows bugs (#3,#4) | |||
|
185 | * @simon-b for terminal vim arrow key issue (#5) | |||
|
186 | * @jorgesca and @kwgoodman for shell (#6) | |||
|
187 | * @zeekay for easily allowing custom mappings (#9) | |||
|
188 | * @minrk for support of connection_file-based IPython connection (#13) | |||
|
189 | * @jorgesca for reporting the lack of profile handling capability (#14) |
@@ -17,10 +17,17 b'' | |||||
17 | " |
|
17 | " | |
18 | " written by Paul Ivanov (http://pirsquared.org) |
|
18 | " written by Paul Ivanov (http://pirsquared.org) | |
19 | python << EOF |
|
19 | python << EOF | |
20 | import time |
|
20 | reselect = False # reselect lines after sending from Visual mode | |
|
21 | show_execution_count = True # wait to get numbers for In[43]: feedback? | |||
|
22 | monitor_subchannel = True # update vim-ipython 'shell' on every send? | |||
|
23 | run_flags= "-i" # flags to for IPython's run magic when using <F5> | |||
|
24 | ||||
21 | import vim |
|
25 | import vim | |
22 | import sys |
|
26 | import sys | |
23 |
|
27 | |||
|
28 | # get around unicode problems when interfacing with vim | |||
|
29 | vim_encoding=vim.eval('&encoding') or 'utf-8' | |||
|
30 | ||||
24 | try: |
|
31 | try: | |
25 | sys.stdout.flush |
|
32 | sys.stdout.flush | |
26 | except AttributeError: |
|
33 | except AttributeError: | |
@@ -34,10 +41,6 b' except AttributeError:' | |||||
34 | sys.stdout = WithFlush(sys.stdout) |
|
41 | sys.stdout = WithFlush(sys.stdout) | |
35 | sys.stderr = WithFlush(sys.stderr) |
|
42 | sys.stderr = WithFlush(sys.stderr) | |
36 |
|
43 | |||
37 | from IPython.zmq.blockingkernelmanager import BlockingKernelManager |
|
|||
38 |
|
||||
39 | from IPython.config.loader import KeyValueConfigLoader |
|
|||
40 | from IPython.zmq.kernelapp import kernel_aliases |
|
|||
41 |
|
44 | |||
42 |
|
45 | |||
43 | ip = '127.0.0.1' |
|
46 | ip = '127.0.0.1' | |
@@ -46,34 +49,62 b' try:' | |||||
46 | except NameError: |
|
49 | except NameError: | |
47 | km = None |
|
50 | km = None | |
48 |
|
51 | |||
49 | def km_from_string(s): |
|
52 | def km_from_string(s=''): | |
50 | """create kernel manager from IPKernelApp string |
|
53 | """create kernel manager from IPKernelApp string | |
51 | such as '--shell=47378 --iopub=39859 --stdin=36778 --hb=52668' |
|
54 | such as '--shell=47378 --iopub=39859 --stdin=36778 --hb=52668' for IPython 0.11 | |
|
55 | or just 'kernel-12345.json' for IPython 0.12 | |||
52 | """ |
|
56 | """ | |
53 | global km,send |
|
57 | from os.path import join as pjoin | |
54 | # vim interface currently only deals with existing kernels |
|
58 | from IPython.zmq.blockingkernelmanager import BlockingKernelManager, Empty | |
55 | s = s.replace('--existing','') |
|
59 | from IPython.config.loader import KeyValueConfigLoader | |
56 | loader = KeyValueConfigLoader(s.split(), aliases=kernel_aliases) |
|
60 | from IPython.zmq.kernelapp import kernel_aliases | |
57 | cfg = loader.load_config()['KernelApp'] |
|
61 | global km,send,Empty | |
58 | try: |
|
62 | ||
59 | km = BlockingKernelManager( |
|
63 | s = s.replace('--existing', '') | |
60 | shell_address=(ip, cfg['shell_port']), |
|
64 | if 'connection_file' in BlockingKernelManager.class_trait_names(): | |
61 | sub_address=(ip, cfg['iopub_port']), |
|
65 | from IPython.lib.kernel import find_connection_file | |
62 | stdin_address=(ip, cfg['stdin_port']), |
|
66 | # 0.12 uses files instead of a collection of ports | |
63 | hb_address=(ip, cfg['hb_port'])) |
|
67 | # include default IPython search path | |
64 | except KeyError,e: |
|
68 | # filefind also allows for absolute paths, in which case the search | |
65 | echo(":IPython " +s + " failed", "Info") |
|
69 | # is ignored | |
66 | echo("^-- failed --"+e.message.replace('_port','')+" not specified", "Error") |
|
70 | try: | |
67 | return |
|
71 | # XXX: the following approach will be brittle, depending on what | |
|
72 | # connection strings will end up looking like in the future, and | |||
|
73 | # whether or not they are allowed to have spaces. I'll have to sync | |||
|
74 | # up with the IPython team to address these issues -pi | |||
|
75 | if '--profile' in s: | |||
|
76 | k,p = s.split('--profile') | |||
|
77 | k = k.lstrip().rstrip() # kernel part of the string | |||
|
78 | p = p.lstrip().rstrip() # profile part of the string | |||
|
79 | fullpath = find_connection_file(k,p) | |||
|
80 | else: | |||
|
81 | fullpath = find_connection_file(s.lstrip().rstrip()) | |||
|
82 | except IOError,e: | |||
|
83 | echo(":IPython " + s + " failed", "Info") | |||
|
84 | echo("^-- failed '" + s + "' not found", "Error") | |||
|
85 | return | |||
|
86 | km = BlockingKernelManager(connection_file = fullpath) | |||
|
87 | km.load_connection_file() | |||
|
88 | else: | |||
|
89 | if s == '': | |||
|
90 | echo(":IPython 0.11 requires the full connection string") | |||
|
91 | return | |||
|
92 | loader = KeyValueConfigLoader(s.split(), aliases=kernel_aliases) | |||
|
93 | cfg = loader.load_config()['KernelApp'] | |||
|
94 | try: | |||
|
95 | km = BlockingKernelManager( | |||
|
96 | shell_address=(ip, cfg['shell_port']), | |||
|
97 | sub_address=(ip, cfg['iopub_port']), | |||
|
98 | stdin_address=(ip, cfg['stdin_port']), | |||
|
99 | hb_address=(ip, cfg['hb_port'])) | |||
|
100 | except KeyError,e: | |||
|
101 | echo(":IPython " +s + " failed", "Info") | |||
|
102 | echo("^-- failed --"+e.message.replace('_port','')+" not specified", "Error") | |||
|
103 | return | |||
68 | km.start_channels() |
|
104 | km.start_channels() | |
69 | send = km.shell_channel.execute |
|
105 | send = km.shell_channel.execute | |
70 | return km |
|
106 | return km | |
71 |
|
107 | |||
72 |
|
||||
73 | reselect = False |
|
|||
74 | show_id= True |
|
|||
75 | run_flags= "-i" |
|
|||
76 |
|
||||
77 | def echo(arg,style="Question"): |
|
108 | def echo(arg,style="Question"): | |
78 | try: |
|
109 | try: | |
79 | vim.command("echohl %s" % style) |
|
110 | vim.command("echohl %s" % style) | |
@@ -88,12 +119,12 b' def disconnect():' | |||||
88 | pass |
|
119 | pass | |
89 |
|
120 | |||
90 | def get_doc(word): |
|
121 | def get_doc(word): | |
|
122 | if km is None: | |||
|
123 | return ["Not connected to IPython, cannot query \"%s\"" %word] | |||
91 | msg_id = km.shell_channel.object_info(word) |
|
124 | msg_id = km.shell_channel.object_info(word) | |
92 | time.sleep(.1) |
|
|||
93 | doc = get_doc_msg(msg_id) |
|
125 | doc = get_doc_msg(msg_id) | |
94 | #if len(doc): |
|
126 | # get around unicode problems when interfacing with vim | |
95 | # echo(word, 'Special') |
|
127 | return [d.encode(vim_encoding) for d in doc] | |
96 | return doc |
|
|||
97 |
|
128 | |||
98 | import re |
|
129 | import re | |
99 | # from http://serverfault.com/questions/71285/in-centos-4-4-how-can-i-strip-escape-sequences-from-a-text-file |
|
130 | # from http://serverfault.com/questions/71285/in-centos-4-4-how-can-i-strip-escape-sequences-from-a-text-file | |
@@ -102,25 +133,19 b' def strip_color_escapes(s):' | |||||
102 | return strip.sub('',s) |
|
133 | return strip.sub('',s) | |
103 |
|
134 | |||
104 | def get_doc_msg(msg_id): |
|
135 | def get_doc_msg(msg_id): | |
105 | content = get_child_msgs(msg_id)[0]['content'] |
|
136 | n = 13 # longest field name (empirically) | |
106 | n = 13 # longest field name |
|
|||
107 | ##XXX: debug (print the whole message) |
|
|||
108 | b=[] |
|
137 | b=[] | |
|
138 | try: | |||
|
139 | content = get_child_msg(msg_id)['content'] | |||
|
140 | except Empty: | |||
|
141 | # timeout occurred | |||
|
142 | return ["no reply from IPython kernel"] | |||
109 |
|
143 | |||
110 | if not content['found']: |
|
144 | if not content['found']: | |
111 | return b |
|
145 | return b | |
112 |
|
146 | |||
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', |
|
147 | for field in ['type_name','base_class','string_form','namespace', | |
122 | 'file','length','definition','source','docstring']: |
|
148 | 'file','length','definition','source','docstring']: | |
123 | # XXX: strip the 'definition' rich formatting |
|
|||
124 | c = content.get(field,None) |
|
149 | c = content.get(field,None) | |
125 | if c: |
|
150 | if c: | |
126 | if field in ['definition']: |
|
151 | if field in ['definition']: | |
@@ -135,21 +160,27 b' def get_doc_msg(msg_id):' | |||||
135 | return b |
|
160 | return b | |
136 |
|
161 | |||
137 | def get_doc_buffer(level=0): |
|
162 | def get_doc_buffer(level=0): | |
138 | word = vim.eval('expand("<cfile>")') |
|
163 | # empty string in case vim.eval return None | |
|
164 | word = vim.eval('expand("<cfile>")') or '' | |||
139 | doc = get_doc(word) |
|
165 | doc = get_doc(word) | |
140 | if len(doc) ==0: |
|
166 | if len(doc) ==0: | |
141 | echo(word+" not found","Error") |
|
167 | echo(word+" not found","Error") | |
142 | return |
|
168 | return | |
|
169 | # close any currently open preview windows | |||
143 | vim.command('pcl') |
|
170 | vim.command('pcl') | |
|
171 | # documentation buffer name is same as the query made to ipython | |||
144 | vim.command('new '+word) |
|
172 | vim.command('new '+word) | |
145 | vim.command('setlocal pvw modifiable noro') |
|
173 | vim.command('setlocal pvw modifiable noro') | |
|
174 | # doc window quick quit keys: 'q' and 'escape' | |||
146 | vim.command('map <buffer> q :q<CR>') |
|
175 | vim.command('map <buffer> q :q<CR>') | |
147 | vim.command('map <buffer> :q<CR>') |
|
176 | # Known issue: to enable the use of arrow keys inside the terminal when | |
148 | #vim.command('pedit '+docbuf.name) |
|
177 | # viewing the documentation, comment out the next line | |
|
178 | vim.command('map <buffer> <Esc> :q<CR>') | |||
|
179 | # and uncomment this line (which will work if you have a timoutlen set) | |||
|
180 | #vim.command('map <buffer> <Esc><Esc> :q<CR>') | |||
149 | b = vim.current.buffer |
|
181 | b = vim.current.buffer | |
150 | #b.append(doc) |
|
182 | b[:] = None | |
151 | b[:] = doc |
|
183 | b[:] = doc | |
152 | #b.append(doc) |
|
|||
153 | vim.command('setlocal nomodified bufhidden=wipe') |
|
184 | vim.command('setlocal nomodified bufhidden=wipe') | |
154 | #vim.command('setlocal previewwindow nomodifiable nomodified ro') |
|
185 | #vim.command('setlocal previewwindow nomodifiable nomodified ro') | |
155 | #vim.command('set previewheight=%d'%len(b))# go to previous window |
|
186 | #vim.command('set previewheight=%d'%len(b))# go to previous window | |
@@ -158,35 +189,153 b' def get_doc_buffer(level=0):' | |||||
158 | #vim.command('pedit doc') |
|
189 | #vim.command('pedit doc') | |
159 | #vim.command('normal ') # go to previous window |
|
190 | #vim.command('normal ') # go to previous window | |
160 |
|
191 | |||
161 | def get_child_msgs(msg_id): |
|
192 | def update_subchannel_msgs(debug=False): | |
|
193 | msgs = km.sub_channel.get_msgs() | |||
|
194 | if debug: | |||
|
195 | #try: | |||
|
196 | # vim.command("b debug_msgs") | |||
|
197 | #except vim.error: | |||
|
198 | # vim.command("new debug_msgs") | |||
|
199 | #finally: | |||
|
200 | db = vim.current.buffer | |||
|
201 | else: | |||
|
202 | db = [] | |||
|
203 | b = vim.current.buffer | |||
|
204 | startedin_vimipython = vim.eval('@%')=='vim-ipython' | |||
|
205 | if not startedin_vimipython: | |||
|
206 | # switch to preview window | |||
|
207 | vim.command( | |||
|
208 | "try" | |||
|
209 | "|silent! wincmd P" | |||
|
210 | "|catch /^Vim\%((\a\+)\)\=:E441/" | |||
|
211 | "|silent pedit +set\ ma vim-ipython" | |||
|
212 | "|silent! wincmd P" | |||
|
213 | "|endtry") | |||
|
214 | # if the current window is called 'vim-ipython' | |||
|
215 | if vim.eval('@%')=='vim-ipython': | |||
|
216 | # set the preview window height to the current height | |||
|
217 | vim.command("set pvh=" + vim.eval('winheight(0)')) | |||
|
218 | else: | |||
|
219 | # close preview window, it was something other than 'vim-ipython' | |||
|
220 | vim.command("pcl") | |||
|
221 | vim.command("silent pedit +set\ ma vim-ipython") | |||
|
222 | vim.command("wincmd P") #switch to preview window | |||
|
223 | # subchannel window quick quit key 'q' | |||
|
224 | vim.command('map <buffer> q :q<CR>') | |||
|
225 | vim.command("set bufhidden=hide buftype=nofile ft=python") | |||
|
226 | ||||
|
227 | #syntax highlighting for python prompt | |||
|
228 | # QtConsole In[] is blue, but I prefer the oldschool green | |||
|
229 | # since it makes the vim-ipython 'shell' look like the holidays! | |||
|
230 | #vim.command("hi Blue ctermfg=Blue guifg=Blue") | |||
|
231 | vim.command("hi Green ctermfg=Green guifg=Green") | |||
|
232 | vim.command("hi Red ctermfg=Red guifg=Red") | |||
|
233 | vim.command("syn keyword Green 'In\ []:'") | |||
|
234 | vim.command("syn match Green /^In \[[0-9]*\]\:/") | |||
|
235 | vim.command("syn match Red /^Out\[[0-9]*\]\:/") | |||
|
236 | b = vim.current.buffer | |||
|
237 | for m in msgs: | |||
|
238 | #db.append(str(m).splitlines()) | |||
|
239 | s = '' | |||
|
240 | if 'msg_type' not in m['header']: | |||
|
241 | # debug information | |||
|
242 | #echo('skipping a message on sub_channel','WarningMsg') | |||
|
243 | #echo(str(m)) | |||
|
244 | continue | |||
|
245 | elif m['header']['msg_type'] == 'status': | |||
|
246 | continue | |||
|
247 | elif m['header']['msg_type'] == 'stream': | |||
|
248 | s = strip_color_escapes(m['content']['data']) | |||
|
249 | elif m['header']['msg_type'] == 'pyout': | |||
|
250 | s = "Out[%d]: " % m['content']['execution_count'] | |||
|
251 | s += m['content']['data']['text/plain'] | |||
|
252 | elif m['header']['msg_type'] == 'pyin': | |||
|
253 | # TODO: the next line allows us to resend a line to ipython if | |||
|
254 | # %doctest_mode is on. In the future, IPython will send the | |||
|
255 | # execution_count on subchannel, so this will need to be updated | |||
|
256 | # once that happens | |||
|
257 | if 'execution_count' in m['content']: | |||
|
258 | s = "\nIn [%d]: "% m['content']['execution_count'] | |||
|
259 | else: | |||
|
260 | s = "\nIn [00]: " | |||
|
261 | s += m['content']['code'].strip() | |||
|
262 | elif m['header']['msg_type'] == 'pyerr': | |||
|
263 | c = m['content'] | |||
|
264 | s = "\n".join(map(strip_color_escapes,c['traceback'])) | |||
|
265 | s += c['ename'] + ":" + c['evalue'] | |||
|
266 | if s.find('\n') == -1: | |||
|
267 | # somewhat ugly unicode workaround from | |||
|
268 | # http://vim.1045645.n5.nabble.com/Limitations-of-vim-python-interface-with-respect-to-character-encodings-td1223881.html | |||
|
269 | if isinstance(s,unicode): | |||
|
270 | s=s.encode(vim_encoding) | |||
|
271 | b.append(s) | |||
|
272 | else: | |||
|
273 | try: | |||
|
274 | b.append(s.splitlines()) | |||
|
275 | except: | |||
|
276 | b.append([l.encode(vim_encoding) for l in s.splitlines()]) | |||
|
277 | # make a newline so we can just start typing there | |||
|
278 | if b[-1] != '': | |||
|
279 | b.append(['']) | |||
|
280 | vim.command('normal G') # go to the end of the file | |||
|
281 | if not startedin_vimipython: | |||
|
282 | vim.command('normal p') # go back to where you were | |||
|
283 | ||||
|
284 | def get_child_msg(msg_id): | |||
162 | # XXX: message handling should be split into its own process in the future |
|
285 | # XXX: message handling should be split into its own process in the future | |
163 | msgs= km.shell_channel.get_msgs() |
|
286 | while True: | |
164 | children = [m for m in msgs if m['parent_header']['msg_id'] == msg_id] |
|
287 | # get_msg will raise with Empty exception if no messages arrive in 1 second | |
165 | return children |
|
288 | m= km.shell_channel.get_msg(timeout=1) | |
|
289 | if m['parent_header']['msg_id'] == msg_id: | |||
|
290 | break | |||
|
291 | else: | |||
|
292 | #got a message, but not the one we were looking for | |||
|
293 | echo('skipping a message on shell_channel','WarningMsg') | |||
|
294 | return m | |||
166 |
|
295 | |||
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): |
|
296 | def print_prompt(prompt,msg_id=None): | |
174 | global show_id |
|
297 | """Print In[] or In[42] style messages""" | |
175 | if show_id and msg_id: |
|
298 | global show_execution_count | |
176 | time.sleep(.1) # wait to get message back from kernel |
|
299 | if show_execution_count and msg_id: | |
177 | children = get_child_msgs(msg_id) |
|
300 | # wait to get message back from kernel | |
178 | if len(children): |
|
301 | try: | |
179 | count = children[0]['content']['execution_count'] |
|
302 | child = get_child_msg(msg_id) | |
|
303 | count = child['content']['execution_count'] | |||
180 | echo("In[%d]: %s" %(count,prompt)) |
|
304 | echo("In[%d]: %s" %(count,prompt)) | |
181 |
e |
|
305 | except Empty: | |
182 | echo("In[]: %s (no reply from kernel)" % prompt) |
|
306 | echo("In[]: %s (no reply from IPython kernel)" % prompt) | |
183 | else: |
|
307 | else: | |
184 | echo("In[]: %s" % prompt) |
|
308 | echo("In[]: %s" % prompt) | |
185 |
|
309 | |||
|
310 | def with_subchannel(f,*args): | |||
|
311 | "conditionally monitor subchannel" | |||
|
312 | def f_with_update(*args): | |||
|
313 | try: | |||
|
314 | f(*args) | |||
|
315 | if monitor_subchannel: | |||
|
316 | update_subchannel_msgs() | |||
|
317 | except AttributeError: #if km is None | |||
|
318 | echo("not connected to IPython", 'Error') | |||
|
319 | return f_with_update | |||
|
320 | ||||
|
321 | @with_subchannel | |||
|
322 | def run_this_file(): | |||
|
323 | msg_id = send('run %s %s' % (run_flags, repr(vim.current.buffer.name),)) | |||
|
324 | print_prompt("In[]: run %s %s" % (run_flags, repr(vim.current.buffer.name)),msg_id) | |||
|
325 | ||||
|
326 | @with_subchannel | |||
186 | def run_this_line(): |
|
327 | def run_this_line(): | |
187 | msg_id = send(vim.current.line) |
|
328 | msg_id = send(vim.current.line) | |
188 | print_prompt(vim.current.line, msg_id) |
|
329 | print_prompt(vim.current.line, msg_id) | |
189 |
|
330 | |||
|
331 | @with_subchannel | |||
|
332 | def run_command(cmd): | |||
|
333 | msg_id = send(cmd) | |||
|
334 | print_prompt(cmd, msg_id) | |||
|
335 | if monitor_subchannel: | |||
|
336 | update_subchannel_msgs() | |||
|
337 | ||||
|
338 | @with_subchannel | |||
190 | def run_these_lines(): |
|
339 | def run_these_lines(): | |
191 | r = vim.current.range |
|
340 | r = vim.current.range | |
192 | lines = "\n".join(vim.current.buffer[r.start:r.end+1]) |
|
341 | lines = "\n".join(vim.current.buffer[r.start:r.end+1]) | |
@@ -208,16 +357,19 b' def run_these_lines():' | |||||
208 | def dedent_run_this_line(): |
|
357 | def dedent_run_this_line(): | |
209 | vim.command("left") |
|
358 | vim.command("left") | |
210 | run_this_line() |
|
359 | run_this_line() | |
211 | vim.command("undo") |
|
360 | vim.command("silent undo") | |
212 |
|
361 | |||
213 | def dedent_run_these_lines(): |
|
362 | def dedent_run_these_lines(): | |
214 | vim.command("'<,'>left") |
|
363 | r = vim.current.range | |
|
364 | shiftwidth = vim.eval('&shiftwidth') | |||
|
365 | count = int(vim.eval('indent(%d+1)/%s' % (r.start,shiftwidth))) | |||
|
366 | vim.command("'<,'>" + "<"*count) | |||
215 | run_these_lines() |
|
367 | run_these_lines() | |
216 | vim.command("undo") |
|
368 | vim.command("silent undo") | |
217 |
|
369 | |||
218 | #def set_this_line(): |
|
370 | #def set_this_line(): | |
219 | # # not sure if there's a way to do this, since we have multiple clients |
|
371 | # # not sure if there's a way to do this, since we have multiple clients | |
220 |
# send(" |
|
372 | # send("get_ipython().shell.set_next_input(\'%s\')" % vim.current.line.replace("\'","\\\'")) | |
221 | # #print "line \'%s\' set at ipython prompt"% vim.current.line |
|
373 | # #print "line \'%s\' set at ipython prompt"% vim.current.line | |
222 | # echo("line \'%s\' set at ipython prompt"% vim.current.line,'Statement') |
|
374 | # echo("line \'%s\' set at ipython prompt"% vim.current.line,'Statement') | |
223 |
|
375 | |||
@@ -262,34 +414,45 b' fun! <SID>toggle_send_on_save()' | |||||
262 | endif |
|
414 | endif | |
263 | endfun |
|
415 | endfun | |
264 |
|
416 | |||
265 | map <silent> <F5> :python run_this_file()<CR> |
|
417 | " Allow custom mappings | |
266 | map <silent> <S-F5> :python run_this_line()<CR> |
|
418 | if !exists('g:ipy_perform_mappings') | |
267 | map <silent> <F9> :python run_these_lines()<CR> |
|
419 | let g:ipy_perform_mappings = 1 | |
268 | map <leader>d :py get_doc_buffer()<CR> |
|
420 | endif | |
269 | map <silent> <S-F9> :python toggle_reselect()<CR> |
|
421 | if g:ipy_perform_mappings != 0 | |
270 |
|
|
422 | map <silent> <F5> :python run_this_file()<CR> | |
271 |
|
|
423 | map <silent> <S-F5> :python run_this_line()<CR> | |
272 |
|
|
424 | map <silent> <F9> :python run_these_lines()<CR> | |
273 | "map <silent> <F7> :python run_this_file_pdb()<CR> |
|
425 | map <silent> <leader>d :py get_doc_buffer()<CR> | |
274 | "map <silent> <s-F7> :python clear_all_breaks()<CR> |
|
426 | map <silent> <leader>s :py update_subchannel_msgs(); echo("vim-ipython shell updated",'Operator')<CR> | |
275 | imap <C-F5> <C-O><F5> |
|
427 | map <silent> <S-F9> :python toggle_reselect()<CR> | |
276 | imap <S-F5> <C-O><S-F5> |
|
428 | "map <silent> <C-F6> :python send('%pdb')<CR> | |
277 | imap <silent> <F5> <C-O><F5> |
|
429 | "map <silent> <F6> :python set_breakpoint()<CR> | |
278 | map <C-F5> :call <SID>toggle_send_on_save()<CR> |
|
430 | "map <silent> <s-F6> :python clear_breakpoint()<CR> | |
279 |
|
431 | "map <silent> <F7> :python run_this_file_pdb()<CR> | ||
280 | "pi custom |
|
432 | "map <silent> <s-F7> :python clear_all_breaks()<CR> | |
281 | map <silent> <C-Return> :python run_this_file()<CR> |
|
433 | imap <C-F5> <C-O><F5> | |
282 | map <silent> <C-s> :python run_this_line()<CR> |
|
434 | imap <S-F5> <C-O><S-F5> | |
283 | map <silent> <M-s> :python dedent_run_this_line()<CR> |
|
435 | imap <silent> <F5> <C-O><F5> | |
284 | vmap <silent> <C-S> :python run_these_lines()<CR> |
|
436 | map <C-F5> :call <SID>toggle_send_on_save()<CR> | |
285 | vmap <silent> <M-s> :python dedent_run_these_lines()<CR> |
|
437 | "" Example of how to quickly clear the current plot with a keystroke | |
286 | "map <silent> <C-p> :python set_this_line()<CR> |
|
438 | map <silent> <F12> :python run_command("plt.clf()")<cr> | |
287 | map <silent> <M-c> I#<ESC> |
|
439 | "" Example of how to quickly close all figures with a keystroke | |
288 | vmap <silent> <M-c> I#<ESC> |
|
440 | map <silent> <F11> :python run_command("plt.close('all')")<cr> | |
289 | map <silent> <M-C> :s/^\([ \t]*\)#/\1/<CR> |
|
441 | ||
290 | vmap <silent> <M-C> :s/^\([ \t]*\)#/\1/<CR> |
|
442 | "pi custom | |
291 |
|
443 | map <silent> <C-Return> :python run_this_file()<CR> | ||
292 | command! -nargs=+ IPython :py km_from_string("<args>") |
|
444 | map <silent> <C-s> :python run_this_line()<CR> | |
|
445 | imap <silent> <C-s> <C-O>:python run_this_line()<CR> | |||
|
446 | map <silent> <M-s> :python dedent_run_this_line()<CR> | |||
|
447 | vmap <silent> <C-S> :python run_these_lines()<CR> | |||
|
448 | vmap <silent> <M-s> :python dedent_run_these_lines()<CR> | |||
|
449 | map <silent> <M-c> I#<ESC> | |||
|
450 | vmap <silent> <M-c> I#<ESC> | |||
|
451 | map <silent> <M-C> :s/^\([ \t]*\)#/\1/<CR> | |||
|
452 | vmap <silent> <M-C> :s/^\([ \t]*\)#/\1/<CR> | |||
|
453 | endif | |||
|
454 | ||||
|
455 | command! -nargs=* IPython :py km_from_string("<args>") | |||
293 | command! -nargs=0 IPythonClipboard :py km_from_string(vim.eval('@+')) |
|
456 | command! -nargs=0 IPythonClipboard :py km_from_string(vim.eval('@+')) | |
294 | command! -nargs=0 IPythonXSelection :py km_from_string(vim.eval('@*')) |
|
457 | command! -nargs=0 IPythonXSelection :py km_from_string(vim.eval('@*')) | |
295 |
|
458 | |||
@@ -297,13 +460,14 b' function! IPythonBalloonExpr()' | |||||
297 | python << endpython |
|
460 | python << endpython | |
298 | word = vim.eval('v:beval_text') |
|
461 | word = vim.eval('v:beval_text') | |
299 | reply = get_doc(word) |
|
462 | reply = get_doc(word) | |
300 | #reply = reply[:40] |
|
|||
301 | vim.command("let l:doc = %s"% reply) |
|
463 | vim.command("let l:doc = %s"% reply) | |
302 | endpython |
|
464 | endpython | |
303 | return l:doc |
|
465 | return l:doc | |
304 | endfunction |
|
466 | endfunction | |
305 | set bexpr=IPythonBalloonExpr() |
|
467 | if has('balloon_eval') | |
306 | set ballooneval |
|
468 | set bexpr=IPythonBalloonExpr() | |
|
469 | set ballooneval | |||
|
470 | endif | |||
307 |
|
471 | |||
308 | fun! CompleteIPython(findstart, base) |
|
472 | fun! CompleteIPython(findstart, base) | |
309 | if a:findstart |
|
473 | if a:findstart | |
@@ -322,21 +486,35 b' fun! CompleteIPython(findstart, base)' | |||||
322 | base = vim.eval("a:base") |
|
486 | base = vim.eval("a:base") | |
323 | findstart = vim.eval("a:findstart") |
|
487 | findstart = vim.eval("a:findstart") | |
324 | msg_id = km.shell_channel.complete(base, vim.current.line, vim.eval("col('.')")) |
|
488 | msg_id = km.shell_channel.complete(base, vim.current.line, vim.eval("col('.')")) | |
325 | time.sleep(.1) |
|
489 | try: | |
326 |
m = get_child_msg |
|
490 | m = get_child_msg(msg_id) | |
327 |
|
491 | matches = m['content']['matches'] | ||
328 | matches = m['content']['matches'] |
|
492 | matches.insert(0,base) # the "no completion" version | |
329 | #end = len(base) |
|
493 | # we need to be careful with unicode, because we can have unicode | |
330 | #completions = [m[end:]+findstart+base for m in matches] |
|
494 | # completions for filenames (for the %run magic, for example). So the next | |
331 | matches.insert(0,base) # the "no completion" version |
|
495 | # line will fail on those: | |
332 |
completions |
|
496 | #completions= [str(u) for u in matches] | |
333 | vim.command("let l:completions = %s"% completions) |
|
497 | # because str() won't work for non-ascii characters | |
|
498 | # and we also have problems with unicode in vim, hence the following: | |||
|
499 | completions = [s.encode(vim_encoding) for s in matches] | |||
|
500 | except Empty: | |||
|
501 | echo("no reply from IPython kernel") | |||
|
502 | completions=[''] | |||
|
503 | ## Additionally, we have no good way of communicating lists to vim, so we have | |||
|
504 | ## to turn in into one long string, which can be problematic if e.g. the | |||
|
505 | ## completions contain quotes. The next line will not work if some filenames | |||
|
506 | ## contain quotes - but if that's the case, the user's just asking for | |||
|
507 | ## it, right? | |||
|
508 | #completions = '["'+ '", "'.join(completions)+'"]' | |||
|
509 | #vim.command("let completions = %s" % completions) | |||
|
510 | ## An alternative for the above, which will insert matches one at a time, so | |||
|
511 | ## if there's a problem with turning a match into a string, it'll just not | |||
|
512 | ## include the problematic match, instead of not including anything. There's a | |||
|
513 | ## bit more indirection here, but I think it's worth it | |||
|
514 | for c in completions: | |||
|
515 | vim.command('call add(res,"'+c+'")') | |||
334 | endpython |
|
516 | endpython | |
335 | for m in l:completions |
|
517 | "call extend(res,completions) | |
336 | "if m =~ '^' . a:base |
|
|||
337 | call add(res, m) |
|
|||
338 | "endif |
|
|||
339 | endfor |
|
|||
340 | return res |
|
518 | return res | |
341 | endif |
|
519 | endif | |
342 | endfun |
|
520 | endfun |
General Comments 0
You need to be logged in to leave comments.
Login now