##// END OF EJS Templates
Better report errors where select is missing POLLHUP/POLERR (win32)....
Fernando Perez -
Show More
@@ -1,240 +1,245 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 from IPython.core import ipapi
70 from IPython.core import ipapi
71 from IPython.core.error import TryNext
71 from IPython.core.error import TryNext
72
72
73 import socket, select
73 import socket, select
74 import os, threading, subprocess
74 import os, threading, subprocess
75 import re
75 import re
76
76
77 ERRCONDS = select.POLLHUP|select.POLLERR
77 try:
78 ERRCONDS = select.POLLHUP|select.POLLERR
79 except AttributeError:
80 raise ImportError("Vim server not supported on this platform - select "
81 "missing necessary POLLHUP/POLLERR functionality")
82
78 SERVER = None
83 SERVER = None
79 ip = ipapi.get()
84 ip = ipapi.get()
80
85
81 # this listens to a unix domain socket in a separate thread, so that comms
86 # this listens to a unix domain socket in a separate thread, so that comms
82 # between a vim instance and ipython can happen in a fun and productive way
87 # between a vim instance and ipython can happen in a fun and productive way
83 class IpyServer(threading.Thread):
88 class IpyServer(threading.Thread):
84 def __init__(self, sname):
89 def __init__(self, sname):
85 super(IpyServer, self).__init__()
90 super(IpyServer, self).__init__()
86 self.keep_running = True
91 self.keep_running = True
87 self.__sname = sname
92 self.__sname = sname
88 self.socket = socket.socket(socket.AF_UNIX)
93 self.socket = socket.socket(socket.AF_UNIX)
89 self.poller = select.poll()
94 self.poller = select.poll()
90 self.current_conns = dict()
95 self.current_conns = dict()
91 self.setDaemon(True)
96 self.setDaemon(True)
92
97
93 def listen(self):
98 def listen(self):
94 self.socket.bind(self.__sname)
99 self.socket.bind(self.__sname)
95 self.socket.listen(1)
100 self.socket.listen(1)
96
101
97 def __handle_error(self, socket):
102 def __handle_error(self, socket):
98 if socket == self.socket.fileno():
103 if socket == self.socket.fileno():
99 self.keep_running = False
104 self.keep_running = False
100 for a in self.current_conns.values():
105 for a in self.current_conns.values():
101 a.close()
106 a.close()
102 return False
107 return False
103 else:
108 else:
104 y = self.current_conns[socket]
109 y = self.current_conns[socket]
105 del self.current_conns[socket]
110 del self.current_conns[socket]
106 y.close()
111 y.close()
107 self.poller.unregister(socket)
112 self.poller.unregister(socket)
108
113
109 def serve_me(self):
114 def serve_me(self):
110 self.listen()
115 self.listen()
111 self.poller.register(self.socket,select.POLLIN|ERRCONDS)
116 self.poller.register(self.socket,select.POLLIN|ERRCONDS)
112
117
113 while self.keep_running:
118 while self.keep_running:
114 try:
119 try:
115 avail = self.poller.poll(1)
120 avail = self.poller.poll(1)
116 except:
121 except:
117 continue
122 continue
118
123
119 if not avail: continue
124 if not avail: continue
120
125
121 for sock, conds in avail:
126 for sock, conds in avail:
122 if conds & (ERRCONDS):
127 if conds & (ERRCONDS):
123 if self.__handle_error(sock): continue
128 if self.__handle_error(sock): continue
124 else: break
129 else: break
125
130
126 if sock == self.socket.fileno():
131 if sock == self.socket.fileno():
127 y = self.socket.accept()[0]
132 y = self.socket.accept()[0]
128 self.poller.register(y, select.POLLIN|ERRCONDS)
133 self.poller.register(y, select.POLLIN|ERRCONDS)
129 self.current_conns[y.fileno()] = y
134 self.current_conns[y.fileno()] = y
130 else: y = self.current_conns.get(sock)
135 else: y = self.current_conns.get(sock)
131
136
132 self.handle_request(y)
137 self.handle_request(y)
133
138
134 os.remove(self.__sname)
139 os.remove(self.__sname)
135
140
136 run = serve_me
141 run = serve_me
137
142
138 def stop(self):
143 def stop(self):
139 self.keep_running = False
144 self.keep_running = False
140
145
141 def handle_request(self,sock):
146 def handle_request(self,sock):
142 sock.settimeout(1)
147 sock.settimeout(1)
143 while self.keep_running:
148 while self.keep_running:
144 try:
149 try:
145 x = sock.recv(4096)
150 x = sock.recv(4096)
146 except socket.timeout:
151 except socket.timeout:
147 pass
152 pass
148 else:
153 else:
149 break
154 break
150 self.do_it(x)
155 self.do_it(x)
151
156
152 def do_it(self, data):
157 def do_it(self, data):
153 data = data.split('\n')
158 data = data.split('\n')
154 cmds = list()
159 cmds = list()
155 for line in data:
160 for line in data:
156 cmds.append(line)
161 cmds.append(line)
157 ip.runlines(cmds)
162 ip.runlines(cmds)
158
163
159
164
160 # try to help ensure that the unix domain socket is cleaned up proper
165 # try to help ensure that the unix domain socket is cleaned up proper
161 def shutdown_server(self):
166 def shutdown_server(self):
162 if SERVER:
167 if SERVER:
163 SERVER.stop()
168 SERVER.stop()
164 SERVER.join(3)
169 SERVER.join(3)
165 raise TryNext
170 raise TryNext
166
171
167 ip.set_hook('shutdown_hook', shutdown_server, 10)
172 ip.set_hook('shutdown_hook', shutdown_server, 10)
168
173
169 # this fun function exists to make setup easier for all, and makes the
174 # this fun function exists to make setup easier for all, and makes the
170 # vimhook function ready for instance specific communication
175 # vimhook function ready for instance specific communication
171 def setup(sessionname='',socketdir=os.path.expanduser('~/.ipython/')):
176 def setup(sessionname='',socketdir=os.path.expanduser('~/.ipython/')):
172 global SERVER
177 global SERVER
173
178
174 if sessionname:
179 if sessionname:
175 session = sessionname
180 session = sessionname
176 elif os.environ.get('IPY_SESSION'):
181 elif os.environ.get('IPY_SESSION'):
177 session = os.environ.get('IPY_SESSION')
182 session = os.environ.get('IPY_SESSION')
178 else:
183 else:
179 session = 'IPYS'
184 session = 'IPYS'
180 vimhook.vimserver=session
185 vimhook.vimserver=session
181 vimhook.ipyserver = os.path.join(socketdir, session)
186 vimhook.ipyserver = os.path.join(socketdir, session)
182 if not SERVER:
187 if not SERVER:
183 SERVER = IpyServer(vimhook.ipyserver)
188 SERVER = IpyServer(vimhook.ipyserver)
184 SERVER.start()
189 SERVER.start()
185
190
186
191
187
192
188 # calls gvim, with all ops happening on the correct gvim instance for this
193 # calls gvim, with all ops happening on the correct gvim instance for this
189 # ipython instance. it then calls edit -x (since gvim will return right away)
194 # ipython instance. it then calls edit -x (since gvim will return right away)
190 # things of note: it sets up a special environment, so that the ipy.vim script
195 # things of note: it sets up a special environment, so that the ipy.vim script
191 # can connect back to the ipython instance and do fun things, like run the file
196 # can connect back to the ipython instance and do fun things, like run the file
192 def vimhook(self, fname, line):
197 def vimhook(self, fname, line):
193 env = os.environ.copy()
198 env = os.environ.copy()
194 vserver = vimhook.vimserver.upper()
199 vserver = vimhook.vimserver.upper()
195 check = subprocess.Popen('gvim --serverlist', stdout = subprocess.PIPE,
200 check = subprocess.Popen('gvim --serverlist', stdout = subprocess.PIPE,
196 shell=True)
201 shell=True)
197 check.wait()
202 check.wait()
198 cval = [l for l in check.stdout.readlines() if vserver in l]
203 cval = [l for l in check.stdout.readlines() if vserver in l]
199
204
200 if cval:
205 if cval:
201 vimargs = '--remote%s' % (vimhook.extras,)
206 vimargs = '--remote%s' % (vimhook.extras,)
202 else:
207 else:
203 vimargs = ''
208 vimargs = ''
204 vimhook.extras = ''
209 vimhook.extras = ''
205
210
206 env['IPY_SESSION'] = vimhook.vimserver
211 env['IPY_SESSION'] = vimhook.vimserver
207 env['IPY_SERVER'] = vimhook.ipyserver
212 env['IPY_SERVER'] = vimhook.ipyserver
208
213
209 if line is None: line = ''
214 if line is None: line = ''
210 else: line = '+' + line
215 else: line = '+' + line
211 vim_cmd = 'gvim --servername %s %s %s %s' % (vimhook.vimserver, vimargs,
216 vim_cmd = 'gvim --servername %s %s %s %s' % (vimhook.vimserver, vimargs,
212 line, fname)
217 line, fname)
213 subprocess.call(vim_cmd, env=env, shell=True)
218 subprocess.call(vim_cmd, env=env, shell=True)
214
219
215
220
216 #default values to keep it sane...
221 #default values to keep it sane...
217 vimhook.vimserver = ''
222 vimhook.vimserver = ''
218 vimhook.ipyserver = ''
223 vimhook.ipyserver = ''
219
224
220 ip.set_hook('editor',vimhook)
225 ip.set_hook('editor',vimhook)
221
226
222 # this is set up so more vim specific commands can be added, instead of just
227 # this is set up so more vim specific commands can be added, instead of just
223 # the current -t. all thats required is a compiled regex, a call to do_arg(pat)
228 # the current -t. all thats required is a compiled regex, a call to do_arg(pat)
224 # and the logic to deal with the new feature
229 # and the logic to deal with the new feature
225 newtab = re.compile(r'-t(?:\s|$)')
230 newtab = re.compile(r'-t(?:\s|$)')
226 def vim(self, argstr):
231 def vim(self, argstr):
227 def do_arg(pat, rarg):
232 def do_arg(pat, rarg):
228 x = len(pat.findall(argstr))
233 x = len(pat.findall(argstr))
229 if x:
234 if x:
230 a = pat.sub('',argstr)
235 a = pat.sub('',argstr)
231 return rarg, a
236 return rarg, a
232 else: return '', argstr
237 else: return '', argstr
233
238
234 t, argstr = do_arg(newtab, '-tab')
239 t, argstr = do_arg(newtab, '-tab')
235 vimhook.extras = t
240 vimhook.extras = t
236 argstr = 'edit -x ' + argstr
241 argstr = 'edit -x ' + argstr
237 ip.magic(argstr)
242 ip.magic(argstr)
238
243
239 ip.define_magic('vim', vim)
244 ip.define_magic('vim', vim)
240
245
General Comments 0
You need to be logged in to leave comments. Login now