260404.2 · 23164c2263a67ea04fbc45a35ac1ccfe1a1b6f07
fish: Completion script rewrite (SHIFT-TAB)
reading declarations
@@ -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`.--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:+- 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.++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}'+# 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)"'"++ # 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- _fzf_complete --multi --reverse --header-lines=3 -- $argv < (ls -al | psub) - functions -e _fzf_complete_foo_post+ # 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)-- 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)+function _fzf_complete_foo+ ls -sh --zero --color=always | fzf_complete --read0 --print0 --ansi --no-multi-line --header-lines=1+end - 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 ```
shell/common.fish
@@ -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++# 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 -function 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 -#----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.+ # 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 - 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+ # Remove any line breaks from token+ set -- token (string replace -ra -- '\\\\\\n' '' $token | string collect) - 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+ # regex: Match token with unescaped/unquoted glob character+ set -l -- r_glob '^(?:[^\'"\\\\*]|\\\\[\\S\\s]|\'(?:\\\\[\\S\\s]|[^\'\\\\])*\'|"(?:\\\\[\\S\\s]|[^"\\\\])*")*\\*[\\S\\s]*$' - 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+ # regex: Match any unbalanced quote character+ set -l -- r_quote '^(?>(?:\\\\[\\s\\S]|"(?:[^"\\\\]|\\\\[\\s\\S])*"|\'(?:[^\'\\\\]|\\\\[\\s\\S])*\'|[^\'"\\\\]+)*)\\K[\'"]' - 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+ # 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) - string escape -n -- $tokens- end+ set -l -- cl_tokenize_opt '--tokens-expanded'+ string match -q -- '3.*' $version+ and set -- cl_tokenize_opt '--tokenize' - 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 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 -l -- fish_major (string match -r -- '^\d+' $version)- set -l -- fish_minor (string match -r -- '^\d+\.(\d+)' $version)[2]+ # Set fzf options+ test -z "$FZF_TMUX_HEIGHT"+ and set -l -- FZF_TMUX_HEIGHT 40% - 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 -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 - 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)' '')+ 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- 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$' '')+ $fzf_cmd | while read -l r; set -a -- result $r; end 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+ # 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' - 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+ set -- result (string escape -n -- ($fzf_cmd --read0 --print0 --scheme=path --no-multi-line | string split0)) - 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+ # 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- 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))' '+ # 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- 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+ 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- 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]+ # 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 '' - # 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+ commandline -rt -- (string join -- ' ' $result)$tail 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+ 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 -# Run setup-fzf_completion_setup+# 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