Skip to content

Commit

Permalink
refactor(completion): rework result formating
Browse files Browse the repository at this point in the history
  • Loading branch information
qvalentin committed May 4, 2024
1 parent 2bf9763 commit 5f95b6b
Show file tree
Hide file tree
Showing 19 changed files with 185 additions and 174 deletions.
2 changes: 1 addition & 1 deletion internal/adapter/yamlls/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
lsp "go.lsp.dev/protocol"
)

func (yamllsConnector Connector) CallCompletion(ctx context.Context, params *lsp.CompletionParams) (*lsp.CompletionList, error) {
func (yamllsConnector *Connector) CallCompletion(ctx context.Context, params *lsp.CompletionParams) (*lsp.CompletionList, error) {
if yamllsConnector.server == nil {
return &lsp.CompletionList{}, nil
}
Expand Down
22 changes: 17 additions & 5 deletions internal/adapter/yamlls/hover.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ package yamlls
import (
"context"

"github.com/mrjosh/helm-ls/internal/util"
"github.com/mrjosh/helm-ls/internal/protocol"
lsp "go.lsp.dev/protocol"
)

// Calls the Hover method of yamlls to get a fitting hover response
// If hover returns nothing appropriate, calls yamlls for completions
//
// Yamlls can not handle hover if the schema validation returns error,
// thats why we fall back to calling completion
func (yamllsConnector Connector) CallHover(ctx context.Context, params lsp.HoverParams, word string) (*lsp.Hover, error) {
if yamllsConnector.server == nil {
return &lsp.Hover{}, nil
Expand All @@ -25,15 +28,17 @@ func (yamllsConnector Connector) CallHover(ctx context.Context, params lsp.Hover
return (yamllsConnector).getHoverFromCompletion(ctx, params, word)
}

func (yamllsConnector Connector) getHoverFromCompletion(ctx context.Context, params lsp.HoverParams, word string) (*lsp.Hover, error) {
func (yamllsConnector Connector) getHoverFromCompletion(ctx context.Context, params lsp.HoverParams, word string) (response *lsp.Hover, err error) {
var (
err error
resultRange lsp.Range
documentation string
completionParams = lsp.CompletionParams{
TextDocumentPositionParams: params.TextDocumentPositionParams,
}
)

word = removeTrailingColon(word)

completionList, err := yamllsConnector.server.Completion(ctx, &completionParams)
if err != nil {
logger.Error("Error calling yamlls for Completion", err)
Expand All @@ -43,10 +48,17 @@ func (yamllsConnector Connector) getHoverFromCompletion(ctx context.Context, par
for _, completionItem := range completionList.Items {
if completionItem.InsertText == word {
documentation = completionItem.Documentation.(string)
resultRange = completionItem.TextEdit.Range
break
}
}

response := util.BuildHoverResponse(documentation, lsp.Range{})
return response, nil
return protocol.BuildHoverResponse(documentation, resultRange), nil
}

func removeTrailingColon(word string) string {
if len(word) > 2 && string(word[len(word)-1]) == ":" {
word = word[0 : len(word)-1]
}
return word
}
8 changes: 1 addition & 7 deletions internal/documentation/helm/helm-documentation.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,13 +261,7 @@ var (
)

func GetFunctionByName(name string) (HelmDocumentation, bool) {
completionItems := [][]HelmDocumentation{
BuiltinFuncs,
SprigFuncs,
HelmFuncs,
}
toSearch := util.ConcatMultipleSlices(completionItems)
for _, completionItem := range toSearch {
for _, completionItem := range AllFuncs {
if name == completionItem.Name {
return completionItem, true
}
Expand Down
92 changes: 2 additions & 90 deletions internal/handler/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,25 @@ package handler

import (
"context"
"fmt"

languagefeatures "github.com/mrjosh/helm-ls/internal/language_features"
lsplocal "github.com/mrjosh/helm-ls/internal/lsp"
gotemplate "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate"
sitter "github.com/smacker/go-tree-sitter"
"go.lsp.dev/protocol"
lsp "go.lsp.dev/protocol"

"github.com/mrjosh/helm-ls/internal/documentation/godocs"
helmdocs "github.com/mrjosh/helm-ls/internal/documentation/helm"
)

var (
emptyItems = make([]lsp.CompletionItem, 0)
textCompletionsItems = make([]lsp.CompletionItem, 0)
)

func init() {
textCompletionsItems = append(textCompletionsItems, getTextCompletionItems(godocs.TextSnippets)...)
}

func (h *langHandler) Completion(ctx context.Context, params *lsp.CompletionParams) (result *lsp.CompletionList, err error) {
logger.Debug("Running completion with params", params)
genericDocumentUseCase, err := h.NewGenericDocumentUseCase(params.TextDocumentPositionParams)
genericDocumentUseCase, err := h.NewGenericDocumentUseCase(params.TextDocumentPositionParams, lsplocal.NestedNodeAtPositionForCompletion)
if err != nil {
return nil, err
}

var (
currentNode = lsplocal.NodeAtPosition(genericDocumentUseCase.Document.Ast, params.Position)
pointToLoopUp = sitter.Point{
Row: params.Position.Line,
Column: params.Position.Character,
}
relevantChildNode = lsplocal.FindRelevantChildNodeCompletion(currentNode, pointToLoopUp)
)
genericDocumentUseCase = genericDocumentUseCase.WithNode(relevantChildNode)

usecases := []languagefeatures.CompletionUseCase{
languagefeatures.NewTemplateContextFeature(genericDocumentUseCase),
languagefeatures.NewFunctionCallFeature(genericDocumentUseCase),
languagefeatures.NewTextFeature(ctx, genericDocumentUseCase, h.yamllsConnector, params.TextDocumentPositionParams),
}

for _, usecase := range usecases {
Expand All @@ -52,17 +29,6 @@ func (h *langHandler) Completion(ctx context.Context, params *lsp.CompletionPara
}
}

word, isTextNode := completionAstParsing(genericDocumentUseCase.Document, params.Position)

if isTextNode {
result := make([]lsp.CompletionItem, 0)
result = append(result, textCompletionsItems...)
result = append(result, yamllsCompletions(ctx, h, params)...)
logger.Debug("Sending completions ", result)
return &protocol.CompletionList{IsIncomplete: false, Items: result}, err
}

logger.Println(fmt.Sprintf("Word found for completions is < %s >", word))
items := []lsp.CompletionItem{}
for _, v := range helmdocs.BuiltInObjects {
items = append(items, lsp.CompletionItem{
Expand All @@ -74,57 +40,3 @@ func (h *langHandler) Completion(ctx context.Context, params *lsp.CompletionPara
}
return &lsp.CompletionList{IsIncomplete: false, Items: items}, err
}

func yamllsCompletions(ctx context.Context, h *langHandler, params *lsp.CompletionParams) []lsp.CompletionItem {
response, err := h.yamllsConnector.CallCompletion(ctx, params)
if err != nil {
logger.Error("Error getting yamlls completions", err)
return []lsp.CompletionItem{}
}
logger.Debug("Got completions from yamlls", response)
return response.Items
}

func completionAstParsing(doc *lsplocal.Document, position lsp.Position) (string, bool) {
var (
currentNode = lsplocal.NodeAtPosition(doc.Ast, position)
pointToLoopUp = sitter.Point{
Row: position.Line,
Column: position.Character,
}
relevantChildNode = lsplocal.FindRelevantChildNode(currentNode, pointToLoopUp)
word string
)

nodeType := relevantChildNode.Type()
switch nodeType {
case gotemplate.NodeTypeIdentifier:
word = relevantChildNode.Content([]byte(doc.Content))
case gotemplate.NodeTypeText, gotemplate.NodeTypeTemplate:
return word, true
}
logger.Debug("word", word)
return word, false
}

func getTextCompletionItems(gotemplateSnippet []godocs.GoTemplateSnippet) (result []lsp.CompletionItem) {
for _, item := range gotemplateSnippet {
result = append(result, textCompletionItem(item))
}
return result
}

func textCompletionItem(gotemplateSnippet godocs.GoTemplateSnippet) lsp.CompletionItem {
return lsp.CompletionItem{
Label: gotemplateSnippet.Name,
// TextEdit: &lsp.TextEdit{
// // Range: lsp.Range{}, // TODO: range must contain the requested range
// NewText: gotemplateSnippet.Snippet,
// },
InsertText: gotemplateSnippet.Snippet,
Detail: gotemplateSnippet.Detail,
Documentation: gotemplateSnippet.Doc,
Kind: lsp.CompletionItemKindText,
InsertTextFormat: lsp.InsertTextFormatSnippet,
}
}
26 changes: 13 additions & 13 deletions internal/handler/completion_main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@ func TestCompletionMain(t *testing.T) {
notExpectedInsertTexts []string
expectedError error
}{
{
desc: "Test completion on text",
position: lsp.Position{
Line: 4,
Character: 0,
},
expectedInsertText: "{{- if $1 }}\n $2 \n{{- else }}\n $0 \n{{- end }}",
notExpectedInsertTexts: []string{
"replicaCount",
"toYaml",
},
expectedError: nil,
},
{
desc: "Test completion on {{ if (and .Values. ) }}",
position: lsp.Position{
Expand Down Expand Up @@ -99,19 +112,6 @@ func TestCompletionMain(t *testing.T) {
},
expectedError: nil,
},
{
desc: "Test completion on text",
position: lsp.Position{
Line: 4,
Character: 0,
},
expectedInsertText: "{{- if $1 }}\n $2 \n{{- else }}\n $0 \n{{- end }}",
notExpectedInsertTexts: []string{
"replicaCount",
"toYaml",
},
expectedError: nil,
},
// {
// desc: "Test completion on {{ }}",
// position: lsp.Position{
Expand Down
3 changes: 2 additions & 1 deletion internal/handler/definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import (
"context"

languagefeatures "github.com/mrjosh/helm-ls/internal/language_features"
lsplocal "github.com/mrjosh/helm-ls/internal/lsp"
lsp "go.lsp.dev/protocol"
)

func (h *langHandler) Definition(_ context.Context, params *lsp.DefinitionParams) (result []lsp.Location, err error) {
genericDocumentUseCase, err := h.NewGenericDocumentUseCase(params.TextDocumentPositionParams)
genericDocumentUseCase, err := h.NewGenericDocumentUseCase(params.TextDocumentPositionParams, lsplocal.NodeAtPosition)
if err != nil {
return nil, err
}
Expand Down
39 changes: 26 additions & 13 deletions internal/handler/generic_document_usecase.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import (
"errors"

languagefeatures "github.com/mrjosh/helm-ls/internal/language_features"
lsplocal "github.com/mrjosh/helm-ls/internal/lsp"
sitter "github.com/smacker/go-tree-sitter"
lsp "go.lsp.dev/protocol"
)

func (h *langHandler) NewGenericDocumentUseCase(params lsp.TextDocumentPositionParams) (*languagefeatures.GenericDocumentUseCase, error) {
func (h *langHandler) NewGenericDocumentUseCase(
params lsp.TextDocumentPositionParams,
nodeSelection func(ast *sitter.Tree, position lsp.Position) (node *sitter.Node),
) (*languagefeatures.GenericDocumentUseCase, error) {
doc, ok := h.documents.Get(params.TextDocument.URI)
if !ok {
return &languagefeatures.GenericDocumentUseCase{}, errors.New("Could not get document: " + params.TextDocument.URI.Filename())
Expand All @@ -18,19 +20,30 @@ func (h *langHandler) NewGenericDocumentUseCase(params lsp.TextDocumentPositionP
if err != nil {
logger.Error("Error getting chart info for file", params.TextDocument.URI, err)
}
node := h.getNode(doc, params.Position)

node := nodeSelection(doc.Ast, params.Position)
if node == nil {
return &languagefeatures.GenericDocumentUseCase{}, errors.New("Could not get node for: " + params.TextDocument.URI.Filename())
}
return languagefeatures.GenericDocumentUseCase{
Document: doc,
DocumentStore: h.documents,
Chart: chart,
ChartStore: h.chartStore,
}.WithNode(node), nil
}

func (h *langHandler) getNode(doc *lsplocal.Document, position lsp.Position) *sitter.Node {
currentNode := lsplocal.NodeAtPosition(doc.Ast, position)
return currentNode
var (
parentNodeType string
nodeType = node.Type()
parentNode = node.Parent()
)

if parentNode != nil {
parentNodeType = parentNode.Type()
}

return &languagefeatures.GenericDocumentUseCase{
Document: doc,
DocumentStore: h.documents,
Chart: chart,
ChartStore: h.chartStore,
Node: node,
NodeType: nodeType,
ParentNode: parentNode,
ParentNodeType: parentNodeType,
}, nil
}
13 changes: 5 additions & 8 deletions internal/handler/hover.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@ import (
"context"

languagefeatures "github.com/mrjosh/helm-ls/internal/language_features"
lspinternal "github.com/mrjosh/helm-ls/internal/lsp"
lsplocal "github.com/mrjosh/helm-ls/internal/lsp"
"github.com/mrjosh/helm-ls/internal/protocol"
"github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate"

"github.com/mrjosh/helm-ls/internal/util"
lsp "go.lsp.dev/protocol"
)

func (h *langHandler) Hover(ctx context.Context, params *lsp.HoverParams) (result *lsp.Hover, err error) {
genericDocumentUseCase, err := h.NewGenericDocumentUseCase(params.TextDocumentPositionParams)
genericDocumentUseCase, err := h.NewGenericDocumentUseCase(params.TextDocumentPositionParams, lsplocal.NodeAtPosition)
if err != nil {
return nil, err
}

wordRange := lspinternal.GetLspRangeForNode(genericDocumentUseCase.Node)
wordRange := lsplocal.GetLspRangeForNode(genericDocumentUseCase.Node)

usecases := []languagefeatures.HoverUseCase{
languagefeatures.NewBuiltInObjectsFeature(genericDocumentUseCase), // has to be before template context
Expand All @@ -29,15 +29,12 @@ func (h *langHandler) Hover(ctx context.Context, params *lsp.HoverParams) (resul
for _, usecase := range usecases {
if usecase.AppropriateForNode() {
result, err := usecase.Hover()
return util.BuildHoverResponse(result, wordRange), err
return protocol.BuildHoverResponse(result, wordRange), err
}
}

if genericDocumentUseCase.NodeType == gotemplate.NodeTypeText {
word := genericDocumentUseCase.Document.WordAt(params.Position)
if len(word) > 2 && string(word[len(word)-1]) == ":" {
word = word[0 : len(word)-1]
}
response, err := h.yamllsConnector.CallHover(ctx, *params, word)
return response, err
}
Expand Down
3 changes: 2 additions & 1 deletion internal/handler/references.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import (
"context"

languagefeatures "github.com/mrjosh/helm-ls/internal/language_features"
lsplocal "github.com/mrjosh/helm-ls/internal/lsp"
lsp "go.lsp.dev/protocol"
)

func (h *langHandler) References(ctx context.Context, params *lsp.ReferenceParams) (result []lsp.Location, err error) {
genericDocumentUseCase, err := h.NewGenericDocumentUseCase(params.TextDocumentPositionParams)
genericDocumentUseCase, err := h.NewGenericDocumentUseCase(params.TextDocumentPositionParams, lsplocal.NodeAtPosition)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion internal/language_features/function_call.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@ func (f *FunctionCallFeature) Hover() (string, error) {
}

func (f *FunctionCallFeature) Completion() (result *lsp.CompletionList, err error) {
return protocol.NewCompletionResults(helmdocs.AllFuncs).ToLSP(), nil
return protocol.CompletionResults{}.WithDocs(helmdocs.AllFuncs, lsp.CompletionItemKindFunction).ToList(), nil
}
1 change: 1 addition & 0 deletions internal/language_features/generic_document_usecase.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ func (u *GenericDocumentUseCase) NodeContent() string {
return u.Node.Content([]byte(u.Document.Content))
}

// Allows for using a custom node selection
func (u GenericDocumentUseCase) WithNode(node *sitter.Node) *GenericDocumentUseCase {
u.Node = node
u.NodeType = node.Type()
Expand Down
Loading

0 comments on commit 5f95b6b

Please sign in to comment.