Working with Vim and IPython

Note: the text below is not for the faint-hearted. Setting up Vim to communicate with IPython is currently not very straightforward. The situation will improve soon.

When I write Python code I tend to use IPython quite a bit to test and experiment. Currently this means quite a bit of reloading and writing test code in the IPython shell. I’ve always been a little jealous of the Emacs folks for being able to communicate with the Python shell with ease. Vim has a different philosophy. We use the editor to edit stuff and the shell to run it. With that in mind, one can set up Vim to talk to IPython.

There are several steps for this:

  • Ensure that Vim is compiled with +python
  • Get the code that enables communication with IPython
  • Write code to send code from Vim to the server

Note: this assumes that you have Python, IPython and Twisted installed.

Ensure that vim is compiled with +python

Open your favourite version of Vim and do :version

In there look for +python. If it is not there, you need to somehow get a version that has +python. It is also important to check which version of Python your Vim was compiled against. For this, execute the following command in vim:

:python import sys; print sys.version

The output should be the same as your primary version of Python. For example, I am on OS X 10.5.8. By default it ships with Apple Python 2.5. I prefer to use Python 2.6 from MacPorts. Because of this I had to build a version of Macvim from source that pointed to my installation of Python 2.6. This deserves its own post.

Getting the code for communicating with IPython

Matt Foster has written a plugin for Textmate called ipythontm-bundle. This Textmate bundle uses the ipy_textmateserver.py to create a Twisted server to which stuff is sent for evaluation from Vim.

This file is can be downloaded from the textmate-server branch of IPython. For Vim we only need this file. Why only this file will be explained at the end of this post. Once the file is downloaded put it in your IPython/Extensions directory. For me this directory is:

/opt/local/Library/Frameworks/Python.framework/Versions/Current/lib/python2.6/site-packages/IPython/Extensions

The Vim side of life

At the moment, Vim has no idea that there will be a server running. Therefore, we have to write a plugin. The following (very very simple) code server as said plugin:

import vim
import socket

def runner(content):
    s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    try:
        s.connect("/Users/petrov/.ipython/IPYS")
        s.send(content)
        s.close()
    except:
        print "Error: could not connect to iPython server."

# --- Functions for running code in iPython --------------------------------- #

def run_current_line():
    runner(vim.current.line)

def run_selection():
    runner("\n".join(vim.current.range))

def run_buffer():
    runner("\n".join([vim.current.buffer] + ["\n"]))

def run_function(func_with_params):
    module_name = vim.current.buffer.name.split('.')[0]
    module_name = module_name.split('/')[-1]
    reload_command = "import %s; reload(%s)" % (module_name, module_name)
    func_line = "%s.%s" % (module_name,
                           func_with_params)
    runner("\n".join([reload_command, func_line]))

# --- Clean up function

def remove_sockets():
    import os
    import sys
    os.remove("/Users/petrov/.ipython/IPYS")

Essentially, the server will create a UNIX socket. By default the socket is $HOME/.ipython/IPSYS. It is possible to rename the socket or use different ones for different Vim windows. For now, I wanted to get the basics going. The Python code above should into a $HOME/.vim/plugin.

Using the code

Now that the plugin code is in place we can start using it. Start IPython and execute the following code:

import ipy_textmateserver
s = ipy_textmateserver.TextMateServer()
s.start()
# in order to stop the server to this:
# s.end()

Now that the server is running we can start sending code from Vim to IPython. Open a Python file in Vim. Create a map for execute the current line in Vim:

:map ,el :python run_current_line()

This should sent the line to IPython and you should see the results of execution in the terminal.