Skip to content
Open
Show file tree
Hide file tree
Changes from 32 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
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
179 changes: 179 additions & 0 deletions shell/completion.fish
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
# ____ ____
# / __/___ / __/
# / /_/_ / / /_
# / __/ / /_/ __/
# /_/ /___/_/ completion.fish
#
# - $FZF_COMPLETION_TRIGGER (default: '**')
# - $FZF_COMPLETION_OPTS (default: empty)
# - $FZF_COMPLETION_PATH_OPTS (default: empty)
# - $FZF_COMPLETION_DIR_OPTS (default: empty)
# - $FZF_COMPLETION_FILE_OPTS (default: empty)
# - $FZF_COMPLETION_DIR_COMMANDS (default: cd pushd rmdir)
# - $FZF_COMPLETION_FILE_COMMANDS (default: cat head tail less more nano)
# - $FZF_COMPLETION_NATIVE_COMMANDS (default: ssh telnet set functions type)

function fzf_completion_setup
# Load helper functions
fzf_key_bindings

# Use complete builtin for specific commands
function __fzf_complete_native
set -l result
if type -q column
set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults "--reverse" \
$FZF_COMPLETION_OPTS $argv[2..-1] --accept-nth=1)
set result (eval complete -C \"$argv[1]\" \| column -t -s \\t \| (__fzfcmd))
else
set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults "--reverse --nth=1 --color=fg:dim,nth:regular" \
$FZF_COMPLETION_OPTS $argv[2..-1] --accept-nth=1)
set result (complete -C "$argv[1]" | eval (__fzfcmd))
end
and commandline -rt -- (string join ' ' -- $result)' '
commandline -f repaint
end

# Generic path completion
function __fzf_generic_path_completion
set -l parsed (__fzf_parse_commandline)
set -lx dir $parsed[1]
set -l fzf_query $parsed[2]
set -l opt_prefix $parsed[3]
set -l compgen $argv[1]
set -l tail " "

# Set fzf options
set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults "--reverse --scheme=path" "$FZF_COMPLETION_OPTS --print0")
set -lx FZF_DEFAULT_COMMAND
set -lx FZF_DEFAULT_OPTS_FILE

if string match -q -- '*dir*' $compgen
set tail ""
set -a -- FZF_DEFAULT_OPTS "--walker=dir,follow $FZF_COMPLETION_DIR_OPTS"
else if string match -q -- '*file*' $compgen
set -a -- FZF_DEFAULT_OPTS "-m --walker=file,follow,hidden $FZF_COMPLETION_FILE_OPTS"
else
set -a -- FZF_DEFAULT_OPTS "-m --walker=file,dir,follow,hidden $FZF_COMPLETION_PATH_OPTS"
end

# Run fzf
set -l result
if functions -q "$compgen"
set result (eval $compgen $dir | eval (__fzfcmd) --query=$fzf_query | string split0)
else
set result (eval (__fzfcmd) --walker-root=$dir --query=$fzf_query | string split0)
end
and commandline -rt -- (string join -- ' ' $opt_prefix(string escape -n -- $result))$tail

commandline -f repaint
end

# Kill completion (process selection)
function _fzf_complete_kill
set -lx FZF_DEFAULT_OPTS (__fzf_defaults "--reverse" "$FZF_COMPLETION_OPTS")
set -lx FZF_DEFAULT_OPTS_FILE

set -l result (begin
command ps -eo user,pid,ppid,start,time,command 2>/dev/null
or command ps -eo user,pid,ppid,time,args 2>/dev/null # BusyBox
or command ps --everyone --full --windows 2>/dev/null # Cygwin
end | eval (__fzfcmd) --accept-nth=2 -m --header-lines=1 --no-preview --wrap --query=$argv[1])
and commandline -rt -- (string join ' ' -- $result)" "
commandline -f repaint
end

# Main completion function
function fzf-completion
set -q FZF_COMPLETION_TRIGGER
or set -l FZF_COMPLETION_TRIGGER '**'

# Set variables containing the major and minor fish version numbers, using
# a method compatible with all supported fish versions.
set -l -- fish_major (string match -r -- '^\d+' $version)
set -l -- fish_minor (string match -r -- '^\d+\.(\d+)' $version)[2]

# Get tokens - use version-appropriate flags
set -l tokens
if test $fish_major -ge 4
set tokens (commandline -xpc)
else
set tokens (commandline -opc)
end

set -l -- current_token (commandline -t)
set -l -- cmd_name $tokens[1]

set -l -- has_trigger false
string match -qr -- (string escape --style regex -- $FZF_COMPLETION_TRIGGER)'$' $current_token
and set has_trigger true

# Strip trigger from commandline before parsing
if $has_trigger; and test -n "$FZF_COMPLETION_TRIGGER" -a -n "$current_token"
set -- current_token (string sub -e -(string length -- "$FZF_COMPLETION_TRIGGER") -- $current_token)
commandline -rt -- $current_token
end

if not $has_trigger
commandline -f complete
return
else if test -z "$tokens"
__fzf_complete_native "" --query=$current_token
return
end

