fzf-change-dirstack () {
local dir
print -rNC1 -- $dirstack |
fzf --read0 --print0 |
IFS= read -rd '' dir &&
cd -- $dir &&
zle -I
}
zle -N fzf-change-dirstack
bindkey '^[p' fzf-change-dirstack # shortcut ALT+P
The main point being zle -I
to I
nvalidate the prompt when the current working directory has changed, the rest fixing a few other issues in your code so it can work with arbitrary directory names.
In particular:
dirs -lv | cut -f2
would fail if any of those directory names contained newline or tab characters. More generally, file paths can be any sequence of non-null bytes. So to pass to fzf
, you want to use its --read0
and provided a null-delimited list (here print
ing r
aw N
ull-delimited on 1
C
olumn).
- If you forget the
--
, then any directory starting with -
would be a problem (wouldn't happen normally if not for the previous bug¹). Forgetting it for print
even introduces command injection vulnerabilities.
- don't forget error handling!
Also note that zsh already has its own dirstack completer when you complete cd +
Tab or just ~+
Tab to complete dirstack elements as argument to any command, not just cd
/pushd
.
For instance, try cd +
TabTab after:
zstyle ':completion:*' format 'Completing %d'
zstyle ':completion:*' menu select=2
autoload compinit
compinit
And navigate with the arrow keys.
(this kind of setting you generally tune with compinstall
)
¹ For cd
, unfortunately, --
doesn't fix all the problems. -
, +1
, -1
for instance are still treated specially by cd
. Those wouldn't occur as elements of $dirstack
but may occur as the second TAB-delimited field of some line in the output of dirs -lv
like after cd $'foo\nx\t+1\twhatever'
.