From f2b3d61d71392672b9aa1bcf4b15f10207ecb6b7 Mon Sep 17 00:00:00 2001 From: Ramesh Padmanabhaiah <22363102+codeforester@users.noreply.github.com> Date: Thu, 17 Sep 2026 20:54:47 +0530 Subject: [PATCH 1/2] fix: limit generated completion to the cursor --- lib/bash/cli/lib_cli.sh | 10 +++++++-- lib/bash/cli/tests/lib_cli.bats | 40 +++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/lib/bash/cli/lib_cli.sh b/lib/bash/cli/lib_cli.sh index 40ef9d0..f49aaed 100644 --- a/lib/bash/cli/lib_cli.sh +++ b/lib/bash/cli/lib_cli.sh @@ -1573,8 +1573,14 @@ base_cli_completion_script() { fi program="${__base_bash_libs_cli_models["$model|meta|name"]}" printf '%s\n' "$function_name() {" - printf '%s\n' ' local current="${COMP_WORDS[COMP_CWORD]-}"' - printf '%s\n' ' local -a cli_words=( "${COMP_WORDS[@]:1}" )' + printf '%s\n' ' local cursor="${COMP_CWORD:-0}" word_count candidate' + printf '%s\n' ' local -a completion_words=( "${COMP_WORDS[@]+${COMP_WORDS[@]}}" ) cli_words=()' + printf '%s\n' ' if [[ "$cursor" =~ ^[0-9]+$ ]]; then cursor=$((10#$cursor)); else cursor=0; fi' + printf '%s\n' ' word_count="${#completion_words[@]}"' + printf '%s\n' ' if ((cursor > 0 && word_count > 1)); then' + printf '%s\n' ' ((cursor < word_count)) || cursor=$((word_count - 1))' + printf '%s\n' ' cli_words=( "${completion_words[@]:1:cursor}" )' + printf '%s\n' ' fi' printf '%s\n' ' COMPREPLY=()' printf ' while IFS= read -r candidate; do COMPREPLY+=("$candidate"); done < <(base_cli_complete %q -- "${cli_words[@]}")\n' "$model" printf '%s\n' '}' diff --git a/lib/bash/cli/tests/lib_cli.bats b/lib/bash/cli/tests/lib_cli.bats index ed4c939..5e5d479 100644 --- a/lib/bash/cli/tests/lib_cli.bats +++ b/lib/bash/cli/tests/lib_cli.bats @@ -602,6 +602,46 @@ EOF [[ "$output" == *"complete -F _demo_complete demo"* ]] } +@test "generated completion honors the cursor and keeps scratch variables local" { + local completion_script="$TEST_TMPDIR/completion.bash" + local candidate=caller-owned + + base_cli_model_init cursor name=cursor + base_cli_command cursor admin "Administration" + base_cli_command cursor admin/user "User" + base_cli_option cursor '' channel value --channel enum=alpha,beta + base_cli_option cursor '' output value --output + base_cli_option cursor admin/user color value --color + base_cli_completion_script cursor _cursor_complete > "$completion_script" + source "$completion_script" + + COMP_WORDS=(cursor --ch stable --output result) + COMP_CWORD=1 + _cursor_complete + [ "${COMPREPLY[*]}" = --channel ] + [ "$candidate" = caller-owned ] + + COMP_WORDS=(cursor --channel a --output result) + COMP_CWORD=2 + _cursor_complete + [ "${#COMPREPLY[@]}" -eq 0 ] + + COMP_WORDS=(cursor admin --out --channel alpha) + COMP_CWORD=2 + _cursor_complete + [ "${COMPREPLY[*]}" = --output ] + + COMP_WORDS=(cursor admin user --co) + COMP_CWORD=3 + _cursor_complete + [ "${COMPREPLY[*]}" = --color ] + + COMP_WORDS=(cursor -- --channel) + COMP_CWORD=2 + _cursor_complete + [ "${#COMPREPLY[@]}" -eq 0 ] +} + @test "completion consumes option values and honors the double-dash boundary" { base_cli_model_init complete name=complete base_cli_command complete admin "Administration" aliases=a From 5e0b40966df4e640449b7535da2c3eefc49cbda9 Mon Sep 17 00:00:00 2001 From: Ramesh Padmanabhaiah <22363102+codeforester@users.noreply.github.com> Date: Fri, 18 Sep 2026 20:45:49 +0530 Subject: [PATCH 2/2] fix(cli): complete next-word suggestions at cursor boundary --- lib/bash/cli/lib_cli.sh | 10 +++++++--- lib/bash/cli/tests/lib_cli.bats | 10 ++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/bash/cli/lib_cli.sh b/lib/bash/cli/lib_cli.sh index f49aaed..917ed75 100644 --- a/lib/bash/cli/lib_cli.sh +++ b/lib/bash/cli/lib_cli.sh @@ -1577,9 +1577,13 @@ base_cli_completion_script() { printf '%s\n' ' local -a completion_words=( "${COMP_WORDS[@]+${COMP_WORDS[@]}}" ) cli_words=()' printf '%s\n' ' if [[ "$cursor" =~ ^[0-9]+$ ]]; then cursor=$((10#$cursor)); else cursor=0; fi' printf '%s\n' ' word_count="${#completion_words[@]}"' - printf '%s\n' ' if ((cursor > 0 && word_count > 1)); then' - printf '%s\n' ' ((cursor < word_count)) || cursor=$((word_count - 1))' - printf '%s\n' ' cli_words=( "${completion_words[@]:1:cursor}" )' + printf '%s\n' ' if ((cursor > 0)); then' + printf '%s\n' ' if ((cursor < word_count)); then' + printf '%s\n' ' cli_words=( "${completion_words[@]:1:cursor}" )' + printf '%s\n' ' else' + printf '%s\n' ' cli_words=( "${completion_words[@]:1}" )' + printf '%s\n' ' cli_words+=("")' + printf '%s\n' ' fi' printf '%s\n' ' fi' printf '%s\n' ' COMPREPLY=()' printf ' while IFS= read -r candidate; do COMPREPLY+=("$candidate"); done < <(base_cli_complete %q -- "${cli_words[@]}")\n' "$model" diff --git a/lib/bash/cli/tests/lib_cli.bats b/lib/bash/cli/tests/lib_cli.bats index 5e5d479..71637ad 100644 --- a/lib/bash/cli/tests/lib_cli.bats +++ b/lib/bash/cli/tests/lib_cli.bats @@ -640,6 +640,16 @@ EOF COMP_CWORD=2 _cursor_complete [ "${#COMPREPLY[@]}" -eq 0 ] + + COMP_WORDS=(cursor admin) + COMP_CWORD=2 + _cursor_complete + [ "${COMPREPLY[*]}" = user ] + + COMP_WORDS=(cursor) + COMP_CWORD=1 + _cursor_complete + [ "${COMPREPLY[*]}" = admin ] } @test "completion consumes option values and honors the double-dash boundary" {