From 4455eb94ae147d8c28b81d90265b2c665e4dc023 Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Fri, 20 Feb 2026 01:53:52 +0200 Subject: [PATCH 1/3] - Fix dynamic completions (regression) --- AGENTS.md | 69 +++++++++++++++++++ lib/completely/pattern.rb | 20 +++++- lib/completely/templates/template.erb | 33 ++++++--- spec/README.md | 25 +++++-- spec/approvals/cli/generated-script | 37 ++++++---- spec/approvals/cli/generated-script-alt | 37 ++++++---- spec/approvals/cli/generated-wrapped-script | 37 ++++++---- .../approvals/cli/test/completely-tester-1.sh | 37 ++++++---- .../approvals/cli/test/completely-tester-2.sh | 37 ++++++---- spec/approvals/cli/test/completely-tester.sh | 37 ++++++---- spec/approvals/completions/function | 33 ++++++--- spec/approvals/completions/script | 33 ++++++--- .../completions/script-complete-options | 33 ++++++--- spec/approvals/completions/script-only-spaces | 33 ++++++--- spec/approvals/completions/script-with-debug | 33 ++++++--- spec/completely/commands/generate_spec.rb | 4 +- spec/completely/integration.yml | 13 +++- spec/fixtures/integration/dynamic.txt | 2 + spec/fixtures/integration/dynamic.yaml | 3 + spec/fixtures/tester/default.bash | 33 ++++++--- 20 files changed, 420 insertions(+), 169 deletions(-) create mode 100644 AGENTS.md create mode 100644 spec/fixtures/integration/dynamic.txt create mode 100644 spec/fixtures/integration/dynamic.yaml diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..252ce00 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,69 @@ +# AGENTS.md + +Guidance for coding agents working in this repository. + +## Repo Snapshot + +- Project: `completely` (Ruby gem that generates Bash completion scripts from YAML). +- Key generation code: + - `lib/completely/pattern.rb` + - `lib/completely/templates/template.erb` +- Core behavior tests: + - `spec/completely/integration_spec.rb` + - `spec/completely/commands/generate_spec.rb` + +## Working Rules + +- Keep changes minimal and localized, especially in: + - completion-word serialization (`Pattern`) + - generated script runtime behavior (`template.erb`) +- Do not change generated approvals. +- Do not run approval prompts interactively on behalf of the developer. +- If an approval spec changes, stop and ask the developer to review/approve manually. +- Prefer adding regression coverage in integration fixtures for completion behavior changes. + +## Fast Validation Loop + +Run these first after edits: + +```bash +respec tagged script_quality +respec only integration +``` + +If touching quoting/escaping or dynamic completions, also run: + +```bash +respec only pattern +respec only completions +``` + +## Formatting and Linting Notes + +- `shellcheck` and `shfmt` requirements are enforced by specs tagged `:script_quality` in `spec/completely/commands/generate_spec.rb`. +- `shfmt` uses flags: + - `shfmt -d -i 2 -ci completely.bash` +- Small whitespace differences in heredoc/redirect forms (like `<<<"$x"` vs `<<< "$x"`) can fail shfmt. + +## Approval Specs + +- Some specs use `rspec_approvals` and may prompt interactively if output changes. +- In non-interactive runs this can fail with `Errno::ENOTTY`. +- Approval decisions are always developer-owned. Agents should not approve/update snapshots. + +## Completion Semantics to Preserve + +- Literal YAML words with spaces/quotes must complete correctly. +- Dynamic `$(...)` entries must produce multiple completion candidates when command output contains multiple words. +- ``, ``, and other `<...>` entries map to `compgen -A ...` actions and should remain unaffected by `-W` serialization changes. + +## Manual Repro Pattern + +Useful local sanity check: + +```bash +cd dev +ruby -I../lib ../bin/completely test "cli " +``` + +Expected: sensible mixed output for dynamic values and quoted/spaced literals. diff --git a/lib/completely/pattern.rb b/lib/completely/pattern.rb index 1c6f6b7..62e954b 100644 --- a/lib/completely/pattern.rb +++ b/lib/completely/pattern.rb @@ -1,5 +1,7 @@ module Completely class Pattern + DYNAMIC_WORD_PREFIX = '__completely_dynamic__' + attr_reader :text, :completions, :function_name def initialize(text, completions, function_name) @@ -54,12 +56,24 @@ def compgen def compgen! result = [] result << actions.join(' ').to_s if actions.any? - result << %[-W "$(#{function_name} #{quoted_words.join ' '})"] if words.any? + result << %[-W "$(#{function_name} #{serialized_words.join ' '})"] if words.any? result.any? ? result.join(' ') : nil end - def quoted_words - @quoted_words ||= words.map { |word| %("#{escape_for_double_quotes word}") } + def serialized_words + @serialized_words ||= words.map { |word| serialize_word(word) } + end + + def serialize_word(word) + if dynamic_word?(word) + return %("#{DYNAMIC_WORD_PREFIX}#{escape_for_double_quotes word}") + end + + %("#{escape_for_double_quotes word}") + end + + def dynamic_word?(word) + word.match?(/\A\$\(.+\)\z/) end def escape_for_double_quotes(word) diff --git a/lib/completely/templates/template.erb b/lib/completely/templates/template.erb index 8b46f92..a2c1bf1 100644 --- a/lib/completely/templates/template.erb +++ b/lib/completely/templates/template.erb @@ -9,6 +9,7 @@ local cur=${COMP_WORDS[COMP_CWORD]} local result=() local want_options=0 + local dynamic_prefix="<%= Completely::Pattern::DYNAMIC_WORD_PREFIX %>" # words the user already typed (excluding the command itself) local used=() @@ -20,19 +21,29 @@ # 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 - [[ "${word:0:1}" == "-" ]] && continue - - for u in "${used[@]}"; do - if [[ "$u" == "$word" ]]; then - continue 2 - fi - done + local candidates=("$word") + if [[ "$word" == "$dynamic_prefix"* ]]; then + word="${word#"$dynamic_prefix"}" + word="${word//$'\r'/ }" + word="${word//$'\n'/ }" + read -r -a candidates <<<"$word" fi - # compgen -W expects shell-escaped words in one space-delimited string. - printf -v word '%q' "$word" - result+=("$word") + for candidate in "${candidates[@]}"; do + if ((!want_options)); then + [[ "${candidate:0:1}" == "-" ]] && continue + + for u in "${used[@]}"; do + if [[ "$u" == "$candidate" ]]; then + continue 2 + fi + done + fi + + # compgen -W expects shell-escaped words in one space-delimited string. + printf -v candidate '%q' "$candidate" + result+=("$candidate") + done done echo "${result[*]}" diff --git a/spec/README.md b/spec/README.md index 25343ef..c00362f 100644 --- a/spec/README.md +++ b/spec/README.md @@ -2,19 +2,29 @@ ## Running tests +You can run specs with `rspec` as usual. + +We recommend using [`respec`][2], which wraps common spec workflows: + ```bash -$ rspec +rspec # or -$ run spec -# or, to run just tests in a given file -$ run spec zsh -# or, to run just specs tagged with :focus -$ run spec :focus +respec ``` You might need to prefix the commands with `bundle exec`, depending on the way Ruby is installed. +Useful helper shortcuts: + +```bash +# script quality checks (shellcheck + shfmt generated script tests) +respec tagged script_quality + +# integration behavior suite +respec only integration +``` + ## Interactive Approvals Some tests may prompt you for an interactive approval of changes. This @@ -29,4 +39,5 @@ ZSH compatibility test is done by running the completely tester script inside a zsh container. This is all done automatically by `spec/completely/zsh_spec.rb`. -[1]: https://github.com/dannyben/rspec_approvals \ No newline at end of file +[1]: https://github.com/dannyben/rspec_approvals +[2]: https://github.com/DannyBen/respec diff --git a/spec/approvals/cli/generated-script b/spec/approvals/cli/generated-script index 8e7ee14..270eff7 100644 --- a/spec/approvals/cli/generated-script +++ b/spec/approvals/cli/generated-script @@ -9,6 +9,7 @@ _mygit_completions_filter() { local cur=${COMP_WORDS[COMP_CWORD]} local result=() local want_options=0 + local dynamic_prefix="__completely_dynamic__" # words the user already typed (excluding the command itself) local used=() @@ -20,19 +21,29 @@ _mygit_completions_filter() { # 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 - [[ "${word:0:1}" == "-" ]] && continue - - for u in "${used[@]}"; do - if [[ "$u" == "$word" ]]; then - continue 2 - fi - done + local candidates=("$word") + if [[ "$word" == "$dynamic_prefix"* ]]; then + word="${word#"$dynamic_prefix"}" + word="${word//$'\r'/ }" + word="${word//$'\n'/ }" + read -r -a candidates <<<"$word" fi - # compgen -W expects shell-escaped words in one space-delimited string. - printf -v word '%q' "$word" - result+=("$word") + for candidate in "${candidates[@]}"; do + if ((!want_options)); then + [[ "${candidate:0:1}" == "-" ]] && continue + + for u in "${used[@]}"; do + if [[ "$u" == "$candidate" ]]; then + continue 2 + fi + done + fi + + # compgen -W expects shell-escaped words in one space-delimited string. + printf -v candidate '%q' "$candidate" + result+=("$candidate") + done done echo "${result[*]}" @@ -50,11 +61,11 @@ _mygit_completions() { case "$compline" in 'status'*'--branch') - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "__completely_dynamic__$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") ;; 'status'*'-b') - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "__completely_dynamic__$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") ;; 'status'*) diff --git a/spec/approvals/cli/generated-script-alt b/spec/approvals/cli/generated-script-alt index 23eb09b..db0ecca 100644 --- a/spec/approvals/cli/generated-script-alt +++ b/spec/approvals/cli/generated-script-alt @@ -9,6 +9,7 @@ _mycomps_filter() { local cur=${COMP_WORDS[COMP_CWORD]} local result=() local want_options=0 + local dynamic_prefix="__completely_dynamic__" # words the user already typed (excluding the command itself) local used=() @@ -20,19 +21,29 @@ _mycomps_filter() { # 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 - [[ "${word:0:1}" == "-" ]] && continue - - for u in "${used[@]}"; do - if [[ "$u" == "$word" ]]; then - continue 2 - fi - done + local candidates=("$word") + if [[ "$word" == "$dynamic_prefix"* ]]; then + word="${word#"$dynamic_prefix"}" + word="${word//$'\r'/ }" + word="${word//$'\n'/ }" + read -r -a candidates <<<"$word" fi - # compgen -W expects shell-escaped words in one space-delimited string. - printf -v word '%q' "$word" - result+=("$word") + for candidate in "${candidates[@]}"; do + if ((!want_options)); then + [[ "${candidate:0:1}" == "-" ]] && continue + + for u in "${used[@]}"; do + if [[ "$u" == "$candidate" ]]; then + continue 2 + fi + done + fi + + # compgen -W expects shell-escaped words in one space-delimited string. + printf -v candidate '%q' "$candidate" + result+=("$candidate") + done done echo "${result[*]}" @@ -50,11 +61,11 @@ _mycomps() { case "$compline" in 'status'*'--branch') - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mycomps_filter "$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mycomps_filter "__completely_dynamic__$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") ;; 'status'*'-b') - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mycomps_filter "$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mycomps_filter "__completely_dynamic__$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") ;; 'status'*) diff --git a/spec/approvals/cli/generated-wrapped-script b/spec/approvals/cli/generated-wrapped-script index 875142e..da503c5 100644 --- a/spec/approvals/cli/generated-wrapped-script +++ b/spec/approvals/cli/generated-wrapped-script @@ -10,6 +10,7 @@ give_comps() { echo $' local cur=${COMP_WORDS[COMP_CWORD]}' echo $' local result=()' echo $' local want_options=0' + echo $' local dynamic_prefix="__completely_dynamic__"' echo $'' echo $' # words the user already typed (excluding the command itself)' echo $' local used=()' @@ -21,19 +22,29 @@ give_comps() { 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 $' [[ "${word:0:1}" == "-" ]] && continue' - echo $'' - echo $' for u in "${used[@]}"; do' - echo $' if [[ "$u" == "$word" ]]; then' - echo $' continue 2' - echo $' fi' - echo $' done' + echo $' local candidates=("$word")' + echo $' if [[ "$word" == "$dynamic_prefix"* ]]; then' + echo $' word="${word#"$dynamic_prefix"}"' + echo $' word="${word//$\'\r\'/ }"' + echo $' word="${word//$\'\n\'/ }"' + echo $' read -r -a candidates <<<"$word"' 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 $' for candidate in "${candidates[@]}"; do' + echo $' if ((!want_options)); then' + echo $' [[ "${candidate:0:1}" == "-" ]] && continue' + echo $'' + echo $' for u in "${used[@]}"; do' + echo $' if [[ "$u" == "$candidate" ]]; then' + echo $' continue 2' + echo $' fi' + echo $' done' + echo $' fi' + echo $'' + echo $' # compgen -W expects shell-escaped words in one space-delimited string.' + echo $' printf -v candidate \'%q\' "$candidate"' + echo $' result+=("$candidate")' + echo $' done' echo $' done' echo $'' echo $' echo "${result[*]}"' @@ -51,11 +62,11 @@ give_comps() { echo $'' echo $' case "$compline" in' echo $' \'status\'*\'--branch\')' - echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "$(git branch --format=\'%(refname:short)\' 2>/dev/null)")" -- "$cur")' + echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "__completely_dynamic__$(git branch --format=\'%(refname:short)\' 2>/dev/null)")" -- "$cur")' echo $' ;;' echo $'' echo $' \'status\'*\'-b\')' - echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "$(git branch --format=\'%(refname:short)\' 2>/dev/null)")" -- "$cur")' + echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "__completely_dynamic__$(git branch --format=\'%(refname:short)\' 2>/dev/null)")" -- "$cur")' echo $' ;;' echo $'' echo $' \'status\'*)' diff --git a/spec/approvals/cli/test/completely-tester-1.sh b/spec/approvals/cli/test/completely-tester-1.sh index 7d141a9..48b87e3 100644 --- a/spec/approvals/cli/test/completely-tester-1.sh +++ b/spec/approvals/cli/test/completely-tester-1.sh @@ -17,6 +17,7 @@ _mygit_completions_filter() { local cur=${COMP_WORDS[COMP_CWORD]} local result=() local want_options=0 + local dynamic_prefix="__completely_dynamic__" # words the user already typed (excluding the command itself) local used=() @@ -28,19 +29,29 @@ _mygit_completions_filter() { # 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 - [[ "${word:0:1}" == "-" ]] && continue - - for u in "${used[@]}"; do - if [[ "$u" == "$word" ]]; then - continue 2 - fi - done + local candidates=("$word") + if [[ "$word" == "$dynamic_prefix"* ]]; then + word="${word#"$dynamic_prefix"}" + word="${word//$'\r'/ }" + word="${word//$'\n'/ }" + read -r -a candidates <<<"$word" fi - # compgen -W expects shell-escaped words in one space-delimited string. - printf -v word '%q' "$word" - result+=("$word") + for candidate in "${candidates[@]}"; do + if ((!want_options)); then + [[ "${candidate:0:1}" == "-" ]] && continue + + for u in "${used[@]}"; do + if [[ "$u" == "$candidate" ]]; then + continue 2 + fi + done + fi + + # compgen -W expects shell-escaped words in one space-delimited string. + printf -v candidate '%q' "$candidate" + result+=("$candidate") + done done echo "${result[*]}" @@ -58,11 +69,11 @@ _mygit_completions() { case "$compline" in 'status'*'--branch') - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "__completely_dynamic__$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") ;; 'status'*'-b') - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "__completely_dynamic__$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") ;; 'status'*) diff --git a/spec/approvals/cli/test/completely-tester-2.sh b/spec/approvals/cli/test/completely-tester-2.sh index 73555b9..978816a 100644 --- a/spec/approvals/cli/test/completely-tester-2.sh +++ b/spec/approvals/cli/test/completely-tester-2.sh @@ -17,6 +17,7 @@ _mygit_completions_filter() { local cur=${COMP_WORDS[COMP_CWORD]} local result=() local want_options=0 + local dynamic_prefix="__completely_dynamic__" # words the user already typed (excluding the command itself) local used=() @@ -28,19 +29,29 @@ _mygit_completions_filter() { # 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 - [[ "${word:0:1}" == "-" ]] && continue - - for u in "${used[@]}"; do - if [[ "$u" == "$word" ]]; then - continue 2 - fi - done + local candidates=("$word") + if [[ "$word" == "$dynamic_prefix"* ]]; then + word="${word#"$dynamic_prefix"}" + word="${word//$'\r'/ }" + word="${word//$'\n'/ }" + read -r -a candidates <<<"$word" fi - # compgen -W expects shell-escaped words in one space-delimited string. - printf -v word '%q' "$word" - result+=("$word") + for candidate in "${candidates[@]}"; do + if ((!want_options)); then + [[ "${candidate:0:1}" == "-" ]] && continue + + for u in "${used[@]}"; do + if [[ "$u" == "$candidate" ]]; then + continue 2 + fi + done + fi + + # compgen -W expects shell-escaped words in one space-delimited string. + printf -v candidate '%q' "$candidate" + result+=("$candidate") + done done echo "${result[*]}" @@ -58,11 +69,11 @@ _mygit_completions() { case "$compline" in 'status'*'--branch') - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "__completely_dynamic__$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") ;; 'status'*'-b') - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "__completely_dynamic__$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") ;; 'status'*) diff --git a/spec/approvals/cli/test/completely-tester.sh b/spec/approvals/cli/test/completely-tester.sh index a3235d5..0f42b58 100644 --- a/spec/approvals/cli/test/completely-tester.sh +++ b/spec/approvals/cli/test/completely-tester.sh @@ -17,6 +17,7 @@ _mygit_completions_filter() { local cur=${COMP_WORDS[COMP_CWORD]} local result=() local want_options=0 + local dynamic_prefix="__completely_dynamic__" # words the user already typed (excluding the command itself) local used=() @@ -28,19 +29,29 @@ _mygit_completions_filter() { # 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 - [[ "${word:0:1}" == "-" ]] && continue - - for u in "${used[@]}"; do - if [[ "$u" == "$word" ]]; then - continue 2 - fi - done + local candidates=("$word") + if [[ "$word" == "$dynamic_prefix"* ]]; then + word="${word#"$dynamic_prefix"}" + word="${word//$'\r'/ }" + word="${word//$'\n'/ }" + read -r -a candidates <<<"$word" fi - # compgen -W expects shell-escaped words in one space-delimited string. - printf -v word '%q' "$word" - result+=("$word") + for candidate in "${candidates[@]}"; do + if ((!want_options)); then + [[ "${candidate:0:1}" == "-" ]] && continue + + for u in "${used[@]}"; do + if [[ "$u" == "$candidate" ]]; then + continue 2 + fi + done + fi + + # compgen -W expects shell-escaped words in one space-delimited string. + printf -v candidate '%q' "$candidate" + result+=("$candidate") + done done echo "${result[*]}" @@ -58,11 +69,11 @@ _mygit_completions() { case "$compline" in 'status'*'--branch') - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "__completely_dynamic__$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") ;; 'status'*'-b') - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "__completely_dynamic__$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") ;; 'status'*) diff --git a/spec/approvals/completions/function b/spec/approvals/completions/function index ffaa11c..c6d1d04 100644 --- a/spec/approvals/completions/function +++ b/spec/approvals/completions/function @@ -10,6 +10,7 @@ send_completions() { echo $' local cur=${COMP_WORDS[COMP_CWORD]}' echo $' local result=()' echo $' local want_options=0' + echo $' local dynamic_prefix="__completely_dynamic__"' echo $'' echo $' # words the user already typed (excluding the command itself)' echo $' local used=()' @@ -21,19 +22,29 @@ send_completions() { 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 $' [[ "${word:0:1}" == "-" ]] && continue' - echo $'' - echo $' for u in "${used[@]}"; do' - echo $' if [[ "$u" == "$word" ]]; then' - echo $' continue 2' - echo $' fi' - echo $' done' + echo $' local candidates=("$word")' + echo $' if [[ "$word" == "$dynamic_prefix"* ]]; then' + echo $' word="${word#"$dynamic_prefix"}"' + echo $' word="${word//$\'\r\'/ }"' + echo $' word="${word//$\'\n\'/ }"' + echo $' read -r -a candidates <<<"$word"' 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 $' for candidate in "${candidates[@]}"; do' + echo $' if ((!want_options)); then' + echo $' [[ "${candidate:0:1}" == "-" ]] && continue' + echo $'' + echo $' for u in "${used[@]}"; do' + echo $' if [[ "$u" == "$candidate" ]]; then' + echo $' continue 2' + echo $' fi' + echo $' done' + echo $' fi' + echo $'' + echo $' # compgen -W expects shell-escaped words in one space-delimited string.' + echo $' printf -v candidate \'%q\' "$candidate"' + echo $' result+=("$candidate")' + echo $' done' echo $' done' echo $'' echo $' echo "${result[*]}"' diff --git a/spec/approvals/completions/script b/spec/approvals/completions/script index bb33f7b..06641ed 100644 --- a/spec/approvals/completions/script +++ b/spec/approvals/completions/script @@ -9,6 +9,7 @@ _completely_completions_filter() { local cur=${COMP_WORDS[COMP_CWORD]} local result=() local want_options=0 + local dynamic_prefix="__completely_dynamic__" # words the user already typed (excluding the command itself) local used=() @@ -20,19 +21,29 @@ _completely_completions_filter() { # 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 - [[ "${word:0:1}" == "-" ]] && continue - - for u in "${used[@]}"; do - if [[ "$u" == "$word" ]]; then - continue 2 - fi - done + local candidates=("$word") + if [[ "$word" == "$dynamic_prefix"* ]]; then + word="${word#"$dynamic_prefix"}" + word="${word//$'\r'/ }" + word="${word//$'\n'/ }" + read -r -a candidates <<<"$word" fi - # compgen -W expects shell-escaped words in one space-delimited string. - printf -v word '%q' "$word" - result+=("$word") + for candidate in "${candidates[@]}"; do + if ((!want_options)); then + [[ "${candidate:0:1}" == "-" ]] && continue + + for u in "${used[@]}"; do + if [[ "$u" == "$candidate" ]]; then + continue 2 + fi + done + fi + + # compgen -W expects shell-escaped words in one space-delimited string. + printf -v candidate '%q' "$candidate" + result+=("$candidate") + done done echo "${result[*]}" diff --git a/spec/approvals/completions/script-complete-options b/spec/approvals/completions/script-complete-options index 8e6347c..6ecdc99 100644 --- a/spec/approvals/completions/script-complete-options +++ b/spec/approvals/completions/script-complete-options @@ -9,6 +9,7 @@ _mygit_completions_filter() { local cur=${COMP_WORDS[COMP_CWORD]} local result=() local want_options=0 + local dynamic_prefix="__completely_dynamic__" # words the user already typed (excluding the command itself) local used=() @@ -20,19 +21,29 @@ _mygit_completions_filter() { # 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 - [[ "${word:0:1}" == "-" ]] && continue - - for u in "${used[@]}"; do - if [[ "$u" == "$word" ]]; then - continue 2 - fi - done + local candidates=("$word") + if [[ "$word" == "$dynamic_prefix"* ]]; then + word="${word#"$dynamic_prefix"}" + word="${word//$'\r'/ }" + word="${word//$'\n'/ }" + read -r -a candidates <<<"$word" fi - # compgen -W expects shell-escaped words in one space-delimited string. - printf -v word '%q' "$word" - result+=("$word") + for candidate in "${candidates[@]}"; do + if ((!want_options)); then + [[ "${candidate:0:1}" == "-" ]] && continue + + for u in "${used[@]}"; do + if [[ "$u" == "$candidate" ]]; then + continue 2 + fi + done + fi + + # compgen -W expects shell-escaped words in one space-delimited string. + printf -v candidate '%q' "$candidate" + result+=("$candidate") + done done echo "${result[*]}" diff --git a/spec/approvals/completions/script-only-spaces b/spec/approvals/completions/script-only-spaces index f980115..401453f 100644 --- a/spec/approvals/completions/script-only-spaces +++ b/spec/approvals/completions/script-only-spaces @@ -9,6 +9,7 @@ _completely_completions_filter() { local cur=${COMP_WORDS[COMP_CWORD]} local result=() local want_options=0 + local dynamic_prefix="__completely_dynamic__" # words the user already typed (excluding the command itself) local used=() @@ -20,19 +21,29 @@ _completely_completions_filter() { # 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 - [[ "${word:0:1}" == "-" ]] && continue - - for u in "${used[@]}"; do - if [[ "$u" == "$word" ]]; then - continue 2 - fi - done + local candidates=("$word") + if [[ "$word" == "$dynamic_prefix"* ]]; then + word="${word#"$dynamic_prefix"}" + word="${word//$'\r'/ }" + word="${word//$'\n'/ }" + read -r -a candidates <<<"$word" fi - # compgen -W expects shell-escaped words in one space-delimited string. - printf -v word '%q' "$word" - result+=("$word") + for candidate in "${candidates[@]}"; do + if ((!want_options)); then + [[ "${candidate:0:1}" == "-" ]] && continue + + for u in "${used[@]}"; do + if [[ "$u" == "$candidate" ]]; then + continue 2 + fi + done + fi + + # compgen -W expects shell-escaped words in one space-delimited string. + printf -v candidate '%q' "$candidate" + result+=("$candidate") + done done echo "${result[*]}" diff --git a/spec/approvals/completions/script-with-debug b/spec/approvals/completions/script-with-debug index 7a3673c..688a963 100644 --- a/spec/approvals/completions/script-with-debug +++ b/spec/approvals/completions/script-with-debug @@ -9,6 +9,7 @@ _completely_completions_filter() { local cur=${COMP_WORDS[COMP_CWORD]} local result=() local want_options=0 + local dynamic_prefix="__completely_dynamic__" # words the user already typed (excluding the command itself) local used=() @@ -20,19 +21,29 @@ _completely_completions_filter() { # 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 - [[ "${word:0:1}" == "-" ]] && continue - - for u in "${used[@]}"; do - if [[ "$u" == "$word" ]]; then - continue 2 - fi - done + local candidates=("$word") + if [[ "$word" == "$dynamic_prefix"* ]]; then + word="${word#"$dynamic_prefix"}" + word="${word//$'\r'/ }" + word="${word//$'\n'/ }" + read -r -a candidates <<<"$word" fi - # compgen -W expects shell-escaped words in one space-delimited string. - printf -v word '%q' "$word" - result+=("$word") + for candidate in "${candidates[@]}"; do + if ((!want_options)); then + [[ "${candidate:0:1}" == "-" ]] && continue + + for u in "${used[@]}"; do + if [[ "$u" == "$candidate" ]]; then + continue 2 + fi + done + fi + + # compgen -W expects shell-escaped words in one space-delimited string. + printf -v candidate '%q' "$candidate" + result+=("$candidate") + done done echo "${result[*]}" diff --git a/spec/completely/commands/generate_spec.rb b/spec/completely/commands/generate_spec.rb index 9d152ea..65eee8e 100644 --- a/spec/completely/commands/generate_spec.rb +++ b/spec/completely/commands/generate_spec.rb @@ -22,12 +22,12 @@ expect(File.read 'completely.bash').to match_approval('cli/generated-script') end - it 'generates a shellcheck compliant script' do + it 'generates a shellcheck compliant script', :script_quality do expect { subject.execute %w[generate] }.to output_approval('cli/generate/no-args') expect(`shellcheck completely.bash 2>&1`).to be_empty end - it 'generates a shfmt compliant script' do + it 'generates a shfmt compliant script', :script_quality do expect { subject.execute %w[generate] }.to output_approval('cli/generate/no-args') expect(`shfmt -d -i 2 -ci completely.bash 2>&1`).to be_empty end diff --git a/spec/completely/integration.yml b/spec/completely/integration.yml index eb90815..05f3b53 100644 --- a/spec/completely/integration.yml +++ b/spec/completely/integration.yml @@ -9,7 +9,7 @@ ftp: expected: [download] - compline: "ftp download " - expected: [another-dir, dir with spaces, dummy-dir, file with spaces.txt, ftp.yaml, gradual.yaml, wildcard.yaml] + expected: [another-dir, dir with spaces, dummy-dir, dynamic.txt, dynamic.yaml, file with spaces.txt, ftp.yaml, gradual.yaml, wildcard.yaml] - compline: "ftp download -" expected: [--help, --override] @@ -29,6 +29,16 @@ ftp: - compline: "/anything/goes/ftp list -" expected: [--help, --short] +dynamic: +- compline: "dynamic " + expected: [foo bar, hello, help] + +- compline: "dynamic h" + expected: [hello, help] + +- compline: "dynamic f" + expected: [foo bar] + gradual: - compline: "cli " expected: [command, conquer] @@ -93,4 +103,3 @@ wildcard: - compline: "wildcard download --contest " expected: [everything, nothing] - diff --git a/spec/fixtures/integration/dynamic.txt b/spec/fixtures/integration/dynamic.txt new file mode 100644 index 0000000..da2146a --- /dev/null +++ b/spec/fixtures/integration/dynamic.txt @@ -0,0 +1,2 @@ +hello +help diff --git a/spec/fixtures/integration/dynamic.yaml b/spec/fixtures/integration/dynamic.yaml new file mode 100644 index 0000000..c677ab1 --- /dev/null +++ b/spec/fixtures/integration/dynamic.yaml @@ -0,0 +1,3 @@ +dynamic: +- $(cat dynamic.txt) +- foo bar diff --git a/spec/fixtures/tester/default.bash b/spec/fixtures/tester/default.bash index c4df327..f31db9d 100644 --- a/spec/fixtures/tester/default.bash +++ b/spec/fixtures/tester/default.bash @@ -9,6 +9,7 @@ _cli_completions_filter() { local cur=${COMP_WORDS[COMP_CWORD]} local result=() local want_options=0 + local dynamic_prefix="__completely_dynamic__" # words the user already typed (excluding the command itself) local used=() @@ -20,19 +21,29 @@ _cli_completions_filter() { # 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 - [[ "${word:0:1}" == "-" ]] && continue - - for u in "${used[@]}"; do - if [[ "$u" == "$word" ]]; then - continue 2 - fi - done + local candidates=("$word") + if [[ "$word" == "$dynamic_prefix"* ]]; then + word="${word#"$dynamic_prefix"}" + word="${word//$'\r'/ }" + word="${word//$'\n'/ }" + read -r -a candidates <<<"$word" fi - # compgen -W expects shell-escaped words in one space-delimited string. - printf -v word '%q' "$word" - result+=("$word") + for candidate in "${candidates[@]}"; do + if ((!want_options)); then + [[ "${candidate:0:1}" == "-" ]] && continue + + for u in "${used[@]}"; do + if [[ "$u" == "$candidate" ]]; then + continue 2 + fi + done + fi + + # compgen -W expects shell-escaped words in one space-delimited string. + printf -v candidate '%q' "$candidate" + result+=("$candidate") + done done echo "${result[*]}" From 95ea525ba36a5bfb328908ae218d7f690b2ca7e7 Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Fri, 20 Feb 2026 02:33:37 +0200 Subject: [PATCH 2/3] simplify dynamic completion check --- lib/completely/pattern.rb | 6 +-- lib/completely/templates/template.erb | 34 ++++++--------- lib/completely/version.rb | 2 +- spec/approvals/cli/generated-script | 42 ++++++++----------- spec/approvals/cli/generated-script-alt | 42 ++++++++----------- spec/approvals/cli/generated-wrapped-script | 42 ++++++++----------- .../approvals/cli/test/completely-tester-1.sh | 42 ++++++++----------- .../approvals/cli/test/completely-tester-2.sh | 42 ++++++++----------- spec/approvals/cli/test/completely-tester.sh | 42 ++++++++----------- spec/approvals/completions/function | 36 +++++++--------- spec/approvals/completions/script | 36 +++++++--------- .../completions/script-complete-options | 34 ++++++--------- spec/approvals/completions/script-only-spaces | 35 ++++++---------- spec/approvals/completions/script-with-debug | 33 +++++---------- spec/fixtures/tester/default.bash | 37 +++++++--------- 15 files changed, 201 insertions(+), 304 deletions(-) diff --git a/lib/completely/pattern.rb b/lib/completely/pattern.rb index 62e954b..92333a2 100644 --- a/lib/completely/pattern.rb +++ b/lib/completely/pattern.rb @@ -1,7 +1,5 @@ module Completely class Pattern - DYNAMIC_WORD_PREFIX = '__completely_dynamic__' - attr_reader :text, :completions, :function_name def initialize(text, completions, function_name) @@ -65,9 +63,7 @@ def serialized_words end def serialize_word(word) - if dynamic_word?(word) - return %("#{DYNAMIC_WORD_PREFIX}#{escape_for_double_quotes word}") - end + return word if dynamic_word?(word) %("#{escape_for_double_quotes word}") end diff --git a/lib/completely/templates/template.erb b/lib/completely/templates/template.erb index a2c1bf1..45bb795 100644 --- a/lib/completely/templates/template.erb +++ b/lib/completely/templates/template.erb @@ -9,7 +9,6 @@ local cur=${COMP_WORDS[COMP_CWORD]} local result=() local want_options=0 - local dynamic_prefix="<%= Completely::Pattern::DYNAMIC_WORD_PREFIX %>" # words the user already typed (excluding the command itself) local used=() @@ -21,29 +20,19 @@ # Completing a non-option: drop options and already-used words. [[ "${cur:0:1}" == "-" ]] && want_options=1 for word in "${words[@]}"; do - local candidates=("$word") - if [[ "$word" == "$dynamic_prefix"* ]]; then - word="${word#"$dynamic_prefix"}" - word="${word//$'\r'/ }" - word="${word//$'\n'/ }" - read -r -a candidates <<<"$word" - fi - - for candidate in "${candidates[@]}"; do - if ((!want_options)); then - [[ "${candidate:0:1}" == "-" ]] && continue + if ((!want_options)); then + [[ "${word:0:1}" == "-" ]] && continue - for u in "${used[@]}"; do - if [[ "$u" == "$candidate" ]]; then - continue 2 - fi - done - fi + for u in "${used[@]}"; do + if [[ "$u" == "$word" ]]; then + continue 2 + fi + done + fi - # compgen -W expects shell-escaped words in one space-delimited string. - printf -v candidate '%q' "$candidate" - result+=("$candidate") - done + # compgen -W expects shell-escaped words in one space-delimited string. + printf -v word '%q' "$word" + result+=("$word") done echo "${result[*]}" @@ -70,6 +59,7 @@ % patterns.each do |pattern| % next if pattern.empty? <%= pattern.case_string %>) + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen <%= pattern.compgen %> -- "$cur") ;; diff --git a/lib/completely/version.rb b/lib/completely/version.rb index 407dc87..caa939d 100644 --- a/lib/completely/version.rb +++ b/lib/completely/version.rb @@ -1,3 +1,3 @@ module Completely - VERSION = '0.7.4' + VERSION = '0.8.0.rc1' end diff --git a/spec/approvals/cli/generated-script b/spec/approvals/cli/generated-script index 270eff7..8ceca03 100644 --- a/spec/approvals/cli/generated-script +++ b/spec/approvals/cli/generated-script @@ -9,7 +9,6 @@ _mygit_completions_filter() { local cur=${COMP_WORDS[COMP_CWORD]} local result=() local want_options=0 - local dynamic_prefix="__completely_dynamic__" # words the user already typed (excluding the command itself) local used=() @@ -21,29 +20,19 @@ _mygit_completions_filter() { # Completing a non-option: drop options and already-used words. [[ "${cur:0:1}" == "-" ]] && want_options=1 for word in "${words[@]}"; do - local candidates=("$word") - if [[ "$word" == "$dynamic_prefix"* ]]; then - word="${word#"$dynamic_prefix"}" - word="${word//$'\r'/ }" - word="${word//$'\n'/ }" - read -r -a candidates <<<"$word" + if ((!want_options)); then + [[ "${word:0:1}" == "-" ]] && continue + + for u in "${used[@]}"; do + if [[ "$u" == "$word" ]]; then + continue 2 + fi + done fi - for candidate in "${candidates[@]}"; do - if ((!want_options)); then - [[ "${candidate:0:1}" == "-" ]] && continue - - for u in "${used[@]}"; do - if [[ "$u" == "$candidate" ]]; then - continue 2 - fi - done - fi - - # compgen -W expects shell-escaped words in one space-delimited string. - printf -v candidate '%q' "$candidate" - result+=("$candidate") - done + # compgen -W expects shell-escaped words in one space-delimited string. + printf -v word '%q' "$word" + result+=("$word") done echo "${result[*]}" @@ -61,22 +50,27 @@ _mygit_completions() { case "$compline" in 'status'*'--branch') - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "__completely_dynamic__$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter $(git branch --format='%(refname:short)' 2>/dev/null))" -- "$cur") ;; 'status'*'-b') - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "__completely_dynamic__$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter $(git branch --format='%(refname:short)' 2>/dev/null))" -- "$cur") ;; 'status'*) + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "--help" "--verbose" "--branch" "-b")" -- "$cur") ;; 'init'*) + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A directory -W "$(_mygit_completions_filter "--bare")" -- "$cur") ;; *) + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "-h" "-v" "--help" "--version" "init" "status")" -- "$cur") ;; diff --git a/spec/approvals/cli/generated-script-alt b/spec/approvals/cli/generated-script-alt index db0ecca..e5ac987 100644 --- a/spec/approvals/cli/generated-script-alt +++ b/spec/approvals/cli/generated-script-alt @@ -9,7 +9,6 @@ _mycomps_filter() { local cur=${COMP_WORDS[COMP_CWORD]} local result=() local want_options=0 - local dynamic_prefix="__completely_dynamic__" # words the user already typed (excluding the command itself) local used=() @@ -21,29 +20,19 @@ _mycomps_filter() { # Completing a non-option: drop options and already-used words. [[ "${cur:0:1}" == "-" ]] && want_options=1 for word in "${words[@]}"; do - local candidates=("$word") - if [[ "$word" == "$dynamic_prefix"* ]]; then - word="${word#"$dynamic_prefix"}" - word="${word//$'\r'/ }" - word="${word//$'\n'/ }" - read -r -a candidates <<<"$word" + if ((!want_options)); then + [[ "${word:0:1}" == "-" ]] && continue + + for u in "${used[@]}"; do + if [[ "$u" == "$word" ]]; then + continue 2 + fi + done fi - for candidate in "${candidates[@]}"; do - if ((!want_options)); then - [[ "${candidate:0:1}" == "-" ]] && continue - - for u in "${used[@]}"; do - if [[ "$u" == "$candidate" ]]; then - continue 2 - fi - done - fi - - # compgen -W expects shell-escaped words in one space-delimited string. - printf -v candidate '%q' "$candidate" - result+=("$candidate") - done + # compgen -W expects shell-escaped words in one space-delimited string. + printf -v word '%q' "$word" + result+=("$word") done echo "${result[*]}" @@ -61,22 +50,27 @@ _mycomps() { case "$compline" in 'status'*'--branch') - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mycomps_filter "__completely_dynamic__$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mycomps_filter $(git branch --format='%(refname:short)' 2>/dev/null))" -- "$cur") ;; 'status'*'-b') - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mycomps_filter "__completely_dynamic__$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mycomps_filter $(git branch --format='%(refname:short)' 2>/dev/null))" -- "$cur") ;; 'status'*) + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mycomps_filter "--help" "--verbose" "--branch" "-b")" -- "$cur") ;; 'init'*) + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A directory -W "$(_mycomps_filter "--bare")" -- "$cur") ;; *) + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mycomps_filter "-h" "-v" "--help" "--version" "init" "status")" -- "$cur") ;; diff --git a/spec/approvals/cli/generated-wrapped-script b/spec/approvals/cli/generated-wrapped-script index da503c5..0770945 100644 --- a/spec/approvals/cli/generated-wrapped-script +++ b/spec/approvals/cli/generated-wrapped-script @@ -10,7 +10,6 @@ give_comps() { echo $' local cur=${COMP_WORDS[COMP_CWORD]}' echo $' local result=()' echo $' local want_options=0' - echo $' local dynamic_prefix="__completely_dynamic__"' echo $'' echo $' # words the user already typed (excluding the command itself)' echo $' local used=()' @@ -22,29 +21,19 @@ give_comps() { 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 $' local candidates=("$word")' - echo $' if [[ "$word" == "$dynamic_prefix"* ]]; then' - echo $' word="${word#"$dynamic_prefix"}"' - echo $' word="${word//$\'\r\'/ }"' - echo $' word="${word//$\'\n\'/ }"' - echo $' read -r -a candidates <<<"$word"' + echo $' if ((!want_options)); then' + echo $' [[ "${word:0:1}" == "-" ]] && continue' + echo $'' + echo $' for u in "${used[@]}"; do' + echo $' if [[ "$u" == "$word" ]]; then' + echo $' continue 2' + echo $' fi' + echo $' done' echo $' fi' echo $'' - echo $' for candidate in "${candidates[@]}"; do' - echo $' if ((!want_options)); then' - echo $' [[ "${candidate:0:1}" == "-" ]] && continue' - echo $'' - echo $' for u in "${used[@]}"; do' - echo $' if [[ "$u" == "$candidate" ]]; then' - echo $' continue 2' - echo $' fi' - echo $' done' - echo $' fi' - echo $'' - echo $' # compgen -W expects shell-escaped words in one space-delimited string.' - echo $' printf -v candidate \'%q\' "$candidate"' - echo $' result+=("$candidate")' - echo $' done' + echo $' # compgen -W expects shell-escaped words in one space-delimited string.' + echo $' printf -v word \'%q\' "$word"' + echo $' result+=("$word")' echo $' done' echo $'' echo $' echo "${result[*]}"' @@ -62,22 +51,27 @@ give_comps() { echo $'' echo $' case "$compline" in' echo $' \'status\'*\'--branch\')' - echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "__completely_dynamic__$(git branch --format=\'%(refname:short)\' 2>/dev/null)")" -- "$cur")' + echo $' # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions' + echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter $(git branch --format=\'%(refname:short)\' 2>/dev/null))" -- "$cur")' echo $' ;;' echo $'' echo $' \'status\'*\'-b\')' - echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "__completely_dynamic__$(git branch --format=\'%(refname:short)\' 2>/dev/null)")" -- "$cur")' + echo $' # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions' + echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter $(git branch --format=\'%(refname:short)\' 2>/dev/null))" -- "$cur")' echo $' ;;' echo $'' echo $' \'status\'*)' + echo $' # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions' echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "--help" "--verbose" "--branch" "-b")" -- "$cur")' echo $' ;;' echo $'' echo $' \'init\'*)' + echo $' # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions' echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A directory -W "$(_mygit_completions_filter "--bare")" -- "$cur")' echo $' ;;' echo $'' echo $' *)' + echo $' # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions' echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "-h" "-v" "--help" "--version" "init" "status")" -- "$cur")' echo $' ;;' echo $'' diff --git a/spec/approvals/cli/test/completely-tester-1.sh b/spec/approvals/cli/test/completely-tester-1.sh index 48b87e3..eb591ef 100644 --- a/spec/approvals/cli/test/completely-tester-1.sh +++ b/spec/approvals/cli/test/completely-tester-1.sh @@ -17,7 +17,6 @@ _mygit_completions_filter() { local cur=${COMP_WORDS[COMP_CWORD]} local result=() local want_options=0 - local dynamic_prefix="__completely_dynamic__" # words the user already typed (excluding the command itself) local used=() @@ -29,29 +28,19 @@ _mygit_completions_filter() { # Completing a non-option: drop options and already-used words. [[ "${cur:0:1}" == "-" ]] && want_options=1 for word in "${words[@]}"; do - local candidates=("$word") - if [[ "$word" == "$dynamic_prefix"* ]]; then - word="${word#"$dynamic_prefix"}" - word="${word//$'\r'/ }" - word="${word//$'\n'/ }" - read -r -a candidates <<<"$word" + if ((!want_options)); then + [[ "${word:0:1}" == "-" ]] && continue + + for u in "${used[@]}"; do + if [[ "$u" == "$word" ]]; then + continue 2 + fi + done fi - for candidate in "${candidates[@]}"; do - if ((!want_options)); then - [[ "${candidate:0:1}" == "-" ]] && continue - - for u in "${used[@]}"; do - if [[ "$u" == "$candidate" ]]; then - continue 2 - fi - done - fi - - # compgen -W expects shell-escaped words in one space-delimited string. - printf -v candidate '%q' "$candidate" - result+=("$candidate") - done + # compgen -W expects shell-escaped words in one space-delimited string. + printf -v word '%q' "$word" + result+=("$word") done echo "${result[*]}" @@ -69,22 +58,27 @@ _mygit_completions() { case "$compline" in 'status'*'--branch') - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "__completely_dynamic__$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter $(git branch --format='%(refname:short)' 2>/dev/null))" -- "$cur") ;; 'status'*'-b') - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "__completely_dynamic__$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter $(git branch --format='%(refname:short)' 2>/dev/null))" -- "$cur") ;; 'status'*) + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "--help" "--verbose" "--branch" "-b")" -- "$cur") ;; 'init'*) + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A directory -W "$(_mygit_completions_filter "--bare")" -- "$cur") ;; *) + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "-h" "-v" "--help" "--version" "init" "status")" -- "$cur") ;; diff --git a/spec/approvals/cli/test/completely-tester-2.sh b/spec/approvals/cli/test/completely-tester-2.sh index 978816a..2fec8fe 100644 --- a/spec/approvals/cli/test/completely-tester-2.sh +++ b/spec/approvals/cli/test/completely-tester-2.sh @@ -17,7 +17,6 @@ _mygit_completions_filter() { local cur=${COMP_WORDS[COMP_CWORD]} local result=() local want_options=0 - local dynamic_prefix="__completely_dynamic__" # words the user already typed (excluding the command itself) local used=() @@ -29,29 +28,19 @@ _mygit_completions_filter() { # Completing a non-option: drop options and already-used words. [[ "${cur:0:1}" == "-" ]] && want_options=1 for word in "${words[@]}"; do - local candidates=("$word") - if [[ "$word" == "$dynamic_prefix"* ]]; then - word="${word#"$dynamic_prefix"}" - word="${word//$'\r'/ }" - word="${word//$'\n'/ }" - read -r -a candidates <<<"$word" + if ((!want_options)); then + [[ "${word:0:1}" == "-" ]] && continue + + for u in "${used[@]}"; do + if [[ "$u" == "$word" ]]; then + continue 2 + fi + done fi - for candidate in "${candidates[@]}"; do - if ((!want_options)); then - [[ "${candidate:0:1}" == "-" ]] && continue - - for u in "${used[@]}"; do - if [[ "$u" == "$candidate" ]]; then - continue 2 - fi - done - fi - - # compgen -W expects shell-escaped words in one space-delimited string. - printf -v candidate '%q' "$candidate" - result+=("$candidate") - done + # compgen -W expects shell-escaped words in one space-delimited string. + printf -v word '%q' "$word" + result+=("$word") done echo "${result[*]}" @@ -69,22 +58,27 @@ _mygit_completions() { case "$compline" in 'status'*'--branch') - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "__completely_dynamic__$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter $(git branch --format='%(refname:short)' 2>/dev/null))" -- "$cur") ;; 'status'*'-b') - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "__completely_dynamic__$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter $(git branch --format='%(refname:short)' 2>/dev/null))" -- "$cur") ;; 'status'*) + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "--help" "--verbose" "--branch" "-b")" -- "$cur") ;; 'init'*) + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A directory -W "$(_mygit_completions_filter "--bare")" -- "$cur") ;; *) + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "-h" "-v" "--help" "--version" "init" "status")" -- "$cur") ;; diff --git a/spec/approvals/cli/test/completely-tester.sh b/spec/approvals/cli/test/completely-tester.sh index 0f42b58..76c486e 100644 --- a/spec/approvals/cli/test/completely-tester.sh +++ b/spec/approvals/cli/test/completely-tester.sh @@ -17,7 +17,6 @@ _mygit_completions_filter() { local cur=${COMP_WORDS[COMP_CWORD]} local result=() local want_options=0 - local dynamic_prefix="__completely_dynamic__" # words the user already typed (excluding the command itself) local used=() @@ -29,29 +28,19 @@ _mygit_completions_filter() { # Completing a non-option: drop options and already-used words. [[ "${cur:0:1}" == "-" ]] && want_options=1 for word in "${words[@]}"; do - local candidates=("$word") - if [[ "$word" == "$dynamic_prefix"* ]]; then - word="${word#"$dynamic_prefix"}" - word="${word//$'\r'/ }" - word="${word//$'\n'/ }" - read -r -a candidates <<<"$word" + if ((!want_options)); then + [[ "${word:0:1}" == "-" ]] && continue + + for u in "${used[@]}"; do + if [[ "$u" == "$word" ]]; then + continue 2 + fi + done fi - for candidate in "${candidates[@]}"; do - if ((!want_options)); then - [[ "${candidate:0:1}" == "-" ]] && continue - - for u in "${used[@]}"; do - if [[ "$u" == "$candidate" ]]; then - continue 2 - fi - done - fi - - # compgen -W expects shell-escaped words in one space-delimited string. - printf -v candidate '%q' "$candidate" - result+=("$candidate") - done + # compgen -W expects shell-escaped words in one space-delimited string. + printf -v word '%q' "$word" + result+=("$word") done echo "${result[*]}" @@ -69,22 +58,27 @@ _mygit_completions() { case "$compline" in 'status'*'--branch') - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "__completely_dynamic__$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter $(git branch --format='%(refname:short)' 2>/dev/null))" -- "$cur") ;; 'status'*'-b') - while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "__completely_dynamic__$(git branch --format='%(refname:short)' 2>/dev/null)")" -- "$cur") + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions + while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter $(git branch --format='%(refname:short)' 2>/dev/null))" -- "$cur") ;; 'status'*) + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "--help" "--verbose" "--branch" "-b")" -- "$cur") ;; 'init'*) + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A directory -W "$(_mygit_completions_filter "--bare")" -- "$cur") ;; *) + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "-h" "-v" "--help" "--version" "init" "status")" -- "$cur") ;; diff --git a/spec/approvals/completions/function b/spec/approvals/completions/function index c6d1d04..788f661 100644 --- a/spec/approvals/completions/function +++ b/spec/approvals/completions/function @@ -10,7 +10,6 @@ send_completions() { echo $' local cur=${COMP_WORDS[COMP_CWORD]}' echo $' local result=()' echo $' local want_options=0' - echo $' local dynamic_prefix="__completely_dynamic__"' echo $'' echo $' # words the user already typed (excluding the command itself)' echo $' local used=()' @@ -22,29 +21,19 @@ send_completions() { 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 $' local candidates=("$word")' - echo $' if [[ "$word" == "$dynamic_prefix"* ]]; then' - echo $' word="${word#"$dynamic_prefix"}"' - echo $' word="${word//$\'\r\'/ }"' - echo $' word="${word//$\'\n\'/ }"' - echo $' read -r -a candidates <<<"$word"' - echo $' fi' - echo $'' - echo $' for candidate in "${candidates[@]}"; do' - echo $' if ((!want_options)); then' - echo $' [[ "${candidate:0:1}" == "-" ]] && continue' + echo $' if ((!want_options)); then' + echo $' [[ "${word:0:1}" == "-" ]] && continue' echo $'' - echo $' for u in "${used[@]}"; do' - echo $' if [[ "$u" == "$candidate" ]]; then' - echo $' continue 2' - echo $' fi' - echo $' done' - echo $' fi' + echo $' for u in "${used[@]}"; do' + echo $' if [[ "$u" == "$word" ]]; then' + echo $' continue 2' + echo $' fi' + echo $' done' + echo $' fi' echo $'' - echo $' # compgen -W expects shell-escaped words in one space-delimited string.' - echo $' printf -v candidate \'%q\' "$candidate"' - echo $' result+=("$candidate")' - echo $' done' + echo $' # compgen -W expects shell-escaped words in one space-delimited string.' + echo $' printf -v word \'%q\' "$word"' + echo $' result+=("$word")' echo $' done' echo $'' echo $' echo "${result[*]}"' @@ -62,14 +51,17 @@ send_completions() { echo $'' echo $' case "$compline" in' echo $' \'generate\'*)' + echo $' # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions' echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A directory -W "$(_completely_completions_filter "--help" "--force")" -- "$cur")' echo $' ;;' echo $'' echo $' \'init\'*)' + echo $' # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions' echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_completely_completions_filter "--help")" -- "$cur")' echo $' ;;' echo $'' echo $' *)' + echo $' # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions' echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_completely_completions_filter "--help" "--version" "init" "generate")" -- "$cur")' echo $' ;;' echo $'' diff --git a/spec/approvals/completions/script b/spec/approvals/completions/script index 06641ed..1e34cc7 100644 --- a/spec/approvals/completions/script +++ b/spec/approvals/completions/script @@ -9,7 +9,6 @@ _completely_completions_filter() { local cur=${COMP_WORDS[COMP_CWORD]} local result=() local want_options=0 - local dynamic_prefix="__completely_dynamic__" # words the user already typed (excluding the command itself) local used=() @@ -21,29 +20,19 @@ _completely_completions_filter() { # Completing a non-option: drop options and already-used words. [[ "${cur:0:1}" == "-" ]] && want_options=1 for word in "${words[@]}"; do - local candidates=("$word") - if [[ "$word" == "$dynamic_prefix"* ]]; then - word="${word#"$dynamic_prefix"}" - word="${word//$'\r'/ }" - word="${word//$'\n'/ }" - read -r -a candidates <<<"$word" - fi - - for candidate in "${candidates[@]}"; do - if ((!want_options)); then - [[ "${candidate:0:1}" == "-" ]] && continue + if ((!want_options)); then + [[ "${word:0:1}" == "-" ]] && continue - for u in "${used[@]}"; do - if [[ "$u" == "$candidate" ]]; then - continue 2 - fi - done - fi + for u in "${used[@]}"; do + if [[ "$u" == "$word" ]]; then + continue 2 + fi + done + fi - # compgen -W expects shell-escaped words in one space-delimited string. - printf -v candidate '%q' "$candidate" - result+=("$candidate") - done + # compgen -W expects shell-escaped words in one space-delimited string. + printf -v word '%q' "$word" + result+=("$word") done echo "${result[*]}" @@ -61,14 +50,17 @@ _completely_completions() { case "$compline" in 'generate'*) + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A directory -W "$(_completely_completions_filter "--help" "--force")" -- "$cur") ;; 'init'*) + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_completely_completions_filter "--help")" -- "$cur") ;; *) + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_completely_completions_filter "--help" "--version" "init" "generate")" -- "$cur") ;; diff --git a/spec/approvals/completions/script-complete-options b/spec/approvals/completions/script-complete-options index 6ecdc99..f81438e 100644 --- a/spec/approvals/completions/script-complete-options +++ b/spec/approvals/completions/script-complete-options @@ -9,7 +9,6 @@ _mygit_completions_filter() { local cur=${COMP_WORDS[COMP_CWORD]} local result=() local want_options=0 - local dynamic_prefix="__completely_dynamic__" # words the user already typed (excluding the command itself) local used=() @@ -21,29 +20,19 @@ _mygit_completions_filter() { # Completing a non-option: drop options and already-used words. [[ "${cur:0:1}" == "-" ]] && want_options=1 for word in "${words[@]}"; do - local candidates=("$word") - if [[ "$word" == "$dynamic_prefix"* ]]; then - word="${word#"$dynamic_prefix"}" - word="${word//$'\r'/ }" - word="${word//$'\n'/ }" - read -r -a candidates <<<"$word" + if ((!want_options)); then + [[ "${word:0:1}" == "-" ]] && continue + + for u in "${used[@]}"; do + if [[ "$u" == "$word" ]]; then + continue 2 + fi + done fi - for candidate in "${candidates[@]}"; do - if ((!want_options)); then - [[ "${candidate:0:1}" == "-" ]] && continue - - for u in "${used[@]}"; do - if [[ "$u" == "$candidate" ]]; then - continue 2 - fi - done - fi - - # compgen -W expects shell-escaped words in one space-delimited string. - printf -v candidate '%q' "$candidate" - result+=("$candidate") - done + # compgen -W expects shell-escaped words in one space-delimited string. + printf -v word '%q' "$word" + result+=("$word") done echo "${result[*]}" @@ -61,6 +50,7 @@ _mygit_completions() { case "$compline" in *) + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_mygit_completions_filter "status" "commit")" -- "$cur") ;; diff --git a/spec/approvals/completions/script-only-spaces b/spec/approvals/completions/script-only-spaces index 401453f..9c7c7fa 100644 --- a/spec/approvals/completions/script-only-spaces +++ b/spec/approvals/completions/script-only-spaces @@ -9,7 +9,6 @@ _completely_completions_filter() { local cur=${COMP_WORDS[COMP_CWORD]} local result=() local want_options=0 - local dynamic_prefix="__completely_dynamic__" # words the user already typed (excluding the command itself) local used=() @@ -21,29 +20,19 @@ _completely_completions_filter() { # Completing a non-option: drop options and already-used words. [[ "${cur:0:1}" == "-" ]] && want_options=1 for word in "${words[@]}"; do - local candidates=("$word") - if [[ "$word" == "$dynamic_prefix"* ]]; then - word="${word#"$dynamic_prefix"}" - word="${word//$'\r'/ }" - word="${word//$'\n'/ }" - read -r -a candidates <<<"$word" - fi - - for candidate in "${candidates[@]}"; do - if ((!want_options)); then - [[ "${candidate:0:1}" == "-" ]] && continue + if ((!want_options)); then + [[ "${word:0:1}" == "-" ]] && continue - for u in "${used[@]}"; do - if [[ "$u" == "$candidate" ]]; then - continue 2 - fi - done - fi + for u in "${used[@]}"; do + if [[ "$u" == "$word" ]]; then + continue 2 + fi + done + fi - # compgen -W expects shell-escaped words in one space-delimited string. - printf -v candidate '%q' "$candidate" - result+=("$candidate") - done + # compgen -W expects shell-escaped words in one space-delimited string. + printf -v word '%q' "$word" + result+=("$word") done echo "${result[*]}" @@ -61,10 +50,12 @@ _completely_completions() { case "$compline" in 'generate'*) + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -A directory -W "$(_completely_completions_filter "--help" "--force")" -- "$cur") ;; 'init'*) + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_completely_completions_filter "--help")" -- "$cur") ;; diff --git a/spec/approvals/completions/script-with-debug b/spec/approvals/completions/script-with-debug index 688a963..7a3673c 100644 --- a/spec/approvals/completions/script-with-debug +++ b/spec/approvals/completions/script-with-debug @@ -9,7 +9,6 @@ _completely_completions_filter() { local cur=${COMP_WORDS[COMP_CWORD]} local result=() local want_options=0 - local dynamic_prefix="__completely_dynamic__" # words the user already typed (excluding the command itself) local used=() @@ -21,29 +20,19 @@ _completely_completions_filter() { # Completing a non-option: drop options and already-used words. [[ "${cur:0:1}" == "-" ]] && want_options=1 for word in "${words[@]}"; do - local candidates=("$word") - if [[ "$word" == "$dynamic_prefix"* ]]; then - word="${word#"$dynamic_prefix"}" - word="${word//$'\r'/ }" - word="${word//$'\n'/ }" - read -r -a candidates <<<"$word" + if ((!want_options)); then + [[ "${word:0:1}" == "-" ]] && continue + + for u in "${used[@]}"; do + if [[ "$u" == "$word" ]]; then + continue 2 + fi + done fi - for candidate in "${candidates[@]}"; do - if ((!want_options)); then - [[ "${candidate:0:1}" == "-" ]] && continue - - for u in "${used[@]}"; do - if [[ "$u" == "$candidate" ]]; then - continue 2 - fi - done - fi - - # compgen -W expects shell-escaped words in one space-delimited string. - printf -v candidate '%q' "$candidate" - result+=("$candidate") - done + # compgen -W expects shell-escaped words in one space-delimited string. + printf -v word '%q' "$word" + result+=("$word") done echo "${result[*]}" diff --git a/spec/fixtures/tester/default.bash b/spec/fixtures/tester/default.bash index f31db9d..2b843ca 100644 --- a/spec/fixtures/tester/default.bash +++ b/spec/fixtures/tester/default.bash @@ -9,7 +9,6 @@ _cli_completions_filter() { local cur=${COMP_WORDS[COMP_CWORD]} local result=() local want_options=0 - local dynamic_prefix="__completely_dynamic__" # words the user already typed (excluding the command itself) local used=() @@ -21,29 +20,19 @@ _cli_completions_filter() { # Completing a non-option: drop options and already-used words. [[ "${cur:0:1}" == "-" ]] && want_options=1 for word in "${words[@]}"; do - local candidates=("$word") - if [[ "$word" == "$dynamic_prefix"* ]]; then - word="${word#"$dynamic_prefix"}" - word="${word//$'\r'/ }" - word="${word//$'\n'/ }" - read -r -a candidates <<<"$word" + if ((!want_options)); then + [[ "${word:0:1}" == "-" ]] && continue + + for u in "${used[@]}"; do + if [[ "$u" == "$word" ]]; then + continue 2 + fi + done fi - for candidate in "${candidates[@]}"; do - if ((!want_options)); then - [[ "${candidate:0:1}" == "-" ]] && continue - - for u in "${used[@]}"; do - if [[ "$u" == "$candidate" ]]; then - continue 2 - fi - done - fi - - # compgen -W expects shell-escaped words in one space-delimited string. - printf -v candidate '%q' "$candidate" - result+=("$candidate") - done + # compgen -W expects shell-escaped words in one space-delimited string. + printf -v word '%q' "$word" + result+=("$word") done echo "${result[*]}" @@ -61,18 +50,22 @@ _cli_completions() { case "$compline" in 'command childcommand'*) + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--quiet" "--verbose" "-q" "-v")" -- "$cur") ;; 'command subcommand'*) + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--force" "--quiet")" -- "$cur") ;; 'command'*) + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "subcommand" "childcommand")" -- "$cur") ;; *) + # shellcheck disable=SC2046 # intentional splitting for dynamic $(...) completions while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(_cli_completions_filter "--help" "--version" "command" "conquer")" -- "$cur") ;; From 8945d73abd8284863bf58452916fce550a842a89 Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Fri, 20 Feb 2026 02:34:34 +0200 Subject: [PATCH 3/3] version 0.8.0.rc2 --- lib/completely/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/completely/version.rb b/lib/completely/version.rb index caa939d..bbe5efd 100644 --- a/lib/completely/version.rb +++ b/lib/completely/version.rb @@ -1,3 +1,3 @@ module Completely - VERSION = '0.8.0.rc1' + VERSION = '0.8.0.rc2' end