-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Add fish completion support #4605
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
base: master
Are you sure you want to change the base?
Conversation
|
I had a quick look at the script, and I have the following observations:
Finally, I want to point out that fish v4.x already does fuzzy tab completion, while showing the results in multiple columns (fitting more items in terminal). So, I’m not sure if it’s worth providing and maintaining such script (but of course, that's not for me to decide). |
|
Hi, thanks for taking the time and the detailed reply
True, I was just following the same upstream convention. Granted we could put another symbol like "##" that has no special use in fish (other than being a comment)
Sorry about that, I did mention that I didn't try it under tmux.
This is intentional, the file is meant to be sourced not auto-loaded. Doing
I totally missed that one 👍
Here we might as well drop the ssh, telnet, set, etc. functions altogether, it's true the native fish does it better
Here I don't fully agree, native fish is not recursive so having fzf does have merit IMO @bitraid would you mind having a second look 🙏 |
OK, I had another look: Now it fails under tmux when
You could still have the completions, but instead of doing all that manual work, the list could be retrieved by calling
This is already provided by
|
This new version should work much better, I was reinventing the wheel in some cases. thoughts? EDIT: I removed a weird behavior that was only on my system |
edf6eff to
509b71f
Compare
|
|
thanks again for all the time spent in this, all your test cases pass now (unless I missed something) |
|
Thanks for your interest in the project. My knowledge and experience with fish are quite limited, and @bitraid has effectively been maintaining the fish module. I respect their judgement, so I'd like to go with their call on this. As the project maintainer, I hope the fish completion aligns with the existing ones, particularly in terms of configuration. You may want to update the README and extend the existing integration tests to cover fish. |
|
Following up on your feedback @bitraid @junegunn:
Added tests for:
All fish tests pass (
So it looks like bash/zsh have some issues with these edge cases that fish handles just fine. Can you please verify this on your end? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @junegunn for your trust, and @lalvarezt for all your work. The script now seems to have no issues. I also tested it on fish v3.1b1, and there aren't any compatibility issues either. I left some suggestions in code review.
Below is the script with the suggested changes, along with some other modifications/additions. I added a function that uses fzf for commands like ssh/set/functions etc. Currently it doesn't perform multi-selection, but it can be optimized to do so when appropriate, if it is decided to be kept.
script
# ____ ____
# / __/___ / __/
# / /_/_ / / /_
# / __/ / /_/ __/
# /_/ /___/_/ 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
# Have the command run in a subshell
set -lx -- FZF_DEFAULT_COMMAND "complete -C\"$argv[1] \" "
set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults '--reverse --nth=1 --color=fg:dim,nth:regular' \
$FZF_COMPLETION_OPTS $argv[2..-1] '--accept-nth=1 --with-shell='(status fish-path)\\ -c)
set -l result (eval (__fzfcmd))
and commandline -rt $result
commandline -f repaint
end
# Generic path completion
function __fzf_generic_path_completion
set -lx dir $argv[1]
set -l fzf_query $argv[2]
set -l opt_prefix $argv[3]
set -l compgen $argv[4]
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
if type -q "$compgen"
set -l result (eval $compgen $dir | eval (__fzfcmd) --query=$fzf_query | string split0)
and commandline -rt -- (string join -- ' ' $opt_prefix(string escape -- $result))$tail
else
set -l result (eval (__fzfcmd) --walker-root=$dir --query=$fzf_query | string split0)
and commandline -rt -- (string join -- ' ' $opt_prefix(string escape -- $result))$tail
end
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 '**'
# Get tokens - use version-appropriate flags
set -l tokens
if test (string match -r -- '^\d+' $version) -ge 4
set tokens (commandline -xpc)
else
set tokens (commandline -opc)
end
if test -z "$tokens"; or string match -qv -- "*"(string escape -n -- $FZF_COMPLETION_TRIGGER) (commandline -t)
commandline -f complete
return
end
# Get the command name
set -l cmd_name $tokens[1]
# Strip trigger from commandline before parsing
test -n "$FZF_COMPLETION_TRIGGER"
and commandline -rt -- (string sub -e -(string length -- "$FZF_COMPLETION_TRIGGER") -- (commandline -t))
# Parse commandline (now without trigger)
set -l parsed (__fzf_parse_commandline)
set -l dir $parsed[1]
set -l fzf_query $parsed[2]
set -l opt_prefix $parsed[3]
# 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 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
set -l -- fzf_opt --query=(commandline -t | string escape)
__fzf_complete_native "$cmd_name" $fzf_opt
else if contains -- "$cmd_name" $FZF_COMPLETION_DIR_COMMANDS
__fzf_generic_path_completion "$dir" "$fzf_query" "$opt_prefix" _fzf_compgen_dir
else if contains -- "$cmd_name" $FZF_COMPLETION_FILE_COMMANDS
__fzf_generic_path_completion "$dir" "$fzf_query" "$opt_prefix" _fzf_compgen_file
else
__fzf_generic_path_completion "$dir" "$fzf_query" "$opt_prefix" _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_setupdiff
diff --git a/completion.fish b/completion.fish
index 5162b6d..f209e82 100644
--- a/completion.fish
+++ b/completion.fish
@@ -4,41 +4,31 @@
# / __/ / /_/ __/
# /_/ /___/_/ completion.fish
#
-# - $FZF_COMPLETION_TRIGGER (default: '++')
+# - $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)
-# - $FZF_COMPLETION_PATH_WALKER (default: 'file,dir,follow,hidden')
-# - $FZF_COMPLETION_DIR_WALKER (default: 'dir,follow')
-# - $FZF_COMPLETION_FILE_WALKER (default: 'file,follow,hidden')
-# - $FZF_COMPLETION_NATIVE_MODE (default: 'complete', or 'complete-and-search')
+# - $FZF_COMPLETION_NATIVE_COMMANDS (default: ssh telnet set functions type)
function fzf_completion_setup
# Load helper functions
fzf_key_bindings
- # Check fish version
- set -l fish_ver (string match -r '^(\d+).(\d+)' $version 2> /dev/null; or echo 0\n0\n0)
- if test \( "$fish_ver[2]" -lt 3 \) -o \( "$fish_ver[2]" -eq 3 -a "$fish_ver[3]" -lt 1 \)
- echo "This script requires fish version 3.1b1 or newer." >&2
- return 1
- else if not type -q fzf
- echo "fzf was not found in path." >&2
- return 1
- end
-
- # Delegate to native fish completion for specific commands
- # Use FZF_COMPLETION_NATIVE_MODE to choose: 'complete' (default) or 'complete-and-search'
+ # Use complete builtin for specific commands
function __fzf_complete_native
- if test "$FZF_COMPLETION_NATIVE_MODE" = complete-and-search
- commandline -f complete-and-search
- else
- commandline -f complete
- end
+ # Have the command run in a subshell
+ set -lx -- FZF_DEFAULT_COMMAND "complete -C\"$argv[1] \" "
+
+ set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults '--reverse --nth=1 --color=fg:dim,nth:regular' \
+ $FZF_COMPLETION_OPTS $argv[2..-1] '--accept-nth=1 --with-shell='(status fish-path)\\ -c)
+
+ set -l result (eval (__fzfcmd))
+ and commandline -rt $result
+
+ commandline -f repaint
end
# Generic path completion
@@ -47,54 +37,34 @@ function fzf_completion_setup
set -l fzf_query $argv[2]
set -l opt_prefix $argv[3]
set -l compgen $argv[4]
- set -l fzf_opts $argv[5]
- set -l tail $argv[6]
-
- # Determine walker based on compgen type
- set -l walker
- set -l rest
- if string match -q '*dir*' -- "$compgen"
- set walker (test -n "$FZF_COMPLETION_DIR_WALKER"; and echo $FZF_COMPLETION_DIR_WALKER; or echo "dir,follow")
- set rest $FZF_COMPLETION_DIR_OPTS
- else if string match -q '*file*' -- "$compgen"
- set walker (test -n "$FZF_COMPLETION_FILE_WALKER"; and echo $FZF_COMPLETION_FILE_WALKER; or echo "file,follow,hidden")
- set rest $FZF_COMPLETION_FILE_OPTS
- else
- set walker (test -n "$FZF_COMPLETION_PATH_WALKER"; and echo $FZF_COMPLETION_PATH_WALKER; or echo "file,dir,follow,hidden")
- set rest $FZF_COMPLETION_PATH_OPTS
- end
+ set -l tail " "
# Set fzf options
- set -lx FZF_DEFAULT_OPTS (__fzf_defaults \
- "--reverse --walker=$walker --scheme=path" \
- "$FZF_COMPLETION_OPTS $fzf_opts --print0 $rest")
+ 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
if type -q "$compgen"
set -l result (eval $compgen $dir | eval (__fzfcmd) --query=$fzf_query | string split0)
- and commandline -rt -- (string join -- ' ' $opt_prefix(string escape -n -- $result))$tail
+ and commandline -rt -- (string join -- ' ' $opt_prefix(string escape -- $result))$tail
else
set -l result (eval (__fzfcmd) --walker-root=$dir --query=$fzf_query | string split0)
- and commandline -rt -- (string join -- ' ' $opt_prefix(string escape -n -- $result))$tail
+ and commandline -rt -- (string join -- ' ' $opt_prefix(string escape -- $result))$tail
end
commandline -f repaint
end
- function _fzf_path_completion
- __fzf_generic_path_completion $argv[1] $argv[2] $argv[3] _fzf_compgen_path -m " "
- end
-
- function _fzf_dir_completion
- __fzf_generic_path_completion $argv[1] $argv[2] $argv[3] _fzf_compgen_dir "" ""
- end
-
- function _fzf_file_completion
- __fzf_generic_path_completion $argv[1] $argv[2] $argv[3] _fzf_compgen_file -m " "
- end
-
# Kill completion (process selection)
function _fzf_complete_kill
set -lx FZF_DEFAULT_OPTS (__fzf_defaults "--reverse" "$FZF_COMPLETION_OPTS")
@@ -104,65 +74,35 @@ function fzf_completion_setup
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) -m --header-lines=1 --no-preview --wrap --print0 --query=$argv[1] | string split0)
-
- if test (count $result) -gt 0
- set -l pids
- for line in $result
- test -z "$line"; and continue
- set -l fields (string split -n ' ' -- "$line")
- if test (count $fields) -ge 2
- set -a pids $fields[2]
- end
- end
- if test (count $pids) -gt 0
- commandline -rt -- (string join ' ' -- $pids)" "
- end
- end
+ 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 -l trigger (test -n "$FZF_COMPLETION_TRIGGER"; and echo "$FZF_COMPLETION_TRIGGER"; or echo '++')
+ set -q FZF_COMPLETION_TRIGGER
+ or set -l FZF_COMPLETION_TRIGGER '**'
# Get tokens - use version-appropriate flags
- # Fish 4.0+: -x (--tokens-expanded) returns expanded tokens
- # Fish 3.1-3.7: -o (--tokenize) returns tokenized output (deprecated in 3.2+ but still works)
- set -l fish_major (string match -r -- '^\d+' $version)
-
set -l tokens
- if test "$fish_major" -ge 4
+ if test (string match -r -- '^\d+' $version) -ge 4
set tokens (commandline -xpc)
else
set tokens (commandline -opc)
end
- if test (count $tokens) -lt 1
- __fzf_complete_native
- return
- end
-
- # Handle empty trigger with space
- if test -z "$trigger"; and test (string sub -s -1 -- (commandline -c)) = ' '
- set -a tokens ""
- end
-
- # Check if the trigger is present at the end
- if test (string sub -s -(string length -- "$trigger") -- (commandline -c)) != "$trigger"
- __fzf_complete_native
+ if test -z "$tokens"; or string match -qv -- "*"(string escape -n -- $FZF_COMPLETION_TRIGGER) (commandline -t)
+ commandline -f complete
return
end
- # Get the command word
- set -l cmd_word $tokens[1]
+ # Get the command name
+ set -l cmd_name $tokens[1]
# Strip trigger from commandline before parsing
- set -l raw_token (commandline --current-token 2>/dev/null | string collect)
- if test -n "$trigger"
- set -l stripped_token (string replace -r (string escape --style=regex -- "$trigger")'$' '' -- "$raw_token")
- commandline -rt -- "$stripped_token"
- end
+ test -n "$FZF_COMPLETION_TRIGGER"
+ and commandline -rt -- (string sub -e -(string length -- "$FZF_COMPLETION_TRIGGER") -- (commandline -t))
# Parse commandline (now without trigger)
set -l parsed (__fzf_parse_commandline)
@@ -171,28 +111,29 @@ function fzf_completion_setup
set -l opt_prefix $parsed[3]
# Directory commands
- set -l d_cmds (string split ' ' -- "$FZF_COMPLETION_DIR_COMMANDS")
- or set d_cmds cd pushd rmdir
+ set -q FZF_COMPLETION_DIR_COMMANDS
+ or set -l FZF_COMPLETION_DIR_COMMANDS cd pushd rmdir
# File-only commands
- set -l f_cmds (string split ' ' -- "$FZF_COMPLETION_FILE_COMMANDS")
- or set f_cmds cat head tail less more
+ set -q FZF_COMPLETION_FILE_COMMANDS
+ or set -l FZF_COMPLETION_FILE_COMMANDS cat head tail less more
# Native completion commands
- set -l n_cmds (string split ' ' -- "$FZF_COMPLETION_NATIVE_COMMANDS")
- or set n_cmds ssh telnet set functions
+ set -q FZF_COMPLETION_NATIVE_COMMANDS
+ or set -l FZF_COMPLETION_NATIVE_COMMANDS ssh telnet set functions type
# Route to appropriate completion function
- if type -q "_fzf_complete_$cmd_word"
- eval "_fzf_complete_$cmd_word" (string escape -- "$fzf_query") (string escape -- "$cmd_word")
- else if contains -- "$cmd_word" $n_cmds
- __fzf_complete_native
- else if contains -- "$cmd_word" $d_cmds
- _fzf_dir_completion "$dir" "$fzf_query" "$opt_prefix"
- else if contains -- "$cmd_word" $f_cmds
- _fzf_file_completion "$dir" "$fzf_query" "$opt_prefix"
+ if functions -q _fzf_complete_$cmd_name
+ _fzf_complete_$cmd_name "$fzf_query" "$cmd_name"
+ else if contains -- "$cmd_name" $FZF_COMPLETION_NATIVE_COMMANDS
+ set -l -- fzf_opt --query=(commandline -t | string escape)
+ __fzf_complete_native "$cmd_name" $fzf_opt
+ else if contains -- "$cmd_name" $FZF_COMPLETION_DIR_COMMANDS
+ __fzf_generic_path_completion "$dir" "$fzf_query" "$opt_prefix" _fzf_compgen_dir
+ else if contains -- "$cmd_name" $FZF_COMPLETION_FILE_COMMANDS
+ __fzf_generic_path_completion "$dir" "$fzf_query" "$opt_prefix" _fzf_compgen_file
else
- _fzf_path_completion "$dir" "$fzf_query" "$opt_prefix"
+ __fzf_generic_path_completion "$dir" "$fzf_query" "$opt_prefix" _fzf_compgen_path
end
end|
@bitraid would you mind running # Run fzf
if type -q "$compgen"
set -l result (eval $compgen $dir | eval (__fzfcmd) --query=$fzf_query | string split0)
- and commandline -rt -- (string join -- ' ' $opt_prefix(string escape -n -- $result))$tail
+ and commandline -rt -- (string join -- ' ' $opt_prefix(string escape -- $result))$tail
else
set -l result (eval (__fzfcmd) --walker-root=$dir --query=$fzf_query | string split0)
- and commandline -rt -- (string join -- ' ' $opt_prefix(string escape -n -- $result))$tail
+ and commandline -rt -- (string join -- ' ' $opt_prefix(string escape -- $result))$tail
endhere you rollback this change, but before it was behaving like bash/zsh and this way is different. See that it only fails for fish but the test passes for zsh/bash EDIT: |
…ion comment, use more explicit query builtin
|
currently here, but I'm stuck at the moment see #4605 (comment). It's not working like I would expect it to. |
|
Probably the README needs some updating, too. Also, I have a final suggestion: How about changing the order of completion functions, so that diff --git a/completion.fish b/completion.fish
index 1d5efb0..fa1fcc4 100644
--- a/completion.fish
+++ b/completion.fish
@@ -154,13 +154,13 @@ function fzf_completion_setup
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
+ 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 functions -q _fzf_complete_$cmd_name
+ _fzf_complete_$cmd_name "$fzf_query" "$cmd_name"
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 |
|
Updated the readme and the changes suggested above |
|
Thank you both for the great work. Some remaining issues. Should we allow opting out?For bash and zsh, the install script lets users opt out of fuzzy completion or key bindings: Do you want to enable fuzzy auto-completion? ([y]/n) n
Do you want to enable key bindings? ([y]/n) yThe fish version doesn't support this, and it may not be trivial to update the script to properly handle it (because we don't generate diff --git a/install b/install
index 6d0149e4..c2b88357 100755
--- a/install
+++ b/install
@@ -362,25 +362,34 @@ 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
+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
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' \
+ " $fish_line" \
'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 " ** You have to replace the line to: $fish_line"
echo
else
echo " - Clear"
echo
- append_line $update_config "fzf --fish | source" "$bind_file"
+ append_line $update_config "$fish_line" "$bind_file"
fi
fi
fiBut,
So what do you think? Is it worth supporting opt-out in fish like we do for bash and zsh, or should fish remain an exception? Custom fuzzy completionFor bash and zsh, we provide a way to write custom fuzzy completion: _fzf_complete_foo() {
_fzf_complete --multi --reverse --header-lines=3 -- "$@" < <(
ls -al
)
}
# Optional post-processing of selected entries
_fzf_complete_foo_post() {
awk '{print $NF}'
}
[ -n "$BASH" ] && complete -F _fzf_complete_foo -o default -o bashdefault fooI see that you implemented the So what's our stance? Should we try to introduce a similar helper on the fish side to make custom fuzzy completion more approachable? Or is the current level of abstraction sufficient for fish users, and we leave the rest as future work? |
|
I think we can address all those issues. Meanwhile, here are some fixes for some regressions, fixes for command completion when the token contains quotes, a fallback in the off chance that diff --git a/completion.fish b/completion.fish
index d03f4ee..4b70099 100644
--- a/completion.fish
+++ b/completion.fish
@@ -22,13 +22,13 @@ function fzf_completion_setup
function __fzf_complete_native
set -l result
if type -q column
- set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults "--reverse" \
+ 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 (eval complete -C \"$argv[1]\" \| (__fzfcmd))
+ set -- result (eval complete -C \"$argv[1]\" \| (__fzfcmd))
end
and commandline -rt -- (string join ' ' -- $result)' '
commandline -f repaint
@@ -36,20 +36,19 @@ function fzf_completion_setup
# 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 -lx -- dir $argv[1]
+ set -l -- fzf_query $argv[2]
+ set -l -- opt_prefix $argv[3]
+ set -l -- compgen $argv[4]
+ set -l -- tail " "
# Set fzf options
- set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults "--reverse --scheme=path" "$FZF_COMPLETION_OPTS --print0")
+ 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 -- 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"
@@ -60,9 +59,9 @@ function fzf_completion_setup
# Run fzf
set -l result
if functions -q "$compgen"
- set result (eval $compgen $dir | eval (__fzfcmd) --query=$fzf_query | string split0)
+ set -- result (eval $compgen $dir \| (__fzfcmd) --query=$fzf_query | string split0)
else
- set result (eval (__fzfcmd) --walker-root=$dir --query=$fzf_query | string split0)
+ 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
@@ -71,22 +70,25 @@ function fzf_completion_setup
# Kill completion (process selection)
function _fzf_complete_kill
- set -lx FZF_DEFAULT_OPTS (__fzf_defaults "--reverse" "$FZF_COMPLETION_OPTS")
+ set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults --reverse $FZF_COMPLETION_OPTS \
+ --accept-nth=2 -m --header-lines=1 --no-preview --wrap)
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)" "
+ if type -q ps
+ set -l -- ps_cmd '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;' \
+ 'or command ps --everyone --full --windows 2>/dev/null; end'
+ set -l -- result (eval $ps_cmd \| (__fzfcmd) --query=$argv[1])
+ and commandline -rt -- (string join ' ' -- $result)" "
+ else
+ __fzf_complete_native "kill " --multi --query=$argv[1]
+ end
commandline -f repaint
end
# Main completion function
function fzf-completion
set -q FZF_COMPLETION_TRIGGER
- or set -l 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.
@@ -96,36 +98,42 @@ function fzf_completion_setup
# Get tokens - use version-appropriate flags
set -l tokens
if test $fish_major -ge 4
- set tokens (commandline -xpc)
+ set -- tokens (commandline -xpc)
else
- set tokens (commandline -opc)
+ set -- tokens (commandline -opc)
end
set -l -- current_token (commandline -t)
set -l -- cmd_name $tokens[1]
+ set -l -- regex_trigger (string escape --style=regex -- $FZF_COMPLETION_TRIGGER)'$'
set -l -- has_trigger false
- string match -qr -- (string escape --style regex -- $FZF_COMPLETION_TRIGGER)'$' $current_token
+ string match -qr -- $regex_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)
+ set -- current_token (string replace -r -- $regex_trigger '' $current_token)
commandline -rt -- $current_token
end
+ set -l -- parsed (__fzf_parse_commandline)
+ set -l -- dir $parsed[1]
+ set -l -- fzf_query $parsed[2]
+ set -l -- opt_prefix $parsed[3]
+
if not $has_trigger
commandline -f complete
return
else if test -z "$tokens"
- __fzf_complete_native "" --query=$current_token
+ __fzf_complete_native "" --query=$opt_prefix$fzf_query
return
end
- set -l disable_opt_comp false
+ 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
+ and set -- disable_opt_comp true
end
if not $disable_opt_comp
@@ -140,34 +148,34 @@ function fzf_completion_setup
# Directory commands
set -q FZF_COMPLETION_DIR_COMMANDS
- or set -l FZF_COMPLETION_DIR_COMMANDS cd pushd rmdir
+ 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 nano
+ or set -l -- FZF_COMPLETION_FILE_COMMANDS cat head tail less more nano
# Native completion commands
set -q FZF_COMPLETION_NATIVE_COMMANDS
- or set -l FZF_COMPLETION_NATIVE_COMMANDS ssh telnet
+ 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
+ or set -l -- FZF_COMPLETION_NATIVE_COMMANDS_MULTI set functions type
# Route to appropriate completion function
if contains -- "$cmd_name" $FZF_COMPLETION_NATIVE_COMMANDS $FZF_COMPLETION_NATIVE_COMMANDS_MULTI
- set -l -- fzf_opt --query=(commandline -t | string escape)
+ set -l -- fzf_opt --query=$fzf_query
contains -- "$cmd_name" $FZF_COMPLETION_NATIVE_COMMANDS_MULTI
and set -a -- fzf_opt --multi
__fzf_complete_native "$cmd_name " $fzf_opt
else if functions -q _fzf_complete_$cmd_name
_fzf_complete_$cmd_name "$fzf_query" "$cmd_name"
else if contains -- "$cmd_name" $FZF_COMPLETION_DIR_COMMANDS
- __fzf_generic_path_completion _fzf_compgen_dir
+ __fzf_generic_path_completion "$dir" "$fzf_query" "$opt_prefix" _fzf_compgen_dir
else if contains -- "$cmd_name" $FZF_COMPLETION_FILE_COMMANDS
- __fzf_generic_path_completion _fzf_compgen_file
+ __fzf_generic_path_completion "$dir" "$fzf_query" "$opt_prefix" _fzf_compgen_file
else
- __fzf_generic_path_completion _fzf_compgen_path
+ __fzf_generic_path_completion "$dir" "$fzf_query" "$opt_prefix" _fzf_compgen_path
end
end
|
|
makes sense, we might as well do the remaining ones (after your patch) diff --git a/shell/completion.fish b/shell/completion.fish
index 4b70099f..e5bc3ee0 100644
--- a/shell/completion.fish
+++ b/shell/completion.fish
@@ -24,9 +24,9 @@ function fzf_completion_setup
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))
+ 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" \
+ 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 (eval complete -C \"$argv[1]\" \| (__fzfcmd))
end
@@ -43,17 +43,17 @@ function fzf_completion_setup
set -l -- tail " "
# Set fzf options
- set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults "--reverse --scheme=path" $FZF_COMPLETION_OPTS --print0)
+ 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"
+ 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"
+ set -a -- FZF_DEFAULT_OPTS --multi --walker=file,follow,hidden $FZF_COMPLETION_FILE_OPTS
else
- set -a -- FZF_DEFAULT_OPTS "-m --walker=file,dir,follow,hidden $FZF_COMPLETION_PATH_OPTS"
+ set -a -- FZF_DEFAULT_OPTS --multi --walker=file,dir,follow,hidden $FZF_COMPLETION_PATH_OPTS
end
# Run fzf |
|
Just keep in mind that when calling |
You're right, this changed the order and allows the user to override causing issues - set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults --reverse --nth=1 --color=fg:dim,nth:regular \
+ set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults "--reverse --nth=1 --color=fg:dim,nth:regular" \
- set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults --reverse --scheme=path $FZF_COMPLETION_OPTS --print0)
+ set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults "--reverse --scheme=path" $FZF_COMPLETION_OPTS --print0) |
|
Actually, the options in the first argument are the ones that are allowed to be changed by the user variables, while the rest are always in effect. |
We could make this work if we refactor the functions from On the other hand we could:
like @bitraid said this shouldn't be a problem. I've started prototyping something, but still needs some work |
|
I have also created a solution for this that doesn't need refactoring, it makes use of a variable |
In bash and zsh, each script file should be individually "source"able for historical reasons. |
|
Should we then merge this one and @bitraid makes another PR on top? |
|
Back to the main script, I have yet one more small fix for the following issue: Suppose we trigger for example diff --git a/completion.fish b/completion.fish
index 17b50aa..5150440 100644
--- a/completion.fish
+++ b/completion.fish
@@ -121,12 +121,13 @@ function fzf_completion_setup
set -l -- dir $parsed[1]
set -l -- fzf_query $parsed[2]
set -l -- opt_prefix $parsed[3]
+ set -l -- full_query $opt_prefix$fzf_query
if not $has_trigger
commandline -f complete
return
else if test -z "$tokens"
- __fzf_complete_native "" --query=$opt_prefix$fzf_query
+ __fzf_complete_native "" --query=$full_query
return
end
@@ -164,12 +165,12 @@ function fzf_completion_setup
# Route to appropriate completion function
if contains -- "$cmd_name" $FZF_COMPLETION_NATIVE_COMMANDS $FZF_COMPLETION_NATIVE_COMMANDS_MULTI
- set -l -- fzf_opt --query=$fzf_query
+ set -l -- fzf_opt --query=$full_query
contains -- "$cmd_name" $FZF_COMPLETION_NATIVE_COMMANDS_MULTI
and set -a -- fzf_opt --multi
__fzf_complete_native "$cmd_name " $fzf_opt
else if functions -q _fzf_complete_$cmd_name
- _fzf_complete_$cmd_name "$fzf_query" "$cmd_name"
+ _fzf_complete_$cmd_name "$full_query" "$cmd_name"
else if contains -- "$cmd_name" $FZF_COMPLETION_DIR_COMMANDS
__fzf_generic_path_completion "$dir" "$fzf_query" "$opt_prefix" _fzf_compgen_dir
else if contains -- "$cmd_name" $FZF_COMPLETION_FILE_COMMANDS |
I think your approach is closer to what is done with bash/zsh (splitting the files). But instead of using different options for fzf, modify the |
please have a look now |
|
The use of 4-space indentation introduces too much unnecessary code changes. Since all script files in this project follow the 2-space indentation convention, I'd like to suggest using 2-space indentation here as well for consistency. |
Oh, my bad, it was not intentional |
Adds fuzzy completion for fish shell, triggered by
**<TAB>, matching the functionality of bash/zsh completions.PS: I didn't test the tmux part since I don't use it, but I tried to keep it faithful to the original code
Configuration Examples
Usage