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