Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lsp/completions: Support snippets based function signatures for built in suggestions #1062

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion internal/lsp/completions/providers/builtins.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package providers

import (
"fmt"
"strings"

"github.com/open-policy-agent/opa/ast"
"github.com/styrainc/regal/internal/lsp/cache"
"github.com/styrainc/regal/internal/lsp/hover"
"github.com/styrainc/regal/internal/lsp/rego"
Expand Down Expand Up @@ -56,6 +58,8 @@ func (*BuiltIns) Run(c *cache.Cache, params types.CompletionParams, _ *Options)
continue
}

insertTextFormat := uint(2) // snippet

items = append(items, types.CompletionItem{
Label: key,
Kind: completion.Function,
Expand All @@ -64,6 +68,7 @@ func (*BuiltIns) Run(c *cache.Cache, params types.CompletionParams, _ *Options)
Kind: "markdown",
Value: hover.CreateHoverContent(builtIn),
},
InsertTextFormat: &insertTextFormat,
TextEdit: &types.TextEdit{
Range: types.Range{
Start: types.Position{
Expand All @@ -75,10 +80,34 @@ func (*BuiltIns) Run(c *cache.Cache, params types.CompletionParams, _ *Options)
Character: params.Position.Character,
},
},
NewText: key,
NewText: newTextForBuiltIn(builtIn),
},
})
}

return items, nil
}

func newTextForBuiltIn(bi *ast.Builtin) string {
args := make([]string, len(bi.Decl.Args()))

for i, arg := range bi.Decl.NamedFuncArgs().Args {
args[i] = strings.Split(arg.String(), ":")[0]
}

if len(args) == 0 {
return bi.Name + "($0)"
}

argString := ""

for i, arg := range args {
if i > 0 {
argString += ", "
}

argString += fmt.Sprintf("${%d:%s}", i+1, arg)
}

return fmt.Sprintf("%s(%s)", bi.Name, argString)
}
41 changes: 41 additions & 0 deletions internal/lsp/completions/providers/builtins_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,50 @@ import (
"testing"

"github.com/styrainc/regal/internal/lsp/cache"
"github.com/styrainc/regal/internal/lsp/rego"
"github.com/styrainc/regal/internal/lsp/types"
)

func TestNewTextForBuiltIn(t *testing.T) {
t.Parallel()

testCases := map[string]struct {
BuiltIn string
Output string
}{
"print": {
BuiltIn: "print",
Output: "print($0)",
},
"strings.count": {
BuiltIn: "strings.count",
Output: "strings.count(${1:search}, ${2:substring})",
},
"json.patch": {
BuiltIn: "json.patch",
Output: "json.patch(${1:object}, ${2:patches)",
},
}

bis := rego.GetBuiltins()

for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
t.Parallel()

bi, ok := bis[tc.BuiltIn]
if !ok {
t.Fatalf("BuiltIn %s not found", tc.BuiltIn)
}

output := newTextForBuiltIn(bi)
if output != tc.Output {
t.Fatalf("Expected\n%s\ngot\n%s", tc.Output, output)
}
})
}
}

func TestBuiltIns_if(t *testing.T) {
t.Parallel()

Expand Down
12 changes: 6 additions & 6 deletions internal/lsp/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,12 @@ type CompletionItem struct {
Label string `json:"label"`
LabelDetails *CompletionItemLabelDetails `json:"labelDetails,omitempty"`
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#completionItemKind
Kind completion.ItemKind `json:"kind"`
Detail string `json:"detail"`
Documentation *MarkupContent `json:"documentation,omitempty"`
Preselect bool `json:"preselect"`
TextEdit *TextEdit `json:"textEdit,omitempty"`
InserTextFormat *uint `json:"insertTextFormat,omitempty"`
Kind completion.ItemKind `json:"kind"`
Detail string `json:"detail"`
Documentation *MarkupContent `json:"documentation,omitempty"`
Preselect bool `json:"preselect"`
TextEdit *TextEdit `json:"textEdit,omitempty"`
InsertTextFormat *uint `json:"insertTextFormat,omitempty"`

// Mandatory is used to indicate that the completion item is mandatory and should be offered
// as an exclusive completion. This is not part of the LSP spec, but used in regal providers
Expand Down
Loading