diff --git a/README.md b/README.md index 04dddebd..e58b7f77 100644 --- a/README.md +++ b/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 ``` diff --git a/shell/common.fish b/shell/common.fish deleted file mode 100644 index b6b7e0bb..00000000 --- a/shell/common.fish +++ /dev/null @@ -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 '(?[\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_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 -- '(?^[\s\S]*?(?=\n?$)$)' \ - (string replace -r -a -- '(?<=/)/|(?[\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 -- '^/?(?[\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 diff --git a/shell/completion.fish b/shell/completion.fish index a1281c31..ca7dec4a 100644 --- a/shell/completion.fish +++ b/shell/completion.fish @@ -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 '(?[\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_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 -- '(?^[\s\S]*?(?=\n?$)$)' \ - (string replace -r -a -- '(?<=/)/|(?[\s\S]*)' $fzf_query - else if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2 - string match -q -r -- '^/?(?[\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 -- '(?(?:\\\\[\\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 -- '(?[\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_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 -- '(?^[\s\S]*?(?=\n?$)$)' \ (string replace -r -a -- '(?<=/)/|(?[\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 -- '^/?(?[\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 diff --git a/shell/update.sh b/shell/update.sh index 4734e807..61374d35 100755 --- a/shell/update.sh +++ b/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 diff --git a/test/lib/common.fish b/test/lib/common.fish index 1192f8f1..781a07f8 100644 --- a/test/lib/common.fish +++ b/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