From f07eba115d58054ef833a502328e28469a6ec1e7 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Tue, 27 Aug 2024 12:53:18 +0700 Subject: [PATCH 1/4] feat: add builder autocompletion script --- resources/builder.inc.sh | 21 +++++- resources/builder_completion.sh | 111 ++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 1 deletion(-) create mode 100755 resources/builder_completion.sh diff --git a/resources/builder.inc.sh b/resources/builder.inc.sh index b233614029d..c67aae409de 100755 --- a/resources/builder.inc.sh +++ b/resources/builder.inc.sh @@ -1399,6 +1399,10 @@ _builder_parse_expanded_parameters() { # internal reporting function, ignores all other parameters _builder_report_dependencies ;; + --builder_completion_describe) + _builder_completion_describe + exit 0 + ;; *) # script does not recognize anything of action or target form at this point. if [[ $key =~ ^: ]]; then @@ -1488,6 +1492,21 @@ _builder_pad() { printf $fmt "$text1" "$text2" } +_builder_completion_describe() { + printf '%s ' "${_builder_actions[@]}" + echo -n "; " + printf '%s ' "${_builder_targets[@]}" + echo -n "; " + local _builder_opts=() + for e in "${!_builder_params[@]}"; do + if [[ $e =~ ^-- ]]; then + # _builder_pad $width " $e" "${_builder_params[$e]}" + _builder_opts+=(${e%+*}) + fi + done + printf '%s ' "${_builder_opts[@]}" +} + builder_display_usage() { local e program description @@ -1495,7 +1514,7 @@ builder_display_usage() { # if you add other, longer, global options (like --verbose, --debug) local width=12 - for e in "${!_builder_params[@]}"; do + for e in "${!_builder_actions[@]}"; do if (( ${#e} > $width )); then width=${#e} fi diff --git a/resources/builder_completion.sh b/resources/builder_completion.sh new file mode 100755 index 00000000000..87bc8c73e71 --- /dev/null +++ b/resources/builder_completion.sh @@ -0,0 +1,111 @@ +#!/usr/bin/env bash + +# Note: must 'source' this file again in the terminal after any edits! +_comp_builder() { + # Does the file actually exist? If not, abort. + local CMD_PATH=`readlink -f "$1"` + if [ -z "${CMD_PATH}" ]; then + exit 0 + fi + + # Is it actually a builder-script? If not, abort. If so, it defines + # a special option used to provide us with completion-target data. + local builder_params=`${CMD_PATH} --builder_completion_describe` || exit 0 + + local BASE_NAME=$(basename "$CMD_PATH") + local BUILDER_ARG_STR="${COMP_LINE##*${BASE_NAME} }" + + local builder_args + # Does not actually preserve an empty token at the end. + read -r -a builder_args <<< "${BUILDER_ARG_STR}" + + # Determine the current token (given the caret position) and + # all existing, already-completed actions, targets, and options + # for the script before the current token. + local current_token= + # If the caret is adjacent to non-whitespace - thus is editing + # a builder argument... + if [ ! -z "${COMP_WORDS[COMP_CWORD]}" ]; then + # Then note the last token as the token being edited... + current_token="${builder_args[-1]}" + # ... and thus not pre-completed. + unset builder_args[-1] + fi + + # Parse the builder-description for completion target data. + local action_str target_str option_str + IFS=";" read -r action_str target_str option_str <<< "$builder_params" + local actions targets options + IFS=" " read -r -a actions <<< "$action_str" + IFS=" " read -r -a targets <<< "$target_str" + IFS=" " read -r -a options <<< "$option_str" + + # echo "current_token=$current_token" + local action target + if [[ $current_token =~ : ]]; then + IFS=: read -r action target <<< "$current_token" + target=:$target + else + action="$current_token" + target= + fi + + # echo "" + # echo "\$action=$action" + # echo "\$target=$target" + + local all=() + # If there is no $action component, it's a standalone target. + + if [[ -z "$action" ]] && [[ -z "$target" ]]; then + all+="${actions[@]}" + all+=" ${targets[@]}" + all+=" ${options[@]}" + + COMPREPLY=( $(compgen -W "${all[@]}" -- "${current_token}") ) + elif [[ -z "$action" ]]; then + # It's an unpaired target. + all="${targets[@]}" + COMPREPLY=( $(compgen -W "${all[@]}" -- "${current_token}") ) + + # bash doesn't handle completion with colons well; we should remove the + # colon prefix from each entry. + local i + for (( i=0; i<${#COMPREPLY[@]}; i++ )); do + COMPREPLY[$i]="${COMPREPLY[$i]##*:}" + done + elif [[ -z "$target" ]]; then + # It's an untargeted action or an option. + all+="${actions[@]}" + all+=" ${options[@]}" + + COMPREPLY=( $(compgen -W "${all[@]}" -- "${current_token}") ) + else + # Ah, an action-target pair. We have an existing action, so we only need to + # complete the target part. + + # First, build up the list of legal paired tokens. + local actiontargets=() + for e in "${targets[@]}"; do + actiontargets+=("${action}${e}") + done + + # Now, complete from that. + all+="${actiontargets[@]}" + # echo + # echo "$all" + COMPREPLY=( $(compgen -W "${all[@]}" -- "${current_token}") ) + + # bash doesn't handle completion with colons well; we should remove the + # colon prefix from each entry. + local i + for (( i=0; i<${#COMPREPLY[@]}; i++ )); do + COMPREPLY[$i]="${COMPREPLY[$i]##*:}" + done + fi +} + +# When this script is sourced by .bashrc or similar, this provides autocompletion for +# builder scripts named build.sh and test.sh. +complete -o bashdefault -F _comp_builder build.sh +complete -o bashdefault -F _comp_builder test.sh \ No newline at end of file From 1b166f580df1b2efc1e9d9ae60e7276a3a477812 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Tue, 27 Aug 2024 13:14:13 +0700 Subject: [PATCH 2/4] chore: builder_completion echo cleanup --- resources/builder_completion.sh | 7 ------- 1 file changed, 7 deletions(-) diff --git a/resources/builder_completion.sh b/resources/builder_completion.sh index 87bc8c73e71..3f1ae7ed177 100755 --- a/resources/builder_completion.sh +++ b/resources/builder_completion.sh @@ -40,7 +40,6 @@ _comp_builder() { IFS=" " read -r -a targets <<< "$target_str" IFS=" " read -r -a options <<< "$option_str" - # echo "current_token=$current_token" local action target if [[ $current_token =~ : ]]; then IFS=: read -r action target <<< "$current_token" @@ -50,10 +49,6 @@ _comp_builder() { target= fi - # echo "" - # echo "\$action=$action" - # echo "\$target=$target" - local all=() # If there is no $action component, it's a standalone target. @@ -92,8 +87,6 @@ _comp_builder() { # Now, complete from that. all+="${actiontargets[@]}" - # echo - # echo "$all" COMPREPLY=( $(compgen -W "${all[@]}" -- "${current_token}") ) # bash doesn't handle completion with colons well; we should remove the From b589aed4c022d97cefcdfe001800c32362cb7f34 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Tue, 27 Aug 2024 13:14:57 +0700 Subject: [PATCH 3/4] chore: builder.inc.sh cleanup in new method --- resources/builder.inc.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/builder.inc.sh b/resources/builder.inc.sh index c67aae409de..6311817c52b 100755 --- a/resources/builder.inc.sh +++ b/resources/builder.inc.sh @@ -1500,7 +1500,6 @@ _builder_completion_describe() { local _builder_opts=() for e in "${!_builder_params[@]}"; do if [[ $e =~ ^-- ]]; then - # _builder_pad $width " $e" "${_builder_params[$e]}" _builder_opts+=(${e%+*}) fi done From e6fb213fa21f757fbf8c0f438604ed83000a2680 Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Tue, 27 Aug 2024 13:23:10 +0700 Subject: [PATCH 4/4] fix: revert unintended change outside of new func --- resources/builder.inc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/builder.inc.sh b/resources/builder.inc.sh index 6311817c52b..ea3b6df6523 100755 --- a/resources/builder.inc.sh +++ b/resources/builder.inc.sh @@ -1513,7 +1513,7 @@ builder_display_usage() { # if you add other, longer, global options (like --verbose, --debug) local width=12 - for e in "${!_builder_actions[@]}"; do + for e in "${!_builder_params[@]}"; do if (( ${#e} > $width )); then width=${#e} fi