I recently started using the fish shell. I tried using it in term-mode in Emacs. One characteristic of this shell is that it shortens the current working directory before printing it as the prompt. For example, if the current directory is /media, the prompt will contain /media but if the current working directory is /media/directory, the prompt will be /m/directory.

Because of this, Dirtrack mode in Emacs cannot keep track of the current directory, so default-directory is not updated. This affects some commands like C-c C-f which, as a prompt, suggests default-directory. Since this variable is not updated, the prompt will be the directory from where I started term-mode and not the current working directory in the shell.

I want default-directory to be updated when I change directory in the shell just like how it works with bash. How can I do this?


  • I like this shortening feature of fish-shell and I don't want to change the prompt style.

  • Since Emacs sends every key-stroke to the shell (with some exceptions), I thought to run a function every time <Ret> is pressed. But I couldn't find out how this can be done. Suggestions on this approach are welcome.

4 Answers 4


i had to fiddle with it for a while, but here's a working version of Gilles's answer. just stuff this at the end of your ~/.config/fish/config.fish.

# emacs dir tracking
if [ -n "$INSIDE_EMACS" ]
  function prompt_AnSiT -e fish_prompt
    printf "\eAnSiTc %s\n" "$PWD"
  printf "\eAnSiTu %s\n" "$USER"

the relevant differences are...

  1. this uses the $INSIDE_EMACS variable being set, instead of relying on the value of $TERM. either approach should work unless you've configured emacs to mimic some other terminal, in which case this should still work, but the other version won't. up to you which you want to use.
  2. this also prints an escape code of your current user which is what i'm assuming was causing the earlier TRAMP error. you'll notice the string it's complaining about has a host but lacks a username.
  3. you gotta have a space between the escape character and the value.

You could try the procfs approach. If you'd use the shell command, this should work.

(defun shell-procfs-dirtrack (str)
  (prog1 str
    (when (string-match comint-prompt-regexp str)
      (let ((directory (file-symlink-p
                        (format "/proc/%s/cwd"
        (when (file-directory-p directory)
          (cd directory))))))

(define-minor-mode shell-procfs-dirtrack-mode
  "Track shell directory by inspecting procfs."
  nil nil nil
  (cond (shell-procfs-dirtrack-mode
         (when (bound-and-true-p shell-dirtrack-mode)
           (shell-dirtrack-mode 0))
         (when (bound-and-true-p dirtrack-mode)
           (dirtrack-mode 0))
         (add-hook 'comint-preoutput-filter-functions
                   'shell-procfs-dirtrack nil t))
         (remove-hook 'comint-preoutput-filter-functions
                      'shell-procfs-dirtrack t))))
  • Awesome idea. But for some reason, hook is setup, shell-procfs-dirtrack works properly but default-directory doesn't change
    – nitishch
    Jun 13, 2015 at 14:03
  • comint-prompt-regexp probably doesn't match. Just C-u C-M-x it and see what's going on.
    – politza
    Jun 13, 2015 at 14:05
  • It says comint-promt-regexp is not defined
    – nitishch
    Jun 13, 2015 at 14:18
  • So, I removed the line with string-match temporarily. Then, value of comint-preoutput-filter-functions is (shell-procfs-dirtrack t), but the value of default-directory doesn't change. But after doing a cd, if I run (shell-procfs-dirtrack t), value of default-directory changes.
    – nitishch
    Jun 13, 2015 at 14:25
  • I've just noticed that you're using term-mode and not shell with comint in the background.
    – politza
    Jun 13, 2015 at 18:59

In Term mode, you can use Term mode's own directory tracking feature. I can't find it documented in the manual. There is some example code in the source code. The program running in the terminal can emit escape AnSiT sequences to notify the terminal about its location:

  • \eAnSiTc ${PWD}\n to indicate the current directory
  • \eAnSiTh ${HOSTNAME}\n to indicate the current host
  • \eAnSiTu ${USER}\n to indicate the current user

In your ~/.config/fish/config.fish, add code to the fish_prompt event handler to emit this escape sequence. Untested:

switch $TERM
  case eterm eterm-color
    function -e fish_prompt prompt_AnSiT
      printf '\eAnSiTc %s\n' "$PWD"
  • Wow I didn't know this. But this doesn't work. fish-shell in a terminal works fine but when I start term-mode, it gives an error error in process filter: Not a Tramp file: "/@HOSTNAME:/path/to/home/folder/.config/fish"
    – nitishch
    Jun 15, 2015 at 14:41

Look inside term.el documentation:

;;             ----------------------------------------
;; There are actually two methods for directory tracking, one
;; implemented in `term-command-hook' which sets the directory
;; according to an escape sequence of the form "\032/<directory>\n".
;; Some shells like bash will already send this escape sequence when
;; they detect they are running in Emacs.  This can be configured or
;; disabled on the Emacs side by setting `term-command-hook' to
;; a different function.
;; The second method is in `term-handle-ansi-terminal-messages' which
;; sets user, host, and directory according to escape sequences of the
;; form "\033AnSiTc <directory>\n" (replace the "c" with "u" and "h"
;; for user and host, respectively).  If the user and host don't
;; match, it will set directory to a remote one, so it is important to
;; set user and host correctly first.  See the example bash
;; configuration below.

I tried the other replies here with the second method of "ASiTc" but it doesn't work with utf8 Unicode (works with ASCII since they are only 1 byte, but Chinese is 3 bytes), only shows the raw bytes, while bash does.

Look inside bash source code eval.c:

if (running_under_emacs == 2)
    send_pwd_to_eterm ();   /* Yuck */

("Yuck" comment isn't mine, part of the source code, someone contributing in bash is an Emacs hater)

/* Send an escape sequence to emacs term mode to tell it the
   current working directory. */
static void
send_pwd_to_eterm ()
  char *pwd, *f;

  f = 0;
  pwd = get_string_value ("PWD");
  if (pwd == 0)
    f = pwd = get_working_directory ("eterm");
  fprintf (stderr, "\032/%s\n", pwd);
  free (f);

Then copy how bash does it into fish:

function emacs_dir_track_escape -e fish_prompt
  printf '\032/%s\n' "$PWD" 1>&2

Now it works with Unicode.

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.