# Remote editing with Sublime Text 2

I’ve been quite taken with Sublime Text 2 recently, and I’ve been trying to find a way to introduce it into my daily work flow. While it’s perfect for editing files locally, it doesn’t have any built-in remote editing facilities such as Coda. As I work remotely, this is a pain.

I do have ExpanDrive, and depending on the speed of your server, this can work fairly well. However, you do miss out on some of the extra features of Sublime Text, such as ‘find in files’ and web development plugins.

I’ve experimented a bit with a mix of rsync and directory watches, but the best solution has been the simplest, and also a quick introduction into the world of Python.

Using the Sublime Text API, I wrote a plugin to monitor any changes to a file and simply scp (secure copy) it across to my remote server. All password details are already setup by dint of it going over SSH.

Save this in the Sublime Text packages directory, under user/remote-edit.py.

import sublime_plugin, os

class RemoteEdit(sublime_plugin.EventListener):
def on_post_save(self, view):
remote = { "/Users/path/to/local/copy": "/usr/bin/scp '$1' user@remotehost.com:'~/$2'" }

for dirname, target in remote.iteritems():
if view.file_name().startswith( dirname ):
target = target.replace( "$1", view.file_name() ) target = target.replace( "$2", view.file_name()[len(dirname):] )

os.system( target + " &" )

The remote variable is a map between a local directory, and a function to run when anything in that local directory is saved by Sublime Text. You can have multiple entries here, if you work on multiple remote projects. The $1 in remote is replaced by the local filename, and$2 by a relative directory (the containing directory of the file changed, relative to $1). For example, your local code is stored: /Users/path/to/local/copy/ And your remote code is stored in: ~/work/ If I edit the file /Users/path/to/local/copy/somedirectory/file.php then this command will execute: /usr/bin/scp '/Users/path/to/local/copy/somedirectory/file.php' user@remotehost.com:'~/work/somedirectory/file.php' Now I can edit my files locally, using an SVN GUI and local diff tools, and still be able to test code remotely. It works very well for me. Things to do: • Make it into a proper Sublime Text package, with config file • Use Growl notifications to tell you when the file has been saved ## 18 thoughts on “Remote editing with Sublime Text 2” 1. peter says: Wonderful work, you’ve saved me countless tedious key-presses. The paths used as keys in remote don’t work with symlinks, though… I guess ST2 uses absolute paths regardless of how the file was opened. Had to figure this out before I got it working. (I also changed scp to rsync -az which works better for my setup.) Growl notifications would be great too, and probably not too much work. Maybe I’ll figure it out and post a comment here 🙂 1. Bob says: What advantages would rsync have in this situation where you are doing one file at a time? 2. Andres Rendon says: Hey, love the solution, i’m new using ST2 but would you mind to show how to do this: Things to do: Make it into a proper Sublime Text package, with config file Use Growl notifications to tell you when the file has been saved I’m sorry if i’m too dumb to do this but i really don’t know how… 1. I haven’t done those things yet so I don’t know. 3. Doesn’t look like the API exposes a post_dir_save hook or anything like that..which means files added to locally created directories won’t get saved. Not a python programmer, but i came up with a solution. Basically, on scp failure it ssh’s and tries to create the folder. When Sublime text 2 upgrades to python 3.2 (which they are planning on doing)…should be able to more precisely catch the reason for the scp failure. import sublime_plugin, os class RemoteEdit(sublime_plugin.EventListener): def on_post_save(self, view): remote = { “/Users/leto/work/project”: [“/usr/bin/scp”, None, “user@server”, “root_remote_path_like ~/project/”, None] } for dirname, target in remote.iteritems(): if view.file_name().startswith( dirname ): if self.copy_file(view, target, dirname) == 256: path = target[3] + os.path.dirname(target[4]) create_target = “ssh %s ‘[ -d %s || mkdir -p %s ]'” % (target[2], path, path) if os.system( create_target ) == 0: self.copy_file(view, target, dirname) def copy_file(self, view, target, dirname): target[1] = view.file_name() target[4] = view.file_name()[len(dirname):] print “%s %s %s:%s%s” % tuple(target) return os.system( “%s %s %s:%s%s” % tuple(target) ) 1. I’ve recently started using sublime and it might be simpler to rsync to perform the directory create for you. normally rsync might be used as follows: ‘rsync -av /src user@host:/dest’ We can modify this command slightly to create paths automatically ” rsync –rsync-path=’mkdir -p {dir}; rsync’ -av /src user@host:/dest Where {dir} is the path that needs to be created. Rsync can work on single files or directories and transport across ssh so this can be tuned as necessary. Good luck! 4. Bob says: This was awesome. Thanks so much. 5. JunkieLand says: You are complicating that way to much ! A very simple way is to use sshfs. It will mount your remote work directory in a local directory, and then you will be able to use Sublime Text on the files. It will be completely transparent. No need to write a boiler plate script, it just needs one simple command : sshfs USER_ID@SERVER_IP:/remote/directory /local/directory And to unmount : fusermount -u /local/directory 1. I mention using a SSH filesystem, but find it slower than this system. 6. Take a look at sshfs. There’s a version for Linux, using fuse, and a version for Mac, using osxfuse. 1. Yep, I’ve seen the various SSH file systems but find them more sluggish and prone to unexpected pauses and crashes. 7. Joe Gilbert says: I made some slight modifications to the original code to get it working under Windows, copying to a Linux server. import sublime_plugin, os class RemoteEdit(sublime_plugin.EventListener): def on_post_save(self, view): remote = { ‘C:\path\to\project’: ‘C:\path\to\pscp.exe -i C:\path\to\private-key.ppk ‘ + “$1” + ‘ username@hostname.com:/path/to/project’ + “$2” } for dirname, target in remote.iteritems(): if view.file_name().startswith( dirname ): target = target.replace( “$1”, view.file_name() )
target = target.replace( “\$2”, view.file_name()[len(dirname):].replace(‘\\’, ‘/’) )
os.system( target )

8. Luiz says: