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