diff --git a/Makefile b/Makefile index c78e7201e..8889d77dd 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ libdir = $(prefix)/lib/uftrace etcdir = $(prefix)/etc mandir = $(prefix)/share/man docdir = $(srcdir)/doc -completiondir = $(etcdir)/bash_completion.d +completiondir = $(compldir) ifeq ($(DOCLANG), ko) docdir = $(srcdir)/doc/ko diff --git a/configure b/configure index fe48e6305..86561386f 100755 --- a/configure +++ b/configure @@ -146,6 +146,17 @@ libdir=${libdir:-${prefix}/lib/uftrace} etcdir=${etcdir:-${prefix}/etc} mandir=${mandir:-${prefix}/share/man} +if [ -z "$compldir" ]; then + if command -v pkg-config >/dev/null 2>&1 && pkg-config --exists bash-completion; then + compldir=$(pkg-config --variable=completionsdir bash-completion) + if [[ "$compldir" == /usr/share/* ]] && [ "$prefix" != "/usr" ]; then + compldir="${prefix}/share/bash-completion/completions" + fi + else + compldir=${prefix}/share/bash-completion/completions + fi +fi + if [ "$etcdir" = /usr/etc ]; then etcdir=/etc fi @@ -280,6 +291,7 @@ override bindir := $bindir override libdir := $libdir override mandir := $mandir override etcdir := $etcdir +override compldir := $compldir EOF if [ ! -z $with_elfutils ]; then diff --git a/misc/bash-completion.sh b/misc/bash-completion.sh index 236d44669..2d1fb134b 100644 --- a/misc/bash-completion.sh +++ b/misc/bash-completion.sh @@ -1,35 +1,58 @@ -_uftrace () { - local cur prev subcmds options uftrace_comp +_uftrace() +{ + local CMD=${COMP_WORDS[0]} + local CUR=${COMP_WORDS[COMP_CWORD]} + local PREV=${COMP_WORDS[COMP_CWORD - 1]} + [[ $PREV = "=" ]] && PREV=${COMP_WORDS[COMP_CWORD-2]} + local IFS=$' \t\n' WORDS OPTS=() OPT1=() OPT2=() TMP i + local COMP_LINE2=${COMP_LINE:0:$COMP_POINT} + [[ ${COMP_LINE2: -1} = " " && -n $CUR ]] && CUR="" - cur=${COMP_WORDS[COMP_CWORD]} - prev=${COMP_WORDS[COMP_CWORD - 1]} + IFS=$'\n' + for TMP in $( $CMD --help | sed -En '/^\s+-/{ s/^\s{,10}((-\w),?\s)?(--[[:alnum:]-]+=?)?.*/ \2 \3/p }') + do + if [[ $TMP =~ "=" ]]; then + TMP=${TMP/=/} OPT1+=( ${TMP// /$'\n'} ) + else + OPT2+=( ${TMP// /$'\n'} ) + fi + done + unset IFS - COMPREPLY=() - - subcmds='record replay report live dump graph info recv script tui' - options=$(uftrace -h | awk '$1 ~ /--[a-z]/ { split($1, r, "="); print r[1] } \ - $2 ~ /--[a-z]/ { split($2, r, "="); print r[1] }') - demangle='full simple no' - sort_key='total self call avg min max' - - uftrace_comp="${subcmds} ${options}" - - case $prev in - -d|--data|--diff|-L|--libmcount-path) - # complete directory name - COMPREPLY=($(compgen -d -- "${cur}")) - ;; - --demangle) - COMPREPLY=($(compgen -W "${demangle}" -- "${cur}")) - ;; - -s|--sort) - COMPREPLY=($(compgen -W "${sort_key}" -- "${cur}")) - ;; - *) - # complete subcommand, long option or (executable) filename - COMPREPLY=($(compgen -f -W "${uftrace_comp}" -- "${cur}")) - ;; - esac - return 0 + OPTS=( "${OPT1[@]}" "${OPT2[@]}" ) + if [[ $CUR =~ ^-- ]]; then + WORDS=${OPTS[@]/#-[^-]*/} + elif [[ $CUR =~ ^- ]]; then + WORDS=${OPTS[@]/#--*/}" -?" + else + if [[ $PREV = @(-d|--data|--diff|-L|--libmcount-path) ]]; then + COMPREPLY=( $(compgen -d -- "$CUR") ) + return + elif [[ $PREV = --color ]]; then + WORDS="yes no auto" + elif [[ $PREV = --demangle ]]; then + WORDS="full simple no" + elif [[ $PREV = --match ]]; then + WORDS="regex glob" + elif [[ $PREV = @(-s|--sort) ]]; then + WORDS="total self call avg min max" + else + WORDS="record replay live report info dump recv graph script tui" + if printf '%s\n' "${OPT1[@]}" | grep -xq -- "$PREV" || + [[ $PREV = @(,|@) ]] || [[ $CUR = @(,|@) ]]; then + WORDS="" + else + for (( i = 1; i < ${#COMP_WORDS[@]}; i++ )); do + for TMP in $WORDS; do + [[ ${COMP_WORDS[i]} = $TMP ]] && WORDS="" + done + done + fi + fi + fi + [[ $CUR = "=" ]] && CUR="" + COMPREPLY=( $(compgen -W "$WORDS" -- "$CUR") ) + [ "${COMPREPLY: -1}" = "=" ] && compopt -o nospace } -complete -o filenames -F _uftrace uftrace + +complete -o default -o bashdefault -F _uftrace uftrace