mirror of
https://github.com/Llewellynvdm/fzf.git
synced 2024-11-22 21:05:09 +00:00
Change custom fuzzy completion API
To make it easier to write more complex fzf options. Although this does not break backward compatibility, users are encouraged to update their code accordingly. # Before _fzf_complete "FZF_ARG1 FZF_ARG2..." "$@" < <( # Print candidates ) # After _fzf_complete FZF_ARG1 FZF_ARG2... -- "$@" < <( # Print candidates )
This commit is contained in:
parent
7085e5b629
commit
50b7608f9d
14
CHANGELOG.md
14
CHANGELOG.md
@ -14,6 +14,20 @@ CHANGELOG
|
|||||||
- Bug fixes and improvements
|
- Bug fixes and improvements
|
||||||
- Vim plugin: Floating windows support
|
- Vim plugin: Floating windows support
|
||||||
- bash: Various improvements in key bindings (CTRL-T, CTRL-R, ALT-C)
|
- bash: Various improvements in key bindings (CTRL-T, CTRL-R, ALT-C)
|
||||||
|
- Fuzzy completion API changed
|
||||||
|
```sh
|
||||||
|
# Previous: fzf arguments given as a single string argument
|
||||||
|
# - This style is still supported, but it is deprecated
|
||||||
|
_fzf_complete "--multi --reverse --prompt=\"doge> \"" "$@" < <(
|
||||||
|
echo foo
|
||||||
|
)
|
||||||
|
|
||||||
|
# New API: multiple fzf arguments before "--"
|
||||||
|
# - More rebust and easier to write options
|
||||||
|
_fzf_complete --multi --reverse --prompt="doge> " -- "$@" < <(
|
||||||
|
echo foo
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
0.20.0
|
0.20.0
|
||||||
------
|
------
|
||||||
|
53
README.md
53
README.md
@ -47,6 +47,7 @@ Table of Contents
|
|||||||
* [Environment variables / Aliases](#environment-variables--aliases)
|
* [Environment variables / Aliases](#environment-variables--aliases)
|
||||||
* [Settings](#settings)
|
* [Settings](#settings)
|
||||||
* [Supported commands](#supported-commands)
|
* [Supported commands](#supported-commands)
|
||||||
|
* [Custom fuzzy completion](#custom-fuzzy-completion)
|
||||||
* [Vim plugin](#vim-plugin)
|
* [Vim plugin](#vim-plugin)
|
||||||
* [Advanced topics](#advanced-topics)
|
* [Advanced topics](#advanced-topics)
|
||||||
* [Performance](#performance)
|
* [Performance](#performance)
|
||||||
@ -414,7 +415,7 @@ _fzf_comprun() {
|
|||||||
|
|
||||||
case "$command" in
|
case "$command" in
|
||||||
cd) fzf "$@" --preview 'tree -C {} | head -200' ;;
|
cd) fzf "$@" --preview 'tree -C {} | head -200' ;;
|
||||||
export|unset) fzf "$@" --preview "eval 'echo \$'{}" "$@" ;;
|
export|unset) fzf "$@" --preview "eval 'echo \$'{}" ;;
|
||||||
ssh) fzf "$@" --preview 'dig {}' ;;
|
ssh) fzf "$@" --preview 'dig {}' ;;
|
||||||
*) fzf "$@" ;;
|
*) fzf "$@" ;;
|
||||||
esac
|
esac
|
||||||
@ -433,6 +434,56 @@ _fzf_setup_completion path ag git kubectl
|
|||||||
_fzf_setup_completion dir tree
|
_fzf_setup_completion dir tree
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Custom fuzzy completion
|
||||||
|
|
||||||
|
_**(Custom completion API is experimental and subject to change)**_
|
||||||
|
|
||||||
|
For a command named _"COMMAND"_, define `_fzf_complete_COMMAND` function using
|
||||||
|
`_fzf_complete` helper.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Custom fuzzy completion for "doge" command
|
||||||
|
# e.g. doge **<TAB>
|
||||||
|
_fzf_complete_doge() {
|
||||||
|
_fzf_complete --multi --reverse --prompt="doge> " -- "$@" < <(
|
||||||
|
echo very
|
||||||
|
echo wow
|
||||||
|
echo such
|
||||||
|
echo doge
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- The arguments before `--` are the options to fzf.
|
||||||
|
- After `--`, simply pass the original completion arguments unchanged (`"$@"`).
|
||||||
|
- Then write a set of commands that generates the completion candidates and
|
||||||
|
feed its output to the function using process substitution (`< <(...)`).
|
||||||
|
|
||||||
|
zsh will automatically pick up the function using the naming convention but in
|
||||||
|
bash you have to manually associate the function with the command using
|
||||||
|
`complete` command.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[ -n "$BASH" ] && complete -F _fzf_complete_doge -o default -o bashdefault doge
|
||||||
|
```
|
||||||
|
|
||||||
|
If you need to post-process the output from fzf, define
|
||||||
|
`_fzf_complete_COMMAND_post` as follows.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
_fzf_complete_foo() {
|
||||||
|
_fzf_complete --multi --reverse --header-lines=3 -- "$@" < <(
|
||||||
|
ls -al
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
_fzf_complete_foo_post() {
|
||||||
|
awk '{print $NF}'
|
||||||
|
}
|
||||||
|
|
||||||
|
[ -n "$BASH" ] && complete -F _fzf_complete_foo -o default -o bashdefault foo
|
||||||
|
```
|
||||||
|
|
||||||
Vim plugin
|
Vim plugin
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
@ -190,6 +190,27 @@ __fzf_generic_path_completion() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_fzf_complete() {
|
_fzf_complete() {
|
||||||
|
# Split arguments around --
|
||||||
|
local args rest str_arg i sep
|
||||||
|
args=("$@")
|
||||||
|
sep=
|
||||||
|
for i in "${!args[@]}"; do
|
||||||
|
if [[ "${args[$i]}" = -- ]]; then
|
||||||
|
sep=$i
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ -n "$sep" ]]; then
|
||||||
|
str_arg=
|
||||||
|
rest=("${args[@]:$((sep + 1)):${#args[@]}}")
|
||||||
|
args=("${args[@]:0:$sep}")
|
||||||
|
else
|
||||||
|
str_arg=$1
|
||||||
|
args=()
|
||||||
|
shift
|
||||||
|
rest=("$@")
|
||||||
|
fi
|
||||||
|
|
||||||
local cur selected trigger cmd post
|
local cur selected trigger cmd post
|
||||||
post="$(caller 0 | awk '{print $2}')_post"
|
post="$(caller 0 | awk '{print $2}')_post"
|
||||||
type -t "$post" > /dev/null 2>&1 || post=cat
|
type -t "$post" > /dev/null 2>&1 || post=cat
|
||||||
@ -200,7 +221,7 @@ _fzf_complete() {
|
|||||||
if [[ "$cur" == *"$trigger" ]]; then
|
if [[ "$cur" == *"$trigger" ]]; then
|
||||||
cur=${cur:0:${#cur}-${#trigger}}
|
cur=${cur:0:${#cur}-${#trigger}}
|
||||||
|
|
||||||
selected=$(FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS $1" __fzf_comprun "$2" -q "$cur" | $post | tr '\n' ' ')
|
selected=$(FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS $str_arg" __fzf_comprun "${rest[0]}" "${args[@]}" -q "$cur" | $post | tr '\n' ' ')
|
||||||
selected=${selected% } # Strip trailing space not to repeat "-o nospace"
|
selected=${selected% } # Strip trailing space not to repeat "-o nospace"
|
||||||
if [ -n "$selected" ]; then
|
if [ -n "$selected" ]; then
|
||||||
COMPREPLY=("$selected")
|
COMPREPLY=("$selected")
|
||||||
@ -210,8 +231,7 @@ _fzf_complete() {
|
|||||||
printf '\e[5n'
|
printf '\e[5n'
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
shift
|
_fzf_handle_dynamic_completion "$cmd" "${rest[@]}"
|
||||||
_fzf_handle_dynamic_completion "$cmd" "$@"
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,7 +263,7 @@ _fzf_complete_kill() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_fzf_host_completion() {
|
_fzf_host_completion() {
|
||||||
_fzf_complete '+m' "$@" < <(
|
_fzf_complete +m -- "$@" < <(
|
||||||
cat <(cat ~/.ssh/config ~/.ssh/config.d/* /etc/ssh/ssh_config 2> /dev/null | command grep -i '^\s*host\(name\)\? ' | awk '{for (i = 2; i <= NF; i++) print $1 " " $i}' | command grep -v '[*?]') \
|
cat <(cat ~/.ssh/config ~/.ssh/config.d/* /etc/ssh/ssh_config 2> /dev/null | command grep -i '^\s*host\(name\)\? ' | awk '{for (i = 2; i <= NF; i++) print $1 " " $i}' | command grep -v '[*?]') \
|
||||||
<(command grep -oE '^[[a-z0-9.,:-]+' ~/.ssh/known_hosts | tr ',' '\n' | tr -d '[' | awk '{ print $1 " " $1 }') \
|
<(command grep -oE '^[[a-z0-9.,:-]+' ~/.ssh/known_hosts | tr ',' '\n' | tr -d '[' | awk '{ print $1 " " $1 }') \
|
||||||
<(command grep -v '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0') |
|
<(command grep -v '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0') |
|
||||||
@ -252,13 +272,13 @@ _fzf_host_completion() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_fzf_var_completion() {
|
_fzf_var_completion() {
|
||||||
_fzf_complete '-m' "$@" < <(
|
_fzf_complete -m -- "$@" < <(
|
||||||
declare -xp | sed 's/=.*//' | sed 's/.* //'
|
declare -xp | sed 's/=.*//' | sed 's/.* //'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
_fzf_alias_completion() {
|
_fzf_alias_completion() {
|
||||||
_fzf_complete '-m' "$@" < <(
|
_fzf_complete -m -- "$@" < <(
|
||||||
alias | sed 's/=.*//' | sed 's/.* //'
|
alias | sed 's/=.*//' | sed 's/.* //'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -107,16 +107,38 @@ _fzf_feed_fifo() (
|
|||||||
)
|
)
|
||||||
|
|
||||||
_fzf_complete() {
|
_fzf_complete() {
|
||||||
local fifo fzf_opts lbuf cmd matches post
|
setopt localoptions ksh_arrays
|
||||||
|
# Split arguments around --
|
||||||
|
local args rest str_arg i sep
|
||||||
|
args=("$@")
|
||||||
|
sep=
|
||||||
|
for i in {0..$#args}; do
|
||||||
|
if [[ "${args[$i]}" = -- ]]; then
|
||||||
|
sep=$i
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ -n "$sep" ]]; then
|
||||||
|
str_arg=
|
||||||
|
rest=("${args[@]:$((sep + 1)):${#args[@]}}")
|
||||||
|
args=("${args[@]:0:$sep}")
|
||||||
|
else
|
||||||
|
str_arg=$1
|
||||||
|
args=()
|
||||||
|
shift
|
||||||
|
rest=("$@")
|
||||||
|
fi
|
||||||
|
|
||||||
|
local fifo lbuf cmd matches post
|
||||||
fifo="${TMPDIR:-/tmp}/fzf-complete-fifo-$$"
|
fifo="${TMPDIR:-/tmp}/fzf-complete-fifo-$$"
|
||||||
fzf_opts=$1
|
lbuf=${rest[0]}
|
||||||
lbuf=$2
|
|
||||||
cmd=$(__fzf_extract_command "$lbuf")
|
cmd=$(__fzf_extract_command "$lbuf")
|
||||||
post="${funcstack[2]}_post"
|
post="${funcstack[1]}_post"
|
||||||
|
echo "$post"
|
||||||
type $post > /dev/null 2>&1 || post=cat
|
type $post > /dev/null 2>&1 || post=cat
|
||||||
|
|
||||||
_fzf_feed_fifo "$fifo"
|
_fzf_feed_fifo "$fifo"
|
||||||
matches=$(FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS" __fzf_comprun "$cmd" ${(Q)${(Z+n+)fzf_opts}} -q "${(Q)prefix}" < "$fifo" | $post | tr '\n' ' ')
|
matches=$(FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS $str_arg" __fzf_comprun "$cmd" "${args[@]}" -q "${(Q)prefix}" < "$fifo" | $post | tr '\n' ' ')
|
||||||
if [ -n "$matches" ]; then
|
if [ -n "$matches" ]; then
|
||||||
LBUFFER="$lbuf$matches"
|
LBUFFER="$lbuf$matches"
|
||||||
fi
|
fi
|
||||||
@ -125,14 +147,14 @@ _fzf_complete() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_fzf_complete_telnet() {
|
_fzf_complete_telnet() {
|
||||||
_fzf_complete '+m' "$@" < <(
|
_fzf_complete +m -- "$@" < <(
|
||||||
command grep -v '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0' |
|
command grep -v '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0' |
|
||||||
awk '{if (length($2) > 0) {print $2}}' | sort -u
|
awk '{if (length($2) > 0) {print $2}}' | sort -u
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
_fzf_complete_ssh() {
|
_fzf_complete_ssh() {
|
||||||
_fzf_complete '+m' "$@" < <(
|
_fzf_complete +m -- "$@" < <(
|
||||||
setopt localoptions nonomatch
|
setopt localoptions nonomatch
|
||||||
command cat <(cat ~/.ssh/config ~/.ssh/config.d/* /etc/ssh/ssh_config 2> /dev/null | command grep -i '^\s*host\(name\)\? ' | awk '{for (i = 2; i <= NF; i++) print $1 " " $i}' | command grep -v '[*?]') \
|
command cat <(cat ~/.ssh/config ~/.ssh/config.d/* /etc/ssh/ssh_config 2> /dev/null | command grep -i '^\s*host\(name\)\? ' | awk '{for (i = 2; i <= NF; i++) print $1 " " $i}' | command grep -v '[*?]') \
|
||||||
<(command grep -oE '^[[a-z0-9.,:-]+' ~/.ssh/known_hosts | tr ',' '\n' | tr -d '[' | awk '{ print $1 " " $1 }') \
|
<(command grep -oE '^[[a-z0-9.,:-]+' ~/.ssh/known_hosts | tr ',' '\n' | tr -d '[' | awk '{ print $1 " " $1 }') \
|
||||||
@ -142,19 +164,19 @@ _fzf_complete_ssh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_fzf_complete_export() {
|
_fzf_complete_export() {
|
||||||
_fzf_complete '-m' "$@" < <(
|
_fzf_complete -m -- "$@" < <(
|
||||||
declare -xp | sed 's/=.*//' | sed 's/.* //'
|
declare -xp | sed 's/=.*//' | sed 's/.* //'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
_fzf_complete_unset() {
|
_fzf_complete_unset() {
|
||||||
_fzf_complete '-m' "$@" < <(
|
_fzf_complete -m -- "$@" < <(
|
||||||
declare -xp | sed 's/=.*//' | sed 's/.* //'
|
declare -xp | sed 's/=.*//' | sed 's/.* //'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
_fzf_complete_unalias() {
|
_fzf_complete_unalias() {
|
||||||
_fzf_complete '+m' "$@" < <(
|
_fzf_complete +m -- "$@" < <(
|
||||||
alias | sed 's/=.*//'
|
alias | sed 's/=.*//'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -2064,6 +2064,21 @@ module CompletionTest
|
|||||||
tmux.send_keys :Enter
|
tmux.send_keys :Enter
|
||||||
tmux.until { |lines| lines[-1].include? 'test3test4' }
|
tmux.until { |lines| lines[-1].include? 'test3test4' }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_custom_completion_api
|
||||||
|
%w[f g].each do |command|
|
||||||
|
tmux.prepare
|
||||||
|
tmux.send_keys "#{command} b**", :Tab
|
||||||
|
tmux.until do |lines|
|
||||||
|
lines.item_count == 2 && lines.match_count == 1 &&
|
||||||
|
lines.any_include?("prompt-#{command}") &&
|
||||||
|
lines.any_include?("preview-#{command}-bar")
|
||||||
|
end
|
||||||
|
tmux.send_keys :Enter
|
||||||
|
tmux.until { |lines| lines[-1].include?("#{command} #{command}barbar") }
|
||||||
|
tmux.send_keys 'C-u'
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class TestBash < TestBase
|
class TestBash < TestBase
|
||||||
@ -2149,3 +2164,41 @@ source "<%= BASE %>/shell/key-bindings.<%= __method__ %>"
|
|||||||
|
|
||||||
PS1= PROMPT_COMMAND= HISTFILE= HISTSIZE=100
|
PS1= PROMPT_COMMAND= HISTFILE= HISTSIZE=100
|
||||||
unset <%= UNSETS.join(' ') %>
|
unset <%= UNSETS.join(' ') %>
|
||||||
|
|
||||||
|
# Old API
|
||||||
|
_fzf_complete_f() {
|
||||||
|
_fzf_complete "--multi --prompt \"prompt-f> \"" "$@" < <(
|
||||||
|
echo foo
|
||||||
|
echo bar
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
# New API
|
||||||
|
_fzf_complete_g() {
|
||||||
|
_fzf_complete --multi --prompt "prompt-g> " -- "$@" < <(
|
||||||
|
echo foo
|
||||||
|
echo bar
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
_fzf_complete_f_post() {
|
||||||
|
awk '{print "f" $0 $0}'
|
||||||
|
}
|
||||||
|
|
||||||
|
_fzf_complete_g_post() {
|
||||||
|
awk '{print "g" $0 $0}'
|
||||||
|
}
|
||||||
|
|
||||||
|
[ -n "$BASH" ] && complete -F _fzf_complete_f -o default -o bashdefault f
|
||||||
|
[ -n "$BASH" ] && complete -F _fzf_complete_g -o default -o bashdefault g
|
||||||
|
|
||||||
|
_fzf_comprun() {
|
||||||
|
local command=$1
|
||||||
|
shift
|
||||||
|
|
||||||
|
case "$command" in
|
||||||
|
f) fzf "$@" --preview 'echo preview-f-{}' ;;
|
||||||
|
g) fzf "$@" --preview 'echo preview-g-{}' ;;
|
||||||
|
*) fzf "$@" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user