Automatically open remote files in local emacs

I prefer to edit text locally in emacs. Most of the files I edit reside on remote servers so I use TRAMP to open remote files locally. What kills me is using emacs remotely via terminal when a shell command invokes $EDITOR (e.g. svn commit). With my new setup, the default editor on the remote machine is my local emacs. I love this.

First, I configure SSH to forward a remote port to my machine. This means that whenever the remote machine tries to connect to itself on that port (localhost:9999) it actually connects to port 9999 on my local OSX machine. I like to keep these details in my ssh_config file (my local ~/.ssh/config):

Host des
User wpdev
ControlMaster auto
ControlPath ~/.ssh/des.sock
RemoteForward 9999 localhost:9999

(I use abbreviated hostnames to save keystrokes. There is a matching entry in my hosts file.)

Second, I configure my local emacs to start the server and copy the server file to the remote host. The server file tells emacsclient how to connect to the server. Adding this to emacs-startup-hook adds a few seconds to my emacs startup time but I rarely start emacs more than once in a day so that’s fine. This is in my local ~/.emacs:

(setq server-use-tcp t
      server-port    9999)
(defun server-start-and-copy ()
  (copy-file "~/.emacs.d/server/server" "/des:.emacs.d/server/server" t))
(add-hook 'emacs-startup-hook 'server-start-and-copy)

Third, I create a bash script on the remote host which calls emacsclient with the necessary TRAMP path prefixed to its arguments. (If you try running emacsclient remotely without the TRAMP path you’ll get an empty emacs buffer.) Here is the script I put in remote ~/bin/ec and then chmod +x:


for p in "$@"; do
    if [ "$p" == "-n" ]; then
        params+=( "$p" )
    elif [ "${p:0:1}" == "+" ]; then
        params+=( "$p" )
        params+=( "/ssh:des:"$(readlink -f $p) )
emacsclient "${params[@]}"

Finally, I set up $EDITOR on the remote machine. I also add my bin directory to $PATH so I can invoke ec. This is in my remote ~/.bashrc:

export PATH=~/bin:$PATH
export EDITOR=~/bin/ec

That’s it! More elegant solutions are possible but my new tool is sufficiently sharp and I have work to do!