feat(completion): unify shell completions behind an opt-in `task __complete` engine
Bash, Fish, Zsh, Nushell and PowerShell now share a single backend: `task __complete` returns the suggestions plus a directive, and every wrapper is a thin shim around it. All five shells offer the same suggestions — task names, aliases, flags, flag values and per-task CLI variables. The Zsh `show-aliases` and `verbose` zstyles keep working, now backed by the `--no-aliases` and `--no-descriptions` completion flags.
The engine is opt-in via `task --new-completion <shell>`, leaving `--completion` and the legacy scripts untouched; it will become the default in a future release. The new wrappers live under `completion/next/`.
Completing a keystroke never reaches the network, never blocks on a stdin entrypoint, and honors every flag that decides how the Taskfile is loaded. Ref resolution shared by `requires` and enum completion moved to `internal/refs`.
A cross-shell test suite exercises the protocol in Go with thin shell smoke tests, and runs in CI.
2026-08-20 13:56:01 +02:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
# Smoke-tests how the bash wrapper routes each directive, by stubbing the
|
|
|
|
|
# bash-completion helpers. Requires TASK_BIN and TASK_FIXTURE.
|
|
|
|
|
set -u
|
|
|
|
|
|
|
|
|
|
: "${TASK_BIN:?}"; : "${TASK_FIXTURE:?}"
|
|
|
|
|
export TASK_EXE="$TASK_BIN"
|
|
|
|
|
cd "$TASK_FIXTURE" || exit 1
|
|
|
|
|
|
|
|
|
|
fails=0
|
|
|
|
|
CAP=""
|
|
|
|
|
|
|
|
|
|
_init_completion() {
|
|
|
|
|
words=("${TEST_WORDS[@]}")
|
|
|
|
|
cword=$TEST_CWORD
|
|
|
|
|
cur="${TEST_WORDS[$TEST_CWORD]}"
|
|
|
|
|
prev="${TEST_WORDS[$((TEST_CWORD - 1))]}"
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
# Records $cur so a test can assert the inline `--flag=` prefix was stripped.
|
|
|
|
|
_filedir() { CAP+="filedir:$* cur=$cur"$'\n'; }
|
|
|
|
|
compopt() { CAP+="compopt:$*"$'\n'; }
|
|
|
|
|
__ltrim_colon_completions() { :; }
|
|
|
|
|
|
2026-08-29 10:36:54 +02:00
|
|
|
source "$(dirname "${BASH_SOURCE[0]}")/../bash/task.bash"
|
feat(completion): unify shell completions behind an opt-in `task __complete` engine
Bash, Fish, Zsh, Nushell and PowerShell now share a single backend: `task __complete` returns the suggestions plus a directive, and every wrapper is a thin shim around it. All five shells offer the same suggestions — task names, aliases, flags, flag values and per-task CLI variables. The Zsh `show-aliases` and `verbose` zstyles keep working, now backed by the `--no-aliases` and `--no-descriptions` completion flags.
The engine is opt-in via `task --new-completion <shell>`, leaving `--completion` and the legacy scripts untouched; it will become the default in a future release. The new wrappers live under `completion/next/`.
Completing a keystroke never reaches the network, never blocks on a stdin entrypoint, and honors every flag that decides how the Taskfile is loaded. Ref resolution shared by `requires` and enum completion moved to `internal/refs`.
A cross-shell test suite exercises the protocol in Go with thin shell smoke tests, and runs in CI.
2026-08-20 13:56:01 +02:00
|
|
|
|
|
|
|
|
run() {
|
|
|
|
|
CAP=""
|
|
|
|
|
TEST_WORDS=("$@")
|
|
|
|
|
TEST_CWORD=$((${#TEST_WORDS[@]} - 1))
|
|
|
|
|
COMPREPLY=()
|
|
|
|
|
_task
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reply_has() { # LABEL VALUE
|
|
|
|
|
local v
|
|
|
|
|
for v in "${COMPREPLY[@]}"; do [[ "$v" == "$2" ]] && { echo " ok $1"; return; }; done
|
|
|
|
|
echo " FAIL $1 — '$2' missing from COMPREPLY: ${COMPREPLY[*]}"
|
|
|
|
|
fails=$((fails + 1))
|
|
|
|
|
}
|
|
|
|
|
cap_has() { # LABEL PATTERN
|
|
|
|
|
if [[ "$CAP" == *"$2"* ]]; then echo " ok $1"; else
|
|
|
|
|
echo " FAIL $1 — expected '$2' in: $CAP"; fails=$((fails + 1)); fi
|
|
|
|
|
}
|
|
|
|
|
cap_hasnot() { # LABEL PATTERN
|
|
|
|
|
if [[ "$CAP" == *"$2"* ]]; then
|
|
|
|
|
echo " FAIL $1 — '$2' should be absent in: $CAP"; fails=$((fails + 1)); else
|
|
|
|
|
echo " ok $1"; fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
echo "bash: :4 (NoFileComp) forwards candidates, no file fallback"
|
|
|
|
|
run task ''
|
|
|
|
|
reply_has "candidate forwarded" build
|
|
|
|
|
cap_hasnot "no file fallback" "filedir:"
|
|
|
|
|
|
fix(completion): complete task names and required vars together
CLI variables are global to the invocation, not scoped to a task: `args.Parse` turns every word holding `=` into a global and every other word into a call, so `task build ENV=dev deploy` and `task build deploy ENV=dev` are the same command. The engine assumed the opposite and, as soon as a word matched a task, served only that task's variables — nothing at all when it had none. `task build <TAB>` and even `task build de<TAB>` went silent, where all five legacy wrappers offered task names at every position.
The engine now unions the still-unset requirements of every task named on the line, and falls through to task names once they are all set. The line resolves itself: fill in what blocks execution, then add another task. Keeping the two families exclusive means each keeps a coherent directive, so nothing loses its trailing space.
Task words are matched with FindMatchingTasks instead of a hand-built list of names truncated at their first `*`, which is why `task wildcard-foo <TAB>` used to offer task names rather than the variables of `wildcard-*`. Completion also disables fuzzy matching: a suggestion list has no "did you mean".
Three fixes ride along. `--sort default` left the sorter nil and cleared the one NewExecutor had set, so completion listed tasks in Taskfile order while `--list` sorted them — and a single templated description silently restored the sort through GetTaskList. The bash wrapper never defined KeepOrder, losing the declaration order of `requires`; it now passes `compopt -o nosort`, which bash 3.2 ignores as it already ignores nospace. And the shell suite unsets TASK_EXE and GO_TASK_PROGNAME: fish, Nushell and PowerShell resolve the binary through them, so an ambient value silently tested something other than the binary just built.
2026-08-20 16:11:49 +02:00
|
|
|
echo "bash: :2|:32 (NoSpace|KeepOrder) disable the trailing space and the sort"
|
feat(completion): unify shell completions behind an opt-in `task __complete` engine
Bash, Fish, Zsh, Nushell and PowerShell now share a single backend: `task __complete` returns the suggestions plus a directive, and every wrapper is a thin shim around it. All five shells offer the same suggestions — task names, aliases, flags, flag values and per-task CLI variables. The Zsh `show-aliases` and `verbose` zstyles keep working, now backed by the `--no-aliases` and `--no-descriptions` completion flags.
The engine is opt-in via `task --new-completion <shell>`, leaving `--completion` and the legacy scripts untouched; it will become the default in a future release. The new wrappers live under `completion/next/`.
Completing a keystroke never reaches the network, never blocks on a stdin entrypoint, and honors every flag that decides how the Taskfile is loaded. Ref resolution shared by `requires` and enum completion moved to `internal/refs`.
A cross-shell test suite exercises the protocol in Go with thin shell smoke tests, and runs in CI.
2026-08-20 13:56:01 +02:00
|
|
|
run task deploy ''
|
|
|
|
|
cap_has "nospace applied" "compopt:-o nospace"
|
fix(completion): complete task names and required vars together
CLI variables are global to the invocation, not scoped to a task: `args.Parse` turns every word holding `=` into a global and every other word into a call, so `task build ENV=dev deploy` and `task build deploy ENV=dev` are the same command. The engine assumed the opposite and, as soon as a word matched a task, served only that task's variables — nothing at all when it had none. `task build <TAB>` and even `task build de<TAB>` went silent, where all five legacy wrappers offered task names at every position.
The engine now unions the still-unset requirements of every task named on the line, and falls through to task names once they are all set. The line resolves itself: fill in what blocks execution, then add another task. Keeping the two families exclusive means each keeps a coherent directive, so nothing loses its trailing space.
Task words are matched with FindMatchingTasks instead of a hand-built list of names truncated at their first `*`, which is why `task wildcard-foo <TAB>` used to offer task names rather than the variables of `wildcard-*`. Completion also disables fuzzy matching: a suggestion list has no "did you mean".
Three fixes ride along. `--sort default` left the sorter nil and cleared the one NewExecutor had set, so completion listed tasks in Taskfile order while `--list` sorted them — and a single templated description silently restored the sort through GetTaskList. The bash wrapper never defined KeepOrder, losing the declaration order of `requires`; it now passes `compopt -o nosort`, which bash 3.2 ignores as it already ignores nospace. And the shell suite unsets TASK_EXE and GO_TASK_PROGNAME: fish, Nushell and PowerShell resolve the binary through them, so an ambient value silently tested something other than the binary just built.
2026-08-20 16:11:49 +02:00
|
|
|
cap_has "keeporder applied" "compopt:-o nosort"
|
feat(completion): unify shell completions behind an opt-in `task __complete` engine
Bash, Fish, Zsh, Nushell and PowerShell now share a single backend: `task __complete` returns the suggestions plus a directive, and every wrapper is a thin shim around it. All five shells offer the same suggestions — task names, aliases, flags, flag values and per-task CLI variables. The Zsh `show-aliases` and `verbose` zstyles keep working, now backed by the `--no-aliases` and `--no-descriptions` completion flags.
The engine is opt-in via `task --new-completion <shell>`, leaving `--completion` and the legacy scripts untouched; it will become the default in a future release. The new wrappers live under `completion/next/`.
Completing a keystroke never reaches the network, never blocks on a stdin entrypoint, and honors every flag that decides how the Taskfile is loaded. Ref resolution shared by `requires` and enum completion moved to `internal/refs`.
A cross-shell test suite exercises the protocol in Go with thin shell smoke tests, and runs in CI.
2026-08-20 13:56:01 +02:00
|
|
|
|
|
|
|
|
echo "bash: :8 (FilterFileExt) routes to extension-filtered files"
|
|
|
|
|
run task --taskfile ''
|
|
|
|
|
cap_has "filedir ext glob" "filedir:@(yml|yaml)"
|
|
|
|
|
|
|
|
|
|
echo "bash: :16 (FilterDirs) routes to directory completion"
|
|
|
|
|
run task --dir ''
|
|
|
|
|
cap_has "filedir -d" "filedir:-d"
|
|
|
|
|
|
|
|
|
|
echo "bash: :0 (Default) falls back to files"
|
|
|
|
|
run task build -- ''
|
|
|
|
|
cap_has "filedir default" "filedir:"
|
|
|
|
|
|
|
|
|
|
echo "bash: inline --flag= strips the prefix before file completion"
|
|
|
|
|
run task --taskfile=sub/x
|
|
|
|
|
cap_has "inline cur stripped" "cur=sub/x"
|
|
|
|
|
|
|
|
|
|
if ((fails)); then
|
|
|
|
|
echo "bash: $fails failure(s)"
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
echo "bash: all passed"
|