Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CTRL-R: How to toggle between global and per-directory history within fzf? #3539

Open
5 tasks done
cohml opened this issue Dec 11, 2023 · 2 comments
Open
5 tasks done

Comments

@cohml
Copy link

cohml commented Dec 11, 2023

  • I have read through the manual page (man fzf)
  • I have searched through the existing issues

Info

  • OS
    • Linux
    • Mac OS X
  • Shell
    • 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?

@junegunn
Copy link
Owner

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\").

@cohml
Copy link
Author

cohml commented Dec 18, 2023

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants