Asymmetrical View

Connecting SLIME to a remote Clojure Repl

SLIME is a powerful extension for Emacs that transforms Emacs into an IDE for Lisp. It sets up Emacs so that it can connect to and interact with a Lisp running as a separate process – even on a separate server. This configuration allows you to connect to a running application with Emacs, allowing you to inspect the state of and debug a running application. In this post I will walk you through how to set up and use this scenario using Emacs, SLIME and SSH to connect to a remote JVM running Clojure.

SLIME and Swank

Slime largely provides the ‘IDE’ portion of the extension, where Swank provides the inter-process communication between Emacs and the running Lisp image. Swank is broken into two parts, a layer run within Emacs and a server which runs within the Lisp image. This provides Emacs the ability to execute forms (expressions) within the running Lisp instance, query it for information and otherwise interact with it in a structured manner.

Clojure

Slime was extended by Jeffery Chu to support Clojure through swank-clojure. Swank-clojure creates a server within the JVM running in its own thread to handle connections from the Emacs part of Swank.

Getting Emacs, SLIME, Clojure and clojure-swank set up is covered by others; technomancy.us has a good getting started guide. My own configuration is available as an example in my GitHub account.

Giving your JVM some Swank

Before you can connect to your running image, you’ll first need to start up Clojure (the Repl) and run the swank-server. You will need to ensure the swank-clojure directory is on your class-path as well as the clojure and clojure-contrib jar files. I use a bash script like the following to run a Clojure Repl. This script sets up a class-path that includes the clojure and clojure-contrib jar files as well as swank-clojure, which we’ll be using in a minute. It also adds all of the files in my $HOME/.clojure directory. I keep commonly used jar files in that directory so this is a convenient way to pull in multiple dependencies.

CLOJURE_JAR="$HOME/.clojure/clojure.jar"
CONTRIB_JAR="$HOME/.clojure/clojure-contrib.jar"
CLASSPATH="$CLOJURE_JAR:$CONTRIB_JAR:$HOME/personal/projects/krbemacs/swank-clojure" 
for f in $HOME/.clojure/*; do CLASSPATH="$CLASSPATH:$f" done
java -server -cp "$CLASSPATH" clojure.lang.Repl "$@"

In addition to the above script, I have the following in a ‘run-swank.clj’ file. It requires swank, sets a required protocol version and finally starts swank in its own thread.

(require 'swank.swank)
(swank.swank/ignore-protocol-version "2009-08-19")
(swank.swank/start-server "/tmp/slime.krb" 
                          :encoding "iso-latin-1-unix"
                          :port 4005)

The ignore-protocol-version is needed by swank-clojure to ensure that the swank-elisp and swank-clojure are in sync (I realize it is named confusingly). The first argument to start-server is a path to which the port swank is listening on will be written to. When you run Lisp from within Emacs these functions are executed by swank-elisp and this file is used so that both sides know what the port is. I run this from either the clojure script or from a clojure repl (outside of Emacs) with load-file:

kyle@indigo64 ~/personal/projects/krbemacs[master]$ clojure
Clojure 1.1.0-alpha-SNAPSHOT
user=> (load-file "run-swank.clj")
Connection opened on local port  4005
4005
user=> 

Swank is now running within this lisp image.

The current version of swank-clojure (as of August 20th 2009) does not bind to only localhost, allowing connections from external hosts. Ensure you have either firewalled off the swank port (4005 is the default) or patch swank-clojure to bind only to localhost (there is a swank-clojure in my Emacs GitHub repository with this modification).

Since I can’t connect directly to this remote-host (the port is not exposed to the network). I need to use ssh to securely forward a port from my local workstation through an ssh-tunnel to the other machine:

kyle@macbook ~/$ ssh -L1099:localhost:4005 indigo64
Linux indigo64 2.6.18.8-x86_64-ubuntu #1 SMP Thu Apr 10 11:20:13 EDT 2008 x86_64
Last login: Fri Aug 21 01:50:59 2009 from macbook
kyle@indigo64 ~$ 

I can now connect to this from my Emacs by executing M-x slime-connect, providing localhost or 127.0.0.1 as the Host-name and 1099 as the port. I chose 1099 arbitrarily, you can use any unused port — 4005 is a fine choice especially since it is the swank default.

At this point, with emacs connected, forms evaluated at the repl within Emacs, or from Clojure buffers will actually be run within the remote JVM.

Executing a print-line from Emacs running on my laptop:

Produces output on the remote Clojure image:

Conclusion

Being able to connect to a remote running image can be useful – especially for times when you can’t have your development environment present on the remote server and still need to introspect the running process.

Kyle Burton, 20th August 2009 – Philadelphia PA

Thanks

Special thanks to Jonathan Tran, and Rob DiMarco for reading drafts and providing valuable feedback and suggestions.

Tags: programming,emacs,slime,clojure,ssh