mirror of
https://github.com/junegunn/fzf.git
synced 2026-04-11 02:14:20 +08:00
fish: Completion script rewrite (SHIFT-TAB)
This commit is contained in:
92
README.md
92
README.md
@@ -747,60 +747,78 @@ _fzf_complete_foo_post() {
|
||||
|
||||
### Fuzzy completion for fish
|
||||
|
||||
(Available in 0.68.0 or later)
|
||||
|
||||
Fuzzy completion for fish differs from bash and zsh in that:
|
||||
|
||||
- It doesn't require a trigger sequence like `**`. Instead, if activates
|
||||
on `Shift-TAB`, while `TAB` preserves fish's native completion behavior.
|
||||
on `Shift-TAB` (replacing the native pager search mode), while `TAB` preserves
|
||||
fish's native completion behavior.
|
||||
- It relies on fish's native completion system to populate the candidate list,
|
||||
rather than performing a recursive file system traversal. For recursive
|
||||
searching, use the `CTRL-T` binding instead.
|
||||
- The only supported configuration variable is `FZF_COMPLETION_OPTS`.
|
||||
- If the current command line token is a wildcard pattern, it performs search on
|
||||
the wildcard expansion path list (instead of the native behavior of inserting
|
||||
all the results in command line). Because the shell is used for the expansion,
|
||||
there is a limit in the number of results.
|
||||
- The only supported configuration variables are `FZF_COMPLETION_OPTS` and
|
||||
`FZF_EXPANSION_OPS`.
|
||||
- The function that is used by custom completion functions is named
|
||||
`fzf_complete`, which only accepts fzf options as arguments, and can be also
|
||||
called without any redirected input, to just modify fzf options while
|
||||
presenting the native completion results. For compatibility with other shells,
|
||||
a function named `_fzf_complete` is provided, that can accept ` -- $argv` in
|
||||
its command line arguments, after fzf options.
|
||||
|
||||
That said, just like in bash and zsh, you can implement custom completion for
|
||||
a specific command by defining an `_fzf_complete_COMMAND` function. For example:
|
||||
For commands that are not covered by fish completions, it is better to create
|
||||
regular fish completion functions (which will work for both `TAB` and
|
||||
`Shift-TAB`), and create fzf completion functions only when needing to modify
|
||||
fzf options for a specific command or want different results for `Shift-TAB`:
|
||||
|
||||
```fish
|
||||
function _fzf_complete_foo
|
||||
function _fzf_complete_foo_post
|
||||
awk '{print $NF}'
|
||||
end
|
||||
_fzf_complete --multi --reverse --header-lines=3 -- $argv < (ls -al | psub)
|
||||
# Customize git completion
|
||||
function _fzf_complete_git
|
||||
# Show header text with active branch for all git completions
|
||||
set -lx -- FZF_COMPLETION_OPTS --header="'"(git branch --show-current 2>/dev/null)"'"
|
||||
|
||||
functions -e _fzf_complete_foo_post
|
||||
# No other changes when less than 3 arguments, or when completing options
|
||||
if not set -q argv[3]; or string match -q -- '-*' $argv[-1]
|
||||
fzf_complete
|
||||
return
|
||||
end
|
||||
|
||||
# Check subcommand
|
||||
switch $argv[2]
|
||||
case checkout diff log show
|
||||
# Set preview and display all branches and commits for subcommands: checkout, diff, log, show
|
||||
begin
|
||||
git branch --all --format='%(refname:short)'
|
||||
git log --all --oneline --color=always
|
||||
end | fzf_complete --no-multi --ansi --accept-nth=1 --query=$argv[-1] --preview='git show --color=always {1}'
|
||||
|
||||
case add rm mv
|
||||
# Only set preview for subcommands: add, rm, mv
|
||||
# Special characters in fish completion lists are escaped, so the r flag must be used.
|
||||
fzf_complete --preview="git diff --color=always {r1}"
|
||||
|
||||
case '*'
|
||||
# No changes for other subcommands
|
||||
fzf_complete
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
And here's a more complex example for customizing `git`
|
||||
Similar to bash and zsh, the output of fzf can be processed before inserted in
|
||||
command line, by defining a function named `_fzf_complete_COMMAND_post` or
|
||||
`_fzf_post_complete_COMMAND`:
|
||||
|
||||
```fish
|
||||
function _fzf_complete_git
|
||||
switch $argv[2]
|
||||
case checkout switch
|
||||
_fzf_complete --reverse --no-preview -- $argv < (git branch --all --format='%(refname:short)' | psub)
|
||||
function _fzf_complete_foo
|
||||
ls -sh --zero --color=always | fzf_complete --read0 --print0 --ansi --no-multi-line --header-lines=1
|
||||
end
|
||||
|
||||
case add
|
||||
function _fzf_complete_git_post
|
||||
awk '{print $NF}'
|
||||
end
|
||||
_fzf_complete --multi --reverse -- $argv < (git status --short | psub)
|
||||
|
||||
case show log diff
|
||||
function _fzf_complete_git_post
|
||||
awk '{print $1}'
|
||||
end
|
||||
_fzf_complete --reverse --no-sort --preview='git show --color=always {1}' -- $argv < (git log --oneline | psub)
|
||||
|
||||
case ''
|
||||
__fzf_complete_native "$argv[1] " --query=(commandline -t | string escape)
|
||||
|
||||
case '*'
|
||||
set -l -- current_token (commandline -t)
|
||||
__fzf_complete_native "$argv $current_token" --query=(string escape -- $current_token) --multi
|
||||
function _fzf_post_complete_foo
|
||||
while read -lz result
|
||||
string escape -n -- $result | string trim -l -c '\ ' | string split -m 1 -f 2 ' '
|
||||
end
|
||||
|
||||
functions -e _fzf_complete_git_post
|
||||
end
|
||||
```
|
||||
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
function __fzf_defaults
|
||||
# $argv[1]: Prepend to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS
|
||||
# $argv[2..]: Append to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS
|
||||
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_cmd_tokens -d 'Return command line tokens, skipping leading env assignments and command prefixes'
|
||||
# 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
|
||||
|
||||
# Filter out leading environment variable assignments
|
||||
set -l -- var_count 0
|
||||
for i in $tokens
|
||||
if string match -qr -- '^[\w]+=' $i
|
||||
set var_count (math $var_count + 1)
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
set -e -- tokens[0..$var_count]
|
||||
|
||||
# Skip command prefixes so callers see the actual command name,
|
||||
# e.g. "builtin cd" → "cd", "env VAR=1 command cd" → "cd"
|
||||
while true
|
||||
switch "$tokens[1]"
|
||||
case builtin command
|
||||
set -e -- tokens[1]
|
||||
test "$tokens[1]" = "--"; and set -e -- tokens[1]
|
||||
case env
|
||||
set -e -- tokens[1]
|
||||
test "$tokens[1]" = "--"; and set -e -- tokens[1]
|
||||
while string match -qr -- '^[\w]+=' "$tokens[1]"
|
||||
set -e -- tokens[1]
|
||||
end
|
||||
case '*'
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
string escape -n -- $tokens
|
||||
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 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]
|
||||
|
||||
# fish v3.3.0 and newer: Don't use option prefix if " -- " is preceded.
|
||||
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
|
||||
|
||||
# Set $prefix and expanded $fzf_query with preserved trailing newlines.
|
||||
if test "$fish_major" -ge 4
|
||||
# fish v4.0.0 and newer
|
||||
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
|
||||
# fish v3.2.0 - v3.7.1 (last v3)
|
||||
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
|
||||
# fish older than v3.2.0 (v3.1b1 - v3.1.2)
|
||||
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"
|
||||
# Normalize path in $fzf_query, set $dir to the longest existing directory.
|
||||
if test \( "$fish_major" -ge 4 \) -o \( "$fish_major" -eq 3 -a "$fish_minor" -ge 5 \)
|
||||
# fish v3.5.0 and newer
|
||||
set -- fzf_query (path normalize -- $fzf_query)
|
||||
set -- dir $fzf_query
|
||||
while not path is -d $dir
|
||||
set -- dir (path dirname $dir)
|
||||
end
|
||||
else
|
||||
# fish older than v3.5.0 (v3.1b1 - v3.4.1)
|
||||
if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2
|
||||
# fish v3.2.0 - v3.4.1
|
||||
string match -q -r -- '(?<fzf_query>^[\s\S]*?(?=\n?$)$)' \
|
||||
(string replace -r -a -- '(?<=/)/|(?<!^)/+(?!\n)$' '' $fzf_query | string collect -N)
|
||||
else
|
||||
# fish v3.1b1 - v3.1.2
|
||||
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
|
||||
# Strip $dir from $fzf_query - preserve trailing newlines.
|
||||
if test "$fish_major" -ge 4
|
||||
# fish v4.0.0 and newer
|
||||
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
|
||||
# fish v3.2.0 - v3.7.1 (last v3)
|
||||
string match -q -r -- '^/?(?<fzf_query>[\s\S]*?(?=\n?$)$)' \
|
||||
(string replace -- "$dir" '' $fzf_query | string collect -N)
|
||||
else
|
||||
# fish older than v3.2.0 (v3.1b1 - v3.1.2)
|
||||
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
|
||||
@@ -4,238 +4,166 @@
|
||||
# / __/ / /_/ __/
|
||||
# /_/ /___/_/ completion.fish
|
||||
#
|
||||
# - $FZF_COMPLETION_OPTS (default: empty)
|
||||
# - $FZF_COMPLETION_OPTS
|
||||
# - $FZF_EXPANSION_OPTS
|
||||
|
||||
function fzf_completion_setup
|
||||
|
||||
#----BEGIN INCLUDE common.fish
|
||||
# NOTE: Do not directly edit this section, which is copied from "common.fish".
|
||||
# To modify it, one can edit "common.fish" and run "./update.sh" to apply
|
||||
# the changes. See code comments in "common.fish" for the implementation details.
|
||||
|
||||
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_cmd_tokens -d 'Return command line tokens, skipping leading env assignments and command prefixes'
|
||||
set -l tokens
|
||||
if test (string match -r -- '^\d+' $version) -ge 4
|
||||
set -- tokens (commandline -xpc)
|
||||
else
|
||||
set -- tokens (commandline -opc)
|
||||
end
|
||||
|
||||
set -l -- var_count 0
|
||||
for i in $tokens
|
||||
if string match -qr -- '^[\w]+=' $i
|
||||
set var_count (math $var_count + 1)
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
set -e -- tokens[0..$var_count]
|
||||
|
||||
while true
|
||||
switch "$tokens[1]"
|
||||
case builtin command
|
||||
set -e -- tokens[1]
|
||||
test "$tokens[1]" = "--"; and set -e -- tokens[1]
|
||||
case env
|
||||
set -e -- tokens[1]
|
||||
test "$tokens[1]" = "--"; and set -e -- tokens[1]
|
||||
while string match -qr -- '^[\w]+=' "$tokens[1]"
|
||||
set -e -- tokens[1]
|
||||
end
|
||||
case '*'
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
string escape -n -- $tokens
|
||||
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
|
||||
#----END INCLUDE
|
||||
|
||||
# Use complete builtin for specific commands
|
||||
function __fzf_complete_native
|
||||
set -l -- token (commandline -t)
|
||||
set -l -- completions (eval complete -C \"$argv[1]\")
|
||||
test -n "$completions"; or begin commandline -f repaint; return; end
|
||||
|
||||
# Calculate tabstop based on longest completion item (sample first 500 for performance)
|
||||
set -l -- tabstop 20
|
||||
set -l -- sample_size (math "min(500, "(count $completions)")")
|
||||
for c in $completions[1..$sample_size]
|
||||
set -l -- len (string length -V -- (string split -- \t $c))
|
||||
test -n "$len[2]" -a "$len[1]" -gt "$tabstop"
|
||||
and set -- tabstop $len[1]
|
||||
end
|
||||
# limit to 120 to prevent long lines
|
||||
set -- tabstop (math "min($tabstop + 4, 120)")
|
||||
|
||||
set -l result
|
||||
set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults \
|
||||
"--reverse --delimiter=\\t --nth=1 --tabstop=$tabstop --color=fg:dim,nth:regular" \
|
||||
$FZF_COMPLETION_OPTS $argv[2..-1] --accept-nth=1 --read0 --print0)
|
||||
set -- result (string join0 -- $completions | eval (__fzfcmd) | string split0)
|
||||
and begin
|
||||
set -l -- tail ' '
|
||||
# Append / to bare ~username results (fish omits it unlike other shells)
|
||||
set -- result (string replace -r -- '^(~\w+)\s?$' '$1/' $result)
|
||||
# Don't add trailing space if single result is a directory
|
||||
test (count $result) -eq 1
|
||||
and string match -q -- '*/' "$result"; and set -- tail ''
|
||||
|
||||
set -l -- result (string escape -n -- $result)
|
||||
|
||||
string match -q -- '~*' "$token"
|
||||
and set result (string replace -r -- '^\\\\~' '~' $result)
|
||||
|
||||
string match -q -- '$*' "$token"
|
||||
and set result (string replace -r -- '^\\\\\$' '\$' $result)
|
||||
|
||||
commandline -rt -- (string join ' ' -- $result)$tail
|
||||
end
|
||||
commandline -f repaint
|
||||
end
|
||||
|
||||
function _fzf_complete
|
||||
set -l -- args (string escape -- $argv | string join ' ' | string split -- ' -- ')
|
||||
set -l -- post_func (status function)_(string split -- ' ' $args[2])[1]_post
|
||||
set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults --reverse $FZF_COMPLETION_OPTS $args[1])
|
||||
set -lx FZF_DEFAULT_OPTS_FILE
|
||||
set -lx FZF_DEFAULT_COMMAND
|
||||
set -l -- fzf_query (commandline -t | string escape)
|
||||
set -l result
|
||||
eval (__fzfcmd) --query=$fzf_query | while read -l r; set -a -- result $r; end
|
||||
and if functions -q $post_func
|
||||
commandline -rt -- (string collect -- $result | eval $post_func $args[2] | string join ' ')' '
|
||||
else
|
||||
commandline -rt -- (string join -- ' ' (string escape -- $result))' '
|
||||
end
|
||||
commandline -f repaint
|
||||
end
|
||||
|
||||
# Kill completion (process selection)
|
||||
function _fzf_complete_kill
|
||||
set -l -- fzf_query (commandline -t | string escape)
|
||||
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
|
||||
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=$fzf_query)
|
||||
and commandline -rt -- (string join ' ' -- $result)" "
|
||||
else
|
||||
__fzf_complete_native "kill " --multi --query=$fzf_query
|
||||
end
|
||||
commandline -f repaint
|
||||
end
|
||||
|
||||
# Main completion function
|
||||
function fzf-completion
|
||||
set -l -- tokens (__fzf_cmd_tokens)
|
||||
set -l -- current_token (commandline -t)
|
||||
set -l -- cmd_name $tokens[1]
|
||||
|
||||
# Route to appropriate completion function
|
||||
if test -n "$tokens"; and functions -q _fzf_complete_$cmd_name
|
||||
_fzf_complete_$cmd_name $tokens
|
||||
else
|
||||
set -l -- fzf_opt --query=$current_token --multi
|
||||
__fzf_complete_native "$tokens $current_token" $fzf_opt
|
||||
end
|
||||
end
|
||||
|
||||
# Bind Shift-Tab to fzf-completion (Tab retains native Fish behavior)
|
||||
if test (string match -r -- '^\d+' $version) -ge 4
|
||||
bind shift-tab fzf-completion
|
||||
bind -M insert shift-tab fzf-completion
|
||||
else
|
||||
bind -k btab fzf-completion
|
||||
bind -M insert -k btab fzf-completion
|
||||
end
|
||||
# The oldest supported fish version is 3.4.0. For this message being able to be
|
||||
# displayed on older versions, the command substitution syntax $() should not
|
||||
# be used anywhere in the script, otherwise the source command will fail.
|
||||
if string match -qr -- '^[12]\\.|^3\\.[0-3]' $version
|
||||
echo "fzf completion script requires fish version 3.4.0 or newer." >&2
|
||||
return 1
|
||||
else if not command -q fzf
|
||||
echo "fzf was not found in path." >&2
|
||||
return 1
|
||||
end
|
||||
|
||||
# Run setup
|
||||
fzf_completion_setup
|
||||
function fzf_complete -w fzf -d 'fzf command completion and wildcard expansion search'
|
||||
# Restore the default shift-tab behavior on tab completions
|
||||
if commandline --paging-mode
|
||||
commandline -f complete-and-search
|
||||
return
|
||||
end
|
||||
|
||||
# Remove any trailing unescaped backslash from token and update command line
|
||||
set -l -- token (string replace -r -- '(?<!\\\\)(?:\\\\\\\\)*\\K\\\\$' '' (commandline -t | string collect) | string collect)
|
||||
commandline -rt -- $token
|
||||
|
||||
# Remove any line breaks from token
|
||||
set -- token (string replace -ra -- '\\\\\\n' '' $token | string collect)
|
||||
|
||||
# regex: Match token with unescaped/unquoted glob character
|
||||
set -l -- r_glob '^(?:[^\'"\\\\*]|\\\\[\\S\\s]|\'(?:\\\\[\\S\\s]|[^\'\\\\])*\'|"(?:\\\\[\\S\\s]|[^"\\\\])*")*\\*[\\S\\s]*$'
|
||||
|
||||
# regex: Match any unbalanced quote character
|
||||
set -l -- r_quote '^(?>(?:\\\\[\\s\\S]|"(?:[^"\\\\]|\\\\[\\s\\S])*"|\'(?:[^\'\\\\]|\\\\[\\s\\S])*\'|[^\'"\\\\]+)*)\\K[\'"]'
|
||||
|
||||
# The expansion pattern is the token with any open quote closed, or is empty.
|
||||
set -l -- glob_pattern (string match -r -- $r_glob $token | string collect)(string match -r -- $r_quote $token | string collect -a)
|
||||
|
||||
set -l -- cl_tokenize_opt '--tokens-expanded'
|
||||
string match -q -- '3.*' $version
|
||||
and set -- cl_tokenize_opt '--tokenize'
|
||||
|
||||
# Set command line tokens without any leading variable definitions or launcher
|
||||
# commands (including their options, but not any option arguments).
|
||||
set -l -- r_cmd '^(?:(?:builtin|command|doas|env|sudo|\\w+=\\S*|-\\S+)\\s+)*\\K[\\s\\S]+'
|
||||
set -l -- cmd (commandline $cl_tokenize_opt --input=(commandline -pc | string match -r $r_cmd))
|
||||
test -z "$token"
|
||||
and set -a -- cmd ''
|
||||
|
||||
# Set fzf options
|
||||
test -z "$FZF_TMUX_HEIGHT"
|
||||
and set -l -- FZF_TMUX_HEIGHT 40%
|
||||
|
||||
set -lax -- FZF_DEFAULT_OPTS \
|
||||
"--height=$FZF_TMUX_HEIGHT --min-height=20+ --bind=ctrl-z:ignore" \
|
||||
(test -r "$FZF_DEFAULT_OPTS_FILE"; and string join -- ' ' <$FZF_DEFAULT_OPTS_FILE) \
|
||||
$FZF_DEFAULT_OPTS '--bind=alt-r:toggle-raw --multi --wrap=word --reverse' \
|
||||
(if test -n "$glob_pattern"; string collect -- $FZF_EXPANSION_OPTS; else;
|
||||
string collect -- $FZF_COMPLETION_OPTS; end; string escape -n -- $argv) \
|
||||
--with-shell=(status fish-path)\\ -c
|
||||
|
||||
set -lx FZF_DEFAULT_OPTS_FILE
|
||||
|
||||
set -l -- fzf_cmd fzf
|
||||
test "$FZF_TMUX" = 1
|
||||
and set -- fzf_cmd fzf-tmux $FZF_TMUX_OPTS -d$FZF_TMUX_HEIGHT --
|
||||
|
||||
set -l result
|
||||
|
||||
# Get the completion list from stdin when it's not a tty
|
||||
if not isatty stdin
|
||||
set -l -- custom_post_func _fzf_post_complete_$cmd[1]
|
||||
functions -q $custom_post_func
|
||||
or set -- custom_post_func _fzf_complete_$cmd[1]_post
|
||||
|
||||
if functions -q $custom_post_func
|
||||
$fzf_cmd | $custom_post_func $cmd | while read -l r; set -a -- result $r; end
|
||||
else if string match -q -- '*--print0*' "$FZF_DEFAULT_OPTS"
|
||||
$fzf_cmd | while read -lz r; set -a -- result $r; end
|
||||
else
|
||||
$fzf_cmd | while read -l r; set -a -- result $r; end
|
||||
end
|
||||
|
||||
# Wildcard expansion
|
||||
else if test -n "$glob_pattern"
|
||||
# Set the command to be run by fzf, so there is a visual indicator and an
|
||||
# easy way to abort on long recursive searches.
|
||||
set -lx -- FZF_DEFAULT_COMMAND "for i in $glob_pattern;" \
|
||||
'test -d "$i"; and string match -qv -- "*/" $i; and set -- i $i/;' \
|
||||
'string join0 -- $i; end'
|
||||
|
||||
set -- result (string escape -n -- ($fzf_cmd --read0 --print0 --scheme=path --no-multi-line | string split0))
|
||||
|
||||
# Command completion
|
||||
else
|
||||
# Call custom function if defined
|
||||
set -l -- custom_func _fzf_complete_$cmd[1]
|
||||
if functions -q $custom_func; and not set -q __fzf_no_custom_complete
|
||||
set -lx __fzf_no_custom_complete
|
||||
$custom_func $cmd
|
||||
return
|
||||
end
|
||||
|
||||
# Workaround for complete not having newlines in results
|
||||
if string match -qr -- '\\n' $token
|
||||
set -- token (string replace -ra -- '(?<!\\\\)(?:\\\\\\\\)*\\K\\\\\$' '\\\\\\\\\$' $token | string collect)
|
||||
set -- token (string unescape -- $token | string collect)
|
||||
set -- token (string replace -ra -- '\\n' '\\\\n' $token | string collect)
|
||||
end
|
||||
|
||||
set -- list (complete -C --escape -- (string join -- ' ' (commandline -pc $cl_tokenize_opt) $token | string collect))
|
||||
if test -n "$list"
|
||||
# Get the initial tabstop value
|
||||
if set -l -- tabstop (string match -rga -- '--tabstop[= ](?:0*)([1-9]\\d+|[4-9])' "$FZF_DEFAULT_OPTS")[-1]
|
||||
set -- tabstop (math $tabstop - 4)
|
||||
else
|
||||
set -- tabstop 4
|
||||
end
|
||||
|
||||
# Determine the tabstop length for description alignment
|
||||
set -l -- max_columns (math $COLUMNS - 40)
|
||||
for i in $list[1..500]
|
||||
set -l -- item (string split -f 1 -- \t $i)
|
||||
and set -l -- len (string length -V -- $item)
|
||||
and test "$len" -gt "$tabstop" -a "$len" -lt "$max_columns"
|
||||
and set -- tabstop $len
|
||||
end
|
||||
set -- tabstop (math $tabstop + 4)
|
||||
|
||||
set -- result (string collect -- $list | $fzf_cmd --delimiter="\t" --tabstop=$tabstop --wrap-sign=\t"↳ " --accept-nth=1)
|
||||
end
|
||||
end
|
||||
|
||||
# Update command line
|
||||
if test -n "$result"
|
||||
# No extra space after single selection that ends with path separator
|
||||
set -l -- tail ' '
|
||||
test (count $result) -eq 1
|
||||
and string match -q -- '*/' "$result"
|
||||
and set -- tail ''
|
||||
|
||||
commandline -rt -- (string join -- ' ' $result)$tail
|
||||
end
|
||||
|
||||
commandline -f repaint
|
||||
end
|
||||
|
||||
function _fzf_complete
|
||||
set -l fzf_args
|
||||
for i in $argv
|
||||
string match -q -- '--' $i; and break
|
||||
set -a -- fzf_args $i
|
||||
end
|
||||
fzf_complete $fzf_args
|
||||
end
|
||||
|
||||
# Bind to shift-tab
|
||||
if string match -qr -- '^\\d\\d+|^[4-9]' $version
|
||||
bind shift-tab fzf_complete
|
||||
bind -M insert shift-tab fzf_complete
|
||||
else
|
||||
bind -k btab fzf_complete
|
||||
bind -M insert -k btab fzf_complete
|
||||
end
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
# - $FZF_ALT_C_COMMAND
|
||||
# - $FZF_ALT_C_OPTS
|
||||
|
||||
|
||||
# Key bindings
|
||||
# ------------
|
||||
# The oldest supported fish version is 3.1b1. To maintain compatibility, the
|
||||
@@ -29,12 +28,9 @@ function fzf_key_bindings
|
||||
return 1
|
||||
end
|
||||
|
||||
#----BEGIN INCLUDE common.fish
|
||||
# NOTE: Do not directly edit this section, which is copied from "common.fish".
|
||||
# To modify it, one can edit "common.fish" and run "./update.sh" to apply
|
||||
# the changes. See code comments in "common.fish" for the implementation details.
|
||||
|
||||
function __fzf_defaults
|
||||
# $argv[1]: Prepend to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS
|
||||
# $argv[2..]: Append to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS
|
||||
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] \
|
||||
@@ -53,51 +49,17 @@ function fzf_key_bindings
|
||||
end
|
||||
end
|
||||
|
||||
function __fzf_cmd_tokens -d 'Return command line tokens, skipping leading env assignments and command prefixes'
|
||||
set -l tokens
|
||||
if test (string match -r -- '^\d+' $version) -ge 4
|
||||
set -- tokens (commandline -xpc)
|
||||
else
|
||||
set -- tokens (commandline -opc)
|
||||
end
|
||||
|
||||
set -l -- var_count 0
|
||||
for i in $tokens
|
||||
if string match -qr -- '^[\w]+=' $i
|
||||
set var_count (math $var_count + 1)
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
set -e -- tokens[0..$var_count]
|
||||
|
||||
while true
|
||||
switch "$tokens[1]"
|
||||
case builtin command
|
||||
set -e -- tokens[1]
|
||||
test "$tokens[1]" = "--"; and set -e -- tokens[1]
|
||||
case env
|
||||
set -e -- tokens[1]
|
||||
test "$tokens[1]" = "--"; and set -e -- tokens[1]
|
||||
while string match -qr -- '^[\w]+=' "$tokens[1]"
|
||||
set -e -- tokens[1]
|
||||
end
|
||||
case '*'
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
string escape -n -- $tokens
|
||||
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 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]
|
||||
|
||||
# fish v3.3.0 and newer: Don't use option prefix if " -- " is preceded.
|
||||
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
|
||||
@@ -105,12 +67,16 @@ function fzf_key_bindings
|
||||
set -- match_regex "(?<prefix>$prefix_regex)?$match_regex"
|
||||
end
|
||||
|
||||
# Set $prefix and expanded $fzf_query with preserved trailing newlines.
|
||||
if test "$fish_major" -ge 4
|
||||
# fish v4.0.0 and newer
|
||||
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
|
||||
# fish v3.2.0 - v3.7.1 (last v3)
|
||||
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
|
||||
# fish older than v3.2.0 (v3.1b1 - v3.1.2)
|
||||
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)
|
||||
@@ -118,17 +84,22 @@ function fzf_key_bindings
|
||||
end
|
||||
|
||||
if test -n "$fzf_query"
|
||||
# Normalize path in $fzf_query, set $dir to the longest existing directory.
|
||||
if test \( "$fish_major" -ge 4 \) -o \( "$fish_major" -eq 3 -a "$fish_minor" -ge 5 \)
|
||||
# fish v3.5.0 and newer
|
||||
set -- fzf_query (path normalize -- $fzf_query)
|
||||
set -- dir $fzf_query
|
||||
while not path is -d $dir
|
||||
set -- dir (path dirname $dir)
|
||||
end
|
||||
else
|
||||
# fish older than v3.5.0 (v3.1b1 - v3.4.1)
|
||||
if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2
|
||||
# fish v3.2.0 - v3.4.1
|
||||
string match -q -r -- '(?<fzf_query>^[\s\S]*?(?=\n?$)$)' \
|
||||
(string replace -r -a -- '(?<=/)/|(?<!^)/+(?!\n)$' '' $fzf_query | string collect -N)
|
||||
else
|
||||
# fish v3.1b1 - v3.1.2
|
||||
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
|
||||
@@ -139,12 +110,16 @@ function fzf_key_bindings
|
||||
end
|
||||
|
||||
if not string match -q -- '.' $dir; or string match -q -r -- '^\./|^\.$' $fzf_query
|
||||
# Strip $dir from $fzf_query - preserve trailing newlines.
|
||||
if test "$fish_major" -ge 4
|
||||
# fish v4.0.0 and newer
|
||||
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
|
||||
# fish v3.2.0 - v3.7.1 (last v3)
|
||||
string match -q -r -- '^/?(?<fzf_query>[\s\S]*?(?=\n?$)$)' \
|
||||
(string replace -- "$dir" '' $fzf_query | string collect -N)
|
||||
else
|
||||
# fish older than v3.2.0 (v3.1b1 - v3.1.2)
|
||||
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
|
||||
@@ -153,7 +128,6 @@ function fzf_key_bindings
|
||||
|
||||
string escape -n -- "$dir" "$fzf_query" "$prefix"
|
||||
end
|
||||
#----END INCLUDE
|
||||
|
||||
# Store current token in $dir as root for the 'find' command
|
||||
function fzf-file-widget -d "List files and folders"
|
||||
@@ -170,7 +144,7 @@ function fzf_key_bindings
|
||||
set -lx FZF_DEFAULT_OPTS_FILE
|
||||
|
||||
set -l result (eval (__fzfcmd) --walker-root=$dir --query=$fzf_query | string split0)
|
||||
and commandline -rt -- (string join -- ' ' $prefix(string escape --no-quoted -- $result))' '
|
||||
and commandline -rt -- (string join -- ' ' $prefix(string escape -n -- $result))' '
|
||||
|
||||
commandline -f repaint
|
||||
end
|
||||
|
||||
@@ -8,26 +8,24 @@ dir=${0%"${0##*/}"}
|
||||
|
||||
update() {
|
||||
{
|
||||
sed -n "1,/^#----BEGIN INCLUDE $1/p" "$2"
|
||||
sed -n '1,/^#----BEGIN INCLUDE common\.sh/p' "$1"
|
||||
cat << EOF
|
||||
# NOTE: Do not directly edit this section, which is copied from "$1".
|
||||
# To modify it, one can edit "$1" and run "./update.sh" to apply
|
||||
# the changes. See code comments in "$1" for the implementation details.
|
||||
# NOTE: Do not directly edit this section, which is copied from "common.sh".
|
||||
# To modify it, one can edit "common.sh" and run "./update.sh" to apply
|
||||
# the changes. See code comments in "common.sh" for the implementation details.
|
||||
EOF
|
||||
echo
|
||||
grep -v '^[[:blank:]]*#' "$dir/$1" # remove code comments from the common file
|
||||
sed -n '/^#----END INCLUDE/,$p' "$2"
|
||||
} > "$2.part"
|
||||
grep -v '^[[:blank:]]*#' "$dir/common.sh" # remove code comments in common.sh
|
||||
sed -n '/^#----END INCLUDE/,$p' "$1"
|
||||
} > "$1.part"
|
||||
|
||||
mv -f "$2.part" "$2"
|
||||
mv -f "$1.part" "$1"
|
||||
}
|
||||
|
||||
update "common.sh" "$dir/completion.bash"
|
||||
update "common.sh" "$dir/completion.zsh"
|
||||
update "common.sh" "$dir/key-bindings.bash"
|
||||
update "common.sh" "$dir/key-bindings.zsh"
|
||||
update "common.fish" "$dir/completion.fish"
|
||||
update "common.fish" "$dir/key-bindings.fish"
|
||||
update "$dir/completion.bash"
|
||||
update "$dir/completion.zsh"
|
||||
update "$dir/key-bindings.bash"
|
||||
update "$dir/key-bindings.zsh"
|
||||
|
||||
# Check if --check is in ARGV
|
||||
check=0
|
||||
|
||||
@@ -3,10 +3,9 @@ set -e FZF_DEFAULT_COMMAND FZF_DEFAULT_OPTS FZF_DEFAULT_OPTS_FILE FZF_TMUX FZF_T
|
||||
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_OPTS FZF_EXPANSION_OPTS
|
||||
|
||||
set -gx FZF_DEFAULT_OPTS "--no-scrollbar --pointer '>' --marker '>'"
|
||||
set -gx FZF_COMPLETION_TRIGGER '++'
|
||||
set -gx fish_history fzf_test
|
||||
|
||||
# Add fzf to PATH
|
||||
|
||||
Reference in New Issue
Block a user