Interpret flags and operands for completion

This commit is contained in:
2026-11-03 03:54:22 +00:00
parent 8bb5383169
commit 4c47a0ca0b
6 changed files with 491 additions and 65 deletions

View File

@@ -4,6 +4,8 @@ import (
"errors"
"fmt"
"strconv"
commandmeta "gitea.maximumdirect.net/eric/CommandKit/internal/commandmeta"
)
// ValueFlag declares a singleton flag with a typed parser, optional default,
@@ -255,60 +257,76 @@ func prepareInputWithSpellings[I any](spec CommandSpec[I], flagValues, flagSpell
func newBindingIdentity() *bindingIdentity { return &bindingIdentity{marker: 1} }
func allocateOperands[I any](operands []string, definitions []Argument[I], enforceRequired bool) ([][]string, error) {
arguments := make([]commandmeta.Argument, len(definitions))
for i, definition := range definitions {
arguments[i] = commandmeta.Argument{Name: definition.binding.meta.Name, Min: definition.binding.min, Max: definition.binding.max}
}
counts, err := allocateOperandCounts(len(operands), arguments, enforceRequired)
if err != nil {
return nil, err
}
allocated := make([][]string, len(definitions))
position := 0
for i, count := range counts {
allocated[i] = operands[position : position+count]
position += count
}
return allocated, nil
}
func allocateOperandCounts(total int, definitions []commandmeta.Argument, enforceRequired bool) ([]int, error) {
for _, argument := range definitions {
binding := argument.binding
if binding.min < 0 || binding.max < -1 || (binding.max >= 0 && binding.min > binding.max) {
return nil, fmt.Errorf("argument %q has invalid bounds %d..%d", binding.meta.Name, binding.min, binding.max)
if argument.Min < 0 || argument.Max < -1 || (argument.Max >= 0 && argument.Min > argument.Max) {
return nil, fmt.Errorf("argument %q has invalid bounds %d..%d", argument.Name, argument.Min, argument.Max)
}
}
if enforceRequired {
minimum := 0
for _, argument := range definitions {
if argument.binding.min > len(operands)-minimum {
if argument.Min > total-minimum {
return nil, fmt.Errorf("not enough positional arguments")
}
minimum += argument.binding.min
minimum += argument.Min
}
}
suffixMinimum := make([]int, len(definitions)+1)
for i := len(definitions) - 1; i >= 0; i-- {
minimum := definitions[i].binding.min
remaining := len(operands) - suffixMinimum[i+1]
minimum := definitions[i].Min
remaining := total - suffixMinimum[i+1]
if minimum > remaining {
suffixMinimum[i] = len(operands)
suffixMinimum[i] = total
} else {
suffixMinimum[i] = suffixMinimum[i+1] + minimum
}
}
allocated := make([][]string, len(definitions))
counts := make([]int, len(definitions))
position := 0
for i, argument := range definitions {
binding := argument.binding
remaining := len(operands) - position
remaining := total - position
available := remaining - suffixMinimum[i+1]
if available < 0 {
available = 0
}
count := available
if binding.max >= 0 && count > binding.max {
count = binding.max
if argument.Max >= 0 && count > argument.Max {
count = argument.Max
}
if !enforceRequired && count < binding.min && remaining > 0 {
if !enforceRequired && count < argument.Min && remaining > 0 {
count = remaining
if binding.max >= 0 && count > binding.max {
count = binding.max
if argument.Max >= 0 && count > argument.Max {
count = argument.Max
}
}
if enforceRequired && count < binding.min {
return nil, fmt.Errorf("not enough values for argument %q", binding.meta.Name)
if enforceRequired && count < argument.Min {
return nil, fmt.Errorf("not enough values for argument %q", argument.Name)
}
allocated[i] = operands[position : position+count]
counts[i] = count
position += count
}
if position < len(operands) {
return nil, fmt.Errorf("received %d extra positional arguments", len(operands)-position)
if position < total {
return nil, fmt.Errorf("received %d extra positional arguments", total-position)
}
return allocated, nil
return counts, nil
}