testMathBashCompletionScript().bash 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. #!/bin/bash
  2. __math_cursor_index_in_current_word() {
  3. local remaining="${COMP_LINE}"
  4. local word
  5. for word in "${COMP_WORDS[@]::COMP_CWORD}"; do
  6. remaining="${remaining##*([[:space:]])"${word}"*([[:space:]])}"
  7. done
  8. local -ir index="$((COMP_POINT - ${#COMP_LINE} + ${#remaining}))"
  9. if [[ "${index}" -le 0 ]]; then
  10. printf 0
  11. else
  12. printf %s "${index}"
  13. fi
  14. }
  15. # positional arguments:
  16. #
  17. # - 1: the current (sub)command's count of positional arguments
  18. #
  19. # required variables:
  20. #
  21. # - repeating_flags: the repeating flags that the current (sub)command can accept
  22. # - non_repeating_flags: the non-repeating flags that the current (sub)command can accept
  23. # - repeating_options: the repeating options that the current (sub)command can accept
  24. # - non_repeating_options: the non-repeating options that the current (sub)command can accept
  25. # - positional_number: value ignored
  26. # - unparsed_words: unparsed words from the current command line
  27. #
  28. # modified variables:
  29. #
  30. # - non_repeating_flags: remove flags for this (sub)command that are already on the command line
  31. # - non_repeating_options: remove options for this (sub)command that are already on the command line
  32. # - positional_number: set to the current positional number
  33. # - unparsed_words: remove all flags, options, and option values for this (sub)command
  34. __math_offer_flags_options() {
  35. local -ir positional_count="${1}"
  36. positional_number=0
  37. local was_flag_option_terminator_seen=false
  38. local is_parsing_option_value=false
  39. local -ar unparsed_word_indices=("${!unparsed_words[@]}")
  40. local -i word_index
  41. for word_index in "${unparsed_word_indices[@]}"; do
  42. if "${is_parsing_option_value}"; then
  43. # This word is an option value:
  44. # Reset marker for next word iff not currently the last word
  45. [[ "${word_index}" -ne "${unparsed_word_indices[${#unparsed_word_indices[@]} - 1]}" ]] && is_parsing_option_value=false
  46. unset "unparsed_words[${word_index}]"
  47. # Do not process this word as a flag or an option
  48. continue
  49. fi
  50. local word="${unparsed_words["${word_index}"]}"
  51. if ! "${was_flag_option_terminator_seen}"; then
  52. case "${word}" in
  53. --)
  54. unset "unparsed_words[${word_index}]"
  55. # by itself -- is a flag/option terminator, but if it is the last word, it is the start of a completion
  56. if [[ "${word_index}" -ne "${unparsed_word_indices[${#unparsed_word_indices[@]} - 1]}" ]]; then
  57. was_flag_option_terminator_seen=true
  58. fi
  59. continue
  60. ;;
  61. -*)
  62. # ${word} is a flag or an option
  63. # If ${word} is an option, mark that the next word to be parsed is an option value
  64. local option
  65. for option in "${repeating_options[@]}" "${non_repeating_options[@]}"; do
  66. [[ "${word}" = "${option}" ]] && is_parsing_option_value=true && break
  67. done
  68. # Remove ${word} from ${non_repeating_flags} or ${non_repeating_options} so it isn't offered again
  69. local not_found=true
  70. local -i index
  71. for index in "${!non_repeating_flags[@]}"; do
  72. if [[ "${non_repeating_flags[${index}]}" = "${word}" ]]; then
  73. unset "non_repeating_flags[${index}]"
  74. non_repeating_flags=("${non_repeating_flags[@]}")
  75. not_found=false
  76. break
  77. fi
  78. done
  79. if "${not_found}"; then
  80. for index in "${!non_repeating_flags[@]}"; do
  81. if [[ "${non_repeating_flags[${index}]}" = "${word}" ]]; then
  82. unset "non_repeating_flags[${index}]"
  83. non_repeating_flags=("${non_repeating_flags[@]}")
  84. break
  85. fi
  86. done
  87. fi
  88. unset "unparsed_words[${word_index}]"
  89. continue
  90. ;;
  91. esac
  92. fi
  93. # ${word} is neither a flag, nor an option, nor an option value
  94. if [[ "${positional_number}" -lt "${positional_count}" || "${positional_count}" -lt 0 ]]; then
  95. # ${word} is a positional
  96. ((positional_number++))
  97. unset "unparsed_words[${word_index}]"
  98. else
  99. if [[ -z "${word}" ]]; then
  100. # Could be completing a flag, option, or subcommand
  101. positional_number=-1
  102. else
  103. # ${word} is a subcommand or invalid, so stop processing this (sub)command
  104. positional_number=-2
  105. fi
  106. break
  107. fi
  108. done
  109. unparsed_words=("${unparsed_words[@]}")
  110. if\
  111. ! "${was_flag_option_terminator_seen}"\
  112. && ! "${is_parsing_option_value}"\
  113. && [[ ("${cur}" = -* && "${positional_number}" -ge 0) || "${positional_number}" -eq -1 ]]
  114. then
  115. COMPREPLY+=($(compgen -W "${repeating_flags[*]} ${non_repeating_flags[*]} ${repeating_options[*]} ${non_repeating_options[*]}" -- "${cur}"))
  116. fi
  117. }
  118. __math_add_completions() {
  119. local completion
  120. while IFS='' read -r completion; do
  121. COMPREPLY+=("${completion}")
  122. done < <(IFS=$'\n' compgen "${@}" -- "${cur}")
  123. }
  124. __math_custom_complete() {
  125. if [[ -n "${cur}" || -z ${COMP_WORDS[${COMP_CWORD}]} || "${COMP_LINE:${COMP_POINT}:1}" != ' ' ]]; then
  126. local -ar words=("${COMP_WORDS[@]}")
  127. else
  128. local -ar words=("${COMP_WORDS[@]::${COMP_CWORD}}" '' "${COMP_WORDS[@]:${COMP_CWORD}}")
  129. fi
  130. "${COMP_WORDS[0]}" "${@}" "${words[@]}"
  131. }
  132. _math() {
  133. local state
  134. state="$(shopt -p;shopt -po)"
  135. trap "${state//$'\n'/;}" RETURN
  136. shopt -s extglob
  137. set +o history +o posix
  138. local -xr SAP_SHELL=bash
  139. local -x SAP_SHELL_VERSION
  140. SAP_SHELL_VERSION="$(IFS='.';printf %s "${BASH_VERSINFO[*]}")"
  141. local -r SAP_SHELL_VERSION
  142. local -r cur="${2}"
  143. local -r prev="${3}"
  144. local -i positional_number
  145. local -a unparsed_words=("${COMP_WORDS[@]:1:${COMP_CWORD}}")
  146. local -a repeating_flags=()
  147. local -a non_repeating_flags=(--version -h --help)
  148. local -a repeating_options=()
  149. local -a non_repeating_options=()
  150. __math_offer_flags_options 0
  151. # Offer subcommand / subcommand argument completions
  152. local -r subcommand="${unparsed_words[0]}"
  153. unset 'unparsed_words[0]'
  154. unparsed_words=("${unparsed_words[@]}")
  155. case "${subcommand}" in
  156. add|multiply|stats|help)
  157. # Offer subcommand argument completions
  158. "_math_${subcommand}"
  159. ;;
  160. *)
  161. # Offer subcommand completions
  162. COMPREPLY+=($(compgen -W 'add multiply stats help' -- "${cur}"))
  163. ;;
  164. esac
  165. }
  166. _math_add() {
  167. repeating_flags=()
  168. non_repeating_flags=(--hex-output -x --version -h --help)
  169. repeating_options=()
  170. non_repeating_options=()
  171. __math_offer_flags_options -1
  172. }
  173. _math_multiply() {
  174. repeating_flags=()
  175. non_repeating_flags=(--hex-output -x --version -h --help)
  176. repeating_options=()
  177. non_repeating_options=()
  178. __math_offer_flags_options -1
  179. }
  180. _math_stats() {
  181. repeating_flags=()
  182. non_repeating_flags=(--version -h --help)
  183. repeating_options=()
  184. non_repeating_options=()
  185. __math_offer_flags_options 0
  186. # Offer subcommand / subcommand argument completions
  187. local -r subcommand="${unparsed_words[0]}"
  188. unset 'unparsed_words[0]'
  189. unparsed_words=("${unparsed_words[@]}")
  190. case "${subcommand}" in
  191. average|stdev|quantiles)
  192. # Offer subcommand argument completions
  193. "_math_stats_${subcommand}"
  194. ;;
  195. *)
  196. # Offer subcommand completions
  197. COMPREPLY+=($(compgen -W 'average stdev quantiles' -- "${cur}"))
  198. ;;
  199. esac
  200. }
  201. _math_stats_average() {
  202. repeating_flags=()
  203. non_repeating_flags=(--version -h --help)
  204. repeating_options=()
  205. non_repeating_options=(--kind)
  206. __math_offer_flags_options -1
  207. # Offer option value completions
  208. case "${prev}" in
  209. '--kind')
  210. __math_add_completions -W 'mean'$'\n''median'$'\n''mode'
  211. return
  212. ;;
  213. esac
  214. }
  215. _math_stats_stdev() {
  216. repeating_flags=()
  217. non_repeating_flags=(--version -h --help)
  218. repeating_options=()
  219. non_repeating_options=()
  220. __math_offer_flags_options -1
  221. }
  222. _math_stats_quantiles() {
  223. repeating_flags=()
  224. non_repeating_flags=(--version -h --help)
  225. repeating_options=()
  226. non_repeating_options=(--file --directory --shell --custom --custom-deprecated)
  227. __math_offer_flags_options -1
  228. # Offer option value completions
  229. case "${prev}" in
  230. '--file')
  231. __math_add_completions -o plusdirs -fX '!*.@(txt|md)'
  232. return
  233. ;;
  234. '--directory')
  235. __math_add_completions -d
  236. return
  237. ;;
  238. '--shell')
  239. __math_add_completions -W "$(eval 'head -100 '\''/usr/share/dict/words'\'' | tail -50')"
  240. return
  241. ;;
  242. '--custom')
  243. __math_add_completions -W "$(__math_custom_complete ---completion stats quantiles -- --custom "${COMP_CWORD}" "$(__math_cursor_index_in_current_word)")"
  244. return
  245. ;;
  246. '--custom-deprecated')
  247. __math_add_completions -W "$(__math_custom_complete ---completion stats quantiles -- --custom-deprecated)"
  248. return
  249. ;;
  250. esac
  251. # Offer positional completions
  252. case "${positional_number}" in
  253. 1)
  254. __math_add_completions -W 'alphabet'$'\n''alligator'$'\n''branch'$'\n''braggart'
  255. return
  256. ;;
  257. 2)
  258. __math_add_completions -W "$(__math_custom_complete ---completion stats quantiles -- positional@1 "${COMP_CWORD}" "$(__math_cursor_index_in_current_word)")"
  259. return
  260. ;;
  261. 3)
  262. __math_add_completions -W "$(__math_custom_complete ---completion stats quantiles -- positional@2)"
  263. return
  264. ;;
  265. esac
  266. }
  267. _math_help() {
  268. repeating_flags=()
  269. non_repeating_flags=(--version)
  270. repeating_options=()
  271. non_repeating_options=()
  272. __math_offer_flags_options -1
  273. }
  274. complete -o filenames -F _math math