""" Integration with gvim, by Erich Heine Provides a %vim magic command, and reuses the same vim session. Uses unix domain sockets for communication between vim and IPython. ipy.vim is available in doc/examples of the IPython distribution. Slightly touched up email announcement (and description how to use it) by Erich Heine is here: Ive recently been playing with ipython, and like it quite a bit. I did however discover a bit of frustration, namely with editor interaction. I am a gvim user, and using the command edit on a new file causes ipython to try and run that file as soon as the text editor opens up. The -x command of course fixes this, but its still a bit annoying, switching windows to do a run file, then back to the text editor. Being a heavy tab user in gvim, another annoyance is not being able to specify weather a new tab is how I choose to open the file. Not being one to shirk my open source duties (and seeing this as a good excuse to poke around ipython internals), Ive created a script for having gvim and ipython work very nicely together. Ive attached both to this email (hoping of course that the mailing list allows such things). There are 2 files: ipy_vimserver.py -- this file contains the ipython stuff ipy.vim -- this file contains the gvim stuff In combination they allow for a few functionalities: #1. the vim magic command. This is a fancy wrapper around the edit magic, that allows for a new option, -t, which opens the text in a new gvim tab. Otherwise it works the same as edit -x. (it internally calls edit -x). This magic command also juggles vim server management, so when it is called when there is not a gvim running, it creates a new gvim instance, named after the ipython session name. Once such a gvim instance is running, it will be used for subsequent uses of the vim command. #2. ipython - gvim interaction. Once a file has been opened with the vim magic (and a session set up, see below), pressing the F5 key in vim will cause the calling ipython instance to execute run filename.py. (if you typo like I do, this is very useful) #3. ipython server - this is a thread wich listens on a unix domain socket, and runs commands sent to that socket. Note, this only works on POSIX systems, that allow for AF_UNIX type sockets. It has only been tested on linux (a fairly recent debian testing distro). To install it put, the ipserver.py in your favorite locaion for sourcing ipython scripts. I put the ipy.vim in ~/.vim/after/ftplugin/python/. To use (this can be scripted im sure, but i usually have 2 or 3 ipythons and corresponding gvims open): import ipy_vimserver ipy_vimserver.setup('sessionname') (Editors note - you can probably add these to your ipy_user_conf.py) Then use ipython as you normally would, until you need to edit something. Instead of edit, use the vim magic. Thats it! """ import IPython.ipapi #import ipythonhooks import socket, select import os, threading, subprocess import re ERRCONDS = select.POLLHUP|select.POLLERR SERVER = None ip = IPython.ipapi.get() # this listens to a unix domain socket in a separate thread, so that comms # between a vim instance and ipython can happen in a fun and productive way class IpyServer(threading.Thread): def __init__(self, sname): super(IpyServer, self).__init__() self.keep_running = True self.__sname = sname self.socket = socket.socket(socket.AF_UNIX) self.poller = select.poll() self.current_conns = dict() self.setDaemon(True) def listen(self): self.socket.bind(self.__sname) self.socket.listen(1) def __handle_error(self, socket): if socket == self.socket.fileno(): self.keep_running = False for a in self.current_conns.values(): a.close() return False else: y = self.current_conns[socket] del self.current_conns[socket] y.close() self.poller.unregister(socket) def serve_me(self): self.listen() self.poller.register(self.socket,select.POLLIN|ERRCONDS) while self.keep_running: try: avail = self.poller.poll(1) except: continue if not avail: continue for sock, conds in avail: if conds & (ERRCONDS): if self.__handle_error(sock): continue else: break if sock == self.socket.fileno(): y = self.socket.accept()[0] self.poller.register(y, select.POLLIN|ERRCONDS) self.current_conns[y.fileno()] = y else: y = self.current_conns.get(sock) self.handle_request(y) os.remove(self.__sname) run = serve_me def stop(self): self.keep_running = False def handle_request(self,sock): sock.settimeout(1) while self.keep_running: try: x = sock.recv(4096) except socket.timeout: pass else: break self.do_it(x) def do_it(self, data): data = data.split('\n') cmds = list() for line in data: cmds.append(line) ip.runlines(cmds) # try to help ensure that the unix domain socket is cleaned up proper def shutdown_server(self): if SERVER: SERVER.stop() SERVER.join(3) raise IPython.ipapi.TryNext ip.set_hook('shutdown_hook', shutdown_server, 10) # this fun function exists to make setup easier for all, and makes the # vimhook function ready for instance specific communication def setup(sessionname='',socketdir=os.path.expanduser('~/.ipython/')): global SERVER if sessionname: session = sessionname elif os.environ.get('IPY_SESSION'): session = os.environ.get('IPY_SESSION') else: session = 'IPYS' vimhook.vimserver=session vimhook.ipyserver = os.path.join(socketdir, session) if not SERVER: SERVER = IpyServer(vimhook.ipyserver) SERVER.start() # calls gvim, with all ops happening on the correct gvim instance for this # ipython instance. it then calls edit -x (since gvim will return right away) # things of note: it sets up a special environment, so that the ipy.vim script # can connect back to the ipython instance and do fun things, like run the file def vimhook(self, fname, line): env = os.environ.copy() vserver = vimhook.vimserver.upper() check = subprocess.Popen('gvim --serverlist', stdout = subprocess.PIPE, shell=True) check.wait() cval = [l for l in check.stdout.readlines() if vserver in l] if cval: vimargs = '--remote%s' % (vimhook.extras,) else: vimargs = '' vimhook.extras = '' env['IPY_SESSION'] = vimhook.vimserver env['IPY_SERVER'] = vimhook.ipyserver if line is None: line = '' else: line = '+' + line vim_cmd = 'gvim --servername %s %s %s %s' % (vimhook.vimserver, vimargs, line, fname) subprocess.call(vim_cmd, env=env, shell=True) #default values to keep it sane... vimhook.vimserver = '' vimhook.ipyserver = '' ip.set_hook('editor',vimhook) # this is set up so more vim specific commands can be added, instead of just # the current -t. all thats required is a compiled regex, a call to do_arg(pat) # and the logic to deal with the new feature newtab = re.compile(r'-t(?:\s|$)') def vim(self, argstr): def do_arg(pat, rarg): x = len(pat.findall(argstr)) if x: a = pat.sub('',argstr) return rarg, a else: return '', argstr t, argstr = do_arg(newtab, '-tab') vimhook.extras = t argstr = 'edit -x ' + argstr ip.magic(argstr) ip.expose_magic('vim', vim)