CTRL-R: How to toggle between global and per-directory history within fzf?
- [x] I have read through the manual page (
man fzf) - [x] I have searched through the existing issues
Info
- OS
- [x] Linux
- [x] Mac OS X
- Shell
- [x] zsh
Sorry to blow up your issues lately. I love how deep you can take things with fzf if suitably motivated!
Inquiry
I use the wonderful per-directory-history plugin in Zsh. Using it, ^G will toggle between history files, either global or PWD-specific.
Without any modification of fzf, the CTRL-R output shows the correct history depending on the mode (i.e., global vs. PWD-specific). However, if I want to switch modes, I have to exit fzf, toggle the mode with ^G, then re-enter fzf. I'm wondering if it's possible to do this all within fzf.
I attempted to achieve this with various flavors of --bind='ctrl-g:execute(per-directory-history-toggle-history)' (NB: that function comes from the plugin [source]), but I get an error that per-directory-history-toggle-history cannot be found. Am I on the right track?
Followup inquiry
Lastly, to add one more layer of complexity that's specific to me, I wrap fzf-history-widget with my own simple logic to add a little indicator of the current history mode to the fzf prompt. I then bind this function to ^R, overwriting fzf's binding. The code shown below displays the correct indicator as intended:
fzf-history-widget-with-dynamic-history-mode-indicator() {
source "file/that/sets/fzf/environment/variables/like/FZF_CTRL_R_OPTS.zsh"
[[ $_per_directory_history_is_global = true ]] && HISTORY_MODE="G" || HISTORY_MODE="L"
FZF_CTRL_R_OPTS+=" --prompt=${HISTORY_MODE}\ --color='prompt:#e57374'"
fzf-history-widget
}
This is where I tried to add the --bind bit, in hopes that ^G within fzf would not only toggle the mode and refresh the results, but also update the indicator:
fzf-history-widget-with-dynamic-history-mode-indicator() {
source "${DOTFILES}/.variables/.variables.fzf"
[[ $_per_directory_history_is_global = true ]] && HISTORY_MODE="G" || HISTORY_MODE="L"
FZF_CTRL_R_OPTS+=" --prompt=${HISTORY_MODE}\ \
--color='prompt:#e57374' \
--bind='ctrl-g:execute(per-directory-history-toggle-history)+become(fzf-history-widget-with-dynamic-history-mode-indicator)'" # <-- this results in errors
fzf-history-widget
}
But when I add in --bind='...', open fzf with ^R, and hit ^G, it fails:
zsh:1: command not found: per-directory-history-toggle-history
I'm quite confident that this should be possible, but I can't quite figure it out. Any assistance @junegunn?
fzf runs an external command with a new child shell process ($SHELL -c YOUR_COMMAND), and it looks like the zsh function is not available in the subshell. Perhaps the function is only defined in interactive sessions. Does $SHELL -ci per-directory-history-toggle-history work? If so, you can start trying something like become($SHELL -ci \"per-directory-history-toggle-history; fzf-history-widget-with-dynamic-history-mode-indicator\").
Hot dog, your change seems to resolve the error! But now I get a different one:
per-directory-history-toggle-history:zle:11: can only be called from widget function
My zsh-fu isn't good enough to debug this, it seems. I tried to invoke zle inside my fzf keybinding, but no success. Then instead of this
function per-directory-history-toggle-history() { ... }
autoload per-directory-history-toggle-history
zle -N per-directory-history-toggle-history
bindkey -M vicmd "^G" per-directory-history-toggle-history
FZF_CTRL_R_OPTS="--bind='ctrl-g:become($SHELL -ci \"per-directory-history-toggle-history\")'"
I did this
function per-directory-history-toggle-history() { ... }
function per-directory-history-toggle-history-widget() { # wrapper function
per-directory-history-toggle-history
}
autoload per-directory-history-toggle-history-widget
zle -N per-directory-history-toggle-history-widget
bindkey -M vicmd "^G" per-directory-history-toggle-history-widget
FZF_CTRL_R_OPTS="--bind='ctrl-g:become($SHELL -ci \"per-directory-history-toggle-history\")'"
The hope was that making making the wrapper function the widget and keeping the original function not a widget would allow fzf to call it directly. But I still got exactly the same error!
per-directory-history-toggle-history:zle:11: can only be called from widget function
Any thoughts or advice?