mirror of
https://github.com/junegunn/fzf
synced 2024-11-06 21:20:28 +00:00
b44ab9e33c
And remove the short preview window for showing the whole command. Because it is important to be able to see the whole command before deciding to kill it.
377 lines
12 KiB
Bash
377 lines
12 KiB
Bash
# ____ ____
|
|
# / __/___ / __/
|
|
# / /_/_ / / /_
|
|
# / __/ / /_/ __/
|
|
# /_/ /___/_/ completion.zsh
|
|
#
|
|
# - $FZF_TMUX (default: 0)
|
|
# - $FZF_TMUX_OPTS (default: empty)
|
|
# - $FZF_COMPLETION_TRIGGER (default: '**')
|
|
# - $FZF_COMPLETION_OPTS (default: empty)
|
|
# - $FZF_COMPLETION_PATH_OPTS (default: empty)
|
|
# - $FZF_COMPLETION_DIR_OPTS (default: empty)
|
|
|
|
|
|
# Both branches of the following `if` do the same thing -- define
|
|
# __fzf_completion_options such that `eval $__fzf_completion_options` sets
|
|
# all options to the same values they currently have. We'll do just that at
|
|
# the bottom of the file after changing options to what we prefer.
|
|
#
|
|
# IMPORTANT: Until we get to the `emulate` line, all words that *can* be quoted
|
|
# *must* be quoted in order to prevent alias expansion. In addition, code must
|
|
# be written in a way works with any set of zsh options. This is very tricky, so
|
|
# careful when you change it.
|
|
#
|
|
# Start by loading the builtin zsh/parameter module. It provides `options`
|
|
# associative array that stores current shell options.
|
|
if 'zmodload' 'zsh/parameter' 2>'/dev/null' && (( ${+options} )); then
|
|
# This is the fast branch and it gets taken on virtually all Zsh installations.
|
|
#
|
|
# ${(kv)options[@]} expands to array of keys (option names) and values ("on"
|
|
# or "off"). The subsequent expansion# with (j: :) flag joins all elements
|
|
# together separated by spaces. __fzf_completion_options ends up with a value
|
|
# like this: "options=(shwordsplit off aliases on ...)".
|
|
__fzf_completion_options="options=(${(j: :)${(kv)options[@]}})"
|
|
else
|
|
# This branch is much slower because it forks to get the names of all
|
|
# zsh options. It's possible to eliminate this fork but it's not worth the
|
|
# trouble because this branch gets taken only on very ancient or broken
|
|
# zsh installations.
|
|
() {
|
|
# That `()` above defines an anonymous function. This is essentially a scope
|
|
# for local parameters. We use it to avoid polluting global scope.
|
|
'local' '__fzf_opt'
|
|
__fzf_completion_options="setopt"
|
|
# `set -o` prints one line for every zsh option. Each line contains option
|
|
# name, some spaces, and then either "on" or "off". We just want option names.
|
|
# Expansion with (@f) flag splits a string into lines. The outer expansion
|
|
# removes spaces and everything that follow them on every line. __fzf_opt
|
|
# ends up iterating over option names: shwordsplit, aliases, etc.
|
|
for __fzf_opt in "${(@)${(@f)$(set -o)}%% *}"; do
|
|
if [[ -o "$__fzf_opt" ]]; then
|
|
# Option $__fzf_opt is currently on, so remember to set it back on.
|
|
__fzf_completion_options+=" -o $__fzf_opt"
|
|
else
|
|
# Option $__fzf_opt is currently off, so remember to set it back off.
|
|
__fzf_completion_options+=" +o $__fzf_opt"
|
|
fi
|
|
done
|
|
# The value of __fzf_completion_options here looks like this:
|
|
# "setopt +o shwordsplit -o aliases ..."
|
|
}
|
|
fi
|
|
|
|
# Enable the default zsh options (those marked with <Z> in `man zshoptions`)
|
|
# but without `aliases`. Aliases in functions are expanded when functions are
|
|
# defined, so if we disable aliases here, we'll be sure to have no pesky
|
|
# aliases in any of our functions. This way we won't need prefix every
|
|
# command with `command` or to quote every word to defend against global
|
|
# aliases. Note that `aliases` is not the only option that's important to
|
|
# control. There are several others that could wreck havoc if they are set
|
|
# to values we don't expect. With the following `emulate` command we
|
|
# sidestep this issue entirely.
|
|
'builtin' 'emulate' 'zsh' && 'builtin' 'setopt' 'no_aliases'
|
|
|
|
# This brace is the start of try-always block. The `always` part is like
|
|
# `finally` in lesser languages. We use it to *always* restore user options.
|
|
{
|
|
# The 'emulate' command should not be placed inside the interactive if check;
|
|
# placing it there fails to disable alias expansion. See #3731.
|
|
if [[ -o interactive ]]; then
|
|
|
|
# To use custom commands instead of find, override _fzf_compgen_{path,dir}
|
|
#
|
|
# _fzf_compgen_path() {
|
|
# echo "$1"
|
|
# command find -L "$1" \
|
|
# -name .git -prune -o -name .hg -prune -o -name .svn -prune -o \( -type d -o -type f -o -type l \) \
|
|
# -a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@'
|
|
# }
|
|
#
|
|
# _fzf_compgen_dir() {
|
|
# command find -L "$1" \
|
|
# -name .git -prune -o -name .hg -prune -o -name .svn -prune -o -type d \
|
|
# -a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@'
|
|
# }
|
|
|
|
###########################################################
|
|
|
|
__fzf_defaults() {
|
|
# $1: Prepend to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS
|
|
# $2: Append to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS
|
|
echo "--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore $1"
|
|
command cat "${FZF_DEFAULT_OPTS_FILE-}" 2> /dev/null
|
|
echo "${FZF_DEFAULT_OPTS-} $2"
|
|
}
|
|
|
|
__fzf_comprun() {
|
|
if [[ "$(type _fzf_comprun 2>&1)" =~ function ]]; then
|
|
_fzf_comprun "$@"
|
|
elif [ -n "${TMUX_PANE-}" ] && { [ "${FZF_TMUX:-0}" != 0 ] || [ -n "${FZF_TMUX_OPTS-}" ]; }; then
|
|
shift
|
|
if [ -n "${FZF_TMUX_OPTS-}" ]; then
|
|
fzf-tmux ${(Q)${(Z+n+)FZF_TMUX_OPTS}} -- "$@"
|
|
else
|
|
fzf-tmux -d ${FZF_TMUX_HEIGHT:-40%} -- "$@"
|
|
fi
|
|
else
|
|
shift
|
|
fzf "$@"
|
|
fi
|
|
}
|
|
|
|
# Extract the name of the command. e.g. foo=1 bar baz**<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]}"
|
|
}
|
|
|
|
__fzf_generic_path_completion() {
|
|
local base lbuf cmd compgen fzf_opts suffix tail dir leftover matches
|
|
base=$1
|
|
lbuf=$2
|
|
cmd=$(__fzf_extract_command "$lbuf")
|
|
compgen=$3
|
|
fzf_opts=$4
|
|
suffix=$5
|
|
tail=$6
|
|
|
|
setopt localoptions nonomatch
|
|
if [[ $base = *'$('* ]] || [[ $base = *'<('* ]] || [[ $base = *'>('* ]] || [[ $base = *':='* ]] || [[ $base = *'`'* ]]; then
|
|
return
|
|
fi
|
|
eval "base=$base" 2> /dev/null || return
|
|
[[ $base = *"/"* ]] && dir="$base"
|
|
while [ 1 ]; do
|
|
if [[ -z "$dir" || -d ${dir} ]]; then
|
|
leftover=${base/#"$dir"}
|
|
leftover=${leftover/#\/}
|
|
[ -z "$dir" ] && dir='.'
|
|
[ "$dir" != "/" ] && dir="${dir/%\//}"
|
|
matches=$(
|
|
export FZF_DEFAULT_OPTS
|
|
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"
|
|
else
|
|
if [[ $compgen =~ dir ]]; then
|
|
walker=dir,follow
|
|
rest=${FZF_COMPLETION_DIR_OPTS-}
|
|
else
|
|
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
|
|
fi | while read -r item; do
|
|
item="${item%$suffix}$suffix"
|
|
echo -n -E "${(q)item} "
|
|
done
|
|
)
|
|
matches=${matches% }
|
|
if [ -n "$matches" ]; then
|
|
LBUFFER="$lbuf$matches$tail"
|
|
fi
|
|
zle reset-prompt
|
|
break
|
|
fi
|
|
dir=$(dirname "$dir")
|
|
dir=${dir%/}/
|
|
done
|
|
}
|
|
|
|
_fzf_path_completion() {
|
|
__fzf_generic_path_completion "$1" "$2" _fzf_compgen_path \
|
|
"-m" "" " "
|
|
}
|
|
|
|
_fzf_dir_completion() {
|
|
__fzf_generic_path_completion "$1" "$2" _fzf_compgen_dir \
|
|
"" "/" ""
|
|
}
|
|
|
|
_fzf_feed_fifo() {
|
|
command rm -f "$1"
|
|
mkfifo "$1"
|
|
cat <&0 > "$1" &|
|
|
}
|
|
|
|
_fzf_complete() {
|
|
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-$$"
|
|
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' ' ')
|
|
if [ -n "$matches" ]; then
|
|
LBUFFER="$lbuf$matches"
|
|
fi
|
|
command rm -f "$fifo"
|
|
}
|
|
|
|
# To use custom hostname lists, override __fzf_list_hosts.
|
|
# The function is expected to print hostnames, one per line as well as in the
|
|
# desired sorting and with any duplicates removed, to standard output.
|
|
if ! declare -f __fzf_list_hosts > /dev/null; then
|
|
__fzf_list_hosts() {
|
|
setopt localoptions nonomatch
|
|
command cat <(command tail -n +1 ~/.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 2> /dev/null | tr ',' '\n' | tr -d '[' | awk '{ print $1 " " $1 }') \
|
|
<(command grep -v '^\s*\(#\|$\)' /etc/hosts 2> /dev/null | command grep -Fv '0.0.0.0' | command sed 's/#.*//') |
|
|
awk '{for (i = 2; i <= NF; i++) print $i}' | sort -u
|
|
}
|
|
fi
|
|
|
|
_fzf_complete_telnet() {
|
|
_fzf_complete +m -- "$@" < <(__fzf_list_hosts)
|
|
}
|
|
|
|
# The first and the only argument is the LBUFFER without the current word that contains the trigger.
|
|
# The current word without the trigger is in the $prefix variable passed from the caller.
|
|
_fzf_complete_ssh() {
|
|
local -a tokens
|
|
tokens=(${(z)1})
|
|
case ${tokens[-1]} in
|
|
-i|-F|-E)
|
|
_fzf_path_completion "$prefix" "$1"
|
|
;;
|
|
*)
|
|
local user
|
|
[[ $prefix =~ @ ]] && user="${prefix%%@*}@"
|
|
_fzf_complete +m -- "$@" < <(__fzf_list_hosts | awk -v user="$user" '{print user $0}')
|
|
;;
|
|
esac
|
|
}
|
|
|
|
_fzf_complete_export() {
|
|
_fzf_complete -m -- "$@" < <(
|
|
declare -xp | sed 's/=.*//' | sed 's/.* //'
|
|
)
|
|
}
|
|
|
|
_fzf_complete_unset() {
|
|
_fzf_complete -m -- "$@" < <(
|
|
declare -xp | sed 's/=.*//' | sed 's/.* //'
|
|
)
|
|
}
|
|
|
|
_fzf_complete_unalias() {
|
|
_fzf_complete +m -- "$@" < <(
|
|
alias | sed 's/=.*//'
|
|
)
|
|
}
|
|
|
|
_fzf_complete_kill() {
|
|
_fzf_complete -m --header-lines=1 --no-preview --wrap -- "$@" < <(
|
|
command ps -eo user,pid,ppid,start,time,command 2> /dev/null ||
|
|
command ps -eo user,pid,ppid,time,args # For BusyBox
|
|
)
|
|
}
|
|
|
|
_fzf_complete_kill_post() {
|
|
awk '{print $2}'
|
|
}
|
|
|
|
fzf-completion() {
|
|
local tokens cmd prefix trigger tail matches lbuf d_cmds
|
|
setopt localoptions noshwordsplit noksh_arrays noposixbuiltins
|
|
|
|
# http://zsh.sourceforge.net/FAQ/zshfaq03.html
|
|
# http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion-Flags
|
|
tokens=(${(z)LBUFFER})
|
|
if [ ${#tokens} -lt 1 ]; then
|
|
zle ${fzf_default_completion:-expand-or-complete}
|
|
return
|
|
fi
|
|
|
|
cmd=$(__fzf_extract_command "$LBUFFER")
|
|
|
|
# Explicitly allow for empty trigger.
|
|
trigger=${FZF_COMPLETION_TRIGGER-'**'}
|
|
[ -z "$trigger" -a ${LBUFFER[-1]} = ' ' ] && tokens+=("")
|
|
|
|
# When the trigger starts with ';', it becomes a separate token
|
|
if [[ ${LBUFFER} = *"${tokens[-2]-}${tokens[-1]}" ]]; then
|
|
tokens[-2]="${tokens[-2]-}${tokens[-1]}"
|
|
tokens=(${tokens[0,-2]})
|
|
fi
|
|
|
|
lbuf=$LBUFFER
|
|
tail=${LBUFFER:$(( ${#LBUFFER} - ${#trigger} ))}
|
|
|
|
# Trigger sequence given
|
|
if [ ${#tokens} -gt 1 -a "$tail" = "$trigger" ]; then
|
|
d_cmds=(${=FZF_COMPLETION_DIR_COMMANDS-cd pushd rmdir})
|
|
|
|
[ -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}
|
|
zle reset-prompt
|
|
elif [ ${d_cmds[(i)$cmd]} -le ${#d_cmds} ]; then
|
|
_fzf_dir_completion "$prefix" "$lbuf"
|
|
else
|
|
_fzf_path_completion "$prefix" "$lbuf"
|
|
fi
|
|
# Fall back to default completion
|
|
else
|
|
zle ${fzf_default_completion:-expand-or-complete}
|
|
fi
|
|
}
|
|
|
|
[ -z "$fzf_default_completion" ] && {
|
|
binding=$(bindkey '^I')
|
|
[[ $binding =~ 'undefined-key' ]] || fzf_default_completion=$binding[(s: :w)2]
|
|
unset binding
|
|
}
|
|
|
|
zle -N fzf-completion
|
|
bindkey '^I' fzf-completion
|
|
fi
|
|
|
|
} always {
|
|
# Restore the original options.
|
|
eval $__fzf_completion_options
|
|
'unset' '__fzf_completion_options'
|
|
}
|