ShellDirtrackByPrompt

The default mechanism for keeping Emacs apprised of the shell’s current directory (shell-dirtrack-mode) relies on monitoring all of the commands entered into the shell, to see if any of them are directory-changing commands (e.g., cd, pushd, popd, etc), and then setting the buffer’s default-directory based on what the user entered. The problem is that this method can be thrown off by any command that it doesn’t recognize as a directory-changing command (e.g., a shell alias like “alias up=’cd ..’”).

This page describes alternative methods that track the current working directory, regardless of what command is being used to set it. They work by having the shell always report the current directory to Emacs.

First, program your shell prompt to emit the PWD

This example is for zsh

.zshrc:

  if [ $INSIDE_EMACS ]; then
    # Emit the PWD in the prompt, taking care that it doesn't
    # get truncated.
    prompt='%30000<<|Pr0mPT|%0d|[%n@%m]%~%# '
    # my default terminal, 'dumb', keeps resetting the COLUMNS value
    # all the time, which triggers zsh to truncate the prompt anyway.
    term=vanilla
    COLUMNS=30000
  fi

Next, Tell Emacs to watch for the prompt, using dirtrack-mode

Newer versions of Emacs (not sure which), ship with ‘dirtrack-mode’, which implements this alternate method of tracking the PWD.

Add the following snippets is to your .emacs:

  (setq dirtrack-list '("^|Pr0mPT|\\([^|]*\\)|" 1 nil))
  
  (add-hook 'shell-mode-hook
          #'(lambda ()
              (dirtrack-mode 1)
              (add-hook 'comint-preoutput-filter-functions
                        'dirtrack-filter-out-pwd-prompt t t)))

This disables the default directory tracking mechanism, and turns on prompt-based tracking in any interactive subshell

If you want the matched PWD to be removed from the output, add this as well:

  (defun dirtrack-filter-out-pwd-prompt (string)
    "dirtrack-mode doesn't remove the PWD match from the prompt.  This does."
    ;; TODO: support dirtrack-mode's multiline regexp.
    (if (and (stringp string) (string-match (first dirtrack-list) string))
        (replace-match "" t t string 0)
      string))

Another try

Hi, different author here. I tried this on my GNU emacs 22.2.1 and it was a bit of a disaster. It’s close but no cigar so I’ll share what I had to do to make this work.

There are two major parts, the shell prompt and emacs lisp. The shell prompt I use is for bash. I ended up with this in my bashrc:

  if [ "x$INSIDE_EMACS" != "x" ]; then
      PS1="|PrOmPt|\w|\u@\h \$ "
  else
    case $TERM in ....

The INSIDE_EMACS variable overrides any other TERM tweaking. I have no excuse for why I chose different magic strings for the prompt but that was the easiest part. The elisp side is a mess since the documentation is amazingly wrong. Here’s what I have in my .emacs.el:

  ;; Shell mode stuff. Set up cwd tracking via the 
  ;; subshell prompt and strip it.
  (require 'dirtrack)
  (add-hook 'shell-mode-hook
            (lambda ()
              (setq dirtrack-list '("^|PrOmPt|\\([^|]*\\)|" 1 nil))
              (shell-dirtrack-mode nil)
              (add-hook 'comint-preoutput-filter-functions
                        'dirtrack nil t)
              (add-hook 'comint-preoutput-filter-functions
                        'dirtrack-filter-out-pwd-prompt t t)))
  ;; Now strip the goofy strings from the prompt before it gets into
  ;; the shell buffer.
  (defun dirtrack-filter-out-pwd-prompt (string)
    "dirtrack-mode doesn't remove the PWD match from the prompt.  This does."
    ;; TODO: support dirtrack-mode's multiline regexp.
    (if (and (stringp string) (string-match (first dirtrack-list) string))
        (replace-match "" t t string 0)
      string))

Problems encountered:

Try without shell prompt change

Compatible with Windows and at least some Linux shells

  (require 'dirtrack) ;change the method used to determine the current directory in a shell
    (add-hook 'shell-mode-hook
      (lambda ()
        (shell-dirtrack-mode 0) ;stop the usual shell-dirtrack mode
        ;(setq ssh-directory-tracking-mode 'ftp)
        (setq dirtrack-list '(":*\\([A-Za-z]*:*~*[\/\\].*?\\)[^-+A-Za-z0-9_.()//\\ ]" 1)) ;for help making this regular expression you may want to use "M-x re-builder", where M is usually alt
        ;(dirtrack-debug-mode) ;this shows any change in directory that dirtrack mode sees
        (dirtrack-mode))) ;enable the more powerful dirtrack mode

CategoryShell