Files
task/internal/complete/context.go
Valentin Maerten 5fe752d48a refactor(completion): tighten the engine after a cleanup pass
`--temp-dir` was missing from the flag-to-directive map, so it fell back to plain file completion while `--dir` and `--remote-cache-dir` offered directories.

The rest is dead weight: `listTasks` re-defaulted a sorter `NewExecutor` already sets and scanned descriptions for templates even with `--no-descriptions`; `detectTaskName` had a `--` branch `Complete` returns before reaching; the two flag-value branches built the same suggestion slice twice. `os.Args[2:]` is now sliced in one place, `complete.Words()`, instead of three, and the test helpers reuse `slicesext.Convert`.
2026-08-20 15:12:47 +02:00

77 lines
1.5 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 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
}