mirror of
https://github.com/go-task/task.git
synced 2026-09-02 12:16:04 +02:00
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.
80 lines
1.6 KiB
Go
80 lines
1.6 KiB
Go
package complete
|
|
|
|
import (
|
|
"slices"
|
|
"strings"
|
|
|
|
"github.com/spf13/pflag"
|
|
)
|
|
|
|
type completionContext struct {
|
|
toComplete string
|
|
prev string
|
|
afterDash bool
|
|
}
|
|
|
|
// Infers the cursor position from args alone, so flag completion never loads
|
|
// the task list.
|
|
func parseContext(args []string) completionContext {
|
|
ctx := completionContext{}
|
|
if len(args) == 0 {
|
|
return ctx
|
|
}
|
|
|
|
ctx.toComplete = args[len(args)-1]
|
|
if len(args) >= 2 {
|
|
ctx.prev = args[len(args)-2]
|
|
}
|
|
|
|
ctx.afterDash = slices.Contains(args[:len(args)-1], "--")
|
|
|
|
return ctx
|
|
}
|
|
|
|
func (ctx completionContext) flagValue(fs *pflag.FlagSet) *pflag.Flag {
|
|
if f := matchFlagName(fs, ctx.prev); f != nil && flagTakesValue(f) {
|
|
return f
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (ctx completionContext) inTaskContext(fs *pflag.FlagSet) bool {
|
|
return !ctx.afterDash && ctx.flagValue(fs) == nil && !strings.HasPrefix(ctx.toComplete, "-")
|
|
}
|
|
|
|
// fs is needed to skip the word after a value-taking flag: `task --dir deploy`
|
|
// must not read "deploy" as a task name.
|
|
func detectTaskName(args []string, knownTasks []string, fs *pflag.FlagSet) string {
|
|
if len(args) <= 1 {
|
|
return ""
|
|
}
|
|
|
|
taskName := ""
|
|
skipNext := false
|
|
for _, w := range args[:len(args)-1] {
|
|
if skipNext {
|
|
skipNext = false
|
|
continue
|
|
}
|
|
if w == "--" {
|
|
return taskName
|
|
}
|
|
if strings.HasPrefix(w, "-") {
|
|
if !strings.Contains(w, "=") {
|
|
if f := matchFlagName(fs, w); f != nil && flagTakesValue(f) {
|
|
skipNext = true
|
|
}
|
|
}
|
|
continue
|
|
}
|
|
if strings.Contains(w, "=") {
|
|
continue
|
|
}
|
|
if slices.Contains(knownTasks, w) {
|
|
taskName = w
|
|
}
|
|
}
|
|
|
|
return taskName
|
|
}
|