Embed shell integration scripts in fzf binary (--bash / --zsh / --fish) (#3675)

This simplifies the distribution, and the users are less likely to have
problems caused by using incompatible scripts and binaries.

    # Set up fzf key bindings and fuzzy completion
    eval "$(fzf --bash)"

    # Set up fzf key bindings and fuzzy completion
    eval "$(fzf --zsh)"

    # Set up fzf key bindings
    fzf --fish | source
This commit is contained in:
Junegunn Choi 2024-03-13 23:59:34 +09:00 committed by GitHub
parent d282a1649d
commit e74b1251c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 197 additions and 54 deletions

View File

@ -3,6 +3,22 @@ CHANGELOG
0.48.0 0.48.0
------ ------
- Shell integration scripts are now embedded in the fzf binary. This simplifies the distribution, and the users are less likely to have problems caused by using incompatible scripts and binaries.
- bash
```sh
# Set up fzf key bindings and fuzzy completion
eval "$(fzf --bash)"
```
- zsh
```sh
# Set up fzf key bindings and fuzzy completion
eval "$(fzf --zsh)"
```
- fish
```fish
# Set up fzf key bindings
fzf --fish | source
```
- Added options for customizing the behavior of the built-in walker - Added options for customizing the behavior of the built-in walker
| Option | Description | Default | | Option | Description | Default |
| --- | --- | --- | | --- | --- | --- |
@ -28,7 +44,7 @@ CHANGELOG
export FZF_DEFAULT_COMMAND='seq 100' export FZF_DEFAULT_COMMAND='seq 100'
fzf --walker=dir fzf --walker=dir
``` ```
- The shell extensions (key bindings and fuzzy completion) have been updated to use the built-in walker with these new options and they are now much faster out of the box. - Shell integration scripts have been updated to use the built-in walker with these new options and they are now much faster out of the box.
0.47.0 0.47.0
------ ------

106
README.md
View File

@ -56,6 +56,7 @@ Table of Contents
* [Using git](#using-git) * [Using git](#using-git)
* [Using Linux package managers](#using-linux-package-managers) * [Using Linux package managers](#using-linux-package-managers)
* [Windows](#windows) * [Windows](#windows)
* [Setting up shell integration](#setting-up-shell-integration)
* [As Vim plugin](#as-vim-plugin) * [As Vim plugin](#as-vim-plugin)
* [Upgrading fzf](#upgrading-fzf) * [Upgrading fzf](#upgrading-fzf)
* [Building fzf](#building-fzf) * [Building fzf](#building-fzf)
@ -104,9 +105,9 @@ fzf project consists of the following components:
- `fzf` executable - `fzf` executable
- `fzf-tmux` script for launching fzf in a tmux pane - `fzf-tmux` script for launching fzf in a tmux pane
- Shell extensions - Shell integration
- Key bindings (`CTRL-T`, `CTRL-R`, and `ALT-C`) (bash, zsh, fish) - Key bindings (`CTRL-T`, `CTRL-R`, and `ALT-C`) (bash, zsh, fish)
- Fuzzy auto-completion (bash, zsh) - Fuzzy completion (bash, zsh)
- Vim/Neovim plugin - Vim/Neovim plugin
You can [download fzf executable][bin] alone if you don't need the extra You can [download fzf executable][bin] alone if you don't need the extra
@ -121,11 +122,12 @@ to install fzf.
```sh ```sh
brew install fzf brew install fzf
# To install useful key bindings and fuzzy completion:
$(brew --prefix)/opt/fzf/install
``` ```
> [!IMPORTANT]
> To set up shell integration (key bindings and fuzzy completion),
> see [the instructions below](#setting-up-shell-integration).
fzf is also available [via MacPorts][portfile]: `sudo port install fzf` fzf is also available [via MacPorts][portfile]: `sudo port install fzf`
[portfile]: https://github.com/macports/macports-ports/blob/master/sysutils/fzf/Portfile [portfile]: https://github.com/macports/macports-ports/blob/master/sysutils/fzf/Portfile
@ -140,6 +142,9 @@ git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
~/.fzf/install ~/.fzf/install
``` ```
The install script will add lines to your shell configuration file to modify
`$PATH` and set up shell integration.
### Using Linux package managers ### Using Linux package managers
| Package Manager | Linux Distribution | Command | | Package Manager | Linux Distribution | Command |
@ -158,10 +163,9 @@ git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
| XBPS | Void Linux | `sudo xbps-install -S fzf` | | XBPS | Void Linux | `sudo xbps-install -S fzf` |
| Zypper | openSUSE | `sudo zypper install fzf` | | Zypper | openSUSE | `sudo zypper install fzf` |
> :warning: **Key bindings (CTRL-T / CTRL-R / ALT-C) and fuzzy auto-completion > [!IMPORTANT]
> may not be enabled by default.** > To set up shell integration (key bindings and fuzzy completion),
> > see [the instructions below](#setting-up-shell-integration).
> Refer to the package documentation for more information. (e.g. `apt show fzf`)
[![Packaging status](https://repology.org/badge/vertical-allrepos/fzf.svg)](https://repology.org/project/fzf/versions) [![Packaging status](https://repology.org/badge/vertical-allrepos/fzf.svg)](https://repology.org/project/fzf/versions)
@ -187,6 +191,31 @@ page][windows-wiki].
[windows-wiki]: https://github.com/junegunn/fzf/wiki/Windows [windows-wiki]: https://github.com/junegunn/fzf/wiki/Windows
### Setting up shell integration
Add the following line to your shell configuration file.
* bash
```sh
# Set up fzf key bindings and fuzzy completion
eval "$(fzf --bash)"
```
* zsh
```sh
# Set up fzf key bindings and fuzzy completion
eval "$(fzf --zsh)"
```
* fish
```fish
# Set up fzf key bindings
fzf --fish | source
```
> [!NOTE]
> `--bash`, `--zsh`, and `--fish` options are only available in
> fzf 0.48.0 or above. If you have an older version of fzf, refer to the
> package documentation for more information. (e.g. `apt show fzf`)
### As Vim plugin ### As Vim plugin
If you use If you use
@ -237,18 +266,20 @@ directory to get the list of files.
vim $(fzf) vim $(fzf)
``` ```
> [!NOTE]
> You can override the default behavior > You can override the default behavior
> * Either by setting `$FZF_DEFAULT_COMMAND` to a command that generates the desired list > * Either by setting `$FZF_DEFAULT_COMMAND` to a command that generates the desired list
> * Or by setting `--walker`, `--walker-root`, and `--walker-skip` options in `$FZF_DEFAULT_OPTS` > * Or by setting `--walker`, `--walker-root`, and `--walker-skip` options in `$FZF_DEFAULT_OPTS`
> *:bulb: A more robust solution would be to use `xargs` but we've presented > [!WARNING]
> the above as it's easier to grasp* > A more robust solution would be to use `xargs` but we've presented
> the above as it's easier to grasp
> ```sh > ```sh
> fzf --print0 | xargs -0 -o vim > fzf --print0 | xargs -0 -o vim
> ``` > ```
> > [!TIP]
> *:bulb: fzf also has the ability to turn itself into a different process.* > fzf also has the ability to turn itself into a different process.
> >
> ```sh > ```sh
> fzf --bind 'enter:become(vim {})' > fzf --bind 'enter:become(vim {})'
@ -322,13 +353,6 @@ or `py`.
- `FZF_DEFAULT_COMMAND` - `FZF_DEFAULT_COMMAND`
- Default command to use when input is tty - Default command to use when input is tty
- e.g. `export FZF_DEFAULT_COMMAND='fd --type f'` - e.g. `export FZF_DEFAULT_COMMAND='fd --type f'`
- > :warning: This variable is not used by shell extensions due to the
> slight difference in requirements.
>
> (e.g. `CTRL-T` runs `$FZF_CTRL_T_COMMAND` instead, `vim **<tab>` runs
> `_fzf_compgen_path()`, and `cd **<tab>` runs `_fzf_compgen_dir()`)
>
> The available options are described later in this document.
- `FZF_DEFAULT_OPTS` - `FZF_DEFAULT_OPTS`
- Default options - Default options
- e.g. `export FZF_DEFAULT_OPTS="--layout=reverse --inline-info"` - e.g. `export FZF_DEFAULT_OPTS="--layout=reverse --inline-info"`
@ -337,6 +361,17 @@ or `py`.
point to the location of the file point to the location of the file
- e.g. `export FZF_DEFAULT_OPTS_FILE=~/.fzfrc` - e.g. `export FZF_DEFAULT_OPTS_FILE=~/.fzfrc`
> [!WARNING]
> `FZF_DEFAULT_COMMAND` is not used by shell integration due to the
> slight difference in requirements.
>
> * `CTRL-T` runs `$FZF_CTRL_T_COMMAND` to get a list of files and directories
> * `ALT-C` runs `$FZF_ALT_C_COMMAND` to get a list of directories
> * `vim ~/**<tab>` runs `fzf_compgen_path()` with the prefix (`~/`) as the first argument
> * `cd foo**<tab>` runs `fzf_compgen_dir()` with the prefix (`foo`) as the first argument
>
> The available options are described later in this document.
### Options ### Options
See the man page (`man fzf`) for the full list of options. See the man page (`man fzf`) for the full list of options.
@ -749,22 +784,21 @@ See the man page (`man fzf`) for the full list of options.
More advanced examples can be found [here](https://github.com/junegunn/fzf/blob/master/ADVANCED.md). More advanced examples can be found [here](https://github.com/junegunn/fzf/blob/master/ADVANCED.md).
---- > [!WARNING]
> Since fzf is a general-purpose text filter rather than a file finder, **it is
Since fzf is a general-purpose text filter rather than a file finder, **it is > not a good idea to add `--preview` option to your `$FZF_DEFAULT_OPTS`**.
not a good idea to add `--preview` option to your `$FZF_DEFAULT_OPTS`**. >
> ```sh
```sh > # *********************
# ********************* > # ** DO NOT DO THIS! **
# ** DO NOT DO THIS! ** > # *********************
# ********************* > export FZF_DEFAULT_OPTS='--preview "bat --style=numbers --color=always --line-range :500 {}"'
export FZF_DEFAULT_OPTS='--preview "bat --style=numbers --color=always --line-range :500 {}"' >
> # bat doesn't work with any input other than the list of files
# bat doesn't work with any input other than the list of files > ps -ef | fzf
ps -ef | fzf > seq 100 | fzf
seq 100 | fzf > history | fzf
history | fzf > ```
```
### Previewing an image ### Previewing an image

34
install
View File

@ -262,6 +262,12 @@ if [[ ! "\$PATH" == *$fzf_base_esc/bin* ]]; then
PATH="\${PATH:+\${PATH}:}$fzf_base/bin" PATH="\${PATH:+\${PATH}:}$fzf_base/bin"
fi fi
EOF
if [[ $auto_completion -eq 1 ]] && [[ $key_bindings -eq 1 ]]; then
echo "eval \"\$(fzf --$shell)\"" >> "$src"
else
cat >> "$src" << EOF
# Auto-completion # Auto-completion
# --------------- # ---------------
$fzf_completion $fzf_completion
@ -270,6 +276,7 @@ $fzf_completion
# ------------ # ------------
$fzf_key_bindings $fzf_key_bindings
EOF EOF
fi
echo "OK" echo "OK"
done done
@ -281,18 +288,6 @@ if [[ "$shells" =~ fish ]]; then
or set --universal fish_user_paths \$fish_user_paths "$fzf_base"/bin or set --universal fish_user_paths \$fish_user_paths "$fzf_base"/bin
EOF EOF
[ $? -eq 0 ] && echo "OK" || echo "Failed" [ $? -eq 0 ] && echo "OK" || echo "Failed"
mkdir -p "${fish_dir}/functions"
fish_binding="${fish_dir}/functions/fzf_key_bindings.fish"
if [ $key_bindings -ne 0 ]; then
echo -n "Symlink $fish_binding ... "
ln -sf "$fzf_base/shell/key-bindings.fish" \
"$fish_binding" && echo "OK" || echo "Failed"
else
echo -n "Removing $fish_binding ... "
rm -f "$fish_binding"
echo "OK"
fi
fi fi
append_line() { append_line() {
@ -355,12 +350,23 @@ done
if [ $key_bindings -eq 1 ] && [[ "$shells" =~ fish ]]; then if [ $key_bindings -eq 1 ] && [[ "$shells" =~ fish ]]; then
bind_file="${fish_dir}/functions/fish_user_key_bindings.fish" bind_file="${fish_dir}/functions/fish_user_key_bindings.fish"
if [ ! -e "$bind_file" ]; then if [ ! -e "$bind_file" ]; then
mkdir -p "${fish_dir}/functions"
create_file "$bind_file" \ create_file "$bind_file" \
'function fish_user_key_bindings' \ 'function fish_user_key_bindings' \
' fzf_key_bindings' \ ' fzf --fish | source' \
'end' 'end'
else else
append_line $update_config "fzf_key_bindings" "$bind_file" echo "Check $bind_file:"
lno=$(\grep -nF "fzf_key_bindings" "$bind_file" | sed 's/:.*//' | tr '\n' ' ')
if [[ -n $lno ]]; then
echo " ** Found 'fzf_key_bindings' in line #$lno"
echo " ** You have to replace the line to 'fzf --fish | source'"
echo
else
echo " - Clear"
echo
append_line $update_config "fzf --fish | source" "$bind_file"
fi
fi fi
fi fi

43
main.go
View File

@ -1,6 +1,10 @@
package main package main
import ( import (
_ "embed"
"fmt"
"strings"
fzf "github.com/junegunn/fzf/src" fzf "github.com/junegunn/fzf/src"
"github.com/junegunn/fzf/src/protector" "github.com/junegunn/fzf/src/protector"
) )
@ -8,7 +12,44 @@ import (
var version string = "0.47" var version string = "0.47"
var revision string = "devel" var revision string = "devel"
//go:embed shell/key-bindings.bash
var bashKeyBindings []byte
//go:embed shell/completion.bash
var bashCompletion []byte
//go:embed shell/key-bindings.zsh
var zshKeyBindings []byte
//go:embed shell/completion.zsh
var zshCompletion []byte
//go:embed shell/key-bindings.fish
var fishKeyBindings []byte
func printScript(label string, content []byte) {
fmt.Println("### " + label + " ###")
fmt.Println(strings.TrimSpace(string(content)))
fmt.Println("### end: " + label + " ###")
}
func main() { func main() {
protector.Protect() protector.Protect()
fzf.Run(fzf.ParseOptions(), version, revision) options := fzf.ParseOptions()
if options.Bash {
printScript("key-bindings.bash", bashKeyBindings)
printScript("completion.bash", bashCompletion)
return
}
if options.Zsh {
printScript("key-bindings.zsh", zshKeyBindings)
printScript("completion.zsh", zshCompletion)
return
}
if options.Fish {
printScript("key-bindings.fish", fishKeyBindings)
fmt.Println("fzf_key_bindings")
return
}
fzf.Run(options, version, revision)
} }

View File

@ -33,6 +33,10 @@ fzf [options]
fzf is a general-purpose command-line fuzzy finder. fzf is a general-purpose command-line fuzzy finder.
.SH OPTIONS .SH OPTIONS
.SS Note
.TP
Most long options have the opposite version with \fB--no-\fR prefix.
.SS Search mode .SS Search mode
.TP .TP
.B "-x, --extended" .B "-x, --extended"
@ -879,9 +883,24 @@ The default value is the current working directory.
Comma-separated list of directory names to skip during the directory walk. Comma-separated list of directory names to skip during the directory walk.
The default value is \fB.git,node_modules\fR. The default value is \fB.git,node_modules\fR.
.SS Note .SS Shell integration
.TP .TP
Most options have the opposite versions with \fB--no-\fR prefix. .B "--bash"
Print script to set up Bash shell integration
e.g. \fBeval "$(fzf --bash)"\fR
.TP
.B "--zsh"
Print script to set up Zsh shell integration
e.g. \fBeval "$(fzf --zsh)"\fR
.TP
.B "--fish"
Print script to set up Fish shell integration
e.g. \fBfzf --fish | source\fR
.SH ENVIRONMENT VARIABLES .SH ENVIRONMENT VARIABLES
.TP .TP

View File

@ -130,6 +130,11 @@ const usage = `usage: fzf [options]
--walker-skip=DIRS Comma-separated list of directory names to skip --walker-skip=DIRS Comma-separated list of directory names to skip
(default: .git,node_modules) (default: .git,node_modules)
Shell integration
--bash Print script to set up Bash shell integration
--zsh Print script to set up Zsh shell integration
--fish Print script to set up Fish shell integration
Environment variables Environment variables
FZF_DEFAULT_COMMAND Default command to use when input is tty FZF_DEFAULT_COMMAND Default command to use when input is tty
FZF_DEFAULT_OPTS Default options (e.g. '--layout=reverse --info=inline') FZF_DEFAULT_OPTS Default options (e.g. '--layout=reverse --info=inline')
@ -289,6 +294,9 @@ type walkerOpts struct {
// Options stores the values of command-line options // Options stores the values of command-line options
type Options struct { type Options struct {
Bash bool
Zsh bool
Fish bool
Fuzzy bool Fuzzy bool
FuzzyAlgo algo.Algo FuzzyAlgo algo.Algo
Scheme string Scheme string
@ -377,6 +385,9 @@ func defaultPreviewOpts(command string) previewOpts {
func defaultOptions() *Options { func defaultOptions() *Options {
return &Options{ return &Options{
Bash: false,
Zsh: false,
Fish: false,
Fuzzy: true, Fuzzy: true,
FuzzyAlgo: algo.FuzzyMatchV2, FuzzyAlgo: algo.FuzzyMatchV2,
Scheme: "default", Scheme: "default",
@ -1655,6 +1666,21 @@ func parseOptions(opts *Options, allArgs []string) {
for i := 0; i < len(allArgs); i++ { for i := 0; i < len(allArgs); i++ {
arg := allArgs[i] arg := allArgs[i]
switch arg { switch arg {
case "--bash":
opts.Bash = true
if opts.Zsh || opts.Fish {
errorExit("cannot specify --bash with --zsh or --fish")
}
case "--zsh":
opts.Zsh = true
if opts.Bash || opts.Fish {
errorExit("cannot specify --zsh with --bash or --fish")
}
case "--fish":
opts.Fish = true
if opts.Bash || opts.Zsh {
errorExit("cannot specify --fish with --bash or --zsh")
}
case "-h", "--help": case "-h", "--help":
help(exitOk) help(exitOk)
case "-x", "--extended": case "-x", "--extended":

View File

@ -94,6 +94,7 @@ done
bind_file="${fish_dir}/functions/fish_user_key_bindings.fish" bind_file="${fish_dir}/functions/fish_user_key_bindings.fish"
if [ -f "$bind_file" ]; then if [ -f "$bind_file" ]; then
remove_line "$bind_file" "fzf_key_bindings" remove_line "$bind_file" "fzf_key_bindings"
remove_line "$bind_file" "fzf --fish | source"
fi fi
if [ -d "${fish_dir}/functions" ]; then if [ -d "${fish_dir}/functions" ]; then