Romain Francoise: Fast directory navigation
About a year ago I stumbled upon this post which mentions the following shell keybindings: M-- for pushd .. and M-= for popd. Since the dash and equal signs are very conveniently placed next to each other, this allows fast directory navigation with accessible keystrokes and is especially convenient to move up in the directory hierarchy. Very neat.
Of course I use zsh and not bash, so I had to "port" this hack to zsh, which resulted in somewhat obscure code compared to the bash version:
Of course I use zsh and not bash, so I had to "port" this hack to zsh, which resulted in somewhat obscure code compared to the bash version:
function up-one-dir set -E; pushd ..; set +E; zle redisplay; zle -M pwdNot so pretty, but it works. The only problem is that I do most of my shell interaction through shell-mode in Emacs, not in zle-capable terminals. So I also had to write a shell-mode version, which turned out to be even uglier because I basically had to rip off the shell-resync-dirs code to handle comint interaction and directory tracking. Here it is in all its glory:
function back-one-dir set -E; popd; set +E; zle redisplay; zle -M pwd
zle -N up-one-dir
zle -N back-one-dir
bindkey "^[-" up-one-dir
bindkey "^[=" back-one-dir
(defun ore-shell-dirnav (direction)Phew. It's a lot of code for a seemingly simple feature, but the time it saves me probably adds up to a few hours per year, so it's worth it.
(interactive)
(let* ((proc (get-buffer-process (current-buffer)))
(pmark (process-mark proc))
(cmd (if (eq direction 'up) "pushd .." "popd")))
(goto-char pmark)
(sit-for 0)
(comint-send-string proc cmd)
(comint-send-string proc "\n")
(set-marker pmark (point))
(let ((pt (point))
(regexp
(concat
(if comint-process-echoes
(concat "\\(" (regexp-quote cmd) "\n\\)")
"\\(\\)")
"\\(.+\n\\)")))
(insert "\n")
(backward-char 1)
(while (not (looking-at regexp))
(accept-process-output proc)
(goto-char pt)))
(goto-char pmark)
(delete-char 1)
(let* ((dl (buffer-substring (match-beginning 2) (1- (match-end 2))))
(dl-len (length dl))
(ds '())
(i 0))
(delete-region (match-beginning 2) (1- (match-end 2)))
(while ( i dl-len)
(string-match "\\s *\\(\\S +\\)\\s *" dl i) ; pick off next dir
(setq ds (cons (concat comint-file-name-prefix
(substring dl (match-beginning 1)
(match-end 1)))
ds))
(setq i (match-end 0)))
(let ((ds (nreverse ds)))
(condition-case nil
(progn (shell-cd (car ds))
(setq shell-dirstack (cdr ds)
shell-last-dir (car shell-dirstack))
(shell-dirstack-message))
(error (message "Couldn't cd")))))))
(defun ore-shell-dirnav-up ()
(interactive)
(ore-shell-dirnav 'up))
(defun ore-shell-dirnav-down ()
(interactive)
(ore-shell-dirnav 'down))
(define-key shell-mode-map (kbd "M--") 'ore-shell-dirnav-up)
(define-key shell-mode-map (kbd "M-=") 'ore-shell-dirnav-down)