diff --git a/pkg/server/completion.go b/pkg/server/completion.go index 153420e..7b5456e 100644 --- a/pkg/server/completion.go +++ b/pkg/server/completion.go @@ -44,6 +44,18 @@ func (s *Server) Completion(_ context.Context, params *protocol.CompletionParams vm := s.getVM(doc.Item.URI.SpanURI().Filename()) + // Inside parentheses, search for completions as if the content was on a separate line + // e.g., this enables completion in function arguments + if strings.LastIndex(line, "(") > strings.LastIndex(line, ")") { + argsPart := line[strings.LastIndex(line, "(")+1:] + // Only consider the last argument for completion + arguments := strings.Split(argsPart, ",") + lastArg := arguments[len(arguments)-1] + lastArg = strings.TrimSpace(lastArg) + lastArg = strings.TrimLeft(lastArg, "{[") // Ignore leading array or object tokens + line = lastArg + } + items := s.completionFromStack(line, searchStack, vm, params.Position) return &protocol.CompletionList{IsIncomplete: false, Items: items}, nil } @@ -62,6 +74,7 @@ func (s *Server) completionFromStack(line string, stack *nodestack.NodeStack, vm lineWords := splitWords(line) lastWord := lineWords[len(lineWords)-1] lastWord = strings.TrimRight(lastWord, ",;") // Ignore trailing commas and semicolons, they can present when someone is modifying an existing line + lastWord = strings.TrimSpace(lastWord) indexes := strings.Split(lastWord, ".") @@ -289,5 +302,9 @@ func splitWords(input string) []string { words = append(words, "") } + if len(words) == 0 { + words = append(words, "") + } + return words } diff --git a/pkg/server/completion_test.go b/pkg/server/completion_test.go index 3aa9181..f7f6c09 100644 --- a/pkg/server/completion_test.go +++ b/pkg/server/completion_test.go @@ -692,6 +692,53 @@ func TestCompletion(t *testing.T) { }, }, }, + { + name: "completion in function arguments", + filename: "testdata/functions.libsonnet", + replaceString: "test: myfunc(arg1, arg2)", + replaceByString: "test: myfunc(arg1, self.", + expected: protocol.CompletionList{ + IsIncomplete: false, + Items: []protocol.CompletionItem{ + { + Label: "a", + Kind: protocol.FieldCompletion, + Detail: "self.a", + InsertText: "a", + LabelDetails: protocol.CompletionItemLabelDetails{ + Description: "variable", + }, + }, + { + Label: "b", + Kind: protocol.FieldCompletion, + Detail: "self.b", + InsertText: "b", + LabelDetails: protocol.CompletionItemLabelDetails{ + Description: "variable", + }, + }, + { + Label: "c", + Kind: protocol.FieldCompletion, + Detail: "self.c", + InsertText: "c", + LabelDetails: protocol.CompletionItemLabelDetails{ + Description: "string", + }, + }, + { + Label: "test", + Kind: protocol.FieldCompletion, + Detail: "self.test", + InsertText: "test", + LabelDetails: protocol.CompletionItemLabelDetails{ + Description: "*ast.Apply", + }, + }, + }, + }, + }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) {