46

I often use TRAMP to manage several remote servers, both for editing files and running remote shells in shell-mode. However, this does not work when a command uses the EDITOR variable to edit a file, such as crontab -e especially because shell-mode is a "dumb" terminal that does not support running another editor inside of it.

Locally, I do this with an appropriate call to emacsclient which opens up a new buffer and makes life very easy. Unfortunately, this does not work from the remote machine.

I guess I could use ed. (Hah!)

How can I set up a remote EDITOR that would let me edit files with my local Emacs instance?

7 Answers 7

27

[NOTE] this answer was heavily edited to follow the updates of with-editor developments. Most comments likely won't make much sense anymore. There are some new comments which do make sense.


Magit contains a library named with-editor available at https://github.com/magit/with-editor which allows you to use your local Emacs as an $EDITOR on remote machines over TRAMP.

Another alternative is https://github.com/habnabit/remote-emacsclient, but it seems more complicated & less generic.

The simplest way to install with-editor is through MELPA:

M-x package-install with-editor

Otherwise, just grab https://github.com/magit/with-editor/blob/master/with-editor.el somewhere to your load path and require it (it also depends on dash).

Then, simply start shell, eshell or ansi-term and do the following:

M-x with-editor-export-editor

It'll ask you which $EDITOR you are interested in, just press enter for the default EDITOR variable. Then inside the shell you can type crontab -e and edit your crontab within emacs. Press C-c C-c to save the crontab or C-c C-k to cancel editing.

If you want a more permanent setup:

(add-hook 'shell-mode-hook  'with-editor-export-editor)
(add-hook 'term-mode-hook   'with-editor-export-editor)
(add-hook 'eshell-mode-hook 'with-editor-export-editor)

Alternatively, you can use M-x with-editor-async-shell-command crontab -e RET for quick commands.

11
  • Could you elaborate on how this with-editor library relates to the question? Sounds useful
    – Malabarba
    Commented Oct 8, 2014 at 16:18
  • @Silex: Would I need to set the $EDITOR variable to something on the remote machine for this to work? Does it just hook into emacsclient? Commented Oct 8, 2014 at 17:40
  • But yeah, this looks like exactly what I want. I'll have to give it a try somehow—I assume it can be installed by itself, without the rest of the branch? Commented Oct 8, 2014 at 17:40
  • 1
    @TikhonJelvi: it certainly will be installable by itself, but for now it's just part of git-modes. I'm following this lib closely and it's just that its author (@tarsius) is busy with releasing magit, but eventually it'd be a package of its own. About $EDITOR, you don't need to set it to anything yourself, it's done when needed when you run whatever commands uses it. Magit uses this lib with $GIT_EDITOR.
    – Silex
    Commented Oct 9, 2014 at 8:36
  • 1
    Note this will stop working after sshing to a remote from within an existing shell-mode/term-mode buffer. This can be addressed with some additional configuration, see emacs.stackexchange.com/questions/5589/…. If you do get it to work, then please report your findings at github.com/magit/magit/issues/1638.
    – tarsius
    Commented Dec 20, 2014 at 17:47
2

I have a tiny script in my path on the remote host at ~/bin/ec, shorthand for emacsclient.

#!/bin/bash

params=()
for p in "$@"; do
  if [ "$p" == "-n" ]; then
    params+=( "$p" )
  elif [ "${p:0:1}" == "+" ]; then
    params+=( "$p" )
  else
    params+=( "/ssh:z:"$(readlink -f $p) )
  fi
done
emacsclient --server-file=$HOME/.emacs.d/server/server "${params[@]}"

This script passes -n and + args unchanged to emacsclient, otherwise args are treated as files for your local Emacs to open. Each file is prefixed with the TRAMP protocol and host so Emacs knows how to open it. You might be able to change ssh: to a different TRAMP protocol if you prefer.

