Skip to content

feat(completion): unify shell completions behind task __complete#2897

Draft
vmaerten wants to merge 7 commits into
mainfrom
feat/completion-engine
Draft

feat(completion): unify shell completions behind task __complete#2897
vmaerten wants to merge 7 commits into
mainfrom
feat/completion-engine

Conversation

@vmaerten

@vmaerten vmaerten commented Jun 29, 2026

Copy link
Copy Markdown
Member

Summary

Unifies the Bash, Fish, Zsh and PowerShell completion scripts behind a single
task __complete command backed by a shared Go engine (internal/complete).

The shell scripts are now thin wrappers that forward the current words to
task __complete and render the suggestions, so every shell gets the same,
richer behaviour:

  • task names (with descriptions) and aliases
  • flags (--xxx)
  • flag values (e.g. file extensions for --taskfile, directories for --dir)
  • per-task CLI variables (task build VAR=)

All suggestion logic lives in Go, which removes the per-shell drift and makes
the completion behaviour testable.

Test plan

  • go build ./...
  • go vet ./...
  • go test ./... (incl. internal/complete)
  • Manual completion smoke test in each shell

@vmaerten vmaerten force-pushed the feat/completion-engine branch 2 times, most recently from 15ca5ef to b13cc89 Compare June 29, 2026 15:26
@vmaerten vmaerten force-pushed the feat/completion-engine branch from b13cc89 to f9f2ecb Compare June 29, 2026 15:42
vmaerten added 5 commits July 3, 2026 16:03
Engine:
- Emit DirectiveKeepOrder for task variables so the `requires` declaration
  order is preserved instead of being sorted by the shell.
- Return full `--flag=value` candidates for the inline `--output=` form so all
  shells match against the whole current token.
- Add `--no-aliases` / `--no-descriptions` completion flags (via complete.Options)
  parsed from the __complete invocation; the zsh wrapper maps its show-aliases
  and verbose zstyles onto them.
- Skip Taskfile setup when completing flags (NeedsTaskfile) and load the task
  list lazily; drop unused parameters; document exported identifiers.

Shell wrappers:
- zsh: reindent to tabs (.editorconfig), bridge zstyles to engine flags.
- fish: reindent to 2 spaces, handle every file-completion directive in the
  wrapper (--no-files disables the native fallback), drop the duplicated
  yml/yaml extension list now that it lives only in the engine.
- bash: prefix-filter by hand to preserve values containing spaces, exclude `=`
  from word breaks so `--output=` reaches the engine as one token.
- powershell: filter candidates by the current word, fall back to file
  completion for DirectiveDefault.

NoSpace is not representable in fish/PowerShell completion APIs; documented in
the wrappers.
Add completion/tests/ harnesses that exercise the engine protocol and every
shell wrapper without a TTY: the engine via `task __complete`, bash/zsh by
stubbing the completion-system helpers, fish via `complete -C`, and PowerShell
via the completion API. A `test:completion` task and a matching CI job (with a
strict mode that fails when an expected shell is missing) run them all.

Writing the suite surfaced and fixed real wrapper bugs:
- fish: `math` has no bitwise `&`, so every directive check errored; test bits
  with integer division + modulo instead. Also filter FilterFileExt results
  ourselves, as __fish_complete_suffix only prioritizes the extension.
- powershell: `Get-ChildItem -Include` is ignored without -Recurse, so file
  extension filtering returned nothing; filter with Where-Object instead.
- bash: guard empty-array expansion so completion with zero candidates does not
  trip `set -u` on bash 3.2.

Also name the completion directive bits (NO_SPACE, FILTER_FILE_EXT, …) in every
wrapper instead of using raw numbers.
Follow cobra's testing philosophy: the completion protocol (candidates +
directive) is business logic and belongs in Go, while the shell wrappers only
need to be checked for how they interpret each directive.

- Add completion/protocol_test.go: a table-driven black-box test that runs the
  real `task __complete` binary and asserts the offered values and the emitted
  directive. Unlike the in-process engine tests, it exercises the actual
  entrypoint dispatch, runComplete wiring and — crucially — the real flag set,
  so it catches drift between the completion enum/directive maps and the flag
  definitions. Runs on every OS, including Windows where the shell smokes don't.
- Delete completion/tests/engine.sh (the protocol is now covered in Go) and drop
  it from run.sh.
- Reduce the bash/zsh/fish/powershell smokes to directive routing only (files vs
  dirs vs no-files vs no-space), removing the task/alias/var assertions that the
  Go tests already own.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant