##// END OF EJS Templates
Better report errors where select is missing POLLHUP/POLERR (win32)....
Fernando Perez -
Show More
@@ -1,239 +1,244 b''
1 """ Integration with gvim, by Erich Heine
1 """ Integration with gvim, by Erich Heine
2
2
3 Provides a %vim magic command, and reuses the same vim session. Uses
3 Provides a %vim magic command, and reuses the same vim session. Uses
4 unix domain sockets for communication between vim and IPython. ipy.vim is
4 unix domain sockets for communication between vim and IPython. ipy.vim is
5 available in doc/examples of the IPython distribution.
5 available in doc/examples of the IPython distribution.
6
6
7 Slightly touched up email announcement (and description how to use it) by
7 Slightly touched up email announcement (and description how to use it) by
8 Erich Heine is here:
8 Erich Heine is here:
9
9
10 Ive recently been playing with ipython, and like it quite a bit. I did
10 Ive recently been playing with ipython, and like it quite a bit. I did
11 however discover a bit of frustration, namely with editor interaction.
11 however discover a bit of frustration, namely with editor interaction.
12 I am a gvim user, and using the command edit on a new file causes
12 I am a gvim user, and using the command edit on a new file causes
13 ipython to try and run that file as soon as the text editor opens
13 ipython to try and run that file as soon as the text editor opens
14 up. The -x command of course fixes this, but its still a bit annoying,
14 up. The -x command of course fixes this, but its still a bit annoying,
15 switching windows to do a run file, then back to the text
15 switching windows to do a run file, then back to the text
16 editor. Being a heavy tab user in gvim, another annoyance is not being
16 editor. Being a heavy tab user in gvim, another annoyance is not being
17 able to specify weather a new tab is how I choose to open the file.
17 able to specify weather a new tab is how I choose to open the file.
18
18
19 Not being one to shirk my open source duties (and seeing this as a
19 Not being one to shirk my open source duties (and seeing this as a
20 good excuse to poke around ipython internals), Ive created a script
20 good excuse to poke around ipython internals), Ive created a script
21 for having gvim and ipython work very nicely together. Ive attached
21 for having gvim and ipython work very nicely together. Ive attached
22 both to this email (hoping of course that the mailing list allows such
22 both to this email (hoping of course that the mailing list allows such
23 things).
23 things).
24
24
25 There are 2 files:
25 There are 2 files:
26
26
27 ipy_vimserver.py -- this file contains the ipython stuff
27 ipy_vimserver.py -- this file contains the ipython stuff
28 ipy.vim -- this file contains the gvim stuff
28 ipy.vim -- this file contains the gvim stuff
29
29
30 In combination they allow for a few functionalities:
30 In combination they allow for a few functionalities:
31
31
32 #1. the vim magic command. This is a fancy wrapper around the edit
32 #1. the vim magic command. This is a fancy wrapper around the edit
33 magic, that allows for a new option, -t, which opens the text in a new
33 magic, that allows for a new option, -t, which opens the text in a new
34 gvim tab. Otherwise it works the same as edit -x. (it internally
34 gvim tab. Otherwise it works the same as edit -x. (it internally
35 calls edit -x). This magic command also juggles vim server management,
35 calls edit -x). This magic command also juggles vim server management,
36 so when it is called when there is not a gvim running, it creates a
36 so when it is called when there is not a gvim running, it creates a
37 new gvim instance, named after the ipython session name. Once such a
37 new gvim instance, named after the ipython session name. Once such a
38 gvim instance is running, it will be used for subsequent uses of the
38 gvim instance is running, it will be used for subsequent uses of the
39 vim command.
39 vim command.
40
40
41 #2. ipython - gvim interaction. Once a file has been opened with the
41 #2. ipython - gvim interaction. Once a file has been opened with the
42 vim magic (and a session set up, see below), pressing the F5 key in
42 vim magic (and a session set up, see below), pressing the F5 key in
43 vim will cause the calling ipython instance to execute run
43 vim will cause the calling ipython instance to execute run
44 filename.py. (if you typo like I do, this is very useful)
44 filename.py. (if you typo like I do, this is very useful)
45
45
46 #3. ipython server - this is a thread wich listens on a unix domain
46 #3. ipython server - this is a thread wich listens on a unix domain
47 socket, and runs commands sent to that socket.
47 socket, and runs commands sent to that socket.
48
48
49 Note, this only works on POSIX systems, that allow for AF_UNIX type
49 Note, this only works on POSIX systems, that allow for AF_UNIX type
50 sockets. It has only been tested on linux (a fairly recent debian
50 sockets. It has only been tested on linux (a fairly recent debian
51 testing distro).
51 testing distro).
52
52
53 To install it put, the ipserver.py in your favorite locaion for
53 To install it put, the ipserver.py in your favorite locaion for
54 sourcing ipython scripts. I put the ipy.vim in
54 sourcing ipython scripts. I put the ipy.vim in
55 ~/.vim/after/ftplugin/python/.
55 ~/.vim/after/ftplugin/python/.
56
56
57 To use (this can be scripted im sure, but i usually have 2 or 3
57 To use (this can be scripted im sure, but i usually have 2 or 3
58 ipythons and corresponding gvims open):
58 ipythons and corresponding gvims open):
59
59
60 import ipy_vimserver
60 import ipy_vimserver
61 ipy_vimserver.setup('sessionname')
61 ipy_vimserver.setup('sessionname')
62
62
63 (Editors note - you can probably add these to your ipy_user_conf.py)
63 (Editors note - you can probably add these to your ipy_user_conf.py)
64
64
65 Then use ipython as you normally would, until you need to edit
65 Then use ipython as you normally would, until you need to edit
66 something. Instead of edit, use the vim magic. Thats it!
66 something. Instead of edit, use the vim magic. Thats it!
67
67
68 """
68 """
69
69
70 import IPython.ipapi
70 import IPython.ipapi
71 #import ipythonhooks
71 #import ipythonhooks
72 import socket, select
72 import socket, select
73 import os, threading, subprocess
73 import os, threading, subprocess
74 import re
74 import re
75
75
76 try:
76 ERRCONDS = select.POLLHUP|select.POLLERR
77 ERRCONDS = select.POLLHUP|select.POLLERR
78 except AttributeError:
79 raise ImportError("Vim server not supported on this platform - select "
80 "missing necessary POLLHUP/POLLERR functionality")
81
77 SERVER = None
82 SERVER = None
78 ip = IPython.ipapi.get()
83 ip = IPython.ipapi.get()
79
84
80 # this listens to a unix domain socket in a separate thread, so that comms
85 # this listens to a unix domain socket in a separate thread, so that comms
81 # between a vim instance and ipython can happen in a fun and productive way
86 # between a vim instance and ipython can happen in a fun and productive way
82 class IpyServer(threading.Thread):
87 class IpyServer(threading.Thread):
83 def __init__(self, sname):
88 def __init__(self, sname):
84 super(IpyServer, self).__init__()
89 super(IpyServer, self).__init__()
85 self.keep_running = True
90 self.keep_running = True
86 self.__sname = sname
91 self.__sname = sname
87 self.socket = socket.socket(socket.AF_UNIX)
92 self.socket = socket.socket(socket.AF_UNIX)
88 self.poller = select.poll()
93 self.poller = select.poll()
89 self.current_conns = dict()
94 self.current_conns = dict()
90 self.setDaemon(True)
95 self.setDaemon(True)
91
96
92 def listen(self):
97 def listen(self):
93 self.socket.bind(self.__sname)
98 self.socket.bind(self.__sname)
94 self.socket.listen(1)
99 self.socket.listen(1)
95
100
96 def __handle_error(self, socket):
101 def __handle_error(self, socket):
97 if socket == self.socket.fileno():
102 if socket == self.socket.fileno():
98 self.keep_running = False
103 self.keep_running = False
99 for a in self.current_conns.values():
104 for a in self.current_conns.values():
100 a.close()
105 a.close()
101 return False
106 return False
102 else:
107 else:
103 y = self.current_conns[socket]
108 y = self.current_conns[socket]
104 del self.current_conns[socket]
109 del self.current_conns[socket]
105 y.close()
110 y.close()
106 self.poller.unregister(socket)
111 self.poller.unregister(socket)
107
112
108 def serve_me(self):
113 def serve_me(self):
109 self.listen()
114 self.listen()
110 self.poller.register(self.socket,select.POLLIN|ERRCONDS)
115 self.poller.register(self.socket,select.POLLIN|ERRCONDS)
111
116
112 while self.keep_running:
117 while self.keep_running:
113 try:
118 try:
114 avail = self.poller.poll(1)
119 avail = self.poller.poll(1)
115 except:
120 except:
116 continue
121 continue
117
122
118 if not avail: continue
123 if not avail: continue
119
124
120 for sock, conds in avail:
125 for sock, conds in avail:
121 if conds & (ERRCONDS):
126 if conds & (ERRCONDS):
122 if self.__handle_error(sock): continue
127 if self.__handle_error(sock): continue
123 else: break
128 else: break
124
129
125 if sock == self.socket.fileno():
130 if sock == self.socket.fileno():
126 y = self.socket.accept()[0]
131 y = self.socket.accept()[0]
127 self.poller.register(y, select.POLLIN|ERRCONDS)
132 self.poller.register(y, select.POLLIN|ERRCONDS)
128 self.current_conns[y.fileno()] = y
133 self.current_conns[y.fileno()] = y
129 else: y = self.current_conns.get(sock)
134 else: y = self.current_conns.get(sock)
130
135
131 self.handle_request(y)
136 self.handle_request(y)
132
137
133 os.remove(self.__sname)
138 os.remove(self.__sname)
134
139
135 run = serve_me
140 run = serve_me
136
141
137 def stop(self):
142 def stop(self):
138 self.keep_running = False
143 self.keep_running = False
139
144
140 def handle_request(self,sock):
145 def handle_request(self,sock):
141 sock.settimeout(1)
146 sock.settimeout(1)
142 while self.keep_running:
147 while self.keep_running:
143 try:
148 try:
144 x = sock.recv(4096)
149 x = sock.recv(4096)
145 except socket.timeout:
150 except socket.timeout:
146 pass
151 pass
147 else:
152 else:
148 break
153 break
149 self.do_it(x)
154 self.do_it(x)
150
155
151 def do_it(self, data):
156 def do_it(self, data):
152 data = data.split('\n')
157 data = data.split('\n')
153 cmds = list()
158 cmds = list()
154 for line in data:
159 for line in data:
155 cmds.append(line)
160 cmds.append(line)
156 ip.runlines(cmds)
161 ip.runlines(cmds)
157
162
158
163
159 # try to help ensure that the unix domain socket is cleaned up proper
164 # try to help ensure that the unix domain socket is cleaned up proper
160 def shutdown_server(self):
165 def shutdown_server(self):
161 if SERVER:
166 if SERVER:
162 SERVER.stop()
167 SERVER.stop()
163 SERVER.join(3)
168 SERVER.join(3)
164 raise IPython.ipapi.TryNext
169 raise IPython.ipapi.TryNext
165
170
166 ip.set_hook('shutdown_hook', shutdown_server, 10)
171 ip.set_hook('shutdown_hook', shutdown_server, 10)
167
172
168 # this fun function exists to make setup easier for all, and makes the
173 # this fun function exists to make setup easier for all, and makes the
169 # vimhook function ready for instance specific communication
174 # vimhook function ready for instance specific communication
170 def setup(sessionname='',socketdir=os.path.expanduser('~/.ipython/')):
175 def setup(sessionname='',socketdir=os.path.expanduser('~/.ipython/')):
171 global SERVER
176 global SERVER
172
177
173 if sessionname:
178 if sessionname:
174 session = sessionname
179 session = sessionname
175 elif os.environ.get('IPY_SESSION'):
180 elif os.environ.get('IPY_SESSION'):
176 session = os.environ.get('IPY_SESSION')
181 session = os.environ.get('IPY_SESSION')
177 else:
182 else:
178 session = 'IPYS'
183 session = 'IPYS'
179 vimhook.vimserver=session
184 vimhook.vimserver=session
180 vimhook.ipyserver = os.path.join(socketdir, session)
185 vimhook.ipyserver = os.path.join(socketdir, session)
181 if not SERVER:
186 if not SERVER:
182 SERVER = IpyServer(vimhook.ipyserver)
187 SERVER = IpyServer(vimhook.ipyserver)
183 SERVER.start()
188 SERVER.start()
184
189
185
190
186
191
187 # calls gvim, with all ops happening on the correct gvim instance for this
192 # calls gvim, with all ops happening on the correct gvim instance for this
188 # ipython instance. it then calls edit -x (since gvim will return right away)
193 # ipython instance. it then calls edit -x (since gvim will return right away)
189 # things of note: it sets up a special environment, so that the ipy.vim script
194 # things of note: it sets up a special environment, so that the ipy.vim script
190 # can connect back to the ipython instance and do fun things, like run the file
195 # can connect back to the ipython instance and do fun things, like run the file
191 def vimhook(self, fname, line):
196 def vimhook(self, fname, line):
192 env = os.environ.copy()
197 env = os.environ.copy()
193 vserver = vimhook.vimserver.upper()
198 vserver = vimhook.vimserver.upper()
194 check = subprocess.Popen('gvim --serverlist', stdout = subprocess.PIPE,
199 check = subprocess.Popen('gvim --serverlist', stdout = subprocess.PIPE,
195 shell=True)
200 shell=True)
196 check.wait()
201 check.wait()
197 cval = [l for l in check.stdout.readlines() if vserver in l]
202 cval = [l for l in check.stdout.readlines() if vserver in l]
198
203
199 if cval:
204 if cval:
200 vimargs = '--remote%s' % (vimhook.extras,)
205 vimargs = '--remote%s' % (vimhook.extras,)
201 else:
206 else:
202 vimargs = ''
207 vimargs = ''
203 vimhook.extras = ''
208 vimhook.extras = ''
204
209
205 env['IPY_SESSION'] = vimhook.vimserver
210 env['IPY_SESSION'] = vimhook.vimserver
206 env['IPY_SERVER'] = vimhook.ipyserver
211 env['IPY_SERVER'] = vimhook.ipyserver
207
212
208 if line is None: line = ''
213 if line is None: line = ''
209 else: line = '+' + line
214 else: line = '+' + line
210 vim_cmd = 'gvim --servername %s %s %s %s' % (vimhook.vimserver, vimargs,
215 vim_cmd = 'gvim --servername %s %s %s %s' % (vimhook.vimserver, vimargs,
211 line, fname)
216 line, fname)
212 subprocess.call(vim_cmd, env=env, shell=True)
217 subprocess.call(vim_cmd, env=env, shell=True)
213
218
214
219
215 #default values to keep it sane...
220 #default values to keep it sane...
216 vimhook.vimserver = ''
221 vimhook.vimserver = ''
217 vimhook.ipyserver = ''
222 vimhook.ipyserver = ''
218
223
219 ip.set_hook('editor',vimhook)
224 ip.set_hook('editor',vimhook)
220
225
221 # this is set up so more vim specific commands can be added, instead of just
226 # this is set up so more vim specific commands can be added, instead of just
222 # the current -t. all thats required is a compiled regex, a call to do_arg(pat)
227 # the current -t. all thats required is a compiled regex, a call to do_arg(pat)
223 # and the logic to deal with the new feature
228 # and the logic to deal with the new feature
224 newtab = re.compile(r'-t(?:\s|$)')
229 newtab = re.compile(r'-t(?:\s|$)')
225 def vim(self, argstr):
230 def vim(self, argstr):
226 def do_arg(pat, rarg):
231 def do_arg(pat, rarg):
227 x = len(pat.findall(argstr))
232 x = len(pat.findall(argstr))
228 if x:
233 if x:
229 a = pat.sub('',argstr)
234 a = pat.sub('',argstr)
230 return rarg, a
235 return rarg, a
231 else: return '', argstr
236 else: return '', argstr
232
237
233 t, argstr = do_arg(newtab, '-tab')
238 t, argstr = do_arg(newtab, '-tab')
234 vimhook.extras = t
239 vimhook.extras = t
235 argstr = 'edit -x ' + argstr
240 argstr = 'edit -x ' + argstr
236 ip.magic(argstr)
241 ip.magic(argstr)
237
242
238 ip.expose_magic('vim', vim)
243 ip.expose_magic('vim', vim)
239
244
General Comments 0
You need to be logged in to leave comments. Login now