From f4ed4c4839d074b923581490231bd8cfab677709 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Tue, 3 Nov 2026 13:13:20 +0000 Subject: [PATCH] Index completion metadata by canonical node --- completion_context.go | 24 +++++++++++------------- completion_query_test.go | 4 ++++ docs/roadmap/remediation.md | 4 +++- parser.go | 9 +++++---- registry.go | 2 ++ 5 files changed, 25 insertions(+), 18 deletions(-) diff --git a/completion_context.go b/completion_context.go index 299c9a8..c90232a 100644 --- a/completion_context.go +++ b/completion_context.go @@ -26,7 +26,7 @@ func resolveCompletionContext(snapshot *routingSnapshot, completed []string, cur context.route = resolveEnteredRoute(completed, snapshot) switch context.route.kind { case routeCanonical: - context.command = completionCommand(snapshot.metadata, context.route.canonicalPath) + context.command = completionCommand(snapshot, context.route.canonicalPath) if context.command == nil || len(context.route.args) < len(context.route.canonicalPath) { context.command = nil return context @@ -42,22 +42,20 @@ func resolveCompletionContext(snapshot *routingSnapshot, completed []string, cur return context } -func completionCommand(root *commandmeta.Command, path commandpath.Path) *commandmeta.Command { - current := root +func completionCommand(snapshot *routingSnapshot, path commandpath.Path) *commandmeta.Command { + if len(path) == 0 { + return snapshot.metadata + } + children := snapshot.canonical + var node *canonicalNode for _, name := range path { - if current == nil { + node = children[name] + if node == nil { return nil } - var next *commandmeta.Command - for _, child := range current.Children { - if child.Name == name { - next = child - break - } - } - current = next + children = node.children } - return current + return node.metadata } func completionChildren(snapshot *routingSnapshot, context completionContext) []commandmeta.CompletionChoice { diff --git a/completion_query_test.go b/completion_query_test.go index 8a4c42f..42026bd 100644 --- a/completion_query_test.go +++ b/completion_query_test.go @@ -73,6 +73,10 @@ func TestCompletionQueryDispatch(t *testing.T) { if outcome := app.Execute(context.Background(), []string{"__complete", "lat"}, Streams{Out: &output}); outcome.Status != 0 || !strings.Contains(output.String(), "later") { t.Fatalf("later command outcome=%#v reply=%q", outcome, output.String()) } + output.Reset() + if outcome := app.Execute(context.Background(), []string{"__complete", "later", "--qui"}, Streams{Out: &output}); outcome.Status != 0 || !strings.Contains(output.String(), "--quiet") { + t.Fatalf("later command flag outcome=%#v reply=%q", outcome, output.String()) + } } func TestCompletionQueryErrorsAndDelivery(t *testing.T) { diff --git a/docs/roadmap/remediation.md b/docs/roadmap/remediation.md index a743daf..152e4fd 100644 --- a/docs/roadmap/remediation.md +++ b/docs/roadmap/remediation.md @@ -169,7 +169,9 @@ the remaining scope. The six existing findings remain open. ### 7. Look up canonical completion metadata without scanning siblings -Priority: P3. Status: for consideration. +Priority: P3. Status: complete. + +- [x] Completed: Canonical snapshot nodes now hold their neutral metadata. Completion uses one map lookup per path component; root, nested and alias context checks and a late-registration leaf query pass. [completionCommand](../../completion_context.go) searches the metadata child slice at every component of an already resolved canonical path. For depth diff --git a/parser.go b/parser.go index e6b6430..81ab63c 100644 --- a/parser.go +++ b/parser.go @@ -99,13 +99,13 @@ func (a *App) buildRoutingSnapshot() *routingSnapshot { sort.Strings(names) var key commandmeta.Key for _, name := range names { - root.Children = append(root.Children, bridgeCommand(a.commands[name].command, snapshot, &key)) + root.Children = append(root.Children, bridgeCommand(snapshot.canonical[name], snapshot, &key)) } return snapshot } -func bridgeCommand(command Command, snapshot *routingSnapshot, next *commandmeta.Key) *commandmeta.Command { - record := command.record +func bridgeCommand(node *canonicalNode, snapshot *routingSnapshot, next *commandmeta.Key) *commandmeta.Command { + record := node.command.record *next = *next + 1 declaration := &commandmeta.Command{ Key: *next, @@ -114,6 +114,7 @@ func bridgeCommand(command Command, snapshot *routingSnapshot, next *commandmeta Description: record.description, CoreFlags: commandmeta.ReportingFlags(), } + node.metadata = declaration if record.payload != nil { snapshot.runners[declaration.Key] = record.payload declaration.Runnable = record.payload.hasRun() @@ -126,7 +127,7 @@ func bridgeCommand(command Command, snapshot *routingSnapshot, next *commandmeta if child.record == nil || child.record.absent { continue } - declaration.Children = append(declaration.Children, bridgeCommand(child, snapshot, next)) + declaration.Children = append(declaration.Children, bridgeCommand(node.children[child.record.name], snapshot, next)) } return declaration } diff --git a/registry.go b/registry.go index 67225a4..c61fb32 100644 --- a/registry.go +++ b/registry.go @@ -7,6 +7,7 @@ import ( internalalias "gitea.maximumdirect.net/eric/CommandKit/internal/alias" internalcobra "gitea.maximumdirect.net/eric/CommandKit/internal/cobra" + "gitea.maximumdirect.net/eric/CommandKit/internal/commandmeta" "gitea.maximumdirect.net/eric/CommandKit/internal/commandpath" ) @@ -31,6 +32,7 @@ type installationCandidate struct { } type canonicalNode struct { + metadata *commandmeta.Command command Command path commandpath.Path children map[string]*canonicalNode