diff --git a/examples/completions/src/lib/send_completions.sh b/examples/completions/src/lib/send_completions.sh index f7c799c2..b5800e2a 100644 --- a/examples/completions/src/lib/send_completions.sh +++ b/examples/completions/src/lib/send_completions.sh @@ -7,10 +7,9 @@ send_completions() { echo $'# Modifying it manually is not recommended' echo $'' echo $'_cli_completions_filter() {' - echo $' local words=("$@")' + echo $' local words="$1"' echo $' local cur=${COMP_WORDS[COMP_CWORD]}' echo $' local result=()' - echo $' local want_options=0' echo $'' echo $' # words the user already typed (excluding the command itself)' echo $' local used=()' @@ -18,26 +17,28 @@ send_completions() { echo $' used=("${COMP_WORDS[@]:1:$((COMP_CWORD - 1))}")' echo $' fi' echo $'' - echo $' # Completing an option: offer everything.' - echo $' # Completing a non-option: drop options and already-used words.' - echo $' [[ "${cur:0:1}" == "-" ]] && want_options=1' - echo $' for word in "${words[@]}"; do' - echo $' if ((!want_options)); then' + echo $' if [[ "${cur:0:1}" == "-" ]]; then' + echo $' # Completing an option: offer everything (including options)' + echo $' echo "$words"' + echo $'' + echo $' else' + echo $' # Completing a non-option: offer only non-options,' + echo $' # and don\'t re-offer ones already used earlier in the line.' + echo $' for word in $words; do' echo $' [[ "${word:0:1}" == "-" ]] && continue' echo $'' + echo $' local seen=0' echo $' for u in "${used[@]}"; do' echo $' if [[ "$u" == "$word" ]]; then' - echo $' continue 2' + echo $' seen=1' + echo $' break' echo $' fi' echo $' done' - echo $' fi' - echo $'' - echo $' # compgen -W expects shell-escaped words in one space-delimited string.' - echo $' printf -v word \'%q\' "$word"' - echo $' result+=("$word")' - echo $' done' + echo $' ((!seen)) && result+=("$word")' + echo $' done' echo $'' - echo $' echo "${result[*]}"' + echo $' echo "${result[*]}"' + echo $' fi' echo $'}' echo $'' echo $'_cli_completions() {' @@ -52,7 +53,7 @@ send_completions() { echo $'' echo $' case "$compline" in' echo $' \'download\'*\'--handler\')' - echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "curl" "wget")" -- "$cur")' + echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "curl wget")" -- "$cur")' echo $' ;;' echo $'' echo $' \'upload\'*\'--user\')' @@ -60,11 +61,11 @@ send_completions() { echo $' ;;' echo $'' echo $' \'completions\'*)' - echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--help" "-h")" -- "$cur")' + echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--help -h")" -- "$cur")' echo $' ;;' echo $'' echo $' \'d\'*\'--handler\')' - echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "curl" "wget")" -- "$cur")' + echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "curl wget")" -- "$cur")' echo $' ;;' echo $'' echo $' \'upload\'*\'-u\')' @@ -72,7 +73,8 @@ send_completions() { echo $' ;;' echo $'' echo $' \'download\'*)' - echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A file -W "$(_cli_completions_filter "--force" "--handler" "--help" "-f" "-h")" -- "$cur")' + echo $' compopt -o filenames 2>/dev/null' + echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A file -W "$(_cli_completions_filter "--force --handler --help -f -h")" -- "$cur")' echo $' ;;' echo $'' echo $' \'u\'*\'--user\')' @@ -80,7 +82,8 @@ send_completions() { echo $' ;;' echo $'' echo $' \'upload\'*)' - echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A directory -A user -W "$(_cli_completions_filter "--help" "--password" "--user" "-h" "-p" "-u" "CHANGELOG.md" "README.md")" -- "$cur")' + echo $' compopt -o filenames 2>/dev/null' + echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A directory -A user -W "$(_cli_completions_filter "--help --password --user -h -p -u CHANGELOG.md README.md")" -- "$cur")' echo $' ;;' echo $'' echo $' \'u\'*\'-u\')' @@ -88,15 +91,17 @@ send_completions() { echo $' ;;' echo $'' echo $' \'d\'*)' - echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A file -W "$(_cli_completions_filter "--force" "--handler" "--help" "-f" "-h")" -- "$cur")' + echo $' compopt -o filenames 2>/dev/null' + echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A file -W "$(_cli_completions_filter "--force --handler --help -f -h")" -- "$cur")' echo $' ;;' echo $'' echo $' \'u\'*)' - echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A directory -A user -W "$(_cli_completions_filter "--help" "--password" "--user" "-h" "-p" "-u" "CHANGELOG.md" "README.md")" -- "$cur")' + echo $' compopt -o filenames 2>/dev/null' + echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A directory -A user -W "$(_cli_completions_filter "--help --password --user -h -p -u CHANGELOG.md README.md")" -- "$cur")' echo $' ;;' echo $'' echo $' *)' - echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--help" "--version" "-h" "-v" "completions" "d" "download" "u" "upload")" -- "$cur")' + echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--help --version -h -v completions d download u upload")" -- "$cur")' echo $' ;;' echo $'' echo $' esac' diff --git a/examples/render-mandoc/docs/download.1 b/examples/render-mandoc/docs/download.1 index a7f1b1b0..2e24bee9 100644 --- a/examples/render-mandoc/docs/download.1 +++ b/examples/render-mandoc/docs/download.1 @@ -1,6 +1,6 @@ .\" Automatically generated by Pandoc 3.9 .\" -.TH "download" "1" "February 2026" "Version 0.1.0" "Sample application" +.TH "download" "1" "March 2026" "Version 0.1.0" "Sample application" .SH NAME \f[B]download\f[R] \- Sample application .SH SYNOPSIS diff --git a/examples/render-mandoc/docs/download.md b/examples/render-mandoc/docs/download.md index 2e70d765..0a7df8d4 100644 --- a/examples/render-mandoc/docs/download.md +++ b/examples/render-mandoc/docs/download.md @@ -1,6 +1,6 @@ % download(1) Version 0.1.0 | Sample application % Lana Lang -% February 2026 +% March 2026 NAME ================================================== diff --git a/lib/bashly/libraries/libraries.yml b/lib/bashly/libraries/libraries.yml index 23187b08..c6afabf1 100644 --- a/lib/bashly/libraries/libraries.yml +++ b/lib/bashly/libraries/libraries.yml @@ -121,21 +121,6 @@ strings: - source: "strings/strings.yml" target: "%{user_source_dir}/bashly-strings.yml" -test: - help: Add approval testing. - files: - - source: "test/approvals.bash" - target: "%{user_target_dir}/test/approvals.bash" - - source: "test/approve" - target: "%{user_target_dir}/test/approve" - - post_install_message: | - Edit your tests in g`test/approve` and then run: - - m`$ test/approve` - - Docs: bu`https://github.com/DannyBen/approvals.bash` - validations: help: Add argument validation functions to the lib directory. files: diff --git a/lib/bashly/libraries/test/approvals.bash b/lib/bashly/libraries/test/approvals.bash deleted file mode 100644 index 9ee20d73..00000000 --- a/lib/bashly/libraries/test/approvals.bash +++ /dev/null @@ -1,172 +0,0 @@ -# approvals.bash v0.5.1 -# -# Interactive approval testing for Bash. -# https://github.com/DannyBen/approvals.bash -# -# shellcheck disable=SC2059 -# Disabling SC2059 (quoted format string) because we use dynamic format strings -approve() { - local expected approval approval_file actual cmd - approvals_dir=${APPROVALS_DIR:=approvals} - - cmd=$1 - last_exit_code=0 - actual=$(eval "$cmd" 2>&1) || last_exit_code=$? - if [[ -v allow_diff_regex && "$allow_diff_regex" ]]; then - actual=$(echo "$actual" | sed -E "s/$allow_diff_regex/*/g") - unset allow_diff_regex - fi - approval=$(printf "%b" "$cmd" | tr -s -c "[:alnum:]" _) - approval_file="$approvals_dir/${2:-"$approval"}" - - [[ -d "$approvals_dir" ]] || mkdir "$approvals_dir" - - if [[ -f "$approval_file" ]]; then - expected=$(cat "$approval_file") - else - printf -- "$new_diff_string\n" "$cmd" - printf "%b\n" "$actual" - printf -- "$new_diff_string\n" "$cmd" - expected="$actual" - user_approval "$cmd" "$actual" "$approval_file" - return - fi - - if [[ "$(printf "%b" "$actual")" = "$(printf "%b" "$expected")" ]]; then - pass "$cmd" - else - printf -- "$changed_diff_string\n" "$cmd" - $diff_cmd <(printf "%b" "$expected\n") <(printf "%b" "$actual\n") | tail -n +4 - printf -- "$changed_diff_string\n" "$cmd" - user_approval "$cmd" "$actual" "$approval_file" - fi -} - -allow_diff() { - allow_diff_regex="$1" -} - -describe() { - printf "$describe_string\n" "$*" -} - -context() { - printf "$context_string\n" "$*" -} - -it() { - printf "$it_string\n" "$*" -} - -fail() { - printf "$fail_string\n" "$*" - exit 1 -} - -pass() { - printf "$pass_string\n" "$*" - return 0 -} - -expect_exit_code() { - if [[ $last_exit_code == "$1" ]]; then - pass "exit $last_exit_code" - else - fail "expected exit code $1, got $last_exit_code" - fi -} - -# Private - -print_in_color() { - local color="$1" - shift - if [[ -z ${NO_COLOR+x} ]]; then - printf "$color%b\e[0m\n" "$*" - else - printf "%b\n" "$*" - fi -} - -red() { print_in_color "\e[31m" "$*"; } -green() { print_in_color "\e[32m" "$*"; } -yellow() { print_in_color "\e[33m" "$*"; } -blue() { print_in_color "\e[34m" "$*"; } -magenta() { print_in_color "\e[35m" "$*"; } -cyan() { print_in_color "\e[36m" "$*"; } -bold() { print_in_color "\e[1m" "$*"; } -underlined() { print_in_color "\e[4m" "$*"; } -red_bold() { print_in_color "\e[1;31m" "$*"; } -green_bold() { print_in_color "\e[1;32m" "$*"; } -yellow_bold() { print_in_color "\e[1;33m" "$*"; } -blue_bold() { print_in_color "\e[1;34m" "$*"; } -magenta_bold() { print_in_color "\e[1;35m" "$*"; } -cyan_bold() { print_in_color "\e[1;36m" "$*"; } -red_underlined() { print_in_color "\e[4;31m" "$*"; } -green_underlined() { print_in_color "\e[4;32m" "$*"; } -yellow_underlined() { print_in_color "\e[4;33m" "$*"; } -blue_underlined() { print_in_color "\e[4;34m" "$*"; } -magenta_underlined() { print_in_color "\e[4;35m" "$*"; } -cyan_underlined() { print_in_color "\e[4;36m" "$*"; } - -user_approval() { - local cmd="$1" - local actual="$2" - local approval_file="$3" - - if [[ -v CI || -v GITHUB_ACTIONS ]] && [[ -z "${AUTO_APPROVE+x}" ]]; then - fail "$cmd" - fi - - if [[ -v AUTO_APPROVE ]]; then - response=a - else - echo - printf "$approval_string" - response=$(bash -c "read -n 1 key; echo \$key") - printf "\b%.s" $(seq 1 $((${#approval_string} + 1))) - fi - - if [[ $response =~ [Aa] ]]; then - printf "%b\n" "$actual" >"$approval_file" - pass "$cmd" - else - fail "$cmd" - fi -} - -onexit() { - exitcode=$? - if [[ "$exitcode" == 0 ]]; then - printf "$exit_success_string\n" "$0" - else - printf "$exit_failed_string\n" "$0" - fi - echo - exit $exitcode -} - -onerror() { - fail "caller: $(caller)" -} - -set -e -trap 'onexit' EXIT -trap 'onerror' ERR - -describe_string="$(bold ▌ describe) %s" -context_string="$(bold ▌ context) %s" -it_string="$(bold ▌ it) %s" -fail_string=" $(red_bold failed) %s" -pass_string=" $(green approved) %s" -exit_success_string="$(green ▌ exit) $(bold %s finished successfully)" -exit_failed_string="$(red_bold ▌ exit) $(bold %s finished with errors)" -new_diff_string="────┤ $(yellow new): $(bold %s) ├────" -changed_diff_string="────┤ $(blue changed): $(bold %s) ├────" -approval_string="[A]pprove? " - -if diff --help | grep -- --color >/dev/null 2>&1; then - diff_cmd="diff --unified --color=always" -else - diff_cmd="diff --unified" -fi diff --git a/lib/bashly/libraries/test/approve b/lib/bashly/libraries/test/approve deleted file mode 100644 index cc90a0a7..00000000 --- a/lib/bashly/libraries/test/approve +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash -# Run this from the root directory - -cd ./test || exit -source approvals.bash - -# Update me -cli=./download - -# Tests (context, describe and indentation are optional) -context "when DEBUG is on" - export DEBUG=1 - - describe "root command" - approve "$cli" - approve "$cli --help" - -context "when DEBUG is off" - unset DEBUG - - describe "some other command" - approve "$cli other" - approve "$cli other --help" - -# ...more tests... \ No newline at end of file diff --git a/spec/approvals/cli/add/comp-function-file b/spec/approvals/cli/add/comp-function-file index c39809fd..b1851a88 100644 --- a/spec/approvals/cli/add/comp-function-file +++ b/spec/approvals/cli/add/comp-function-file @@ -7,10 +7,9 @@ send_completions() { echo $'# Modifying it manually is not recommended' echo $'' echo $'_cli_completions_filter() {' - echo $' local words=("$@")' + echo $' local words="$1"' echo $' local cur=${COMP_WORDS[COMP_CWORD]}' echo $' local result=()' - echo $' local want_options=0' echo $'' echo $' # words the user already typed (excluding the command itself)' echo $' local used=()' @@ -18,26 +17,28 @@ send_completions() { echo $' used=("${COMP_WORDS[@]:1:$((COMP_CWORD - 1))}")' echo $' fi' echo $'' - echo $' # Completing an option: offer everything.' - echo $' # Completing a non-option: drop options and already-used words.' - echo $' [[ "${cur:0:1}" == "-" ]] && want_options=1' - echo $' for word in "${words[@]}"; do' - echo $' if ((!want_options)); then' + echo $' if [[ "${cur:0:1}" == "-" ]]; then' + echo $' # Completing an option: offer everything (including options)' + echo $' echo "$words"' + echo $'' + echo $' else' + echo $' # Completing a non-option: offer only non-options,' + echo $' # and don\'t re-offer ones already used earlier in the line.' + echo $' for word in $words; do' echo $' [[ "${word:0:1}" == "-" ]] && continue' echo $'' + echo $' local seen=0' echo $' for u in "${used[@]}"; do' echo $' if [[ "$u" == "$word" ]]; then' - echo $' continue 2' + echo $' seen=1' + echo $' break' echo $' fi' echo $' done' - echo $' fi' - echo $'' - echo $' # compgen -W expects shell-escaped words in one space-delimited string.' - echo $' printf -v word \'%q\' "$word"' - echo $' result+=("$word")' - echo $' done' + echo $' ((!seen)) && result+=("$word")' + echo $' done' echo $'' - echo $' echo "${result[*]}"' + echo $' echo "${result[*]}"' + echo $' fi' echo $'}' echo $'' echo $'_cli_completions() {' @@ -52,23 +53,23 @@ send_completions() { echo $'' echo $' case "$compline" in' echo $' \'download\'*)' - echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--force" "--help" "-f" "-h")" -- "$cur")' + echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--force --help -f -h")" -- "$cur")' echo $' ;;' echo $'' echo $' \'upload\'*)' - echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--help" "--password" "--user" "-h" "-p" "-u")" -- "$cur")' + echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--help --password --user -h -p -u")" -- "$cur")' echo $' ;;' echo $'' echo $' \'d\'*)' - echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--force" "--help" "-f" "-h")" -- "$cur")' + echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--force --help -f -h")" -- "$cur")' echo $' ;;' echo $'' echo $' \'u\'*)' - echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--help" "--password" "--user" "-h" "-p" "-u")" -- "$cur")' + echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--help --password --user -h -p -u")" -- "$cur")' echo $' ;;' echo $'' echo $' *)' - echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--help" "--version" "-h" "-v" "d" "download" "u" "upload")" -- "$cur")' + echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--help --version -h -v d download u upload")" -- "$cur")' echo $' ;;' echo $'' echo $' esac' diff --git a/spec/approvals/cli/add/comp-script-file b/spec/approvals/cli/add/comp-script-file index b6f128fa..81a69f94 100644 --- a/spec/approvals/cli/add/comp-script-file +++ b/spec/approvals/cli/add/comp-script-file @@ -5,10 +5,9 @@ # Modifying it manually is not recommended _cli_completions_filter() { - local words=("$@") + local words="$1" local cur=${COMP_WORDS[COMP_CWORD]} local result=() - local want_options=0 # words the user already typed (excluding the command itself) local used=() @@ -16,26 +15,28 @@ _cli_completions_filter() { used=("${COMP_WORDS[@]:1:$((COMP_CWORD - 1))}") fi - # Completing an option: offer everything. - # Completing a non-option: drop options and already-used words. - [[ "${cur:0:1}" == "-" ]] && want_options=1 - for word in "${words[@]}"; do - if ((!want_options)); then + if [[ "${cur:0:1}" == "-" ]]; then + # Completing an option: offer everything (including options) + echo "$words" + + else + # Completing a non-option: offer only non-options, + # and don't re-offer ones already used earlier in the line. + for word in $words; do [[ "${word:0:1}" == "-" ]] && continue + local seen=0 for u in "${used[@]}"; do if [[ "$u" == "$word" ]]; then - continue 2 + seen=1 + break fi done - fi - - # compgen -W expects shell-escaped words in one space-delimited string. - printf -v word '%q' "$word" - result+=("$word") - done + ((!seen)) && result+=("$word") + done - echo "${result[*]}" + echo "${result[*]}" + fi } _cli_completions() { @@ -50,23 +51,23 @@ _cli_completions() { case "$compline" in 'download'*) - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--force" "--help" "-f" "-h")" -- "$cur") + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--force --help -f -h")" -- "$cur") ;; 'upload'*) - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--help" "--password" "--user" "-h" "-p" "-u")" -- "$cur") + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--help --password --user -h -p -u")" -- "$cur") ;; 'd'*) - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--force" "--help" "-f" "-h")" -- "$cur") + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--force --help -f -h")" -- "$cur") ;; 'u'*) - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--help" "--password" "--user" "-h" "-p" "-u")" -- "$cur") + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--help --password --user -h -p -u")" -- "$cur") ;; *) - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--help" "--version" "-h" "-v" "d" "download" "u" "upload")" -- "$cur") + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--help --version -h -v d download u upload")" -- "$cur") ;; esac diff --git a/spec/approvals/cli/add/list b/spec/approvals/cli/add/list index fbdd60c5..ccde5e5c 100644 --- a/spec/approvals/cli/add/list +++ b/spec/approvals/cli/add/list @@ -49,9 +49,6 @@ strings Copy an additional configuration file to your project, allowing you to customize all the tips and error strings. -test - Add approval testing. - validations Add argument validation functions to the lib directory. diff --git a/spec/approvals/completions/function b/spec/approvals/completions/function index a8831ded..7c865f25 100644 --- a/spec/approvals/completions/function +++ b/spec/approvals/completions/function @@ -6,10 +6,9 @@ custom_name() { echo $'# Modifying it manually is not recommended' echo $'' echo $'_get_completions_filter() {' - echo $' local words=("$@")' + echo $' local words="$1"' echo $' local cur=${COMP_WORDS[COMP_CWORD]}' echo $' local result=()' - echo $' local want_options=0' echo $'' echo $' # words the user already typed (excluding the command itself)' echo $' local used=()' @@ -17,26 +16,28 @@ custom_name() { echo $' used=("${COMP_WORDS[@]:1:$((COMP_CWORD - 1))}")' echo $' fi' echo $'' - echo $' # Completing an option: offer everything.' - echo $' # Completing a non-option: drop options and already-used words.' - echo $' [[ "${cur:0:1}" == "-" ]] && want_options=1' - echo $' for word in "${words[@]}"; do' - echo $' if ((!want_options)); then' + echo $' if [[ "${cur:0:1}" == "-" ]]; then' + echo $' # Completing an option: offer everything (including options)' + echo $' echo "$words"' + echo $'' + echo $' else' + echo $' # Completing a non-option: offer only non-options,' + echo $' # and don\'t re-offer ones already used earlier in the line.' + echo $' for word in $words; do' echo $' [[ "${word:0:1}" == "-" ]] && continue' echo $'' + echo $' local seen=0' echo $' for u in "${used[@]}"; do' echo $' if [[ "$u" == "$word" ]]; then' - echo $' continue 2' + echo $' seen=1' + echo $' break' echo $' fi' echo $' done' - echo $' fi' - echo $'' - echo $' # compgen -W expects shell-escaped words in one space-delimited string.' - echo $' printf -v word \'%q\' "$word"' - echo $' result+=("$word")' - echo $' done' + echo $' ((!seen)) && result+=("$word")' + echo $' done' echo $'' - echo $' echo "${result[*]}"' + echo $' echo "${result[*]}"' + echo $' fi' echo $'}' echo $'' echo $'_get_completions() {' @@ -51,7 +52,8 @@ custom_name() { echo $'' echo $' case "$compline" in' echo $' *)' - echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A file -W "$(_get_completions_filter "--force" "--help" "--verbose" "--version" "-h" "-v")" -- "$cur")' + echo $' compopt -o filenames 2>/dev/null' + echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A file -W "$(_get_completions_filter "--force --help --verbose --version -h -v")" -- "$cur")' echo $' ;;' echo $'' echo $' esac' diff --git a/spec/approvals/completions/script b/spec/approvals/completions/script index 68e0e75c..92a91a34 100644 --- a/spec/approvals/completions/script +++ b/spec/approvals/completions/script @@ -5,10 +5,9 @@ # Modifying it manually is not recommended _say_completions_filter() { - local words=("$@") + local words="$1" local cur=${COMP_WORDS[COMP_CWORD]} local result=() - local want_options=0 # words the user already typed (excluding the command itself) local used=() @@ -16,26 +15,28 @@ _say_completions_filter() { used=("${COMP_WORDS[@]:1:$((COMP_CWORD - 1))}") fi - # Completing an option: offer everything. - # Completing a non-option: drop options and already-used words. - [[ "${cur:0:1}" == "-" ]] && want_options=1 - for word in "${words[@]}"; do - if ((!want_options)); then + if [[ "${cur:0:1}" == "-" ]]; then + # Completing an option: offer everything (including options) + echo "$words" + + else + # Completing a non-option: offer only non-options, + # and don't re-offer ones already used earlier in the line. + for word in $words; do [[ "${word:0:1}" == "-" ]] && continue + local seen=0 for u in "${used[@]}"; do if [[ "$u" == "$word" ]]; then - continue 2 + seen=1 + break fi done - fi - - # compgen -W expects shell-escaped words in one space-delimited string. - printf -v word '%q' "$word" - result+=("$word") - done + ((!seen)) && result+=("$word") + done - echo "${result[*]}" + echo "${result[*]}" + fi } _say_completions() { @@ -50,35 +51,37 @@ _say_completions() { case "$compline" in 'goodbye universe'*'--color') - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_say_completions_filter "green" "red")" -- "$cur") + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_say_completions_filter "green red")" -- "$cur") ;; 'goodbye universe'*'--path') + compopt -o filenames 2>/dev/null while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A file -- "$cur") ;; 'goodbye universe'*'-c') - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_say_completions_filter "green" "red")" -- "$cur") + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_say_completions_filter "green red")" -- "$cur") ;; 'goodbye universe'*) - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_say_completions_filter "$(git branch)" "--color" "--help" "--path" "--verbose" "-c" "-h" "-v")" -- "$cur") + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_say_completions_filter "$(git branch) --color --help --path --verbose -c -h -v")" -- "$cur") ;; 'hello world'*) - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A directory -A user -W "$(_say_completions_filter "--force" "--help" "--verbose" "-h")" -- "$cur") + compopt -o filenames 2>/dev/null + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A directory -A user -W "$(_say_completions_filter "--force --help --verbose -h")" -- "$cur") ;; 'goodbye'*) - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_say_completions_filter "--help" "-h" "universe")" -- "$cur") + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_say_completions_filter "--help -h universe")" -- "$cur") ;; 'hello'*) - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_say_completions_filter "--help" "-h" "world")" -- "$cur") + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_say_completions_filter "--help -h world")" -- "$cur") ;; *) - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_say_completions_filter "--help" "--version" "-h" "-v" "goodbye" "hello")" -- "$cur") + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_say_completions_filter "--help --version -h -v goodbye hello")" -- "$cur") ;; esac diff --git a/spec/approvals/examples/completions b/spec/approvals/examples/completions index dba6fd46..9dd2f329 100644 --- a/spec/approvals/examples/completions +++ b/spec/approvals/examples/completions @@ -69,10 +69,9 @@ Options: # Modifying it manually is not recommended _cli_completions_filter() { - local words=("$@") + local words="$1" local cur=${COMP_WORDS[COMP_CWORD]} local result=() - local want_options=0 # words the user already typed (excluding the command itself) local used=() @@ -80,26 +79,28 @@ _cli_completions_filter() { used=("${COMP_WORDS[@]:1:$((COMP_CWORD - 1))}") fi - # Completing an option: offer everything. - # Completing a non-option: drop options and already-used words. - [[ "${cur:0:1}" == "-" ]] && want_options=1 - for word in "${words[@]}"; do - if ((!want_options)); then + if [[ "${cur:0:1}" == "-" ]]; then + # Completing an option: offer everything (including options) + echo "$words" + + else + # Completing a non-option: offer only non-options, + # and don't re-offer ones already used earlier in the line. + for word in $words; do [[ "${word:0:1}" == "-" ]] && continue + local seen=0 for u in "${used[@]}"; do if [[ "$u" == "$word" ]]; then - continue 2 + seen=1 + break fi done - fi - - # compgen -W expects shell-escaped words in one space-delimited string. - printf -v word '%q' "$word" - result+=("$word") - done + ((!seen)) && result+=("$word") + done - echo "${result[*]}" + echo "${result[*]}" + fi } _cli_completions() { @@ -114,7 +115,7 @@ _cli_completions() { case "$compline" in 'download'*'--handler') - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "curl" "wget")" -- "$cur") + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "curl wget")" -- "$cur") ;; 'upload'*'--user') @@ -122,11 +123,11 @@ _cli_completions() { ;; 'completions'*) - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--help" "-h")" -- "$cur") + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--help -h")" -- "$cur") ;; 'd'*'--handler') - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "curl" "wget")" -- "$cur") + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "curl wget")" -- "$cur") ;; 'upload'*'-u') @@ -134,7 +135,8 @@ _cli_completions() { ;; 'download'*) - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A file -W "$(_cli_completions_filter "--force" "--handler" "--help" "-f" "-h")" -- "$cur") + compopt -o filenames 2>/dev/null + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A file -W "$(_cli_completions_filter "--force --handler --help -f -h")" -- "$cur") ;; 'u'*'--user') @@ -142,7 +144,8 @@ _cli_completions() { ;; 'upload'*) - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A directory -A user -W "$(_cli_completions_filter "--help" "--password" "--user" "-h" "-p" "-u" "CHANGELOG.md" "README.md")" -- "$cur") + compopt -o filenames 2>/dev/null + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A directory -A user -W "$(_cli_completions_filter "--help --password --user -h -p -u CHANGELOG.md README.md")" -- "$cur") ;; 'u'*'-u') @@ -150,15 +153,17 @@ _cli_completions() { ;; 'd'*) - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A file -W "$(_cli_completions_filter "--force" "--handler" "--help" "-f" "-h")" -- "$cur") + compopt -o filenames 2>/dev/null + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A file -W "$(_cli_completions_filter "--force --handler --help -f -h")" -- "$cur") ;; 'u'*) - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A directory -A user -W "$(_cli_completions_filter "--help" "--password" "--user" "-h" "-p" "-u" "CHANGELOG.md" "README.md")" -- "$cur") + compopt -o filenames 2>/dev/null + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A directory -A user -W "$(_cli_completions_filter "--help --password --user -h -p -u CHANGELOG.md README.md")" -- "$cur") ;; *) - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--help" "--version" "-h" "-v" "completions" "d" "download" "u" "upload")" -- "$cur") + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--help --version -h -v completions d download u upload")" -- "$cur") ;; esac diff --git a/spec/approvals/examples/render-mandoc b/spec/approvals/examples/render-mandoc index cabe46c7..e3a8095f 100644 --- a/spec/approvals/examples/render-mandoc +++ b/spec/approvals/examples/render-mandoc @@ -3,45 +3,45 @@ saved docs/download.1 download(1) Sample application download(1) NAME - download - Sample application + download - Sample application SYNOPSIS - download SOURCE [TARGET...] OPTIONS + download SOURCE [TARGET...] OPTIONS DESCRIPTION - Sample application + Sample application ARGUMENTS SOURCE - Source to download from + Source to download from - • Required + • Required - • Allowed Values: server1, server2 + • Allowed Values: server1, server2 TARGET - Target filename (default: same as source) + Target filename (default: same as source) - • Repeatable + • Repeatable OPTIONS --force, -f - Overwrite existing files + Overwrite existing files --debug, -d - Show debug information + Show debug information DEPENDENCIES aws-cli - Download from + Download from SEE ALSO - docker(1), docker-compose.yml(5) + docker(1), docker-compose.yml(5) ISSUE TRACKER - Report issues at + Report issues at AUTHORS - Lana Lang. + Lana Lang.