Dirtrack-mode

Emacs has two main ways of changing the default directory when in shell-mode. The first, default method is shell-dirtrack-mode. This mode keeps track of commands such as ‘cd’ that change the working directory of the prompt. This, however, runs into problems with commands it doesn’t recognize, for example because you aliased a command to move to a popular directory.

The other mode is dirtrack-mode, which changes the default directory of the shell buffer based on the prompt. This does require you to have the working directory in your prompt, but a) I like having it and b) if you really want, you can strip it out using hooks.

Dir track-mode is built into GNU Emacs, so you don’t have to download anything special. You do have to customize a regexp to conform to the prompt you want(or change your prompt to conform to the regex). For me, my shell prompts are of the form: username@hostname:path/to/file$ , so I ended up with the following value:

(setq-default dirtrack-list '("^[^@]*@\\([^:]*:[^$].*\\)\\$" 1))

The group used to determine the path, in this case, is hostname:path/to/file. This allows me to have dirtrack-mode work when I ssh into other computers, as long as I configure their prompt to follow this format. This does require me to have the machine I consider ‘local’ to have a ‘hostname’ of ‘/’, but in the following section I’ll show how to get this to work correctly no matter what computer you are using. It took me a long time to figure out how to get tab-complition work in shell-mode when I was sshe’d into another computer. To set your prompt to this value, put the following line in your .bashrc:

export PS1="username@hostname:\w$ "

I like to have my shell buffers contain the directory name in them, so I use the following hook. This causes the shell buffer’s name to change whenever dirtrack changes. It ended up being fairly complicated, as I wanted to be able to have more than one shell buffer open in the same directory without things breaking:

(add-hook 'dirtrack-directory-change-hook
          (lambda ()
            (let ((base-buffer-name (concat "shell-" default-directory "-shell"))
                  (i 1)
                  (full-buffer-name base-buffer-name))
              (while (get-buffer full-buffer-name)
                (setq i (1+ i))
                (setq full-buffer-name (concat base-buffer-name "<" (number-to-string i) ">")))
              (rename-buffer full-buffer-name))))

We have only two things left to do: turn on dirtrack mode, and set the prompt to contain ‘/’ instead of the hostname on the local comuter. The following advice first turns off shell-dirtrack-mode, changes the prompt, and then turns on dirtrack mode. You want to do things in this order, because otherwise dirtrack-mode will try and use the actual ‘localhost’ value, which may confuse tramp.

(add-hook 'shell-mode-hook
          (lambda ()
            (shell-dirtrack-mode -1)
            (insert "export PS1=\"nflath@/:\\w$ \"")
            (comint-send-input)
            (dirtrack-mode 1)
            ))

Whenever a shell buffer is opened, shell-dirtrack-mode is turned off. Then, we send the command that will change the prompt and execute it, and then finally turn on dirtrack mode.

Tags:

2 Responses to “Dirtrack-mode”

  1. Erik says:

    Hello,

    This looks pretty interesting, but I don’t think it works “as is”, at least for SSH connections. Can you confirm that this is exactly your setup, and that this works for filename TAB completion after you ssh somewhere?
    Thanks!

  2. nflath says:

    Erik:
    I’m pretty sure that I haven’t modified anything since then, and my setup still works. You do have to set the PS1 on the remote machine before ssh’ing in to it? My configuration for this is also in http://github.com/nflath/emacs-repos/blob/master/internal/shell.el in case there are any differences, and I can confirm this works.

Leave a Reply