When I launch an
ansi-term in a remote (TRAMP) buffer, I'd like the resulting shell to reflect the current
default-directory. That is, I'd like
ansi-term to give me a remote shell, so I can do things in the directory of the current file.
This is not exactly what you are searching for, but it gives you a very similar functionality, albeit outside of Emacs and not in ansi-term. So, I still decided to submit it.
For my own convenience (and because I still like the native OS shell a lot), I implemented the possibility to open a gnome-terminal set to the current
default-directory. Will be easy to adapt to any other shell.
This also works with remote directories:
(defun dfeich/gnome-terminal (&optional path) "Opens a gnome terminal at PATH. If no PATH is given, it uses the value of `default-directory'. PATH may be a tramp remote path." (interactive) (unless path (setq path default-directory)) (if (tramp-tramp-file-p path) (let ((tstruct (tramp-dissect-file-name path))) (cond ((equal (tramp-file-name-method tstruct) "ssh") (call-process "/usr/bin/gnome-terminal" nil nil nil "-x" "sh" "-c" (format "ssh -t %s 'cd %s; exec bash'; exec bash" (tramp-file-name-host tstruct) (tramp-file-name-localname tstruct)))) (t (error "not implemented for method%s" (tramp-file-name-method tstruct))))) (call-process "/usr/bin/gnome-terminal" nil nil nil "-x" "sh" "-c" (format "cd %s;exec bash" path))))
You can't do this directly with the
ansi-term command, but you can define an equivalent command using the approach demonstrated by dfeich, which might look something like:
(defun my-ansi-term (command &optional name) "Runs COMMAND in a `term' buffer." (interactive (let ((shell (or explicit-shell-file-name (getenv "ESHELL") (getenv "SHELL") "/bin/sh")) suggestion name) (if (tramp-tramp-file-p default-directory) (let ((tstruct (tramp-dissect-file-name default-directory))) (when (string= (tramp-file-name-method tstruct) "ssh") (setq suggestion (apply #'format "ssh -t %s@%s -p %s cd %s; exec %s" (mapcar #'shell-quote-argument (list (tramp-file-name-user tstruct) (tramp-file-name-host tstruct) (number-to-string (tramp-file-name-port tstruct)) (tramp-file-name-localname tstruct) shell)))) (setq name (concat "ansi-term: " default-directory)))) (setq suggestion shell name "ansi-term")) (list (read-from-minibuffer "Run program: " suggestion) name))) (let* ((name (or name command)) (switches (split-string-and-unquote command)) (command (pop switches)) (termbuf (apply 'make-term name command nil switches))) (set-buffer termbuf) (term-mode) (term-char-mode) (switch-to-buffer termbuf)))
Tramp almost certainly provides a better way to generate the ssh command line from the parsed filename, but I couldn't find something appropriate. Also, assuming the local shell is valid for the remote host is clearly not correct in all situations.
As such, this function doesn't account for all use-cases by any means; but I would guess that it probably works for most.
As it turns out, this can be achieved by modifying
term-exec-1. (comments removed for brevity)
(defun nadvice/term-exec-1 (name buffer command switches) (let* ((environment (list (format "TERM=%s" term-term-name) (format "TERMINFO=%s" data-directory) (format term-termcap-format "TERMCAP=" term-term-name term-height term-width) (format "EMACS=%s (term:%s)" emacs-version term-protocol-version) (format "INSIDE_EMACS=%s,term:%s" emacs-version term-protocol-version) (format "LINES=%d" term-height) (format "COLUMNS=%d" term-width))) (process-environment (append environment process-environment)) (tramp-remote-process-environment (append environment tramp-remote-process-environment)) (process-connection-type t) (coding-system-for-read 'binary)) (apply 'start-file-process name buffer "/bin/sh" "-c" (format "stty -nl echo rows %d columns %d sane 2>/dev/null;\ if [ $1 = .. ]; then shift; fi; exec \"$@\"" term-height term-width) ".." command switches))) (advice-add 'term-exec-1 :override #'nadvice/term-exec-1)
As phils notes, this still has some issues (such as not correctly detecting the remote shell under certain circumstances), but it's more robust overall. For example, it should work with all
tramp methods that support asynchronous processes, which includes,
I should note that I did need to remove
(inhibit-eol-conversion t) from the
let, because the newlines were being incorrectly displayed in remote sessions. So far, I haven't seen anything break, but makes me ill-at-ease.
I feel like this is generic enough to be submitted as a patch with a bit more work, but I'd honestly like to get bug#19455 (which I submitted a patch for) squared away first.