You must replace z with the hostname of your remote machine. This is used by local Emacs to connect via TRAMP. (You might be able to use hostname here for generality. I prefer to use tiny entries like z in my local ssh_config for brevity, and the remote has no idea I'm doing this. Try it!)

Usage:

  • ec file in the remote shell opens file in local Emacs and waits
  • ec -n file in the remote shell opens file in local Emacs and returns
  • export EDITOR=~/bin/ec in remote .bashrc makes the magic happen

To ensure my server file is good I have this in my local .emacs, again using the tiny hostname z:

(setq server-use-tcp t
      server-port    9999)
(defun server-start-and-copy ()
  "Start server and copy server file to remote box."
  (interactive)
  (server-start)
  (copy-file "~/.emacs.d/server/server" "/z:.emacs.d/server/server" t)
  (chmod "/z:.emacs.d/server/server" (string-to-number "644" 8))
  )
(add-hook 'emacs-startup-hook 'server-start-and-copy)

Port 9999 is a RemoteForward. I put this in my local ~/.ssh/ssh_config to automate the forwarding, plus the ControlMaster stuff for speed.

Host z
HostName dev.example.com
User dev
ControlMaster auto
ControlPath ~/.ssh/z.sock
RemoteForward 9999 localhost:9999

Finally, make sure TRAMP knows about your ssh_config if you use it:

(require 'tramp)
(tramp-set-completion-function "ssh"
  '((tramp-parse-sconfig "~/.ssh/config")))
1

Disclaimer: I have not tried this.

You could get part of the way by having a function watch command output for a command to open the file using TRAMP. Security hole? Yes. Functional? Probably.

Use a shell-mode hook to add a hook to after-change-hook in shell-mode. This hook watches for a specific sequence. Example:

;; RemoteTRAMP token: <authentication token maybe?>
;; RemoteTRAMP edit: /path/to/my/file

It then uses tramp-find-file to open the file. This is relatively secure because the ONLY THING that the remote can do is trigger a tramp-find-file. A confirmation first would be good but optional.

When editing is finished, another hook can trigger the exit of the dummy program (eg by sending C-c).

The worst case (for security) is that they find a way to run arbitrary code. If you have buffer-variables set to always evaluate, a malicious attacker could overwrite important settings without your knowledge. They could also launch a denial of service attack by causing many buffers to open. Confirmations could likely prevent all of the above.

The program on the remote could be trivially implemented in C (or any other language).

1

https://stackoverflow.com/questions/2231902/originate-edit-of-remote-file-using-emacs-tramp-from-ssh-session has a fairly simple accepted answer which amounts to

(setq server-use-tcp t)
(setq server-host "name_of_local_machine")
(server-start)
;; Maybe also muck with `server-auth-dir`

and then use

emacsclient -f ~/.emacs.d/server/server /`hostname`:/path/to/local/file

There is also https://stackoverflow.com/questions/12546722/using-emacs-server-and-emacsclient-on-other-machines-as-other-users which is more complex but where the answers also (roughly) touch similar bases.

0

Might require a bit of tweaking, but here's the idea:

EDITOR="ssh artagnon@luneth \"emacsclient -n /`hostname`:$1\""
4
  • 1
    Do you mean ssh'ing back from the remote server to the client where Emacs is running? That's often not possible. Commented Sep 23, 2014 at 21:49
  • Well, if you can't reach the client where Emacs is running, it's impossible to do anything.
    – artagnon
    Commented Sep 23, 2014 at 21:53
  • 2
    You can ask ssh, via it's ~/.ssh/config to always forward the local ssh listener to a port on the remote machine. You'd probably want to forward your ssh-agent as well, though opinions vary as to how secure that is. This will let you can get back by doing EDITOR="ssh $user@localhost:1234 ..."
    – Ben Hyde
    Commented Sep 24, 2014 at 0:51
  • Another option perhaps would be writing a small wrapper to call edit-server.el
    – stsquad
    Commented Sep 24, 2014 at 13:09
0

Harold Abnabit came up with this approach1:

http://blog.habnab.it/blog/2013/06/25/emacsclient-and-tramp/

1 based on an earlier article by Ryan Barrett (to which he links).

FWIW there's also https://stackoverflow.com/q/5154224/324105 in which Petri Lehtinen came up with https://gist.github.com/akheron/850795. I imagine the more complex solutions are better, but a very simple approach might still be of interest.

0

Really surprised no one has mentioned sshfs yet. This is what I usually do when I go remote:

1) multi-term; sshfs user@host:/dir/i/want/ /mnt/point/on/my/machine
2) open whatever I want to edit in my local emacs
3) get-term; ssh user@host to launch executables etc

While multi-term doesn't sync the local working dir with the remote dir, it beats all the other solutions I've tried by a long shot. Although dir tracking would be a welcome feature for sure.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.