9

For years I've been using a zsh script that causes a pushd to implicitly happen everytime you change directories. The script also sets up 'b' and 'f' aliases, short for backwards and forwards, that move you up and down the stack, similar to the back and forward buttons in a web browser. Example session:

/etc $ cd /tmp
/tmp $ cd /bin
/bin $ b
/tmp $ b
/etc $ f
/tmp $ f
/bin $

I've found it incredibly useful and I was wondering, what's the best way to set this up in eshell?

Edit: See the code I went with here, slight modification of below: https://gist.github.com/jgarvin/0627ed76a773ade222f6

Joseph Garvin
  • 2,061
  • 8
  • 21

1 Answers1

6

The following saves cd history and provides eshell commands b and f to navigate that history:

;;*--- track cd history ------------------------------------------------*/
(defvar-local eshell-hist-dirs nil)

(add-hook 'eshell-directory-change-hook
          (defun eshell-update-hist-dir ()
            (push (eshell/pwd) eshell-hist-dirs)
            (setq eshell-hist-index 0)))

;;*--- navigate history ------------------------------------------------*/
(defvar-local eshell-hist-index 0)

(defun eshell-forward (n)
  (unless eshell-hist-dirs
    (user-error "eshell-hist-dirs is empty, cd a few times"))
  (let ((dirs eshell-hist-dirs)
        (index (+ eshell-hist-index n)))
    (prog1 (eshell/cd (nth index dirs))
      (setq eshell-hist-dirs dirs
            eshell-hist-index index))))

(defun eshell/b ()
  (eshell-forward 1))

(defun eshell/f ()
  (eshell-forward -1))

BTW, eshell's built-in cd command already provides several ways to jump to history directories, for example, cd - for the last directory, cd -1 for the penultimate directory and cd =tmp for the last directory which contains tmp. See (eshell) Built-ins for more information.

xuchunyang
  • 14,302
  • 1
  • 18
  • 39
  • This works great! Only caveat I have is that if you do 'b' over and over passed the end of the stack, it will push the last directory onto the stack over and over. So if you do 'b' over and over again until it stops going backward, then press it 4 more times, you will have to do 'f' 4 times before it actually starts going forward in the dir history again. Maybe just needs a check to make sure in the directory change hook that it actually changed? – Joseph Garvin Nov 04 '15 at 18:01
  • Oop, I might need to reset `eshell-hist-index` to zero once user runs `cd` from eshell. – xuchunyang Nov 04 '15 at 18:10
  • One other suggestion, wouldn't it be better to use defvar-local? Otherwise I think you're sharing the same cd history across all eshells which I think would be pretty surprising behavior. – Joseph Garvin Nov 04 '15 at 18:39
  • Yes, using buffer-local variables here is *correct* and I will do that, currently using `setq` is for ease of development (because I'm wriing these in `*scratch*` buffer and I need to examine their values frequently). – xuchunyang Nov 04 '15 at 18:46
  • Here's my attempt: https://gist.github.com/jgarvin/0627ed76a773ade222f6 . I changed it to use defvar-local and made it so the starting directory is put in the history. – Joseph Garvin Nov 04 '15 at 18:49
  • Hmm, something is still not quite right when using Tramp mode. If I do `cd /$USER:somehost:/` then do `b` it will work, but then doing `f` does not, I get a backtrace complaining about not finding the folder in `$CDPATH`. – Joseph Garvin Nov 30 '15 at 15:42