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
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 🙂
What advantages would rsync have in this situation where you are doing one file at a time?
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…
I haven’t done those things yet so I don’t know.
Thanks! This should be useful for a project I’m working on.
This will hook into growl if you have growlnotify installed:
fname = view.file_name().replace(dirname, ”)
os.system( “/usr/local/bin/growlnotify -m ‘Copied %s'” % fname )
Check this out on packaging it as a proper plugin:
http://net.tutsplus.com/tutorials/python-tutorials/how-to-create-a-sublime-text-2-plugin/
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) )
ugh..
https://gist.github.com/1876859
Super stuff, thanks Karl!
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!
This was awesome. Thanks so much.
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
I mention using a SSH filesystem, but find it slower than this system.
Take a look at sshfs. There’s a version for Linux, using fuse, and a version for Mac, using osxfuse.
Yep, I’ve seen the various SSH file systems but find them more sluggish and prone to unexpected pauses and crashes.
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 )
Please,
What does you mean with “Save this in the Sublime Text packages directory, under user/remote-edit.py.”?
I use Sublime Text2 in my Mac. So, the remote-edit.py file should be saved into directory /Users// ?
Hello,
i’d just want to say thank you, as your tutorial massively improved my workflow for development on the Raspberry Pi.
I’ve made a tutorial for Raspberry users out of it and referenced your original tutorial, i hope that’s fine for you.
http://www.raspython.org/host-computer-coding-on-raspberry-scp/
Thanks again,
Jo Bergs