mirror of
https://github.com/go-task/task.git
synced 2026-09-02 04:02:08 +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.
65 lines
1.7 KiB
Go
65 lines
1.7 KiB
Go
// Package refs resolves the `ref` fields of a Taskfile into concrete values.
|
|
package refs
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/go-task/task/v3/internal/slicesext"
|
|
"github.com/go-task/task/v3/internal/templater"
|
|
"github.com/go-task/task/v3/taskfile/ast"
|
|
)
|
|
|
|
// Declared lists resolve to a []any, but `keys` and `splitList` return a []string.
|
|
func AsList(v any) ([]any, bool) {
|
|
switch value := v.(type) {
|
|
case []any:
|
|
return value, true
|
|
case []string:
|
|
return slicesext.AsAny(value), true
|
|
case []int:
|
|
return slicesext.AsAny(value), true
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
func ResolveEnums(requires *ast.Requires, cache *templater.Cache) error {
|
|
if requires == nil || len(requires.Vars) == 0 {
|
|
return nil
|
|
}
|
|
for _, v := range requires.Vars {
|
|
if v.Enum == nil || v.Enum.Ref == "" {
|
|
continue
|
|
}
|
|
resolved := templater.ResolveRef(v.Enum.Ref, cache)
|
|
if cache.Err() != nil {
|
|
return cache.Err()
|
|
}
|
|
arr, ok := AsList(resolved)
|
|
if !ok {
|
|
return fmt.Errorf("enum reference %q must resolve to a list", v.Enum.Ref)
|
|
}
|
|
strValues := make([]string, 0, len(arr))
|
|
for _, item := range arr {
|
|
s, ok := item.(string)
|
|
if !ok {
|
|
return fmt.Errorf("enum reference %q must contain only strings", v.Enum.Ref)
|
|
}
|
|
strValues = append(strValues, s)
|
|
}
|
|
v.Enum.Value = strValues
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// A ref depending on dynamic vars may not resolve: the copy then has no enum
|
|
// value, which the interactive prompter treats as free-form input.
|
|
func ResolveEnum(v *ast.VarsWithValidation, vars *ast.Vars) *ast.VarsWithValidation {
|
|
if v.Enum == nil || v.Enum.Ref == "" || len(v.Enum.Value) > 0 {
|
|
return v
|
|
}
|
|
vCopy := v.DeepCopy()
|
|
cache := &templater.Cache{Vars: vars}
|
|
_ = ResolveEnums(&ast.Requires{Vars: []*ast.VarsWithValidation{vCopy}}, cache)
|
|
return vCopy
|
|
}
|