set -l disable_opt_comp false
if not test "$fish_major" -eq 3 -a "$fish_minor" -lt 3
string match -qe -- ' -- ' (string sub -l (commandline -Cp) -- (commandline -p))
and set disable_opt_comp true
end

if not $disable_opt_comp
# Not using the --groups-only option of string-match, because it is
# not available on fish versions older that 3.4.0
set -l -- cmd_opt (string match -r -- '^(-{1,2})([\w.,:+-]*)$' $current_token)
if test -n "$cmd_opt[2]" -a \( "$cmd_opt[2]" = -- -o (string length -- "$cmd_opt[3]") -ne 1 \)
__fzf_complete_native "$cmd_name $cmd_opt[2]" --query=$cmd_opt[3] --multi
return
end
end

# Directory commands
set -q FZF_COMPLETION_DIR_COMMANDS
or set -l FZF_COMPLETION_DIR_COMMANDS cd pushd rmdir

# File-only commands
set -q FZF_COMPLETION_FILE_COMMANDS
or set -l FZF_COMPLETION_FILE_COMMANDS cat head tail less more

# Native completion commands
set -q FZF_COMPLETION_NATIVE_COMMANDS
or set -l FZF_COMPLETION_NATIVE_COMMANDS ssh telnet

# Native completion commands (multi-selection)
set -q FZF_COMPLETION_NATIVE_COMMANDS_MULTI
or set -l FZF_COMPLETION_NATIVE_COMMANDS_MULTI set functions type

# Route to appropriate completion function
if functions -q _fzf_complete_$cmd_name
_fzf_complete_$cmd_name "$fzf_query" "$cmd_name"
else if contains -- "$cmd_name" $FZF_COMPLETION_NATIVE_COMMANDS $FZF_COMPLETION_NATIVE_COMMANDS_MULTI
set -l -- fzf_opt --query=(commandline -t | string escape)
contains -- "$cmd_name" $FZF_COMPLETION_NATIVE_COMMANDS_MULTI
and set -a -- fzf_opt --multi
__fzf_complete_native "$cmd_name " $fzf_opt
else if contains -- "$cmd_name" $FZF_COMPLETION_DIR_COMMANDS
__fzf_generic_path_completion _fzf_compgen_dir
else if contains -- "$cmd_name" $FZF_COMPLETION_FILE_COMMANDS
__fzf_generic_path_completion _fzf_compgen_file
else
__fzf_generic_path_completion _fzf_compgen_path
end
end

# Bind tab to fzf-completion
bind \t fzf-completion
bind -M insert \t fzf-completion
end

# Run setup
fzf_completion_setup
20 changes: 20 additions & 0 deletions test/lib/common.fish
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Unset fzf variables
set -e FZF_DEFAULT_COMMAND FZF_DEFAULT_OPTS FZF_DEFAULT_OPTS_FILE FZF_TMUX FZF_TMUX_OPTS
set -e FZF_CTRL_T_COMMAND FZF_CTRL_T_OPTS FZF_ALT_C_COMMAND FZF_ALT_C_OPTS FZF_CTRL_R_OPTS
set -e FZF_API_KEY
# Unset completion-specific variables
set -e FZF_COMPLETION_TRIGGER FZF_COMPLETION_OPTS
set -e FZF_COMPLETION_PATH_OPTS FZF_COMPLETION_DIR_OPTS FZF_COMPLETION_FILE_OPTS
set -e FZF_COMPLETION_DIR_COMMANDS FZF_COMPLETION_FILE_COMMANDS FZF_COMPLETION_NATIVE_COMMANDS
set -e FZF_COMPLETION_NATIVE_MODE

set -gx FZF_DEFAULT_OPTS "--no-scrollbar --pointer '>' --marker '>'"
set -gx FZF_COMPLETION_TRIGGER '++'
set -gx fish_history fzf_test

# Add fzf to PATH
fish_add_path <%= BASE %>/bin

# Source key bindings and completion
source "<%= BASE %>/shell/key-bindings.fish"
source "<%= BASE %>/shell/completion.fish"
12 changes: 11 additions & 1 deletion test/lib/common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
require 'json'

TEMPLATE = File.read(File.expand_path('common.sh', __dir__))
FISH_TEMPLATE = File.read(File.expand_path('common.fish', __dir__))
UNSETS = %w[
FZF_DEFAULT_COMMAND FZF_DEFAULT_OPTS
FZF_TMUX FZF_TMUX_OPTS
Expand Down Expand Up @@ -66,7 +67,16 @@ def zsh
end

def fish
"unset #{UNSETS.join(' ')}; rm -f ~/.local/share/fish/fzf_test_history; FZF_DEFAULT_OPTS=\"--no-scrollbar --pointer '>' --marker '>'\" fish_history=fzf_test fish"
@fish ||=
begin
confdir = '/tmp/fzf-fish'
FileUtils.rm_rf(confdir)
FileUtils.mkdir_p("#{confdir}/fish/conf.d")
File.open("#{confdir}/fish/conf.d/fzf.fish", 'w') do |f|
f.puts ERB.new(FISH_TEMPLATE).result(binding)
end
"rm -f ~/.local/share/fish/fzf_test_history; XDG_CONFIG_HOME=#{confdir} fish"
end
end
end
end
Expand Down
Loading