@ 261102.1

260404.2 · 23164c2263a67ea04fbc45a35ac1ccfe1a1b6f07

fish: Completion script rewrite (SHIFT-TAB)

reading declarations

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`.--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

deleted file mode 100644@@ -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

shell/completion.fish

@@ -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

shell/key-bindings.fish

@@ -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

shell/update.sh

@@ -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

test/lib/common.fish

@@ -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