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

fix(completion): use upstream go-jsonnet Formatter #137

Merged
merged 3 commits into from
May 16, 2024
Merged
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
85 changes: 27 additions & 58 deletions pkg/server/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/google/go-jsonnet"
"github.com/google/go-jsonnet/ast"
"github.com/google/go-jsonnet/formatter"
"github.com/grafana/jsonnet-language-server/pkg/ast/processing"
"github.com/grafana/jsonnet-language-server/pkg/nodestack"
position "github.com/grafana/jsonnet-language-server/pkg/position_conversion"
Expand Down Expand Up @@ -163,30 +164,39 @@ func createCompletionItemsFromRanges(ranges []processing.ObjectRange, completion
return items
}

func createCompletionItem(label, prefix string, kind protocol.CompletionItemKind, body ast.Node, position protocol.Position) protocol.CompletionItem {
mustNotQuoteLabel := IsValidIdentifier(label)

insertText := label
detail := label
if prefix != "" {
detail = prefix + "." + insertText
}
if !mustNotQuoteLabel {
insertText = "['" + label + "']"
detail = prefix + insertText
}
func formatLabel(str string) string {
interStr := "interimPath" + str
fmtStr, _ := formatter.Format("", interStr, formatter.DefaultOptions())
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we somehow should pass along the formatter options that the LSP already knows of. Also not sure how that would work.

ret, _ := strings.CutPrefix(fmtStr, "interimPath")
Comment on lines +168 to +170
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This tricks the formatter into thinking it is formatting an ast.Index path, without interimPath, it would be formatting ['something'] as an array instead.

ret, _ = strings.CutPrefix(ret, ".")
ret = strings.TrimRight(ret, "\n")
return ret
}

func createCompletionItem(label, prefix string, kind protocol.CompletionItemKind, body ast.Node, position protocol.Position) protocol.CompletionItem {
paramsString := ""
if asFunc, ok := body.(*ast.Function); ok {
kind = protocol.FunctionCompletion
params := []string{}
for _, param := range asFunc.Parameters {
params = append(params, string(param.Name))
}
paramsString := "(" + strings.Join(params, ", ") + ")"
detail += paramsString
insertText += paramsString
paramsString = "(" + strings.Join(params, ", ") + ")"
}

insertText := formatLabel("['" + label + "']" + paramsString)

concat := ""
characterStartPosition := position.Character - 1
if prefix == "" {
characterStartPosition = position.Character
}
if prefix != "" && !strings.HasPrefix(insertText, "[") {
concat = "."
characterStartPosition = position.Character
}
detail := prefix + concat + insertText

item := protocol.CompletionItem{
Label: label,
Detail: detail,
Expand All @@ -197,13 +207,12 @@ func createCompletionItem(label, prefix string, kind protocol.CompletionItemKind
InsertText: insertText,
}

// Remove leading `.` character when quoting label
if !mustNotQuoteLabel {
if strings.HasPrefix(insertText, "[") {
item.TextEdit = &protocol.TextEdit{
Range: protocol.Range{
Start: protocol.Position{
Line: position.Line,
Character: position.Character - 1,
Character: characterStartPosition,
},
End: protocol.Position{
Line: position.Line,
Expand All @@ -217,46 +226,6 @@ func createCompletionItem(label, prefix string, kind protocol.CompletionItemKind
return item
}

// Start - Copied from go-jsonnet/internal/parser/lexer.go

func isUpper(r rune) bool {
return r >= 'A' && r <= 'Z'
}
func isLower(r rune) bool {
return r >= 'a' && r <= 'z'
}
func isNumber(r rune) bool {
return r >= '0' && r <= '9'
}
func isIdentifierFirst(r rune) bool {
return isUpper(r) || isLower(r) || r == '_'
}
func isIdentifier(r rune) bool {
return isIdentifierFirst(r) || isNumber(r)
}
func IsValidIdentifier(str string) bool {
if len(str) == 0 {
return false
}
for i, r := range str {
if i == 0 {
if !isIdentifierFirst(r) {
return false
}
} else {
if !isIdentifier(r) {
return false
}
}
}
// Ignore tokens for now, we should ask upstream to make the formatter a public package
// so we can use go-jsonnet/internal/formatter/pretty_field_names.go directly.
// return getTokenKindFromID(str) == tokenIdentifier
return true
}

// End - Copied from go-jsonnet/internal/parser/lexer.go

func typeToString(t ast.Node) string {
switch t.(type) {
case *ast.Array:
Expand Down
Loading