junegunn/fzf

Fzf cannot find a function configured as FZF_ALT_C_COMMAND

henrebotha opened this issue · 5 comments

Checklist

  • I have read through the manual page (man fzf)
  • I have searched through the existing issues
  • For bug reports, I have checked if the bug is reproducible in the latest version of fzf

Output of fzf --version

0.50.0 (brew)

OS

  • Linux
  • macOS
  • Windows
  • Etc.

Shell

  • bash
  • zsh
  • fish

Problem / Steps to reproduce

I updated Fzf yesterday (I was a few releases behind) and now my custom FZF_ALT_C_COMMAND doesn't work any more.

This is what the config looks like. It's all in my .zshrc.

eval "$(fzf --zsh)"
export FZF_ALT_C_COMMAND='altc'

altc() {
  echo 'test'
}

If I press Alt-C, Fzf gives me the error, "[Command failed: altc]". If I change FZF_ALT_C_COMMAND='altc 2> /tmp/error-log' and inspect the log file, it says, "zsh:1: command not found: altc".

Putting eval "$(fzf --zsh)" after export FZF_… doesn't make a difference.

Hmm, that's a case I hadn't considered.

Simply put, we used to eval $FZF_ALT_C_COMMAND | fzf, but we changed it to FZF_DEFAULT_COMMAND=$FZF_ALT_C_COMMAND fzf in d282a16, to make fzf use its builtin walker when FZF_DEFAULT_COMMAND is empty without having to add conditional branches.

So now fzf will try to execute $FZF_ALT_C_COMMAND, but your zsh function is not available to fzf process, hence the error. bash allows you to export a function (i.e. export -f altc), but since that is not available on zsh, there is no easy solution at hand.

If we want to keep supporting zsh functions, we need to use eval with conditional branches like so:

if [[ -n $FZF_ALT_C_COMMAND ]]; then
  eval $FZF_ALT_C_COMMAND | fzf --options --options --options
else
  FZF_DEFAULT_COMMAND='' fzf --options --options --options
fi

It's ugly and tedious, so you can see why I wanted to avoid it.

If we want to keep supporting zsh functions

What if we instruct the user to cautiously place functions in their .zshenv for such rare cases?

The only file you can alter which is started with every zsh (unless you use the -f option) is .zshenv, …

Source: Z-Shell Frequently-Asked Questions

Bart Schaefer: There's also nothing to stop you putting your functions in ~/.zshenv
so that they're available in every shell that starts up. It's all
down to dividing things appropriately across your init files …

Source: zsh.org 2017/166 - Zsh Mailing List Archive

Bart Schaefer: …and you should be cautious about e.g. "ssh somehost somecommand"
behaving differently because somehost has stuff in .zshenv.

Source: zsh.org 2017/168 - Zsh Mailing List Archive

Furthermore:


# .zshenv
altc() {
  # [look] display lines beginning with a given string
  /usr/bin/look foo
}
# .zshrc
eval "$(fzf --zsh)"
export FZF_ALT_C_COMMAND='altc'
fzf --bind "start:reload:altc" --preview 'typeset -f'

@LangLangBart thanks for the suggestion. It does allow me to work around this issue (though I did learn something important in the process: If you set $ZDOTDIR in ~/.zshenv, subsequent shell invocations will miss .zshenv as they now look for it in $ZDOTDIR instead of $HOME).

What if we instruct the user to cautiously place functions in their .zshenv for such rare cases?

@LangLangBart Thanks for the tip. That would help a lot.

Since zsh functions used to work, but not anymore, it's not wrong to see this as a regression and it's unfortunate, but considering that $FZF_DEFAULT_COMMAND has had the same limitation since the beginning and we have a workaround for bash and zsh, I think we can decide to keep it this way for the simplicity of the code.

FYI this issue has nothing to do with zsh, I also had the same problem with bash.

Quick fix that worked for me:

# instead of:
export FZF_ALT_C_COMMAND='my_function'

# one can do:
export FZF_ALT_C_COMMAND='$(declare -f my_function); my_function'