Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
afa4d96
Add fish completion support
lalvarezt Nov 21, 2025
0e3e405
Fix parsing of opts variables with quoted values containing spaces
lalvarezt Nov 21, 2025
1d01c7f
Add file-only completion and walker/opts customization
lalvarezt Nov 21, 2025
7a32421
Fix tmux detection, add option prefix handling
lalvarezt Nov 22, 2025
156205d
Remove telnet,ssh,set,etc. handling in favor on native fish completion
lalvarezt Nov 22, 2025
509b71f
Dropped tmux support for now, better completion handling
lalvarezt Nov 23, 2025
53343fd
Add native completion commands and configurable fallback mode
lalvarezt Nov 23, 2025
027daea
Escape trigger and improve path handling, and many other minor tweaks
lalvarezt Nov 24, 2025
8b5b0d1
Rudimentary fish test setup
lalvarezt Nov 25, 2025
a4779d1
Make behavior consistent with bash/zsh, escape not quote. Load helper…
lalvarezt Nov 25, 2025
f555556
Add more tests
lalvarezt Nov 26, 2025
39484f5
Make trigger the same as other shells, fix error in tokenize deprecat…
lalvarezt Nov 26, 2025
e41bf94
Remove FZF_COMPLETION_*_WALKER variables in favor of setting FZF_COMP…
lalvarezt Nov 26, 2025
d9f47bc
Add additional native command
lalvarezt Nov 26, 2025
6b29404
Remove redundant checks, fix typo
lalvarezt Nov 26, 2025
5631ed4
Simplify fzf-completion, generic_path_completion, complete_native, an…
lalvarezt Nov 26, 2025
e7bfdc4
Fix completion behavior
lalvarezt Nov 26, 2025
a17a7b0
Make fzf completion work only with trigger
lalvarezt Nov 27, 2025
af8fd08
Add command completion
lalvarezt Nov 27, 2025
b9d0632
Use colums for better alignment native completion support
lalvarezt Nov 27, 2025
6709883
Add command and flag completion tests
lalvarezt Nov 27, 2025
b74c6f0
Early exit if no trigger
lalvarezt Nov 27, 2025
c384e0d
Add multi-select for native commands
lalvarezt Nov 27, 2025
a12868a
Add equal option completion test
lalvarezt Nov 28, 2025
9c81c5e
Code refactoring by bitraid
lalvarezt Nov 28, 2025
3b38a0e
Make test_option_equals_completion more robust to capture --opt= pref…
lalvarezt Nov 28, 2025
e62bbdc
Remove -o option from column, BSD and Linux compliant
lalvarezt Nov 29, 2025
e592ce7
Update compatibility matrix for test_option_equals_completion
lalvarezt Nov 29, 2025
ea56270
Add more cases
lalvarezt Nov 29, 2025
0dec399
Simplify complete native with column as suggested by bitraid
lalvarezt Nov 29, 2025
69e7e98
Make all fields searchable when column alignment is used
lalvarezt Nov 29, 2025
5f31af9
Remove duplicated call, this is handled in completion.fish
lalvarezt Nov 29, 2025
bf2a5b1
Add missing eval
lalvarezt Nov 29, 2025
866a6e8
Split tests and formalize for every shell the expected response
lalvarezt Nov 29, 2025
854d67a
Cleanup duplicated tests, and promote to all instead of just fish
lalvarezt Nov 29, 2025
a350581
Fix RuboCop errors
junegunn Nov 30, 2025
cca8f47
Refactor test to be more specific
lalvarezt Nov 30, 2025
e9d7991
Tweak execution order for additional flexibility when defining native…
lalvarezt Nov 30, 2025
a25149a
Updated README
lalvarezt Nov 30, 2025
762f2f0
Fix missing export flag
lalvarezt Nov 30, 2025
241c21b
Improvements by bitraid
lalvarezt Nov 30, 2025
7c45371
Fix some leftovers
lalvarezt Nov 30, 2025
3439f2b
Reversing incorrect behavior
lalvarezt Nov 30, 2025
57be384
Improvements by bitraid
lalvarezt Dec 1, 2025
2351ee8
Add more binaries as default (from bash completion list)
lalvarezt Dec 1, 2025
372b969
Make fish scripts independent and integrate them into update.sh
lalvarezt Dec 1, 2025
01ab3f3
Fix indent issues
lalvarezt Dec 2, 2025
0816ed1
More indent group
lalvarezt Dec 2, 2025
1efbdf7
Update install script to support fish
lalvarezt Dec 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 40 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -557,8 +557,8 @@ Display modes for these bindings can be separately configured via
More tips can be found on [the wiki page](https://github.com/junegunn/fzf/wiki/Configuring-shell-key-bindings).
Fuzzy completion for bash and zsh
---------------------------------
Fuzzy completion for bash, zsh, and fish
----------------------------------------
### Files and directories
Expand Down Expand Up @@ -725,6 +725,44 @@ _fzf_complete_foo_post() {
[ -n "$BASH" ] && complete -F _fzf_complete_foo -o default -o bashdefault foo
```
#### Fish
Fish shell supports fuzzy completion with the same customizable `**` trigger as bash and zsh.
```fish
# Files and directories
vim **<TAB>
cd **<TAB>
# Process IDs
kill -9 **<TAB>
# Native completions (uses fish's complete -C builtin)
ssh **<TAB>
set **<TAB>
```
Fish completion system supports the same customization options as bash and zsh (`FZF_COMPLETION_TRIGGER`,
`FZF_COMPLETION_OPTS`, `FZF_COMPLETION_PATH_OPTS`, `FZF_COMPLETION_DIR_COMMANDS`, `FZF_COMPLETION_DIR_OPTS`),
and adds the following:
```fish
# Options for file-only completion
set -gx FZF_COMPLETION_FILE_OPTS '--walker file,follow,hidden'
# Commands that trigger file-only completion
set -gx FZF_COMPLETION_FILE_COMMANDS cat head tail less more nano
# Commands that use fish's native completion system
set -gx FZF_COMPLETION_NATIVE_COMMANDS ssh telnet
# Commands that use native completion with multi-select
set -gx FZF_COMPLETION_NATIVE_COMMANDS_MULTI set functions type
```
When `**NATIVE**` options are set the Fish will use its native `complete -C` builtin for
command-specific completions.
Vim plugin
----------
Expand Down
60 changes: 42 additions & 18 deletions install
Original file line number Diff line number Diff line change
Expand Up @@ -362,26 +362,50 @@ for shell in $shells; do
append_line $update_config "[ -f ${prefix}.${shell} ] && source ${prefix}.${shell}" "$dest" "${prefix}.${shell}"
done

if [ $key_bindings -eq 1 ] && [[ $shells =~ fish ]]; then
bind_file="${fish_dir}/functions/fish_user_key_bindings.fish"
if [ ! -e "$bind_file" ]; then
mkdir -p "${fish_dir}/functions"
create_file "$bind_file" \
'function fish_user_key_bindings' \
' fzf --fish | source' \
'end'
else
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
fish_line=
if [[ $auto_completion -eq 1 ]] && [[ $key_bindings -eq 1 ]]; then
fish_line='fzf --fish | source'
elif [[ $key_bindings -eq 1 ]]; then
fish_line="fzf --fish | sed -n '/^### key-bindings/,/^### end/p' | source"
elif [[ $auto_completion -eq 1 ]]; then
fish_line="fzf --fish | sed -n '/^### completion/,/^### end/p' | source"
fi

if [[ -n $fish_line ]] && [[ $shells =~ fish ]]; then
fish_config="${fish_dir}/config.fish"
if [ $update_config -eq 1 ]; then
echo "Update $fish_config:"
mkdir -p "${fish_dir}"
if [ -f "$fish_config" ] && \grep -qF "#----BEGIN FZF" "$fish_config"; then
echo " - Already exists: #----BEGIN FZF block found"
# Update existing block
{
sed -n '1,/^[[:space:]]*#----BEGIN FZF/p' "$fish_config" | sed '$d'
cat << EOF
#----BEGIN FZF
$fish_line
#----END FZF
EOF
sed -n '/^[[:space:]]*#----END FZF/,$p' "$fish_config" | sed '1d'
} > "$fish_config.part"
mv -f "$fish_config.part" "$fish_config"
echo " + Updated"
else
echo " - Clear"
echo
append_line $update_config "fzf --fish | source" "$bind_file"
[ -f "$fish_config" ] && echo >> "$fish_config"
cat >> "$fish_config" << EOF
#----BEGIN FZF
$fish_line
#----END FZF
EOF
echo " - $fish_line"
echo " + Added"
fi
echo
else
echo "Update $fish_config:"
echo " - $fish_line"
echo " ~ Skipped"
echo
fi
fi

Expand Down
5 changes: 4 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ var zshCompletion []byte
//go:embed shell/key-bindings.fish
var fishKeyBindings []byte

//go:embed shell/completion.fish
var fishCompletion []byte

//go:embed man/man1/fzf.1
var manPage []byte

Expand Down Expand Up @@ -65,7 +68,7 @@ func main() {
}
if options.Fish {
printScript("key-bindings.fish", fishKeyBindings)
fmt.Println("fzf_key_bindings")
printScript("completion.fish", fishCompletion)
return
}
if options.Help {
Expand Down
82 changes: 82 additions & 0 deletions shell/common.fish
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
function __fzf_defaults
test -n "$FZF_TMUX_HEIGHT"; or set -l FZF_TMUX_HEIGHT 40%
string join ' ' -- \
"--height $FZF_TMUX_HEIGHT --min-height=20+ --bind=ctrl-z:ignore" $argv[1] \
(test -r "$FZF_DEFAULT_OPTS_FILE"; and string join -- ' ' <$FZF_DEFAULT_OPTS_FILE) \
$FZF_DEFAULT_OPTS $argv[2..-1]
end

function __fzfcmd
test -n "$FZF_TMUX_HEIGHT"; or set -l FZF_TMUX_HEIGHT 40%
if test -n "$FZF_TMUX_OPTS"
echo "fzf-tmux $FZF_TMUX_OPTS -- "
else if test "$FZF_TMUX" = "1"
echo "fzf-tmux -d$FZF_TMUX_HEIGHT -- "
else
echo "fzf"
end
end

function __fzf_parse_commandline -d 'Parse the current command line token and return split of existing filepath, fzf query, and optional -option= prefix'
set -l fzf_query ''
set -l prefix ''
set -l dir '.'

set -l -- fish_major (string match -r -- '^\d+' $version)
set -l -- fish_minor (string match -r -- '^\d+\.(\d+)' $version)[2]

set -l -- match_regex '(?<fzf_query>[\s\S]*?(?=\n?$)$)'
set -l -- prefix_regex '^-[^\s=]+=|^-(?!-)\S'
if test "$fish_major" -eq 3 -a "$fish_minor" -lt 3
or string match -q -v -- '* -- *' (string sub -l (commandline -Cp) -- (commandline -p))
set -- match_regex "(?<prefix>$prefix_regex)?$match_regex"
end

if test "$fish_major" -ge 4
string match -q -r -- $match_regex (commandline --current-token --tokens-expanded | string collect -N)
else if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2
string match -q -r -- $match_regex (commandline --current-token --tokenize | string collect -N)
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^\\\(?=~)|\\\(?=\$\w)' '')
else
set -l -- cl_token (commandline --current-token --tokenize | string collect -N)
set -- prefix (string match -r -- $prefix_regex $cl_token)
set -- fzf_query (string replace -- "$prefix" '' $cl_token | string collect -N)
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^\\\(?=~)|\\\(?=\$\w)|\\\n\\\n$' '')
end

if test -n "$fzf_query"
if test \( "$fish_major" -ge 4 \) -o \( "$fish_major" -eq 3 -a "$fish_minor" -ge 5 \)
set -- fzf_query (path normalize -- $fzf_query)
set -- dir $fzf_query
while not path is -d $dir
set -- dir (path dirname $dir)
end
else
if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2
string match -q -r -- '(?<fzf_query>^[\s\S]*?(?=\n?$)$)' \
(string replace -r -a -- '(?<=/)/|(?<!^)/+(?!\n)$' '' $fzf_query | string collect -N)
else
set -- fzf_query (string replace -r -a -- '(?<=/)/|(?<!^)/+(?!\n)$' '' $fzf_query | string collect -N)
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r '\\\n$' '')
end
set -- dir $fzf_query
while not test -d "$dir"
set -- dir (dirname -z -- "$dir" | string split0)
end
end

if not string match -q -- '.' $dir; or string match -q -r -- '^\./|^\.$' $fzf_query
if test "$fish_major" -ge 4
string match -q -r -- '^'(string escape --style=regex -- $dir)'/?(?<fzf_query>[\s\S]*)' $fzf_query
else if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2
string match -q -r -- '^/?(?<fzf_query>[\s\S]*?(?=\n?$)$)' \
(string replace -- "$dir" '' $fzf_query | string collect -N)
else
set -- fzf_query (string replace -- "$dir" '' $fzf_query | string collect -N)
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^/?|\\\n$' '')
end
end
end

string escape -n -- "$dir" "$fzf_query" "$prefix"
end
Loading