mirror of
https://github.com/junegunn/fzf.git
synced 2026-02-06 01:47:55 +08:00
Add fish completion support (#4605)
This commit is contained in:
96
README.md
96
README.md
@@ -69,15 +69,17 @@ Table of Contents
|
||||
* [Demo](#demo)
|
||||
* [Examples](#examples)
|
||||
* [Key bindings for command-line](#key-bindings-for-command-line)
|
||||
* [Fuzzy completion for bash and zsh](#fuzzy-completion-for-bash-and-zsh)
|
||||
* [Fuzzy completion](#fuzzy-completion)
|
||||
* [Files and directories](#files-and-directories)
|
||||
* [Process IDs](#process-ids)
|
||||
* [Host names](#host-names)
|
||||
* [Environment variables / Aliases](#environment-variables--aliases)
|
||||
* [Customizing fzf options for completion](#customizing-fzf-options-for-completion)
|
||||
* [Customizing completion source for paths and directories](#customizing-completion-source-for-paths-and-directories)
|
||||
* [Supported commands](#supported-commands)
|
||||
* [Custom fuzzy completion](#custom-fuzzy-completion)
|
||||
* [Customizing fuzzy completion for bash and zsh](#customizing-fuzzy-completion-for-bash-and-zsh)
|
||||
* [Customizing fzf options for completion](#customizing-fzf-options-for-completion)
|
||||
* [Customizing completion source for paths and directories](#customizing-completion-source-for-paths-and-directories)
|
||||
* [Supported commands (bash)](#supported-commands-bash)
|
||||
* [Custom fuzzy completion](#custom-fuzzy-completion)
|
||||
* [Fuzzy completion for fish](#fuzzy-completion-for-fish)
|
||||
* [Vim plugin](#vim-plugin)
|
||||
* [Advanced topics](#advanced-topics)
|
||||
* [Customizing for different types of input](#customizing-for-different-types-of-input)
|
||||
@@ -556,8 +558,10 @@ Display modes for these bindings can be separately configured via
|
||||
|
||||
More tips can be found on [the wiki page](https://github.com/junegunn/fzf/wiki/Configuring-shell-key-bindings).
|
||||
|
||||
Fuzzy completion for bash and zsh
|
||||
---------------------------------
|
||||
Fuzzy completion
|
||||
----------------
|
||||
|
||||
Shell integration also provides fuzzy completion for bash, zsh, and fish.
|
||||
|
||||
### Files and directories
|
||||
|
||||
@@ -599,23 +603,28 @@ kill -9 **<TAB>
|
||||
|
||||
### Host names
|
||||
|
||||
For ssh and telnet commands, fuzzy completion for hostnames is provided. The
|
||||
names are extracted from /etc/hosts and ~/.ssh/config.
|
||||
For ssh command, fuzzy completion for hostnames is provided. The names are
|
||||
extracted from /etc/hosts and ~/.ssh/config.
|
||||
|
||||
```sh
|
||||
ssh **<TAB>
|
||||
telnet **<TAB>
|
||||
```
|
||||
|
||||
### Environment variables / Aliases
|
||||
|
||||
```sh
|
||||
# bash and zsh
|
||||
unset **<TAB>
|
||||
export **<TAB>
|
||||
unalias **<TAB>
|
||||
|
||||
# fish
|
||||
set <SHIFT-TAB>
|
||||
```
|
||||
|
||||
### Customizing fzf options for completion
|
||||
### Customizing fuzzy completion for bash and zsh
|
||||
|
||||
#### Customizing fzf options for completion
|
||||
|
||||
```sh
|
||||
# Use ~~ as the trigger sequence instead of the default **
|
||||
@@ -646,7 +655,7 @@ _fzf_comprun() {
|
||||
}
|
||||
```
|
||||
|
||||
### Customizing completion source for paths and directories
|
||||
#### Customizing completion source for paths and directories
|
||||
|
||||
```sh
|
||||
# Use fd (https://github.com/sharkdp/fd) for listing path candidates.
|
||||
@@ -662,7 +671,7 @@ _fzf_compgen_dir() {
|
||||
}
|
||||
```
|
||||
|
||||
### Supported commands
|
||||
#### Supported commands (bash)
|
||||
|
||||
On bash, fuzzy completion is enabled only for a predefined set of commands
|
||||
(`complete | grep _fzf` to see the list). But you can enable it for other
|
||||
@@ -674,7 +683,7 @@ _fzf_setup_completion path ag git kubectl
|
||||
_fzf_setup_completion dir tree
|
||||
```
|
||||
|
||||
### Custom fuzzy completion
|
||||
#### Custom fuzzy completion
|
||||
|
||||
_**(Custom completion API is experimental and subject to change)**_
|
||||
|
||||
@@ -724,6 +733,65 @@ _fzf_complete_foo_post() {
|
||||
[ -n "$BASH" ] && complete -F _fzf_complete_foo -o default -o bashdefault foo
|
||||
```
|
||||
|
||||
### 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.
|
||||
- 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:
|
||||
|
||||
```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)
|
||||
|
||||
functions -e _fzf_complete_foo_post
|
||||
end
|
||||
```
|
||||
|
||||
And here's a more complex example for customizing `git`
|
||||
|
||||
```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)
|
||||
|
||||
case '*'
|
||||
set -l -- current_token (commandline -t)
|
||||
__fzf_complete_native "$argv $current_token" --query=(string escape -- $current_token) --multi
|
||||
end
|
||||
|
||||
functions -e _fzf_complete_git_post
|
||||
end
|
||||
```
|
||||
|
||||
Vim plugin
|
||||
----------
|
||||
|
||||
|
||||
84
install
84
install
@@ -243,16 +243,16 @@ fi
|
||||
|
||||
echo
|
||||
for shell in $shells; do
|
||||
fzf_completion="source \"$fzf_base/shell/completion.${shell}\""
|
||||
fzf_key_bindings="source \"$fzf_base/shell/key-bindings.${shell}\""
|
||||
[[ $shell == fish ]] && continue
|
||||
src=${prefix_expand}.${shell}
|
||||
echo -n "Generate $src ... "
|
||||
|
||||
fzf_completion="source \"$fzf_base/shell/completion.${shell}\""
|
||||
if [ $auto_completion -eq 0 ]; then
|
||||
fzf_completion="# $fzf_completion"
|
||||
fi
|
||||
|
||||
fzf_key_bindings="source \"$fzf_base/shell/key-bindings.${shell}\""
|
||||
if [ $key_bindings -eq 0 ]; then
|
||||
fzf_key_bindings="# $fzf_key_bindings"
|
||||
fi
|
||||
@@ -302,15 +302,16 @@ append_line() {
|
||||
line="$2"
|
||||
file="$3"
|
||||
pat="${4:-}"
|
||||
at_lno="${5:-}"
|
||||
lines=""
|
||||
|
||||
echo "Update $file:"
|
||||
echo " - $line"
|
||||
if [ -f "$file" ]; then
|
||||
if [ $# -lt 4 ]; then
|
||||
lines=$(\grep -nF "$line" "$file")
|
||||
else
|
||||
if [[ -n $pat ]]; then
|
||||
lines=$(\grep -nF "$pat" "$file")
|
||||
else
|
||||
lines=$(\grep -nF "${line#"${line%%[![:space:]]*}"}" "$file")
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -328,8 +329,12 @@ append_line() {
|
||||
|
||||
set -e
|
||||
if [ "$update" -eq 1 ]; then
|
||||
[ -f "$file" ] && echo >> "$file"
|
||||
echo "$line" >> "$file"
|
||||
if [[ -z $at_lno ]]; then
|
||||
[ -f "$file" ] && echo >> "$file"
|
||||
echo "$line" >> "$file"
|
||||
else
|
||||
sed -i.~fzf_bak "${at_lno}a\\"$'\n'"$line" "$file" && rm "$file.~fzf_bak"
|
||||
fi
|
||||
echo " + Added"
|
||||
else
|
||||
echo " ~ Skipped"
|
||||
@@ -362,25 +367,66 @@ for shell in $shells; do
|
||||
append_line $update_config "[ -f ${prefix}.${shell} ] && source ${prefix}.${shell}" "$dest" "${prefix}.${shell}"
|
||||
done
|
||||
|
||||
if [ $key_bindings -eq 1 ] && [[ $shells =~ fish ]]; then
|
||||
if [[ $shells =~ fish ]]; then
|
||||
bind_file="${fish_dir}/functions/fish_user_key_bindings.fish"
|
||||
if [ ! -e "$bind_file" ]; then
|
||||
mkdir -p "${fish_dir}/functions"
|
||||
create_file "$bind_file" \
|
||||
'function fish_user_key_bindings' \
|
||||
' fzf --fish | source' \
|
||||
'end'
|
||||
if [[ $key_bindings -eq 1 || $auto_completion -eq 1 ]]; then
|
||||
mkdir -p "${fish_dir}/functions"
|
||||
if [[ $key_bindings -eq 1 && $auto_completion -eq 1 ]]; then
|
||||
create_file "$bind_file" \
|
||||
'function fish_user_key_bindings' \
|
||||
' fzf --fish | source' \
|
||||
'end'
|
||||
elif [[ $key_bindings -eq 1 ]]; then
|
||||
create_file "$bind_file" \
|
||||
'function fish_user_key_bindings' \
|
||||
" $fzf_key_bindings" \
|
||||
'end'
|
||||
elif [[ $auto_completion -eq 1 ]]; then
|
||||
create_file "$bind_file" \
|
||||
'function fish_user_key_bindings' \
|
||||
" $fzf_completion" \
|
||||
'end'
|
||||
fi
|
||||
lno_func=$(\grep -nF "function fish_user_key_bindings" "$bind_file" | sed 's/:.*//' | tr '\n' ' ')
|
||||
else
|
||||
lno_func=0
|
||||
fi
|
||||
else
|
||||
echo "Check $bind_file:"
|
||||
lno=$(\grep -nF "fzf_key_bindings" "$bind_file" | sed 's/:.*//' | tr '\n' ' ')
|
||||
if [[ -n $lno ]]; then
|
||||
echo " ** Found 'fzf_key_bindings' in line #$lno"
|
||||
echo " ** You have to replace the line to 'fzf --fish | source'"
|
||||
lno_func=$(\grep -nF "function fish_user_key_bindings" "$bind_file" | sed 's/:.*//' | tr '\n' ' ')
|
||||
if [[ -z $lno_func ]]; then
|
||||
echo -e "function fish_user_key_bindings\nend" >> "$bind_file"
|
||||
lno_func=$(\grep -nF "function fish_user_key_bindings" "$bind_file" | sed 's/:.*//' | tr '\n' ' ')
|
||||
fi
|
||||
lno_keys=$(\grep -nF "fzf_key_bindings" "$bind_file" | sed 's/:.*//' | tr '\n' ' ')
|
||||
if [[ -n $lno_keys ]]; then
|
||||
echo " ** Found 'fzf_key_bindings' in line #$lno_keys"
|
||||
if [[ $key_bindings -eq 1 && $auto_completion -eq 1 ]]; then
|
||||
echo " ** You have to replace the line to 'fzf --fish | source'"
|
||||
elif [[ $key_bindings -eq 1 ]]; then
|
||||
echo " ** You have to replace the line to '$fzf_key_bindings'"
|
||||
else
|
||||
echo " ** You have to remove the line"
|
||||
fi
|
||||
echo
|
||||
else
|
||||
echo " - Clear"
|
||||
echo
|
||||
append_line $update_config "fzf --fish | source" "$bind_file"
|
||||
if [[ $key_bindings -eq 1 && $auto_completion -eq 1 ]]; then
|
||||
sed -i.~fzf_bak "\#$fzf_completion#d" "$bind_file" && rm "$bind_file.~fzf_bak"
|
||||
sed -i.~fzf_bak "\#$fzf_key_bindings#d" "$bind_file" && rm "$bind_file.~fzf_bak"
|
||||
append_line $update_config " fzf --fish | source" "$bind_file" "" "$lno_func"
|
||||
else
|
||||
sed -i.~fzf_bak '/fzf --fish \| source/d' "$bind_file" && rm "$bind_file.~fzf_bak"
|
||||
if [[ $key_bindings -eq 1 ]]; then
|
||||
sed -i.~fzf_bak "\#$fzf_completion#d" "$bind_file" && rm "$bind_file.~fzf_bak"
|
||||
append_line $update_config " $fzf_key_bindings" "$bind_file" "" "$lno_func"
|
||||
elif [[ $auto_completion -eq 1 ]]; then
|
||||
sed -i.~fzf_bak "\#$fzf_key_bindings#d" "$bind_file" && rm "$bind_file.~fzf_bak"
|
||||
append_line $update_config " $fzf_completion" "$bind_file" "" "$lno_func"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -393,7 +439,7 @@ if [ $update_config -eq 1 ]; then
|
||||
echo
|
||||
fi
|
||||
[[ $shells =~ zsh ]] && echo " source ${ZDOTDIR:-~}/.zshrc # zsh"
|
||||
[[ $shells =~ fish ]] && [ $key_bindings -eq 1 ] && echo ' fzf_key_bindings # fish'
|
||||
[[ $shells =~ fish && $lno_func -ne 0 ]] && echo ' fzf_user_key_bindings # fish'
|
||||
echo
|
||||
echo 'Use uninstall script to remove fzf.'
|
||||
echo
|
||||
|
||||
5
main.go
5
main.go
@@ -29,6 +29,9 @@ var zshCompletion []byte
|
||||
//go:embed shell/key-bindings.fish
|
||||
var fishKeyBindings []byte
|
||||
|
||||
//go:embed shell/completion.fish
|
||||
var fishCompletion []byte
|
||||
|
||||
//go:embed man/man1/fzf.1
|
||||
var manPage []byte
|
||||
|
||||
@@ -65,7 +68,7 @@ func main() {
|
||||
}
|
||||
if options.Fish {
|
||||
printScript("key-bindings.fish", fishKeyBindings)
|
||||
fmt.Println("fzf_key_bindings")
|
||||
printScript("completion.fish", fishCompletion)
|
||||
return
|
||||
}
|
||||
if options.Help {
|
||||
|
||||
141
shell/common.fish
Normal file
141
shell/common.fish
Normal file
@@ -0,0 +1,141 @@
|
||||
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
|
||||
241
shell/completion.fish
Normal file
241
shell/completion.fish
Normal file
@@ -0,0 +1,241 @@
|
||||
# ____ ____
|
||||
# / __/___ / __/
|
||||
# / /_/_ / / /_
|
||||
# / __/ / /_/ __/
|
||||
# /_/ /___/_/ completion.fish
|
||||
#
|
||||
# - $FZF_COMPLETION_OPTS (default: empty)
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
# Run setup
|
||||
fzf_completion_setup
|
||||
@@ -30,9 +30,12 @@ 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] \
|
||||
@@ -51,17 +54,51 @@ 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
|
||||
@@ -69,16 +106,12 @@ 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)
|
||||
@@ -86,22 +119,17 @@ 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
|
||||
@@ -112,16 +140,12 @@ 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
|
||||
@@ -130,6 +154,7 @@ 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"
|
||||
@@ -140,13 +165,13 @@ function fzf_key_bindings
|
||||
|
||||
set -lx FZF_DEFAULT_OPTS (__fzf_defaults \
|
||||
"--reverse --walker=file,dir,follow,hidden --scheme=path" \
|
||||
"$FZF_CTRL_T_OPTS --multi --print0")
|
||||
"--multi $FZF_CTRL_T_OPTS --print0")
|
||||
|
||||
set -lx FZF_DEFAULT_COMMAND "$FZF_CTRL_T_COMMAND"
|
||||
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 -- $result))' '
|
||||
and commandline -rt -- (string join -- ' ' $prefix(string escape --no-quoted -- $result))' '
|
||||
|
||||
commandline -f repaint
|
||||
end
|
||||
@@ -234,3 +259,6 @@ function fzf_key_bindings
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Run setup
|
||||
fzf_key_bindings
|
||||
|
||||
@@ -8,24 +8,26 @@ dir=${0%"${0##*/}"}
|
||||
|
||||
update() {
|
||||
{
|
||||
sed -n '1,/^#----BEGIN INCLUDE common\.sh/p' "$1"
|
||||
sed -n "1,/^#----BEGIN INCLUDE $1/p" "$2"
|
||||
cat << EOF
|
||||
# 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.
|
||||
# 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.
|
||||
EOF
|
||||
echo
|
||||
grep -v '^[[:blank:]]*#' "$dir/common.sh" # remove code comments in common.sh
|
||||
sed -n '/^#----END INCLUDE/,$p' "$1"
|
||||
} > "$1.part"
|
||||
grep -v '^[[:blank:]]*#' "$dir/$1" # remove code comments from the common file
|
||||
sed -n '/^#----END INCLUDE/,$p' "$2"
|
||||
} > "$2.part"
|
||||
|
||||
mv -f "$1.part" "$1"
|
||||
mv -f "$2.part" "$2"
|
||||
}
|
||||
|
||||
update "$dir/completion.bash"
|
||||
update "$dir/completion.zsh"
|
||||
update "$dir/key-bindings.bash"
|
||||
update "$dir/key-bindings.zsh"
|
||||
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"
|
||||
|
||||
# Check if --check is in ARGV
|
||||
check=0
|
||||
|
||||
17
test/lib/common.fish
Normal file
17
test/lib/common.fish
Normal file
@@ -0,0 +1,17 @@
|
||||
# Unset fzf variables
|
||||
set -e FZF_DEFAULT_COMMAND FZF_DEFAULT_OPTS FZF_DEFAULT_OPTS_FILE FZF_TMUX FZF_TMUX_OPTS
|
||||
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 -gx FZF_DEFAULT_OPTS "--no-scrollbar --pointer '>' --marker '>'"
|
||||
set -gx FZF_COMPLETION_TRIGGER '++'
|
||||
set -gx fish_history fzf_test
|
||||
|
||||
# Add fzf to PATH
|
||||
fish_add_path <%= BASE %>/bin
|
||||
|
||||
# Source key bindings and completion
|
||||
source "<%= BASE %>/shell/key-bindings.fish"
|
||||
source "<%= BASE %>/shell/completion.fish"
|
||||
@@ -11,6 +11,7 @@ require 'net/http'
|
||||
require 'json'
|
||||
|
||||
TEMPLATE = File.read(File.expand_path('common.sh', __dir__))
|
||||
FISH_TEMPLATE = File.read(File.expand_path('common.fish', __dir__))
|
||||
UNSETS = %w[
|
||||
FZF_DEFAULT_COMMAND FZF_DEFAULT_OPTS
|
||||
FZF_TMUX FZF_TMUX_OPTS
|
||||
@@ -66,7 +67,16 @@ class Shell
|
||||
end
|
||||
|
||||
def fish
|
||||
"unset #{UNSETS.join(' ')}; rm -f ~/.local/share/fish/fzf_test_history; FZF_DEFAULT_OPTS=\"--no-scrollbar --pointer '>' --marker '>'\" fish_history=fzf_test fish"
|
||||
@fish ||=
|
||||
begin
|
||||
confdir = '/tmp/fzf-fish'
|
||||
FileUtils.rm_rf(confdir)
|
||||
FileUtils.mkdir_p("#{confdir}/fish/conf.d")
|
||||
File.open("#{confdir}/fish/conf.d/fzf.fish", 'w') do |f|
|
||||
f.puts ERB.new(FISH_TEMPLATE).result(binding)
|
||||
end
|
||||
"rm -f ~/.local/share/fish/fzf_test_history; XDG_CONFIG_HOME=#{confdir} fish"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -27,6 +27,10 @@ module TestShell
|
||||
tmux.prepare
|
||||
end
|
||||
|
||||
def trigger
|
||||
'**'
|
||||
end
|
||||
|
||||
def test_ctrl_t
|
||||
set_var('FZF_CTRL_T_COMMAND', 'seq 100')
|
||||
|
||||
@@ -165,7 +169,11 @@ module CompletionTest
|
||||
FileUtils.touch(File.expand_path(f))
|
||||
end
|
||||
tmux.prepare
|
||||
tmux.send_keys 'cat /tmp/fzf-test/10**', :Tab
|
||||
if shell == :fish
|
||||
tmux.send_keys 'cat /tmp/fzf-test/10', 'C-t'
|
||||
else
|
||||
tmux.send_keys "cat /tmp/fzf-test/10#{trigger}", :Tab
|
||||
end
|
||||
tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
|
||||
tmux.send_keys ' !d'
|
||||
tmux.until { |lines| assert_equal 2, lines.match_count }
|
||||
@@ -179,7 +187,11 @@ module CompletionTest
|
||||
# ~USERNAME**<TAB>
|
||||
user = `whoami`.chomp
|
||||
tmux.send_keys 'C-u'
|
||||
tmux.send_keys "cat ~#{user}**", :Tab
|
||||
if shell == :fish
|
||||
tmux.send_keys "cat ~#{user}", 'C-t'
|
||||
else
|
||||
tmux.send_keys "cat ~#{user}#{trigger}", :Tab
|
||||
end
|
||||
tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
|
||||
tmux.send_keys "/#{user}"
|
||||
tmux.until { |lines| assert(lines.any? { |l| l.end_with?("/#{user}") }) }
|
||||
@@ -190,14 +202,29 @@ module CompletionTest
|
||||
|
||||
# ~INVALID_USERNAME**<TAB>
|
||||
tmux.send_keys 'C-u'
|
||||
tmux.send_keys 'cat ~such**', :Tab
|
||||
if shell == :fish
|
||||
tmux.send_keys 'cat ~such', 'C-t'
|
||||
else
|
||||
tmux.send_keys "cat ~such#{trigger}", :Tab
|
||||
end
|
||||
tmux.until(true) { |lines| assert lines.any_include?('no~such~user') }
|
||||
tmux.send_keys :Enter
|
||||
tmux.until(true) { |lines| assert_equal 'cat no~such~user', lines[-1] }
|
||||
tmux.until(true) do |lines|
|
||||
if shell == :fish
|
||||
# Fish's string escape quotes filenames with ~ to prevent tilde expansion
|
||||
assert_equal 'cat no\\~such\\~user', lines[-1]
|
||||
else
|
||||
assert_equal 'cat no~such~user', lines[-1]
|
||||
end
|
||||
end
|
||||
|
||||
# /tmp/fzf\ test**<TAB>
|
||||
tmux.send_keys 'C-u'
|
||||
tmux.send_keys 'cat /tmp/fzf\ test/**', :Tab
|
||||
if shell == :fish
|
||||
tmux.send_keys 'cat /tmp/fzf\\ test/', 'C-t'
|
||||
else
|
||||
tmux.send_keys "cat /tmp/fzf\\ test/#{trigger}", :Tab
|
||||
end
|
||||
tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
|
||||
tmux.send_keys 'foobar$'
|
||||
tmux.until do |lines|
|
||||
@@ -210,7 +237,11 @@ module CompletionTest
|
||||
# Should include hidden files
|
||||
(1..100).each { |i| FileUtils.touch("/tmp/fzf-test/.hidden-#{i}") }
|
||||
tmux.send_keys 'C-u'
|
||||
tmux.send_keys 'cat /tmp/fzf-test/hidden**', :Tab
|
||||
if shell == :fish
|
||||
tmux.send_keys 'cat /tmp/fzf-test/hidden', 'C-t'
|
||||
else
|
||||
tmux.send_keys "cat /tmp/fzf-test/hidden#{trigger}", :Tab
|
||||
end
|
||||
tmux.until(true) do |lines|
|
||||
assert_equal 100, lines.match_count
|
||||
assert lines.any_include?('/tmp/fzf-test/.hidden-')
|
||||
@@ -223,70 +254,89 @@ module CompletionTest
|
||||
end
|
||||
|
||||
def test_file_completion_root
|
||||
tmux.send_keys 'ls /**', :Tab
|
||||
if shell == :fish
|
||||
tmux.send_keys 'ls /', 'C-t'
|
||||
else
|
||||
tmux.send_keys "ls /#{trigger}", :Tab
|
||||
end
|
||||
tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
|
||||
tmux.send_keys :Enter
|
||||
end
|
||||
|
||||
def test_dir_completion
|
||||
FileUtils.mkdir_p('/tmp/fzf-test-dir')
|
||||
(1..100).each do |idx|
|
||||
FileUtils.mkdir_p("/tmp/fzf-test/d#{idx}")
|
||||
FileUtils.mkdir_p("/tmp/fzf-test-dir/d#{idx}")
|
||||
end
|
||||
FileUtils.touch('/tmp/fzf-test/d55/xxx')
|
||||
FileUtils.touch('/tmp/fzf-test-dir/d55/xxx')
|
||||
tmux.prepare
|
||||
tmux.send_keys 'cd /tmp/fzf-test/**', :Tab
|
||||
if shell == :fish
|
||||
tmux.send_keys 'cd /tmp/fzf-test-dir/', 'C-t'
|
||||
else
|
||||
tmux.send_keys "cd /tmp/fzf-test-dir/#{trigger}", :Tab
|
||||
end
|
||||
tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
|
||||
tmux.send_keys :Tab, :Tab # Tab does not work here
|
||||
tmux.send_keys 55
|
||||
# Tab selects items in C-t's --multi mode, so skip for fish
|
||||
tmux.send_keys :Tab, :Tab unless shell == :fish # Tab does not work here
|
||||
tmux.send_keys '55/$'
|
||||
tmux.until do |lines|
|
||||
assert_equal 1, lines.match_count
|
||||
assert_includes lines, '> 55'
|
||||
assert_includes lines, '> /tmp/fzf-test/d55/'
|
||||
assert_includes lines, '> 55/$'
|
||||
assert_includes lines, '> /tmp/fzf-test-dir/d55/'
|
||||
end
|
||||
tmux.send_keys :Enter
|
||||
tmux.until(true) { |lines| assert_equal 'cd /tmp/fzf-test/d55/', lines[-1] }
|
||||
tmux.until(true) { |lines| assert_equal 'cd /tmp/fzf-test-dir/d55/', lines[-1] }
|
||||
# C-t appends a trailing space after the result
|
||||
tmux.send_keys :BSpace if shell == :fish
|
||||
tmux.send_keys :xx
|
||||
tmux.until { |lines| assert_equal 'cd /tmp/fzf-test/d55/xx', lines[-1] }
|
||||
tmux.until { |lines| assert_equal 'cd /tmp/fzf-test-dir/d55/xx', lines[-1] }
|
||||
|
||||
# Should not match regular files (bash-only)
|
||||
if instance_of?(TestBash)
|
||||
tmux.send_keys :Tab
|
||||
tmux.until { |lines| assert_equal 'cd /tmp/fzf-test/d55/xx', lines[-1] }
|
||||
tmux.until { |lines| assert_equal 'cd /tmp/fzf-test-dir/d55/xx', lines[-1] }
|
||||
end
|
||||
|
||||
# Fail back to plusdirs
|
||||
tmux.send_keys :BSpace, :BSpace, :BSpace
|
||||
tmux.until { |lines| assert_equal 'cd /tmp/fzf-test/d55', lines[-1] }
|
||||
tmux.until { |lines| assert_equal 'cd /tmp/fzf-test-dir/d55', lines[-1] }
|
||||
tmux.send_keys :Tab
|
||||
tmux.until { |lines| assert_equal 'cd /tmp/fzf-test/d55/', lines[-1] }
|
||||
tmux.until { |lines| assert_equal 'cd /tmp/fzf-test-dir/d55/', lines[-1] }
|
||||
ensure
|
||||
FileUtils.rm_rf('/tmp/fzf-test-dir')
|
||||
end
|
||||
|
||||
def test_process_completion
|
||||
tmux.send_keys 'sleep 12345 &', :Enter
|
||||
lines = tmux.until { |lines| assert lines[-1]&.start_with?('[1] ') }
|
||||
pid = lines[-1]&.split&.last
|
||||
tmux.prepare
|
||||
tmux.send_keys 'C-L'
|
||||
tmux.send_keys 'kill **', :Tab
|
||||
tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
|
||||
tmux.send_keys 'sleep12345'
|
||||
tmux.until { |lines| assert lines.any_include?('sleep 12345') }
|
||||
tmux.send_keys :Enter
|
||||
tmux.until(true) { |lines| assert_equal "kill #{pid}", lines[-1] }
|
||||
ensure
|
||||
if pid
|
||||
begin
|
||||
Process.kill('KILL', pid.to_i)
|
||||
rescue StandardError
|
||||
nil
|
||||
skip('fish background job format differs') if shell == :fish
|
||||
|
||||
begin
|
||||
tmux.send_keys 'sleep 12345 &', :Enter
|
||||
lines = tmux.until { |lines| assert lines[-1]&.start_with?('[1] ') }
|
||||
pid = lines[-1]&.split&.last
|
||||
tmux.prepare
|
||||
tmux.send_keys 'C-L'
|
||||
tmux.send_keys "kill #{trigger}", :Tab
|
||||
tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
|
||||
tmux.send_keys 'sleep12345'
|
||||
tmux.until { |lines| assert lines.any_include?('sleep 12345') }
|
||||
tmux.send_keys :Enter
|
||||
tmux.until(true) { |lines| assert_equal "kill #{pid}", lines[-1] }
|
||||
ensure
|
||||
if pid
|
||||
begin
|
||||
Process.kill('KILL', pid.to_i)
|
||||
rescue StandardError
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_custom_completion
|
||||
skip('fish does not use _fzf_compgen_path; path completion is via ctrl-t') if shell == :fish
|
||||
tmux.send_keys '_fzf_compgen_path() { echo "$1"; seq 10; }', :Enter
|
||||
tmux.prepare
|
||||
tmux.send_keys 'ls /tmp/**', :Tab
|
||||
tmux.send_keys "ls /tmp/#{trigger}", :Tab
|
||||
tmux.until { |lines| assert_equal 11, lines.match_count }
|
||||
tmux.send_keys :Tab, :Tab, :Tab
|
||||
tmux.until { |lines| assert_equal 3, lines.select_count }
|
||||
@@ -295,11 +345,12 @@ module CompletionTest
|
||||
end
|
||||
|
||||
def test_unset_completion
|
||||
skip('fish has native completion for set and unset variables') if shell == :fish
|
||||
tmux.send_keys 'export FZFFOOBAR=BAZ', :Enter
|
||||
tmux.prepare
|
||||
|
||||
# Using tmux
|
||||
tmux.send_keys 'unset FZFFOOBR**', :Tab
|
||||
tmux.send_keys "unset FZFFOOBR#{trigger}", :Tab
|
||||
tmux.until { |lines| assert_equal 1, lines.match_count }
|
||||
tmux.send_keys :Enter
|
||||
tmux.until { |lines| assert_equal 'unset FZFFOOBAR', lines[-1] }
|
||||
@@ -308,34 +359,57 @@ module CompletionTest
|
||||
# FZF_TMUX=1
|
||||
new_shell
|
||||
tmux.focus
|
||||
tmux.send_keys 'unset FZFFOOBR**', :Tab
|
||||
tmux.send_keys "unset FZFFOOBR#{trigger}", :Tab
|
||||
tmux.until { |lines| assert_equal 1, lines.match_count }
|
||||
tmux.send_keys :Enter
|
||||
tmux.until { |lines| assert_equal 'unset FZFFOOBAR', lines[-1] }
|
||||
end
|
||||
|
||||
def test_completion_in_command_sequence
|
||||
tmux.send_keys 'export FZFFOOBAR=BAZ', :Enter
|
||||
tmux.prepare
|
||||
|
||||
triggers = ['**', '~~', '++', 'ff', '/']
|
||||
triggers.push('&', '[', ';', '`') if instance_of?(TestZsh)
|
||||
|
||||
triggers.each do |trigger|
|
||||
set_var('FZF_COMPLETION_TRIGGER', trigger)
|
||||
command = "echo foo; QUX=THUD unset FZFFOOBR#{trigger}"
|
||||
tmux.send_keys command.sub(/(;|`)$/, '\\\\\1'), :Tab
|
||||
if shell == :fish
|
||||
FileUtils.mkdir_p('/tmp/fzf-test-seq')
|
||||
FileUtils.touch('/tmp/fzf-test-seq/fzffoobar')
|
||||
tmux.prepare
|
||||
# Fish uses Shift-Tab for fzf completion (no trigger system)
|
||||
command = 'echo foo; QUX=THUD ls /tmp/fzf-test-seq/fzffoobr'
|
||||
expected = 'echo foo; QUX=THUD ls /tmp/fzf-test-seq/fzffoobar'
|
||||
tmux.send_keys command, :BTab
|
||||
tmux.until { |lines| assert_equal 1, lines.match_count }
|
||||
tmux.send_keys :Enter
|
||||
tmux.until { |lines| assert_equal 'echo foo; QUX=THUD unset FZFFOOBAR', lines[-1] }
|
||||
tmux.until { |lines| assert_equal expected, lines[-1] }
|
||||
else
|
||||
tmux.send_keys 'export FZFFOOBAR=BAZ', :Enter
|
||||
tmux.prepare
|
||||
|
||||
triggers = ['**', '~~', '++', 'ff', '/']
|
||||
triggers.push('&', '[', ';', '`') if instance_of?(TestZsh)
|
||||
|
||||
triggers.each do |trigger|
|
||||
set_var('FZF_COMPLETION_TRIGGER', trigger)
|
||||
command = "echo foo; QUX=THUD unset FZFFOOBR#{trigger}"
|
||||
expected = 'echo foo; QUX=THUD unset FZFFOOBAR'
|
||||
tmux.send_keys command.sub(/(;|`)$/, '\\\\\1'), :Tab
|
||||
tmux.until { |lines| assert_equal 1, lines.match_count }
|
||||
tmux.send_keys :Enter
|
||||
tmux.until { |lines| assert_equal expected, lines[-1] }
|
||||
end
|
||||
end
|
||||
ensure
|
||||
FileUtils.rm_rf('/tmp/fzf-test-seq') if shell == :fish
|
||||
end
|
||||
|
||||
def test_file_completion_unicode
|
||||
FileUtils.mkdir_p('/tmp/fzf-test')
|
||||
tmux.paste "cd /tmp/fzf-test; echo test3 > $'fzf-unicode \\355\\205\\214\\354\\212\\244\\355\\212\\2701'; echo test4 > $'fzf-unicode \\355\\205\\214\\354\\212\\244\\355\\212\\2702'"
|
||||
# Shell-agnostic file creation
|
||||
File.write('/tmp/fzf-test/fzf-unicode 테스트1', "test3\n")
|
||||
File.write('/tmp/fzf-test/fzf-unicode 테스트2', "test4\n")
|
||||
tmux.send_keys 'cd /tmp/fzf-test', :Enter
|
||||
tmux.prepare
|
||||
tmux.send_keys 'cat fzf-unicode**', :Tab
|
||||
if shell == :fish
|
||||
tmux.send_keys 'cat fzf-unicode', 'C-t'
|
||||
else
|
||||
tmux.send_keys "cat fzf-unicode#{trigger}", :Tab
|
||||
end
|
||||
tmux.until { |lines| assert_equal 2, lines.match_count }
|
||||
|
||||
tmux.send_keys '1'
|
||||
@@ -358,36 +432,41 @@ module CompletionTest
|
||||
end
|
||||
|
||||
def test_custom_completion_api
|
||||
tmux.send_keys 'eval "_fzf$(declare -f _comprun)"', :Enter
|
||||
%w[f g].each do |command|
|
||||
tmux.prepare
|
||||
tmux.send_keys "#{command} b**", :Tab
|
||||
tmux.until do |lines|
|
||||
assert_equal 2, lines.item_count
|
||||
assert_equal 1, lines.match_count
|
||||
assert lines.any_include?("prompt-#{command}")
|
||||
assert lines.any_include?("preview-#{command}-bar")
|
||||
skip('bash-specific _comprun/declare syntax') if shell == :fish
|
||||
|
||||
begin
|
||||
tmux.send_keys 'eval "_fzf$(declare -f _comprun)"', :Enter
|
||||
%w[f g].each do |command|
|
||||
tmux.prepare
|
||||
tmux.send_keys "#{command} b#{trigger}", :Tab
|
||||
tmux.until do |lines|
|
||||
assert_equal 2, lines.item_count
|
||||
assert_equal 1, lines.match_count
|
||||
assert lines.any_include?("prompt-#{command}")
|
||||
assert lines.any_include?("preview-#{command}-bar")
|
||||
end
|
||||
tmux.send_keys :Enter
|
||||
tmux.until { |lines| assert_equal "#{command} #{command}barbar", lines[-1] }
|
||||
tmux.send_keys 'C-u'
|
||||
end
|
||||
tmux.send_keys :Enter
|
||||
tmux.until { |lines| assert_equal "#{command} #{command}barbar", lines[-1] }
|
||||
tmux.send_keys 'C-u'
|
||||
ensure
|
||||
tmux.prepare
|
||||
tmux.send_keys 'unset -f _fzf_comprun', :Enter
|
||||
end
|
||||
ensure
|
||||
tmux.prepare
|
||||
tmux.send_keys 'unset -f _fzf_comprun', :Enter
|
||||
end
|
||||
|
||||
def test_ssh_completion
|
||||
skip('fish uses native ssh completion') if shell == :fish
|
||||
(1..5).each { |i| FileUtils.touch("/tmp/fzf-test-ssh-#{i}") }
|
||||
|
||||
tmux.send_keys 'ssh jg@localhost**', :Tab
|
||||
tmux.send_keys "ssh jg@localhost#{trigger}", :Tab
|
||||
tmux.until do |lines|
|
||||
assert_operator lines.match_count, :>=, 1
|
||||
end
|
||||
|
||||
tmux.send_keys :Enter
|
||||
tmux.until { |lines| assert lines.any_include?('ssh jg@localhost') }
|
||||
tmux.send_keys ' -i /tmp/fzf-test-ssh**', :Tab
|
||||
tmux.send_keys " -i /tmp/fzf-test-ssh#{trigger}", :Tab
|
||||
tmux.until do |lines|
|
||||
assert_operator lines.match_count, :>=, 5
|
||||
assert_equal 0, lines.select_count
|
||||
@@ -399,11 +478,344 @@ module CompletionTest
|
||||
tmux.send_keys :Enter
|
||||
tmux.until { |lines| assert lines.any_include?('ssh jg@localhost -i /tmp/fzf-test-ssh-') }
|
||||
|
||||
tmux.send_keys 'localhost**', :Tab
|
||||
tmux.send_keys "localhost#{trigger}", :Tab
|
||||
tmux.until do |lines|
|
||||
assert_operator lines.match_count, :>=, 1
|
||||
end
|
||||
end
|
||||
|
||||
def test_option_equals_long_option
|
||||
FileUtils.mkdir_p('/tmp/fzf-test-opt-eq-long')
|
||||
FileUtils.touch('/tmp/fzf-test-opt-eq-long/SECURITY.md')
|
||||
tmux.prepare
|
||||
tmux.send_keys 'cd /tmp/fzf-test-opt-eq-long', :Enter
|
||||
tmux.prepare
|
||||
if shell == :fish
|
||||
tmux.send_keys 'some-command --opt=SECURI', 'C-t'
|
||||
else
|
||||
tmux.send_keys "some-command --opt=SECURI#{trigger}", :Tab
|
||||
end
|
||||
|
||||
case shell
|
||||
when :bash
|
||||
tmux.until do |lines|
|
||||
assert_equal 1, lines.match_count
|
||||
assert_includes lines, '> SECURI'
|
||||
end
|
||||
tmux.send_keys :Enter
|
||||
tmux.until(true) { |lines| assert_equal 'some-command --opt=SECURITY.md', lines[-1] }
|
||||
when :fish
|
||||
tmux.until do |lines|
|
||||
assert_equal 1, lines.match_count
|
||||
assert lines.any_include?('SECURITY.md')
|
||||
end
|
||||
tmux.send_keys :Enter
|
||||
tmux.until(true) { |lines| assert_equal 'some-command --opt=SECURITY.md', lines[-1] }
|
||||
when :zsh
|
||||
tmux.until do |lines|
|
||||
assert_equal 0, lines.match_count
|
||||
assert_includes lines, '> --opt=SECURI'
|
||||
end
|
||||
end
|
||||
ensure
|
||||
FileUtils.rm_rf('/tmp/fzf-test-opt-eq-long')
|
||||
end
|
||||
|
||||
def test_option_equals_long_option_after_double_dash
|
||||
FileUtils.mkdir_p('/tmp/fzf-test-opt-eq-long-ddash')
|
||||
FileUtils.touch('/tmp/fzf-test-opt-eq-long-ddash/SECURITY.md')
|
||||
tmux.prepare
|
||||
tmux.send_keys 'cd /tmp/fzf-test-opt-eq-long-ddash', :Enter
|
||||
tmux.prepare
|
||||
if shell == :fish
|
||||
tmux.send_keys 'some-command -- --opt=SECURI', 'C-t'
|
||||
else
|
||||
tmux.send_keys "some-command -- --opt=SECURI#{trigger}", :Tab
|
||||
end
|
||||
|
||||
case shell
|
||||
when :bash
|
||||
tmux.until do |lines|
|
||||
assert_equal 1, lines.match_count
|
||||
assert_includes lines, '> SECURI'
|
||||
end
|
||||
tmux.send_keys :Enter
|
||||
tmux.until(true) { |lines| assert_equal 'some-command -- --opt=SECURITY.md', lines[-1] }
|
||||
when :fish, :zsh
|
||||
tmux.until do |lines|
|
||||
assert_equal 0, lines.match_count
|
||||
assert_includes lines, '> --opt=SECURI'
|
||||
end
|
||||
end
|
||||
ensure
|
||||
FileUtils.rm_rf('/tmp/fzf-test-opt-eq-long-ddash')
|
||||
end
|
||||
|
||||
def test_option_equals_short_option
|
||||
FileUtils.mkdir_p('/tmp/fzf-test-opt-eq-short')
|
||||
FileUtils.touch('/tmp/fzf-test-opt-eq-short/SECURITY.md')
|
||||
tmux.prepare
|
||||
tmux.send_keys 'cd /tmp/fzf-test-opt-eq-short', :Enter
|
||||
tmux.prepare
|
||||
if shell == :fish
|
||||
tmux.send_keys 'some-command -o=SECURI', 'C-t'
|
||||
else
|
||||
tmux.send_keys "some-command -o=SECURI#{trigger}", :Tab
|
||||
end
|
||||
|
||||
case shell
|
||||
when :bash, :fish
|
||||
tmux.until do |lines|
|
||||
assert_equal 1, lines.match_count
|
||||
assert lines.any_include?('> SECURITY.md')
|
||||
end
|
||||
tmux.send_keys :Enter
|
||||
tmux.until(true) { |lines| assert_equal 'some-command -o=SECURITY.md', lines[-1] }
|
||||
when :zsh
|
||||
tmux.until do |lines|
|
||||
assert_equal 0, lines.match_count
|
||||
assert_includes lines, '> -o=SECURI'
|
||||
end
|
||||
end
|
||||
ensure
|
||||
FileUtils.rm_rf('/tmp/fzf-test-opt-eq-short')
|
||||
end
|
||||
|
||||
def test_option_equals_short_option_after_double_dash
|
||||
FileUtils.mkdir_p('/tmp/fzf-test-opt-eq-short-ddash')
|
||||
FileUtils.touch('/tmp/fzf-test-opt-eq-short-ddash/SECURITY.md')
|
||||
tmux.prepare
|
||||
tmux.send_keys 'cd /tmp/fzf-test-opt-eq-short-ddash', :Enter
|
||||
tmux.prepare
|
||||
if shell == :fish
|
||||
tmux.send_keys 'some-command -- -o=SECURI', 'C-t'
|
||||
else
|
||||
tmux.send_keys "some-command -- -o=SECURI#{trigger}", :Tab
|
||||
end
|
||||
|
||||
case shell
|
||||
when :bash
|
||||
tmux.until do |lines|
|
||||
assert_equal 1, lines.match_count
|
||||
assert_includes lines, '> SECURITY.md'
|
||||
end
|
||||
tmux.send_keys :Enter
|
||||
tmux.until(true) { |lines| assert_equal 'some-command -- -o=SECURITY.md', lines[-1] }
|
||||
when :fish, :zsh
|
||||
tmux.until do |lines|
|
||||
assert_equal 0, lines.match_count
|
||||
assert_includes lines, '> -o=SECURI'
|
||||
end
|
||||
end
|
||||
ensure
|
||||
FileUtils.rm_rf('/tmp/fzf-test-opt-eq-short-ddash')
|
||||
end
|
||||
|
||||
def test_option_no_equals_long_option
|
||||
FileUtils.mkdir_p('/tmp/fzf-test-opt-no-eq-long')
|
||||
FileUtils.touch('/tmp/fzf-test-opt-no-eq-long/SECURITY.md')
|
||||
tmux.prepare
|
||||
tmux.send_keys 'cd /tmp/fzf-test-opt-no-eq-long', :Enter
|
||||
tmux.prepare
|
||||
if shell == :fish
|
||||
tmux.send_keys 'some-command --optSECURI', 'C-t'
|
||||
else
|
||||
tmux.send_keys "some-command --optSECURI#{trigger}", :Tab
|
||||
end
|
||||
|
||||
tmux.until do |lines|
|
||||
assert_equal 0, lines.match_count
|
||||
assert_includes lines, '> --optSECURI'
|
||||
end
|
||||
ensure
|
||||
FileUtils.rm_rf('/tmp/fzf-test-opt-no-eq-long')
|
||||
end
|
||||
|
||||
def test_option_no_equals_long_option_after_double_dash
|
||||
FileUtils.mkdir_p('/tmp/fzf-test-opt-no-eq-long-ddash')
|
||||
FileUtils.touch('/tmp/fzf-test-opt-no-eq-long-ddash/SECURITY.md')
|
||||
tmux.prepare
|
||||
tmux.send_keys 'cd /tmp/fzf-test-opt-no-eq-long-ddash', :Enter
|
||||
tmux.prepare
|
||||
if shell == :fish
|
||||
tmux.send_keys 'some-command -- --optSECURI', 'C-t'
|
||||
else
|
||||
tmux.send_keys "some-command -- --optSECURI#{trigger}", :Tab
|
||||
end
|
||||
|
||||
tmux.until do |lines|
|
||||
assert_equal 0, lines.match_count
|
||||
assert_includes lines, '> --optSECURI'
|
||||
end
|
||||
ensure
|
||||
FileUtils.rm_rf('/tmp/fzf-test-opt-no-eq-long-ddash')
|
||||
end
|
||||
|
||||
def test_option_no_equals_short_option
|
||||
FileUtils.mkdir_p('/tmp/fzf-test-opt-no-eq-short')
|
||||
FileUtils.touch('/tmp/fzf-test-opt-no-eq-short/SECURITY.md')
|
||||
tmux.prepare
|
||||
tmux.send_keys 'cd /tmp/fzf-test-opt-no-eq-short', :Enter
|
||||
tmux.prepare
|
||||
if shell == :fish
|
||||
tmux.send_keys 'some-command -oSECURI', 'C-t'
|
||||
else
|
||||
tmux.send_keys "some-command -oSECURI#{trigger}", :Tab
|
||||
end
|
||||
|
||||
case shell
|
||||
when :bash, :zsh
|
||||
tmux.until do |lines|
|
||||
assert_equal 0, lines.match_count
|
||||
assert_includes lines, '> -oSECURI'
|
||||
end
|
||||
when :fish
|
||||
tmux.until do |lines|
|
||||
assert_equal 1, lines.match_count
|
||||
assert lines.any_include?('> SECURITY.md')
|
||||
end
|
||||
tmux.send_keys :Enter
|
||||
tmux.until(true) { |lines| assert_equal 'some-command -oSECURITY.md', lines[-1] }
|
||||
end
|
||||
ensure
|
||||
FileUtils.rm_rf('/tmp/fzf-test-opt-no-eq-short')
|
||||
end
|
||||
|
||||
def test_option_no_equals_short_option_after_double_dash
|
||||
FileUtils.mkdir_p('/tmp/fzf-test-opt-no-eq-short-ddash')
|
||||
FileUtils.touch('/tmp/fzf-test-opt-no-eq-short-ddash/SECURITY.md')
|
||||
tmux.prepare
|
||||
tmux.send_keys 'cd /tmp/fzf-test-opt-no-eq-short-ddash', :Enter
|
||||
tmux.prepare
|
||||
if shell == :fish
|
||||
tmux.send_keys 'some-command -- -oSECURI', 'C-t'
|
||||
else
|
||||
tmux.send_keys "some-command -- -oSECURI#{trigger}", :Tab
|
||||
end
|
||||
|
||||
tmux.until do |lines|
|
||||
assert_equal 0, lines.match_count
|
||||
assert_includes lines, '> -oSECURI'
|
||||
end
|
||||
ensure
|
||||
FileUtils.rm_rf('/tmp/fzf-test-opt-no-eq-short-ddash')
|
||||
end
|
||||
|
||||
def test_filename_with_newline
|
||||
FileUtils.mkdir_p('/tmp/fzf-test-newline')
|
||||
FileUtils.touch("/tmp/fzf-test-newline/xyz\nwith\nnewlines")
|
||||
tmux.prepare
|
||||
tmux.send_keys 'cd /tmp/fzf-test-newline', :Enter
|
||||
tmux.prepare
|
||||
if shell == :fish
|
||||
tmux.send_keys 'cat xyz', 'C-t'
|
||||
else
|
||||
tmux.send_keys "cat xyz#{trigger}", :Tab
|
||||
end
|
||||
|
||||
case shell
|
||||
when :fish
|
||||
tmux.until do |lines|
|
||||
assert_equal 1, lines.match_count
|
||||
assert_includes lines, '> xyz'
|
||||
end
|
||||
tmux.send_keys :Enter
|
||||
# fish escapes newlines in filenames
|
||||
tmux.until(true) { |lines| assert_equal 'cat xyz\\nwith\\nnewlines', lines[-1] }
|
||||
when :bash, :zsh
|
||||
tmux.until do |lines|
|
||||
assert_equal 1, lines.match_count
|
||||
assert_includes lines, '> xyz'
|
||||
end
|
||||
tmux.send_keys :Enter
|
||||
# bash and zsh replace newlines with spaces in filenames
|
||||
tmux.until(true) { |lines| assert_equal 'cat xyz with newlines', lines[-1] }
|
||||
end
|
||||
ensure
|
||||
FileUtils.rm_rf('/tmp/fzf-test-newline')
|
||||
end
|
||||
|
||||
def test_path_with_special_chars
|
||||
FileUtils.mkdir_p('/tmp/fzf-test-[special]')
|
||||
FileUtils.touch('/tmp/fzf-test-[special]/xyz123')
|
||||
tmux.prepare
|
||||
if shell == :fish
|
||||
tmux.send_keys 'ls /tmp/fzf-test-\[special\]/xyz', 'C-t'
|
||||
else
|
||||
tmux.send_keys "ls /tmp/fzf-test-\\[special\\]/xyz#{trigger}", :Tab
|
||||
end
|
||||
tmux.until { |lines| assert_equal 1, lines.match_count }
|
||||
tmux.send_keys :Enter
|
||||
tmux.until(true) { |lines| assert_equal 'ls /tmp/fzf-test-\\[special\\]/xyz123', lines[-1] }
|
||||
ensure
|
||||
FileUtils.rm_rf('/tmp/fzf-test-[special]')
|
||||
end
|
||||
|
||||
def test_query_with_dollar_anchor
|
||||
FileUtils.mkdir_p('/tmp/fzf-test-dollar-anchor')
|
||||
FileUtils.touch('/tmp/fzf-test-dollar-anchor/file.txt')
|
||||
FileUtils.touch('/tmp/fzf-test-dollar-anchor/filetxt.md')
|
||||
tmux.prepare
|
||||
tmux.send_keys 'cd /tmp/fzf-test-dollar-anchor', :Enter
|
||||
tmux.prepare
|
||||
if shell == :fish
|
||||
tmux.send_keys 'ls txt$', 'C-t'
|
||||
else
|
||||
tmux.send_keys "ls txt$#{trigger}", :Tab
|
||||
end
|
||||
tmux.until do |lines|
|
||||
assert_equal 1, lines.match_count
|
||||
assert_includes lines, '> txt$'
|
||||
end
|
||||
tmux.send_keys :Enter
|
||||
tmux.until(true) { |lines| assert_equal 'ls file.txt', lines[-1] }
|
||||
ensure
|
||||
FileUtils.rm_rf('/tmp/fzf-test-dollar-anchor')
|
||||
end
|
||||
|
||||
def test_single_flag_completion
|
||||
FileUtils.mkdir_p('/tmp/fzf-test-single-flag')
|
||||
FileUtils.touch('/tmp/fzf-test-single-flag/-testfile.txt')
|
||||
tmux.prepare
|
||||
tmux.send_keys 'cd /tmp/fzf-test-single-flag', :Enter
|
||||
tmux.prepare
|
||||
if shell == :fish
|
||||
tmux.send_keys 'ls -', 'C-t'
|
||||
else
|
||||
tmux.send_keys "ls -#{trigger}", :Tab
|
||||
end
|
||||
|
||||
tmux.until do |lines|
|
||||
assert_equal 1, lines.match_count
|
||||
assert_includes lines, '> -'
|
||||
end
|
||||
tmux.send_keys :Enter
|
||||
tmux.until(true) { |lines| assert_equal 'ls -testfile.txt', lines[-1] }
|
||||
ensure
|
||||
FileUtils.rm_rf('/tmp/fzf-test-single-flag')
|
||||
end
|
||||
|
||||
def test_double_flag_completion
|
||||
FileUtils.mkdir_p('/tmp/fzf-test-double-flag')
|
||||
FileUtils.touch('/tmp/fzf-test-double-flag/--testfile.txt')
|
||||
tmux.prepare
|
||||
tmux.send_keys 'cd /tmp/fzf-test-double-flag', :Enter
|
||||
tmux.prepare
|
||||
if shell == :fish
|
||||
tmux.send_keys 'ls --', 'C-t'
|
||||
else
|
||||
tmux.send_keys "ls --#{trigger}", :Tab
|
||||
end
|
||||
|
||||
tmux.until do |lines|
|
||||
assert_equal 1, lines.match_count
|
||||
assert_includes lines, '> --'
|
||||
end
|
||||
tmux.send_keys :Enter
|
||||
tmux.until(true) { |lines| assert_equal 'ls --testfile.txt', lines[-1] }
|
||||
ensure
|
||||
FileUtils.rm_rf('/tmp/fzf-test-double-flag')
|
||||
end
|
||||
end
|
||||
|
||||
class TestBash < TestBase
|
||||
@@ -424,7 +836,7 @@ class TestBash < TestBase
|
||||
tmux.paste 'touch /tmp/foo; _fzf_completion_loader=1'
|
||||
tmux.paste '_completion_loader() { complete -o default fake; }'
|
||||
tmux.paste 'complete -F _fzf_path_completion -o default -o bashdefault fake'
|
||||
tmux.send_keys 'fake /tmp/foo**', :Tab
|
||||
tmux.send_keys "fake /tmp/foo#{trigger}", :Tab
|
||||
tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
|
||||
tmux.send_keys 'C-c'
|
||||
|
||||
@@ -433,7 +845,7 @@ class TestBash < TestBase
|
||||
tmux.send_keys :Tab, 'C-u'
|
||||
|
||||
tmux.prepare
|
||||
tmux.send_keys 'fake /tmp/foo**', :Tab
|
||||
tmux.send_keys "fake /tmp/foo#{trigger}", :Tab
|
||||
tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
|
||||
end
|
||||
end
|
||||
@@ -455,7 +867,7 @@ class TestZsh < TestBase
|
||||
tmux.send_keys 'export FZFFOOBAR=BAZ', :Enter
|
||||
['unset', '\unset', "'unset'"].each do |command|
|
||||
tmux.prepare
|
||||
tmux.send_keys "#{command} FZFFOOBR**", :Tab
|
||||
tmux.send_keys "#{command} FZFFOOBR#{trigger}", :Tab
|
||||
tmux.until { |lines| assert_equal 1, lines.match_count }
|
||||
tmux.send_keys :Enter
|
||||
tmux.until { |lines| assert_equal "#{command} FZFFOOBAR", lines[-1] }
|
||||
@@ -579,13 +991,18 @@ end
|
||||
|
||||
class TestFish < TestBase
|
||||
include TestShell
|
||||
include CompletionTest
|
||||
|
||||
def shell
|
||||
:fish
|
||||
end
|
||||
|
||||
def trigger
|
||||
'++'
|
||||
end
|
||||
|
||||
def new_shell
|
||||
tmux.send_keys 'env FZF_TMUX=1 FZF_DEFAULT_OPTS=--no-scrollbar fish', :Enter
|
||||
tmux.send_keys 'env FZF_TMUX=1 XDG_CONFIG_HOME=/tmp/fzf-fish fish', :Enter
|
||||
tmux.send_keys 'function fish_prompt; end; clear', :Enter
|
||||
tmux.until { |lines| assert_empty lines }
|
||||
end
|
||||
|
||||
@@ -33,6 +33,9 @@ for opt in "$@"; do
|
||||
esac
|
||||
done
|
||||
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||
fzf_base=$(pwd)
|
||||
|
||||
ask() {
|
||||
while true; do
|
||||
read -p "$1 ([y]/n) " -r
|
||||
@@ -94,12 +97,15 @@ done
|
||||
bind_file="${fish_dir}/functions/fish_user_key_bindings.fish"
|
||||
if [ -f "$bind_file" ]; then
|
||||
remove_line "$bind_file" "fzf_key_bindings"
|
||||
remove_line "$bind_file" "fzf_completion_setup"
|
||||
remove_line "$bind_file" "fzf --fish | source"
|
||||
fi
|
||||
|
||||
if [ -d "${fish_dir}/functions" ]; then
|
||||
remove "${fish_dir}/functions/fzf.fish"
|
||||
remove "${fish_dir}/functions/fzf_key_bindings.fish"
|
||||
remove_line "$bind_file" "source \"${fzf_base}/shell/completion.fish\""
|
||||
remove_line "$bind_file" "source \"${fzf_base}/shell/key-bindings.fish\""
|
||||
|
||||
if [ -z "$(ls -A "${fish_dir}/functions")" ]; then
|
||||
rmdir "${fish_dir}/functions"
|
||||
|
||||
Reference in New Issue
Block a user