Skip to content

Commit

Permalink
Enhance command extraction in zsh completion (#4082)
Browse files Browse the repository at this point in the history
Fix #1992
  • Loading branch information
LangLangBart authored Nov 10, 2024
1 parent 51623a5 commit 57c08d9
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 25 deletions.
53 changes: 29 additions & 24 deletions shell/completion.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -120,25 +120,19 @@ __fzf_comprun() {
fi
}

# Extract the name of the command. e.g. foo=1 bar baz**<tab>
# Extract the name of the command. e.g. ls; foo=1 ssh **<tab>
__fzf_extract_command() {
local token tokens
tokens=(${(z)1})
for token in $tokens; do
token=${(Q)token}
if [[ "$token" =~ [[:alnum:]] && ! "$token" =~ "=" ]]; then
echo "$token"
return
fi
done
echo "${tokens[1]}"
setopt localoptions noksh_arrays
# Control completion with the "compstate" parameter, insert and list noting
compstate[insert]=
compstate[list]=
cmd_word="${words[1]}"
}

__fzf_generic_path_completion() {
local base lbuf cmd compgen fzf_opts suffix tail dir leftover matches
local base lbuf compgen fzf_opts suffix tail dir leftover matches
base=$1
lbuf=$2
cmd=$(__fzf_extract_command "$lbuf")
compgen=$3
fzf_opts=$4
suffix=$5
Expand All @@ -161,7 +155,7 @@ __fzf_generic_path_completion() {
FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse --scheme=path" "${FZF_COMPLETION_OPTS-}")
unset FZF_DEFAULT_COMMAND FZF_DEFAULT_OPTS_FILE
if declare -f "$compgen" > /dev/null; then
eval "$compgen $(printf %q "$dir")" | __fzf_comprun "$cmd" ${(Q)${(Z+n+)fzf_opts}} -q "$leftover"
eval "$compgen $(printf %q "$dir")" | __fzf_comprun "$cmd_word" ${(Q)${(Z+n+)fzf_opts}} -q "$leftover"
else
if [[ $compgen =~ dir ]]; then
walker=dir,follow
Expand All @@ -170,7 +164,7 @@ __fzf_generic_path_completion() {
walker=file,dir,follow,hidden
rest=${FZF_COMPLETION_PATH_OPTS-}
fi
__fzf_comprun "$cmd" ${(Q)${(Z+n+)fzf_opts}} -q "$leftover" --walker "$walker" --walker-root="$dir" ${(Q)${(Z+n+)rest}} < /dev/tty
__fzf_comprun "$cmd_word" ${(Q)${(Z+n+)fzf_opts}} -q "$leftover" --walker "$walker" --walker-root="$dir" ${(Q)${(Z+n+)rest}} < /dev/tty
fi | while read -r item; do
item="${item%$suffix}$suffix"
echo -n -E "${(q)item} "
Expand Down Expand Up @@ -227,18 +221,17 @@ _fzf_complete() {
rest=("$@")
fi

local fifo lbuf cmd matches post
local fifo lbuf matches post
fifo="${TMPDIR:-/tmp}/fzf-complete-fifo-$$"
lbuf=${rest[0]}
cmd=$(__fzf_extract_command "$lbuf")
post="${funcstack[1]}_post"
type $post > /dev/null 2>&1 || post=cat

_fzf_feed_fifo "$fifo"
matches=$(
FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse" "${FZF_COMPLETION_OPTS-} $str_arg") \
FZF_DEFAULT_OPTS_FILE='' \
__fzf_comprun "$cmd" "${args[@]}" -q "${(Q)prefix}" < "$fifo" | $post | tr '\n' ' ')
__fzf_comprun "$cmd_word" "${args[@]}" -q "${(Q)prefix}" < "$fifo" | $post | tr '\n' ' ')
if [ -n "$matches" ]; then
LBUFFER="$lbuf$matches"
fi
Expand Down Expand Up @@ -310,9 +303,16 @@ _fzf_complete_kill_post() {
}

fzf-completion() {
local tokens cmd prefix trigger tail matches lbuf d_cmds
trap 'unset cmd_word' EXIT
local tokens prefix trigger tail matches lbuf d_cmds
setopt localoptions noshwordsplit noksh_arrays noposixbuiltins

# Check if at least one completion system (old or new) is active
if ! zmodload -F zsh/parameter p:functions 2>/dev/null || ! (( $+functions[compdef] )); then
if ! zmodload -e zsh/compctl; then
zmodload -i zsh/compctl
fi
fi
# http://zsh.sourceforge.net/FAQ/zshfaq03.html
# http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion-Flags
tokens=(${(z)LBUFFER})
Expand All @@ -321,8 +321,6 @@ fzf-completion() {
return
fi

cmd=$(__fzf_extract_command "$LBUFFER")

# Explicitly allow for empty trigger.
trigger=${FZF_COMPLETION_TRIGGER-'**'}
[ -z "$trigger" -a ${LBUFFER[-1]} = ' ' ] && tokens+=("")
Expand All @@ -340,16 +338,20 @@ fzf-completion() {
if [ ${#tokens} -gt 1 -a "$tail" = "$trigger" ]; then
d_cmds=(${=FZF_COMPLETION_DIR_COMMANDS-cd pushd rmdir})

# Make the 'cmd_word' global
zle __fzf_extract_command || :
[[ -z "$cmd_word" ]] && return

[ -z "$trigger" ] && prefix=${tokens[-1]} || prefix=${tokens[-1]:0:-${#trigger}}
if [[ $prefix = *'$('* ]] || [[ $prefix = *'<('* ]] || [[ $prefix = *'>('* ]] || [[ $prefix = *':='* ]] || [[ $prefix = *'`'* ]]; then
return
fi
[ -n "${tokens[-1]}" ] && lbuf=${lbuf:0:-${#tokens[-1]}}

if eval "type _fzf_complete_${cmd} > /dev/null"; then
prefix="$prefix" eval _fzf_complete_${cmd} ${(q)lbuf}
if eval "type _fzf_complete_${cmd_word} > /dev/null"; then
prefix="$prefix" eval _fzf_complete_${cmd_word} ${(q)lbuf}
zle reset-prompt
elif [ ${d_cmds[(i)$cmd]} -le ${#d_cmds} ]; then
elif [ ${d_cmds[(i)$cmd_word]} -le ${#d_cmds} ]; then
_fzf_dir_completion "$prefix" "$lbuf"
else
_fzf_path_completion "$prefix" "$lbuf"
Expand All @@ -366,6 +368,9 @@ fzf-completion() {
unset binding
}

# Completion widget to gain access to the 'words' array (man zshcompwid)
zle -C __fzf_extract_command .complete-word __fzf_extract_command
# Normal widget
zle -N fzf-completion
bindkey '^I' fzf-completion
fi
Expand Down
2 changes: 1 addition & 1 deletion shell/key-bindings.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ fzf-history-widget() {
# Ensure the module is loaded if not already, and the required features, such
# as the associative 'history' array, which maps event numbers to full history
# lines, are set. Also, make sure Perl is installed for multi-line output.
if zmodload -F zsh/parameter p:{commands,history} 2>/dev/null && (( ${#commands[perl]} )); then
if zmodload -F zsh/parameter p:{commands,history} 2>/dev/null && (( $+commands[perl] )); then
# Import commands from other shells if SHARE_HISTORY is enabled, as the
# 'history' array only updates after executing a non-empty command.
selected="$(
Expand Down

0 comments on commit 57c08d9

Please sign in to comment.