mirror of
https://github.com/junegunn/fzf.git
synced 2026-05-26 02:08:50 +08:00
Add fish completion support (#4605)
This commit is contained in:
@@ -69,15 +69,17 @@ Table of Contents
|
|||||||
* [Demo](#demo)
|
* [Demo](#demo)
|
||||||
* [Examples](#examples)
|
* [Examples](#examples)
|
||||||
* [Key bindings for command-line](#key-bindings-for-command-line)
|
* [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)
|
* [Files and directories](#files-and-directories)
|
||||||
* [Process IDs](#process-ids)
|
* [Process IDs](#process-ids)
|
||||||
* [Host names](#host-names)
|
* [Host names](#host-names)
|
||||||
* [Environment variables / Aliases](#environment-variables--aliases)
|
* [Environment variables / Aliases](#environment-variables--aliases)
|
||||||
* [Customizing fzf options for completion](#customizing-fzf-options-for-completion)
|
* [Customizing fuzzy completion for bash and zsh](#customizing-fuzzy-completion-for-bash-and-zsh)
|
||||||
* [Customizing completion source for paths and directories](#customizing-completion-source-for-paths-and-directories)
|
* [Customizing fzf options for completion](#customizing-fzf-options-for-completion)
|
||||||
* [Supported commands](#supported-commands)
|
* [Customizing completion source for paths and directories](#customizing-completion-source-for-paths-and-directories)
|
||||||
* [Custom fuzzy completion](#custom-fuzzy-completion)
|
* [Supported commands (bash)](#supported-commands-bash)
|
||||||
|
* [Custom fuzzy completion](#custom-fuzzy-completion)
|
||||||
|
* [Fuzzy completion for fish](#fuzzy-completion-for-fish)
|
||||||
* [Vim plugin](#vim-plugin)
|
* [Vim plugin](#vim-plugin)
|
||||||
* [Advanced topics](#advanced-topics)
|
* [Advanced topics](#advanced-topics)
|
||||||
* [Customizing for different types of input](#customizing-for-different-types-of-input)
|
* [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).
|
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
|
### Files and directories
|
||||||
|
|
||||||
@@ -599,23 +603,28 @@ kill -9 **<TAB>
|
|||||||
|
|
||||||
### Host names
|
### Host names
|
||||||
|
|
||||||
For ssh and telnet commands, fuzzy completion for hostnames is provided. The
|
For ssh command, fuzzy completion for hostnames is provided. The names are
|
||||||
names are extracted from /etc/hosts and ~/.ssh/config.
|
extracted from /etc/hosts and ~/.ssh/config.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
ssh **<TAB>
|
ssh **<TAB>
|
||||||
telnet **<TAB>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Environment variables / Aliases
|
### Environment variables / Aliases
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
# bash and zsh
|
||||||
unset **<TAB>
|
unset **<TAB>
|
||||||
export **<TAB>
|
export **<TAB>
|
||||||
unalias **<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
|
```sh
|
||||||
# Use ~~ as the trigger sequence instead of the default **
|
# 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
|
```sh
|
||||||
# Use fd (https://github.com/sharkdp/fd) for listing path candidates.
|
# 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
|
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
|
(`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
|
_fzf_setup_completion dir tree
|
||||||
```
|
```
|
||||||
|
|
||||||
### Custom fuzzy completion
|
#### Custom fuzzy completion
|
||||||
|
|
||||||
_**(Custom completion API is experimental and subject to change)**_
|
_**(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
|
[ -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
|
Vim plugin
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|||||||
@@ -243,16 +243,16 @@ fi
|
|||||||
|
|
||||||
echo
|
echo
|
||||||
for shell in $shells; do
|
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
|
[[ $shell == fish ]] && continue
|
||||||
src=${prefix_expand}.${shell}
|
src=${prefix_expand}.${shell}
|
||||||
echo -n "Generate $src ... "
|
echo -n "Generate $src ... "
|
||||||
|
|
||||||
fzf_completion="source \"$fzf_base/shell/completion.${shell}\""
|
|
||||||
if [ $auto_completion -eq 0 ]; then
|
if [ $auto_completion -eq 0 ]; then
|
||||||
fzf_completion="# $fzf_completion"
|
fzf_completion="# $fzf_completion"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
fzf_key_bindings="source \"$fzf_base/shell/key-bindings.${shell}\""
|
|
||||||
if [ $key_bindings -eq 0 ]; then
|
if [ $key_bindings -eq 0 ]; then
|
||||||
fzf_key_bindings="# $fzf_key_bindings"
|
fzf_key_bindings="# $fzf_key_bindings"
|
||||||
fi
|
fi
|
||||||
@@ -302,15 +302,16 @@ append_line() {
|
|||||||
line="$2"
|
line="$2"
|
||||||
file="$3"
|
file="$3"
|
||||||
pat="${4:-}"
|
pat="${4:-}"
|
||||||
|
at_lno="${5:-}"
|
||||||
lines=""
|
lines=""
|
||||||
|
|
||||||
echo "Update $file:"
|
echo "Update $file:"
|
||||||
echo " - $line"
|
echo " - $line"
|
||||||
if [ -f "$file" ]; then
|
if [ -f "$file" ]; then
|
||||||
if [ $# -lt 4 ]; then
|
if [[ -n $pat ]]; then
|
||||||
lines=$(\grep -nF "$line" "$file")
|
|
||||||
else
|
|
||||||
lines=$(\grep -nF "$pat" "$file")
|
lines=$(\grep -nF "$pat" "$file")
|
||||||
|
else
|
||||||
|
lines=$(\grep -nF "${line#"${line%%[![:space:]]*}"}" "$file")
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -328,8 +329,12 @@ append_line() {
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
if [ "$update" -eq 1 ]; then
|
if [ "$update" -eq 1 ]; then
|
||||||
[ -f "$file" ] && echo >> "$file"
|
if [[ -z $at_lno ]]; then
|
||||||
echo "$line" >> "$file"
|
[ -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"
|
echo " + Added"
|
||||||
else
|
else
|
||||||
echo " ~ Skipped"
|
echo " ~ Skipped"
|
||||||
@@ -362,25 +367,66 @@ for shell in $shells; do
|
|||||||
append_line $update_config "[ -f ${prefix}.${shell} ] && source ${prefix}.${shell}" "$dest" "${prefix}.${shell}"
|
append_line $update_config "[ -f ${prefix}.${shell} ] && source ${prefix}.${shell}" "$dest" "${prefix}.${shell}"
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ $key_bindings -eq 1 ] && [[ $shells =~ fish ]]; then
|
if [[ $shells =~ fish ]]; then
|
||||||
bind_file="${fish_dir}/functions/fish_user_key_bindings.fish"
|
bind_file="${fish_dir}/functions/fish_user_key_bindings.fish"
|
||||||
if [ ! -e "$bind_file" ]; then
|
if [ ! -e "$bind_file" ]; then
|
||||||
mkdir -p "${fish_dir}/functions"
|
if [[ $key_bindings -eq 1 || $auto_completion -eq 1 ]]; then
|
||||||
create_file "$bind_file" \
|
mkdir -p "${fish_dir}/functions"
|
||||||
'function fish_user_key_bindings' \
|
if [[ $key_bindings -eq 1 && $auto_completion -eq 1 ]]; then
|
||||||
' fzf --fish | source' \
|
create_file "$bind_file" \
|
||||||
'end'
|
'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
|
else
|
||||||
echo "Check $bind_file:"
|
echo "Check $bind_file:"
|
||||||
lno=$(\grep -nF "fzf_key_bindings" "$bind_file" | sed 's/:.*//' | tr '\n' ' ')
|
lno_func=$(\grep -nF "function fish_user_key_bindings" "$bind_file" | sed 's/:.*//' | tr '\n' ' ')
|
||||||
if [[ -n $lno ]]; then
|
if [[ -z $lno_func ]]; then
|
||||||
echo " ** Found 'fzf_key_bindings' in line #$lno"
|
echo -e "function fish_user_key_bindings\nend" >> "$bind_file"
|
||||||
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' ' ')
|
||||||
|
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
|
echo
|
||||||
else
|
else
|
||||||
echo " - Clear"
|
echo " - Clear"
|
||||||
echo
|
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
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -393,7 +439,7 @@ if [ $update_config -eq 1 ]; then
|
|||||||
echo
|
echo
|
||||||
fi
|
fi
|
||||||
[[ $shells =~ zsh ]] && echo " source ${ZDOTDIR:-~}/.zshrc # zsh"
|
[[ $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
|
||||||
echo 'Use uninstall script to remove fzf.'
|
echo 'Use uninstall script to remove fzf.'
|
||||||
echo
|
echo
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ var zshCompletion []byte
|
|||||||
//go:embed shell/key-bindings.fish
|
//go:embed shell/key-bindings.fish
|
||||||
var fishKeyBindings []byte
|
var fishKeyBindings []byte
|
||||||
|
|
||||||
|
//go:embed shell/completion.fish
|
||||||
|
var fishCompletion []byte
|
||||||
|
|
||||||
//go:embed man/man1/fzf.1
|
//go:embed man/man1/fzf.1
|
||||||
var manPage []byte
|
var manPage []byte
|
||||||
|
|
||||||
@@ -65,7 +68,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
if options.Fish {
|
if options.Fish {
|
||||||
printScript("key-bindings.fish", fishKeyBindings)
|
printScript("key-bindings.fish", fishKeyBindings)
|
||||||
fmt.Println("fzf_key_bindings")
|
printScript("completion.fish", fishCompletion)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if options.Help {
|
if options.Help {
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
+48
-20
@@ -30,9 +30,12 @@ function fzf_key_bindings
|
|||||||
return 1
|
return 1
|
||||||
end
|
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
|
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%
|
test -n "$FZF_TMUX_HEIGHT"; or set -l FZF_TMUX_HEIGHT 40%
|
||||||
string join ' ' -- \
|
string join ' ' -- \
|
||||||
"--height $FZF_TMUX_HEIGHT --min-height=20+ --bind=ctrl-z:ignore" $argv[1] \
|
"--height $FZF_TMUX_HEIGHT --min-height=20+ --bind=ctrl-z:ignore" $argv[1] \
|
||||||
@@ -51,17 +54,51 @@ function fzf_key_bindings
|
|||||||
end
|
end
|
||||||
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'
|
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 fzf_query ''
|
||||||
set -l prefix ''
|
set -l prefix ''
|
||||||
set -l dir '.'
|
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_major (string match -r -- '^\d+' $version)
|
||||||
set -l -- fish_minor (string match -r -- '^\d+\.(\d+)' $version)[2]
|
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 -- match_regex '(?<fzf_query>[\s\S]*?(?=\n?$)$)'
|
||||||
set -l -- prefix_regex '^-[^\s=]+=|^-(?!-)\S'
|
set -l -- prefix_regex '^-[^\s=]+=|^-(?!-)\S'
|
||||||
if test "$fish_major" -eq 3 -a "$fish_minor" -lt 3
|
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"
|
set -- match_regex "(?<prefix>$prefix_regex)?$match_regex"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Set $prefix and expanded $fzf_query with preserved trailing newlines.
|
|
||||||
if test "$fish_major" -ge 4
|
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)
|
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
|
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)
|
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)' '')
|
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^\\\(?=~)|\\\(?=\$\w)' '')
|
||||||
else
|
else
|
||||||
# fish older than v3.2.0 (v3.1b1 - v3.1.2)
|
|
||||||
set -l -- cl_token (commandline --current-token --tokenize | string collect -N)
|
set -l -- cl_token (commandline --current-token --tokenize | string collect -N)
|
||||||
set -- prefix (string match -r -- $prefix_regex $cl_token)
|
set -- prefix (string match -r -- $prefix_regex $cl_token)
|
||||||
set -- fzf_query (string replace -- "$prefix" '' $cl_token | string collect -N)
|
set -- fzf_query (string replace -- "$prefix" '' $cl_token | string collect -N)
|
||||||
@@ -86,22 +119,17 @@ function fzf_key_bindings
|
|||||||
end
|
end
|
||||||
|
|
||||||
if test -n "$fzf_query"
|
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 \)
|
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 -- fzf_query (path normalize -- $fzf_query)
|
||||||
set -- dir $fzf_query
|
set -- dir $fzf_query
|
||||||
while not path is -d $dir
|
while not path is -d $dir
|
||||||
set -- dir (path dirname $dir)
|
set -- dir (path dirname $dir)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
# fish older than v3.5.0 (v3.1b1 - v3.4.1)
|
|
||||||
if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2
|
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 match -q -r -- '(?<fzf_query>^[\s\S]*?(?=\n?$)$)' \
|
||||||
(string replace -r -a -- '(?<=/)/|(?<!^)/+(?!\n)$' '' $fzf_query | string collect -N)
|
(string replace -r -a -- '(?<=/)/|(?<!^)/+(?!\n)$' '' $fzf_query | string collect -N)
|
||||||
else
|
else
|
||||||
# fish v3.1b1 - v3.1.2
|
|
||||||
set -- fzf_query (string replace -r -a -- '(?<=/)/|(?<!^)/+(?!\n)$' '' $fzf_query | string collect -N)
|
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$' '')
|
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r '\\\n$' '')
|
||||||
end
|
end
|
||||||
@@ -112,16 +140,12 @@ function fzf_key_bindings
|
|||||||
end
|
end
|
||||||
|
|
||||||
if not string match -q -- '.' $dir; or string match -q -r -- '^\./|^\.$' $fzf_query
|
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
|
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
|
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
|
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 match -q -r -- '^/?(?<fzf_query>[\s\S]*?(?=\n?$)$)' \
|
||||||
(string replace -- "$dir" '' $fzf_query | string collect -N)
|
(string replace -- "$dir" '' $fzf_query | string collect -N)
|
||||||
else
|
else
|
||||||
# fish older than v3.2.0 (v3.1b1 - v3.1.2)
|
|
||||||
set -- fzf_query (string replace -- "$dir" '' $fzf_query | string collect -N)
|
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$' '')
|
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^/?|\\\n$' '')
|
||||||
end
|
end
|
||||||
@@ -130,6 +154,7 @@ function fzf_key_bindings
|
|||||||
|
|
||||||
string escape -n -- "$dir" "$fzf_query" "$prefix"
|
string escape -n -- "$dir" "$fzf_query" "$prefix"
|
||||||
end
|
end
|
||||||
|
#----END INCLUDE
|
||||||
|
|
||||||
# Store current token in $dir as root for the 'find' command
|
# Store current token in $dir as root for the 'find' command
|
||||||
function fzf-file-widget -d "List files and folders"
|
function fzf-file-widget -d "List files and folders"
|
||||||
@@ -140,13 +165,13 @@ function fzf_key_bindings
|
|||||||
|
|
||||||
set -lx FZF_DEFAULT_OPTS (__fzf_defaults \
|
set -lx FZF_DEFAULT_OPTS (__fzf_defaults \
|
||||||
"--reverse --walker=file,dir,follow,hidden --scheme=path" \
|
"--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_COMMAND "$FZF_CTRL_T_COMMAND"
|
||||||
set -lx FZF_DEFAULT_OPTS_FILE
|
set -lx FZF_DEFAULT_OPTS_FILE
|
||||||
|
|
||||||
set -l result (eval (__fzfcmd) --walker-root=$dir --query=$fzf_query | string split0)
|
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
|
commandline -f repaint
|
||||||
end
|
end
|
||||||
@@ -234,3 +259,6 @@ function fzf_key_bindings
|
|||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Run setup
|
||||||
|
fzf_key_bindings
|
||||||
|
|||||||
+14
-12
@@ -8,24 +8,26 @@ dir=${0%"${0##*/}"}
|
|||||||
|
|
||||||
update() {
|
update() {
|
||||||
{
|
{
|
||||||
sed -n '1,/^#----BEGIN INCLUDE common\.sh/p' "$1"
|
sed -n "1,/^#----BEGIN INCLUDE $1/p" "$2"
|
||||||
cat << EOF
|
cat << EOF
|
||||||
# NOTE: Do not directly edit this section, which is copied from "common.sh".
|
# NOTE: Do not directly edit this section, which is copied from "$1".
|
||||||
# To modify it, one can edit "common.sh" and run "./update.sh" to apply
|
# To modify it, one can edit "$1" and run "./update.sh" to apply
|
||||||
# the changes. See code comments in "common.sh" for the implementation details.
|
# the changes. See code comments in "$1" for the implementation details.
|
||||||
EOF
|
EOF
|
||||||
echo
|
echo
|
||||||
grep -v '^[[:blank:]]*#' "$dir/common.sh" # remove code comments in common.sh
|
grep -v '^[[:blank:]]*#' "$dir/$1" # remove code comments from the common file
|
||||||
sed -n '/^#----END INCLUDE/,$p' "$1"
|
sed -n '/^#----END INCLUDE/,$p' "$2"
|
||||||
} > "$1.part"
|
} > "$2.part"
|
||||||
|
|
||||||
mv -f "$1.part" "$1"
|
mv -f "$2.part" "$2"
|
||||||
}
|
}
|
||||||
|
|
||||||
update "$dir/completion.bash"
|
update "common.sh" "$dir/completion.bash"
|
||||||
update "$dir/completion.zsh"
|
update "common.sh" "$dir/completion.zsh"
|
||||||
update "$dir/key-bindings.bash"
|
update "common.sh" "$dir/key-bindings.bash"
|
||||||
update "$dir/key-bindings.zsh"
|
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 if --check is in ARGV
|
||||||
check=0
|
check=0
|
||||||
|
|||||||
@@ -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
-1
@@ -11,6 +11,7 @@ require 'net/http'
|
|||||||
require 'json'
|
require 'json'
|
||||||
|
|
||||||
TEMPLATE = File.read(File.expand_path('common.sh', __dir__))
|
TEMPLATE = File.read(File.expand_path('common.sh', __dir__))
|
||||||
|
FISH_TEMPLATE = File.read(File.expand_path('common.fish', __dir__))
|
||||||
UNSETS = %w[
|
UNSETS = %w[
|
||||||
FZF_DEFAULT_COMMAND FZF_DEFAULT_OPTS
|
FZF_DEFAULT_COMMAND FZF_DEFAULT_OPTS
|
||||||
FZF_TMUX FZF_TMUX_OPTS
|
FZF_TMUX FZF_TMUX_OPTS
|
||||||
@@ -66,7 +67,16 @@ class Shell
|
|||||||
end
|
end
|
||||||
|
|
||||||
def fish
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
+491
-74
@@ -27,6 +27,10 @@ module TestShell
|
|||||||
tmux.prepare
|
tmux.prepare
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def trigger
|
||||||
|
'**'
|
||||||
|
end
|
||||||
|
|
||||||
def test_ctrl_t
|
def test_ctrl_t
|
||||||
set_var('FZF_CTRL_T_COMMAND', 'seq 100')
|
set_var('FZF_CTRL_T_COMMAND', 'seq 100')
|
||||||
|
|
||||||
@@ -165,7 +169,11 @@ module CompletionTest
|
|||||||
FileUtils.touch(File.expand_path(f))
|
FileUtils.touch(File.expand_path(f))
|
||||||
end
|
end
|
||||||
tmux.prepare
|
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.until { |lines| assert_operator lines.match_count, :>, 0 }
|
||||||
tmux.send_keys ' !d'
|
tmux.send_keys ' !d'
|
||||||
tmux.until { |lines| assert_equal 2, lines.match_count }
|
tmux.until { |lines| assert_equal 2, lines.match_count }
|
||||||
@@ -179,7 +187,11 @@ module CompletionTest
|
|||||||
# ~USERNAME**<TAB>
|
# ~USERNAME**<TAB>
|
||||||
user = `whoami`.chomp
|
user = `whoami`.chomp
|
||||||
tmux.send_keys 'C-u'
|
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.until { |lines| assert_operator lines.match_count, :>, 0 }
|
||||||
tmux.send_keys "/#{user}"
|
tmux.send_keys "/#{user}"
|
||||||
tmux.until { |lines| assert(lines.any? { |l| l.end_with?("/#{user}") }) }
|
tmux.until { |lines| assert(lines.any? { |l| l.end_with?("/#{user}") }) }
|
||||||
@@ -190,14 +202,29 @@ module CompletionTest
|
|||||||
|
|
||||||
# ~INVALID_USERNAME**<TAB>
|
# ~INVALID_USERNAME**<TAB>
|
||||||
tmux.send_keys 'C-u'
|
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.until(true) { |lines| assert lines.any_include?('no~such~user') }
|
||||||
tmux.send_keys :Enter
|
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>
|
# /tmp/fzf\ test**<TAB>
|
||||||
tmux.send_keys 'C-u'
|
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.until { |lines| assert_operator lines.match_count, :>, 0 }
|
||||||
tmux.send_keys 'foobar$'
|
tmux.send_keys 'foobar$'
|
||||||
tmux.until do |lines|
|
tmux.until do |lines|
|
||||||
@@ -210,7 +237,11 @@ module CompletionTest
|
|||||||
# Should include hidden files
|
# Should include hidden files
|
||||||
(1..100).each { |i| FileUtils.touch("/tmp/fzf-test/.hidden-#{i}") }
|
(1..100).each { |i| FileUtils.touch("/tmp/fzf-test/.hidden-#{i}") }
|
||||||
tmux.send_keys 'C-u'
|
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|
|
tmux.until(true) do |lines|
|
||||||
assert_equal 100, lines.match_count
|
assert_equal 100, lines.match_count
|
||||||
assert lines.any_include?('/tmp/fzf-test/.hidden-')
|
assert lines.any_include?('/tmp/fzf-test/.hidden-')
|
||||||
@@ -223,70 +254,89 @@ module CompletionTest
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_file_completion_root
|
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.until { |lines| assert_operator lines.match_count, :>, 0 }
|
||||||
tmux.send_keys :Enter
|
tmux.send_keys :Enter
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_dir_completion
|
def test_dir_completion
|
||||||
|
FileUtils.mkdir_p('/tmp/fzf-test-dir')
|
||||||
(1..100).each do |idx|
|
(1..100).each do |idx|
|
||||||
FileUtils.mkdir_p("/tmp/fzf-test/d#{idx}")
|
FileUtils.mkdir_p("/tmp/fzf-test-dir/d#{idx}")
|
||||||
end
|
end
|
||||||
FileUtils.touch('/tmp/fzf-test/d55/xxx')
|
FileUtils.touch('/tmp/fzf-test-dir/d55/xxx')
|
||||||
tmux.prepare
|
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.until { |lines| assert_operator lines.match_count, :>, 0 }
|
||||||
tmux.send_keys :Tab, :Tab # Tab does not work here
|
# Tab selects items in C-t's --multi mode, so skip for fish
|
||||||
tmux.send_keys 55
|
tmux.send_keys :Tab, :Tab unless shell == :fish # Tab does not work here
|
||||||
|
tmux.send_keys '55/$'
|
||||||
tmux.until do |lines|
|
tmux.until do |lines|
|
||||||
assert_equal 1, lines.match_count
|
assert_equal 1, lines.match_count
|
||||||
assert_includes lines, '> 55'
|
assert_includes lines, '> 55/$'
|
||||||
assert_includes lines, '> /tmp/fzf-test/d55/'
|
assert_includes lines, '> /tmp/fzf-test-dir/d55/'
|
||||||
end
|
end
|
||||||
tmux.send_keys :Enter
|
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.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)
|
# Should not match regular files (bash-only)
|
||||||
if instance_of?(TestBash)
|
if instance_of?(TestBash)
|
||||||
tmux.send_keys :Tab
|
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
|
end
|
||||||
|
|
||||||
# Fail back to plusdirs
|
# Fail back to plusdirs
|
||||||
tmux.send_keys :BSpace, :BSpace, :BSpace
|
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.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
|
end
|
||||||
|
|
||||||
def test_process_completion
|
def test_process_completion
|
||||||
tmux.send_keys 'sleep 12345 &', :Enter
|
skip('fish background job format differs') if shell == :fish
|
||||||
lines = tmux.until { |lines| assert lines[-1]&.start_with?('[1] ') }
|
|
||||||
pid = lines[-1]&.split&.last
|
begin
|
||||||
tmux.prepare
|
tmux.send_keys 'sleep 12345 &', :Enter
|
||||||
tmux.send_keys 'C-L'
|
lines = tmux.until { |lines| assert lines[-1]&.start_with?('[1] ') }
|
||||||
tmux.send_keys 'kill **', :Tab
|
pid = lines[-1]&.split&.last
|
||||||
tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
|
tmux.prepare
|
||||||
tmux.send_keys 'sleep12345'
|
tmux.send_keys 'C-L'
|
||||||
tmux.until { |lines| assert lines.any_include?('sleep 12345') }
|
tmux.send_keys "kill #{trigger}", :Tab
|
||||||
tmux.send_keys :Enter
|
tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
|
||||||
tmux.until(true) { |lines| assert_equal "kill #{pid}", lines[-1] }
|
tmux.send_keys 'sleep12345'
|
||||||
ensure
|
tmux.until { |lines| assert lines.any_include?('sleep 12345') }
|
||||||
if pid
|
tmux.send_keys :Enter
|
||||||
begin
|
tmux.until(true) { |lines| assert_equal "kill #{pid}", lines[-1] }
|
||||||
Process.kill('KILL', pid.to_i)
|
ensure
|
||||||
rescue StandardError
|
if pid
|
||||||
nil
|
begin
|
||||||
|
Process.kill('KILL', pid.to_i)
|
||||||
|
rescue StandardError
|
||||||
|
nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_custom_completion
|
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.send_keys '_fzf_compgen_path() { echo "$1"; seq 10; }', :Enter
|
||||||
tmux.prepare
|
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.until { |lines| assert_equal 11, lines.match_count }
|
||||||
tmux.send_keys :Tab, :Tab, :Tab
|
tmux.send_keys :Tab, :Tab, :Tab
|
||||||
tmux.until { |lines| assert_equal 3, lines.select_count }
|
tmux.until { |lines| assert_equal 3, lines.select_count }
|
||||||
@@ -295,11 +345,12 @@ module CompletionTest
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_unset_completion
|
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.send_keys 'export FZFFOOBAR=BAZ', :Enter
|
||||||
tmux.prepare
|
tmux.prepare
|
||||||
|
|
||||||
# Using tmux
|
# 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.until { |lines| assert_equal 1, lines.match_count }
|
||||||
tmux.send_keys :Enter
|
tmux.send_keys :Enter
|
||||||
tmux.until { |lines| assert_equal 'unset FZFFOOBAR', lines[-1] }
|
tmux.until { |lines| assert_equal 'unset FZFFOOBAR', lines[-1] }
|
||||||
@@ -308,34 +359,57 @@ module CompletionTest
|
|||||||
# FZF_TMUX=1
|
# FZF_TMUX=1
|
||||||
new_shell
|
new_shell
|
||||||
tmux.focus
|
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.until { |lines| assert_equal 1, lines.match_count }
|
||||||
tmux.send_keys :Enter
|
tmux.send_keys :Enter
|
||||||
tmux.until { |lines| assert_equal 'unset FZFFOOBAR', lines[-1] }
|
tmux.until { |lines| assert_equal 'unset FZFFOOBAR', lines[-1] }
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_completion_in_command_sequence
|
def test_completion_in_command_sequence
|
||||||
tmux.send_keys 'export FZFFOOBAR=BAZ', :Enter
|
if shell == :fish
|
||||||
tmux.prepare
|
FileUtils.mkdir_p('/tmp/fzf-test-seq')
|
||||||
|
FileUtils.touch('/tmp/fzf-test-seq/fzffoobar')
|
||||||
triggers = ['**', '~~', '++', 'ff', '/']
|
tmux.prepare
|
||||||
triggers.push('&', '[', ';', '`') if instance_of?(TestZsh)
|
# Fish uses Shift-Tab for fzf completion (no trigger system)
|
||||||
|
command = 'echo foo; QUX=THUD ls /tmp/fzf-test-seq/fzffoobr'
|
||||||
triggers.each do |trigger|
|
expected = 'echo foo; QUX=THUD ls /tmp/fzf-test-seq/fzffoobar'
|
||||||
set_var('FZF_COMPLETION_TRIGGER', trigger)
|
tmux.send_keys command, :BTab
|
||||||
command = "echo foo; QUX=THUD unset FZFFOOBR#{trigger}"
|
|
||||||
tmux.send_keys command.sub(/(;|`)$/, '\\\\\1'), :Tab
|
|
||||||
tmux.until { |lines| assert_equal 1, lines.match_count }
|
tmux.until { |lines| assert_equal 1, lines.match_count }
|
||||||
tmux.send_keys :Enter
|
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
|
end
|
||||||
|
ensure
|
||||||
|
FileUtils.rm_rf('/tmp/fzf-test-seq') if shell == :fish
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_file_completion_unicode
|
def test_file_completion_unicode
|
||||||
FileUtils.mkdir_p('/tmp/fzf-test')
|
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.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.until { |lines| assert_equal 2, lines.match_count }
|
||||||
|
|
||||||
tmux.send_keys '1'
|
tmux.send_keys '1'
|
||||||
@@ -358,36 +432,41 @@ module CompletionTest
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_custom_completion_api
|
def test_custom_completion_api
|
||||||
tmux.send_keys 'eval "_fzf$(declare -f _comprun)"', :Enter
|
skip('bash-specific _comprun/declare syntax') if shell == :fish
|
||||||
%w[f g].each do |command|
|
|
||||||
tmux.prepare
|
begin
|
||||||
tmux.send_keys "#{command} b**", :Tab
|
tmux.send_keys 'eval "_fzf$(declare -f _comprun)"', :Enter
|
||||||
tmux.until do |lines|
|
%w[f g].each do |command|
|
||||||
assert_equal 2, lines.item_count
|
tmux.prepare
|
||||||
assert_equal 1, lines.match_count
|
tmux.send_keys "#{command} b#{trigger}", :Tab
|
||||||
assert lines.any_include?("prompt-#{command}")
|
tmux.until do |lines|
|
||||||
assert lines.any_include?("preview-#{command}-bar")
|
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
|
end
|
||||||
tmux.send_keys :Enter
|
ensure
|
||||||
tmux.until { |lines| assert_equal "#{command} #{command}barbar", lines[-1] }
|
tmux.prepare
|
||||||
tmux.send_keys 'C-u'
|
tmux.send_keys 'unset -f _fzf_comprun', :Enter
|
||||||
end
|
end
|
||||||
ensure
|
|
||||||
tmux.prepare
|
|
||||||
tmux.send_keys 'unset -f _fzf_comprun', :Enter
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_ssh_completion
|
def test_ssh_completion
|
||||||
|
skip('fish uses native ssh completion') if shell == :fish
|
||||||
(1..5).each { |i| FileUtils.touch("/tmp/fzf-test-ssh-#{i}") }
|
(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|
|
tmux.until do |lines|
|
||||||
assert_operator lines.match_count, :>=, 1
|
assert_operator lines.match_count, :>=, 1
|
||||||
end
|
end
|
||||||
|
|
||||||
tmux.send_keys :Enter
|
tmux.send_keys :Enter
|
||||||
tmux.until { |lines| assert lines.any_include?('ssh jg@localhost') }
|
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|
|
tmux.until do |lines|
|
||||||
assert_operator lines.match_count, :>=, 5
|
assert_operator lines.match_count, :>=, 5
|
||||||
assert_equal 0, lines.select_count
|
assert_equal 0, lines.select_count
|
||||||
@@ -399,11 +478,344 @@ module CompletionTest
|
|||||||
tmux.send_keys :Enter
|
tmux.send_keys :Enter
|
||||||
tmux.until { |lines| assert lines.any_include?('ssh jg@localhost -i /tmp/fzf-test-ssh-') }
|
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|
|
tmux.until do |lines|
|
||||||
assert_operator lines.match_count, :>=, 1
|
assert_operator lines.match_count, :>=, 1
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
class TestBash < TestBase
|
class TestBash < TestBase
|
||||||
@@ -424,7 +836,7 @@ class TestBash < TestBase
|
|||||||
tmux.paste 'touch /tmp/foo; _fzf_completion_loader=1'
|
tmux.paste 'touch /tmp/foo; _fzf_completion_loader=1'
|
||||||
tmux.paste '_completion_loader() { complete -o default fake; }'
|
tmux.paste '_completion_loader() { complete -o default fake; }'
|
||||||
tmux.paste 'complete -F _fzf_path_completion -o default -o bashdefault 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.until { |lines| assert_operator lines.match_count, :>, 0 }
|
||||||
tmux.send_keys 'C-c'
|
tmux.send_keys 'C-c'
|
||||||
|
|
||||||
@@ -433,7 +845,7 @@ class TestBash < TestBase
|
|||||||
tmux.send_keys :Tab, 'C-u'
|
tmux.send_keys :Tab, 'C-u'
|
||||||
|
|
||||||
tmux.prepare
|
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 }
|
tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -455,7 +867,7 @@ class TestZsh < TestBase
|
|||||||
tmux.send_keys 'export FZFFOOBAR=BAZ', :Enter
|
tmux.send_keys 'export FZFFOOBAR=BAZ', :Enter
|
||||||
['unset', '\unset', "'unset'"].each do |command|
|
['unset', '\unset', "'unset'"].each do |command|
|
||||||
tmux.prepare
|
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.until { |lines| assert_equal 1, lines.match_count }
|
||||||
tmux.send_keys :Enter
|
tmux.send_keys :Enter
|
||||||
tmux.until { |lines| assert_equal "#{command} FZFFOOBAR", lines[-1] }
|
tmux.until { |lines| assert_equal "#{command} FZFFOOBAR", lines[-1] }
|
||||||
@@ -579,13 +991,18 @@ end
|
|||||||
|
|
||||||
class TestFish < TestBase
|
class TestFish < TestBase
|
||||||
include TestShell
|
include TestShell
|
||||||
|
include CompletionTest
|
||||||
|
|
||||||
def shell
|
def shell
|
||||||
:fish
|
:fish
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def trigger
|
||||||
|
'++'
|
||||||
|
end
|
||||||
|
|
||||||
def new_shell
|
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.send_keys 'function fish_prompt; end; clear', :Enter
|
||||||
tmux.until { |lines| assert_empty lines }
|
tmux.until { |lines| assert_empty lines }
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ for opt in "$@"; do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
|
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||||
|
fzf_base=$(pwd)
|
||||||
|
|
||||||
ask() {
|
ask() {
|
||||||
while true; do
|
while true; do
|
||||||
read -p "$1 ([y]/n) " -r
|
read -p "$1 ([y]/n) " -r
|
||||||
@@ -94,12 +97,15 @@ done
|
|||||||
bind_file="${fish_dir}/functions/fish_user_key_bindings.fish"
|
bind_file="${fish_dir}/functions/fish_user_key_bindings.fish"
|
||||||
if [ -f "$bind_file" ]; then
|
if [ -f "$bind_file" ]; then
|
||||||
remove_line "$bind_file" "fzf_key_bindings"
|
remove_line "$bind_file" "fzf_key_bindings"
|
||||||
|
remove_line "$bind_file" "fzf_completion_setup"
|
||||||
remove_line "$bind_file" "fzf --fish | source"
|
remove_line "$bind_file" "fzf --fish | source"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -d "${fish_dir}/functions" ]; then
|
if [ -d "${fish_dir}/functions" ]; then
|
||||||
remove "${fish_dir}/functions/fzf.fish"
|
remove "${fish_dir}/functions/fzf.fish"
|
||||||
remove "${fish_dir}/functions/fzf_key_bindings.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
|
if [ -z "$(ls -A "${fish_dir}/functions")" ]; then
|
||||||
rmdir "${fish_dir}/functions"
|
rmdir "${fish_dir}/functions"
|
||||||
|
|||||||
Reference in New Issue
Block a user