1*4882a593Smuzhiyun# perf bash and zsh completion 2*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0 3*4882a593Smuzhiyun 4*4882a593Smuzhiyun# Taken from git.git's completion script. 5*4882a593Smuzhiyun__my_reassemble_comp_words_by_ref() 6*4882a593Smuzhiyun{ 7*4882a593Smuzhiyun local exclude i j first 8*4882a593Smuzhiyun # Which word separators to exclude? 9*4882a593Smuzhiyun exclude="${1//[^$COMP_WORDBREAKS]}" 10*4882a593Smuzhiyun cword_=$COMP_CWORD 11*4882a593Smuzhiyun if [ -z "$exclude" ]; then 12*4882a593Smuzhiyun words_=("${COMP_WORDS[@]}") 13*4882a593Smuzhiyun return 14*4882a593Smuzhiyun fi 15*4882a593Smuzhiyun # List of word completion separators has shrunk; 16*4882a593Smuzhiyun # re-assemble words to complete. 17*4882a593Smuzhiyun for ((i=0, j=0; i < ${#COMP_WORDS[@]}; i++, j++)); do 18*4882a593Smuzhiyun # Append each nonempty word consisting of just 19*4882a593Smuzhiyun # word separator characters to the current word. 20*4882a593Smuzhiyun first=t 21*4882a593Smuzhiyun while 22*4882a593Smuzhiyun [ $i -gt 0 ] && 23*4882a593Smuzhiyun [ -n "${COMP_WORDS[$i]}" ] && 24*4882a593Smuzhiyun # word consists of excluded word separators 25*4882a593Smuzhiyun [ "${COMP_WORDS[$i]//[^$exclude]}" = "${COMP_WORDS[$i]}" ] 26*4882a593Smuzhiyun do 27*4882a593Smuzhiyun # Attach to the previous token, 28*4882a593Smuzhiyun # unless the previous token is the command name. 29*4882a593Smuzhiyun if [ $j -ge 2 ] && [ -n "$first" ]; then 30*4882a593Smuzhiyun ((j--)) 31*4882a593Smuzhiyun fi 32*4882a593Smuzhiyun first= 33*4882a593Smuzhiyun words_[$j]=${words_[j]}${COMP_WORDS[i]} 34*4882a593Smuzhiyun if [ $i = $COMP_CWORD ]; then 35*4882a593Smuzhiyun cword_=$j 36*4882a593Smuzhiyun fi 37*4882a593Smuzhiyun if (($i < ${#COMP_WORDS[@]} - 1)); then 38*4882a593Smuzhiyun ((i++)) 39*4882a593Smuzhiyun else 40*4882a593Smuzhiyun # Done. 41*4882a593Smuzhiyun return 42*4882a593Smuzhiyun fi 43*4882a593Smuzhiyun done 44*4882a593Smuzhiyun words_[$j]=${words_[j]}${COMP_WORDS[i]} 45*4882a593Smuzhiyun if [ $i = $COMP_CWORD ]; then 46*4882a593Smuzhiyun cword_=$j 47*4882a593Smuzhiyun fi 48*4882a593Smuzhiyun done 49*4882a593Smuzhiyun} 50*4882a593Smuzhiyun 51*4882a593Smuzhiyun# Define preload_get_comp_words_by_ref="false", if the function 52*4882a593Smuzhiyun# __perf_get_comp_words_by_ref() is required instead. 53*4882a593Smuzhiyunpreload_get_comp_words_by_ref="true" 54*4882a593Smuzhiyun 55*4882a593Smuzhiyunif [ $preload_get_comp_words_by_ref = "true" ]; then 56*4882a593Smuzhiyun type _get_comp_words_by_ref &>/dev/null || 57*4882a593Smuzhiyun preload_get_comp_words_by_ref="false" 58*4882a593Smuzhiyunfi 59*4882a593Smuzhiyun[ $preload_get_comp_words_by_ref = "true" ] || 60*4882a593Smuzhiyun__perf_get_comp_words_by_ref() 61*4882a593Smuzhiyun{ 62*4882a593Smuzhiyun local exclude cur_ words_ cword_ 63*4882a593Smuzhiyun if [ "$1" = "-n" ]; then 64*4882a593Smuzhiyun exclude=$2 65*4882a593Smuzhiyun shift 2 66*4882a593Smuzhiyun fi 67*4882a593Smuzhiyun __my_reassemble_comp_words_by_ref "$exclude" 68*4882a593Smuzhiyun cur_=${words_[cword_]} 69*4882a593Smuzhiyun while [ $# -gt 0 ]; do 70*4882a593Smuzhiyun case "$1" in 71*4882a593Smuzhiyun cur) 72*4882a593Smuzhiyun cur=$cur_ 73*4882a593Smuzhiyun ;; 74*4882a593Smuzhiyun prev) 75*4882a593Smuzhiyun prev=${words_[$cword_-1]} 76*4882a593Smuzhiyun ;; 77*4882a593Smuzhiyun words) 78*4882a593Smuzhiyun words=("${words_[@]}") 79*4882a593Smuzhiyun ;; 80*4882a593Smuzhiyun cword) 81*4882a593Smuzhiyun cword=$cword_ 82*4882a593Smuzhiyun ;; 83*4882a593Smuzhiyun esac 84*4882a593Smuzhiyun shift 85*4882a593Smuzhiyun done 86*4882a593Smuzhiyun} 87*4882a593Smuzhiyun 88*4882a593Smuzhiyun# Define preload__ltrim_colon_completions="false", if the function 89*4882a593Smuzhiyun# __perf__ltrim_colon_completions() is required instead. 90*4882a593Smuzhiyunpreload__ltrim_colon_completions="true" 91*4882a593Smuzhiyun 92*4882a593Smuzhiyunif [ $preload__ltrim_colon_completions = "true" ]; then 93*4882a593Smuzhiyun type __ltrim_colon_completions &>/dev/null || 94*4882a593Smuzhiyun preload__ltrim_colon_completions="false" 95*4882a593Smuzhiyunfi 96*4882a593Smuzhiyun[ $preload__ltrim_colon_completions = "true" ] || 97*4882a593Smuzhiyun__perf__ltrim_colon_completions() 98*4882a593Smuzhiyun{ 99*4882a593Smuzhiyun if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then 100*4882a593Smuzhiyun # Remove colon-word prefix from COMPREPLY items 101*4882a593Smuzhiyun local colon_word=${1%"${1##*:}"} 102*4882a593Smuzhiyun local i=${#COMPREPLY[*]} 103*4882a593Smuzhiyun while [[ $((--i)) -ge 0 ]]; do 104*4882a593Smuzhiyun COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"} 105*4882a593Smuzhiyun done 106*4882a593Smuzhiyun fi 107*4882a593Smuzhiyun} 108*4882a593Smuzhiyun 109*4882a593Smuzhiyun__perfcomp () 110*4882a593Smuzhiyun{ 111*4882a593Smuzhiyun COMPREPLY=( $( compgen -W "$1" -- "$2" ) ) 112*4882a593Smuzhiyun} 113*4882a593Smuzhiyun 114*4882a593Smuzhiyun__perfcomp_colon () 115*4882a593Smuzhiyun{ 116*4882a593Smuzhiyun __perfcomp "$1" "$2" 117*4882a593Smuzhiyun if [ $preload__ltrim_colon_completions = "true" ]; then 118*4882a593Smuzhiyun __ltrim_colon_completions $cur 119*4882a593Smuzhiyun else 120*4882a593Smuzhiyun __perf__ltrim_colon_completions $cur 121*4882a593Smuzhiyun fi 122*4882a593Smuzhiyun} 123*4882a593Smuzhiyun 124*4882a593Smuzhiyun__perf_prev_skip_opts () 125*4882a593Smuzhiyun{ 126*4882a593Smuzhiyun local i cmd_ cmds_ 127*4882a593Smuzhiyun 128*4882a593Smuzhiyun let i=cword-1 129*4882a593Smuzhiyun cmds_=$($cmd $1 --list-cmds) 130*4882a593Smuzhiyun prev_skip_opts=() 131*4882a593Smuzhiyun while [ $i -ge 0 ]; do 132*4882a593Smuzhiyun if [[ ${words[i]} == $1 ]]; then 133*4882a593Smuzhiyun return 134*4882a593Smuzhiyun fi 135*4882a593Smuzhiyun for cmd_ in $cmds_; do 136*4882a593Smuzhiyun if [[ ${words[i]} == $cmd_ ]]; then 137*4882a593Smuzhiyun prev_skip_opts=${words[i]} 138*4882a593Smuzhiyun return 139*4882a593Smuzhiyun fi 140*4882a593Smuzhiyun done 141*4882a593Smuzhiyun ((i--)) 142*4882a593Smuzhiyun done 143*4882a593Smuzhiyun} 144*4882a593Smuzhiyun 145*4882a593Smuzhiyun__perf_main () 146*4882a593Smuzhiyun{ 147*4882a593Smuzhiyun local cmd 148*4882a593Smuzhiyun 149*4882a593Smuzhiyun cmd=${words[0]} 150*4882a593Smuzhiyun COMPREPLY=() 151*4882a593Smuzhiyun 152*4882a593Smuzhiyun # Skip options backward and find the last perf command 153*4882a593Smuzhiyun __perf_prev_skip_opts 154*4882a593Smuzhiyun # List perf subcommands or long options 155*4882a593Smuzhiyun if [ -z $prev_skip_opts ]; then 156*4882a593Smuzhiyun if [[ $cur == --* ]]; then 157*4882a593Smuzhiyun cmds=$($cmd --list-opts) 158*4882a593Smuzhiyun else 159*4882a593Smuzhiyun cmds=$($cmd --list-cmds) 160*4882a593Smuzhiyun fi 161*4882a593Smuzhiyun __perfcomp "$cmds" "$cur" 162*4882a593Smuzhiyun # List possible events for -e option 163*4882a593Smuzhiyun elif [[ $prev == @("-e"|"--event") && 164*4882a593Smuzhiyun $prev_skip_opts == @(record|stat|top) ]]; then 165*4882a593Smuzhiyun 166*4882a593Smuzhiyun local cur1=${COMP_WORDS[COMP_CWORD]} 167*4882a593Smuzhiyun local raw_evts=$($cmd list --raw-dump) 168*4882a593Smuzhiyun local arr s tmp result 169*4882a593Smuzhiyun 170*4882a593Smuzhiyun if [[ "$cur1" == */* && ${cur1#*/} =~ ^[A-Z] ]]; then 171*4882a593Smuzhiyun OLD_IFS="$IFS" 172*4882a593Smuzhiyun IFS=" " 173*4882a593Smuzhiyun arr=($raw_evts) 174*4882a593Smuzhiyun IFS="$OLD_IFS" 175*4882a593Smuzhiyun 176*4882a593Smuzhiyun for s in ${arr[@]} 177*4882a593Smuzhiyun do 178*4882a593Smuzhiyun if [[ "$s" == *cpu/* ]]; then 179*4882a593Smuzhiyun tmp=${s#*cpu/} 180*4882a593Smuzhiyun result=$result" ""cpu/"${tmp^^} 181*4882a593Smuzhiyun else 182*4882a593Smuzhiyun result=$result" "$s 183*4882a593Smuzhiyun fi 184*4882a593Smuzhiyun done 185*4882a593Smuzhiyun 186*4882a593Smuzhiyun evts=${result}" "$(ls /sys/bus/event_source/devices/cpu/events) 187*4882a593Smuzhiyun else 188*4882a593Smuzhiyun evts=${raw_evts}" "$(ls /sys/bus/event_source/devices/cpu/events) 189*4882a593Smuzhiyun fi 190*4882a593Smuzhiyun 191*4882a593Smuzhiyun if [[ "$cur1" == , ]]; then 192*4882a593Smuzhiyun __perfcomp_colon "$evts" "" 193*4882a593Smuzhiyun else 194*4882a593Smuzhiyun __perfcomp_colon "$evts" "$cur1" 195*4882a593Smuzhiyun fi 196*4882a593Smuzhiyun else 197*4882a593Smuzhiyun # List subcommands for perf commands 198*4882a593Smuzhiyun if [[ $prev_skip_opts == @(kvm|kmem|mem|lock|sched| 199*4882a593Smuzhiyun |data|help|script|test|timechart|trace) ]]; then 200*4882a593Smuzhiyun subcmds=$($cmd $prev_skip_opts --list-cmds) 201*4882a593Smuzhiyun __perfcomp_colon "$subcmds" "$cur" 202*4882a593Smuzhiyun fi 203*4882a593Smuzhiyun # List long option names 204*4882a593Smuzhiyun if [[ $cur == --* ]]; then 205*4882a593Smuzhiyun subcmd=$prev_skip_opts 206*4882a593Smuzhiyun __perf_prev_skip_opts $subcmd 207*4882a593Smuzhiyun subcmd=$subcmd" "$prev_skip_opts 208*4882a593Smuzhiyun opts=$($cmd $subcmd --list-opts) 209*4882a593Smuzhiyun __perfcomp "$opts" "$cur" 210*4882a593Smuzhiyun fi 211*4882a593Smuzhiyun fi 212*4882a593Smuzhiyun} 213*4882a593Smuzhiyun 214*4882a593Smuzhiyunif [[ -n ${ZSH_VERSION-} ]]; then 215*4882a593Smuzhiyun autoload -U +X compinit && compinit 216*4882a593Smuzhiyun 217*4882a593Smuzhiyun __perfcomp () 218*4882a593Smuzhiyun { 219*4882a593Smuzhiyun emulate -L zsh 220*4882a593Smuzhiyun 221*4882a593Smuzhiyun local c IFS=$' \t\n' 222*4882a593Smuzhiyun local -a array 223*4882a593Smuzhiyun 224*4882a593Smuzhiyun for c in ${=1}; do 225*4882a593Smuzhiyun case $c in 226*4882a593Smuzhiyun --*=*|*.) ;; 227*4882a593Smuzhiyun *) c="$c " ;; 228*4882a593Smuzhiyun esac 229*4882a593Smuzhiyun array[${#array[@]}+1]="$c" 230*4882a593Smuzhiyun done 231*4882a593Smuzhiyun 232*4882a593Smuzhiyun compset -P '*[=:]' 233*4882a593Smuzhiyun compadd -Q -S '' -a -- array && _ret=0 234*4882a593Smuzhiyun } 235*4882a593Smuzhiyun 236*4882a593Smuzhiyun __perfcomp_colon () 237*4882a593Smuzhiyun { 238*4882a593Smuzhiyun emulate -L zsh 239*4882a593Smuzhiyun 240*4882a593Smuzhiyun local cur_="${2-$cur}" 241*4882a593Smuzhiyun local c IFS=$' \t\n' 242*4882a593Smuzhiyun local -a array 243*4882a593Smuzhiyun 244*4882a593Smuzhiyun if [[ "$cur_" == *:* ]]; then 245*4882a593Smuzhiyun local colon_word=${cur_%"${cur_##*:}"} 246*4882a593Smuzhiyun fi 247*4882a593Smuzhiyun 248*4882a593Smuzhiyun for c in ${=1}; do 249*4882a593Smuzhiyun case $c in 250*4882a593Smuzhiyun --*=*|*.) ;; 251*4882a593Smuzhiyun *) c="$c " ;; 252*4882a593Smuzhiyun esac 253*4882a593Smuzhiyun array[$#array+1]=${c#"$colon_word"} 254*4882a593Smuzhiyun done 255*4882a593Smuzhiyun 256*4882a593Smuzhiyun compset -P '*[=:]' 257*4882a593Smuzhiyun compadd -Q -S '' -a -- array && _ret=0 258*4882a593Smuzhiyun } 259*4882a593Smuzhiyun 260*4882a593Smuzhiyun _perf () 261*4882a593Smuzhiyun { 262*4882a593Smuzhiyun local _ret=1 cur cword prev 263*4882a593Smuzhiyun cur=${words[CURRENT]} 264*4882a593Smuzhiyun prev=${words[CURRENT-1]} 265*4882a593Smuzhiyun let cword=CURRENT-1 266*4882a593Smuzhiyun emulate ksh -c __perf_main 267*4882a593Smuzhiyun let _ret && _default && _ret=0 268*4882a593Smuzhiyun return _ret 269*4882a593Smuzhiyun } 270*4882a593Smuzhiyun 271*4882a593Smuzhiyun compdef _perf perf 272*4882a593Smuzhiyun return 273*4882a593Smuzhiyunfi 274*4882a593Smuzhiyun 275*4882a593Smuzhiyuntype perf &>/dev/null && 276*4882a593Smuzhiyun_perf() 277*4882a593Smuzhiyun{ 278*4882a593Smuzhiyun if [[ "$COMP_WORDBREAKS" != *,* ]]; then 279*4882a593Smuzhiyun COMP_WORDBREAKS="${COMP_WORDBREAKS}," 280*4882a593Smuzhiyun export COMP_WORDBREAKS 281*4882a593Smuzhiyun fi 282*4882a593Smuzhiyun 283*4882a593Smuzhiyun if [[ "$COMP_WORDBREAKS" == *:* ]]; then 284*4882a593Smuzhiyun COMP_WORDBREAKS="${COMP_WORDBREAKS/:/}" 285*4882a593Smuzhiyun export COMP_WORDBREAKS 286*4882a593Smuzhiyun fi 287*4882a593Smuzhiyun 288*4882a593Smuzhiyun local cur words cword prev 289*4882a593Smuzhiyun if [ $preload_get_comp_words_by_ref = "true" ]; then 290*4882a593Smuzhiyun _get_comp_words_by_ref -n =:, cur words cword prev 291*4882a593Smuzhiyun else 292*4882a593Smuzhiyun __perf_get_comp_words_by_ref -n =:, cur words cword prev 293*4882a593Smuzhiyun fi 294*4882a593Smuzhiyun __perf_main 295*4882a593Smuzhiyun} && 296*4882a593Smuzhiyun 297*4882a593Smuzhiyuncomplete -o bashdefault -o default -o nospace -F _perf perf 2>/dev/null \ 298*4882a593Smuzhiyun || complete -o default -o nospace -F _perf perf 299