From 94ee7e4880e22db7077b608b85ab1441a34fb13d Mon Sep 17 00:00:00 2001 From: Martino Ferrari Date: Fri, 23 Jan 2026 14:18:41 +0100 Subject: [PATCH] added support to enum in completion --- internal/lsp/server.go | 76 +++++++++++++++++++++++++++++++ internal/parser/parser.go | 2 +- test/lsp_completion_test.go | 89 +++++++++++++++++++++++++++++++++++++ 3 files changed, 166 insertions(+), 1 deletion(-) diff --git a/internal/lsp/server.go b/internal/lsp/server.go index fadf912..323cb55 100644 --- a/internal/lsp/server.go +++ b/internal/lsp/server.go @@ -723,6 +723,82 @@ func suggestFieldValues(container *index.ProjectNode, field string, path string) if field == "Functions" { return suggestObjects(root, "GAM") } + if field == "Type" { + return suggestSignalTypes() + } + + if list := suggestCUEEnums(container, field); list != nil { + return list + } + + return nil +} + +func suggestSignalTypes() *CompletionList { + types := []string{ + "uint8", "int8", "uint16", "int16", "uint32", "int32", "uint64", "int64", + "float32", "float64", "string", "bool", "char8", + } + var items []CompletionItem + for _, t := range types { + items = append(items, CompletionItem{ + Label: t, + Kind: 13, // EnumMember + Detail: "Signal Type", + }) + } + return &CompletionList{Items: items} +} + +func suggestCUEEnums(container *index.ProjectNode, field string) *CompletionList { + if GlobalSchema == nil { + return nil + } + cls := container.Metadata["Class"] + if cls == "" { + return nil + } + + classPath := cue.ParsePath(fmt.Sprintf("#Classes.%s.%s", cls, field)) + val := GlobalSchema.Value.LookupPath(classPath) + if val.Err() != nil { + return nil + } + + op, args := val.Expr() + var values []cue.Value + if op == cue.OrOp { + values = args + } else { + values = []cue.Value{val} + } + + var items []CompletionItem + for _, v := range values { + if !v.IsConcrete() { + continue + } + + str, err := v.String() // Returns quoted string for string values? + if err != nil { + continue + } + + // Ensure strings are quoted + if v.Kind() == cue.StringKind && !strings.HasPrefix(str, "\"") { + str = fmt.Sprintf("\"%s\"", str) + } + + items = append(items, CompletionItem{ + Label: str, + Kind: 13, // EnumMember + Detail: "Enum Value", + }) + } + + if len(items) > 0 { + return &CompletionList{Items: items} + } return nil } diff --git a/internal/parser/parser.go b/internal/parser/parser.go index 6b296f2..b6decea 100644 --- a/internal/parser/parser.go +++ b/internal/parser/parser.go @@ -208,7 +208,7 @@ func (p *Parser) parseSubnode() (Subnode, bool) { if t.Type == TokenEOF { p.addError(t.Position, "unexpected EOF, expected }") sub.EndPosition = t.Position - return sub, false + return sub, true } def, ok := p.parseDefinition() if ok { diff --git a/test/lsp_completion_test.go b/test/lsp_completion_test.go index 0bfddf3..dcc3565 100644 --- a/test/lsp_completion_test.go +++ b/test/lsp_completion_test.go @@ -228,4 +228,93 @@ $App = { t.Error("Expected ProjectDS in project file suggestions") } }) + + t.Run("Suggest Signal Types", func(t *testing.T) { + setup() + content := ` ++DS = { + Class = FileReader + Signals = { + S1 = { Type = } + } +} +` + lsp.Documents[uri] = content + p := parser.NewParser(content) + cfg, _ := p.Parse() + lsp.Tree.AddFile(path, cfg) + + params := lsp.CompletionParams{ + TextDocument: lsp.TextDocumentIdentifier{URI: uri}, + Position: lsp.Position{Line: 4, Character: strings.Index(content, "Type = ") + len("Type = ") + 1}, + } + + list := lsp.HandleCompletion(params) + if list == nil { + t.Fatal("Expected signal type suggestions") + } + + foundUint32 := false + for _, item := range list.Items { + if item.Label == "uint32" { + foundUint32 = true + break + } + } + if !foundUint32 { + t.Error("Expected uint32 in suggestions") + } + }) + + t.Run("Suggest CUE Enums", func(t *testing.T) { + setup() + // Inject custom schema with enum + custom := []byte(` +package schema +#Classes: { + TestEnumClass: { + Mode: "Auto" | "Manual" + } +} +`) + val := lsp.GlobalSchema.Context.CompileBytes(custom) + lsp.GlobalSchema.Value = lsp.GlobalSchema.Value.Unify(val) + + content := ` ++Obj = { + Class = TestEnumClass + Mode = +} +` + lsp.Documents[uri] = content + p := parser.NewParser(content) + cfg, _ := p.Parse() + lsp.Tree.AddFile(path, cfg) + + params := lsp.CompletionParams{ + TextDocument: lsp.TextDocumentIdentifier{URI: uri}, + Position: lsp.Position{Line: 3, Character: strings.Index(content, "Mode = ") + len("Mode = ") + 1}, + } + + list := lsp.HandleCompletion(params) + if list == nil { + t.Fatal("Expected enum suggestions") + } + + foundAuto := false + for _, item := range list.Items { + if item.Label == "\"Auto\"" { // CUE string value includes quotes + foundAuto = true + break + } + } + if !foundAuto { + // Check if it returned without quotes? + // v.String() returns quoted for string. + t.Error("Expected \"Auto\" in suggestions") + for _, item := range list.Items { + t.Logf("Suggestion: %s", item.Label) + } + } + }) }