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 Invalidate 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 printing raw Null-delimited on 1 Column).
- 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'.