From bdc4b6cb6521d78cabb1acdae2c67103a480ca71 Mon Sep 17 00:00:00 2001 From: qvalentin Date: Wed, 8 May 2024 12:31:57 +0200 Subject: [PATCH] fix: cleanup for templateContext completio --- internal/handler/completion.go | 2 + internal/handler/completion_main_test.go | 76 +++++++++++++++++++ internal/handler/references.go | 2 +- .../template_context_completion.go | 35 ++++++--- internal/util/values.go | 10 +-- 5 files changed, 107 insertions(+), 18 deletions(-) diff --git a/internal/handler/completion.go b/internal/handler/completion.go index 7f301f5e..2407c503 100644 --- a/internal/handler/completion.go +++ b/internal/handler/completion.go @@ -30,6 +30,8 @@ func (h *langHandler) Completion(ctx context.Context, params *lsp.CompletionPara } } + // If no usecase matched, we assume we are at {{ }} + // and provide the basic BuiltInObjects and functions items := []helmdocs.HelmDocumentation{} for _, v := range helmdocs.BuiltInObjects { v.Name = "." + v.Name diff --git a/internal/handler/completion_main_test.go b/internal/handler/completion_main_test.go index 4e304e1a..59e4d6d1 100644 --- a/internal/handler/completion_main_test.go +++ b/internal/handler/completion_main_test.go @@ -2,7 +2,9 @@ package handler import ( "context" + "errors" "os" + "strings" "testing" "github.com/mrjosh/helm-ls/internal/adapter/yamlls" @@ -11,6 +13,7 @@ import ( lsplocal "github.com/mrjosh/helm-ls/internal/lsp" "github.com/mrjosh/helm-ls/internal/util" "github.com/stretchr/testify/assert" + "go.lsp.dev/protocol" lsp "go.lsp.dev/protocol" "go.lsp.dev/uri" ) @@ -178,3 +181,76 @@ func TestCompletionMain(t *testing.T) { }) } } + +func TestCompletionMainSingleLines(t *testing.T) { + testCases := []struct { + templateWithMark string + expectedInsertTexts []string + notExpectedInsertTexts []string + err error + }{ + {"Test completion on {{ .Bad.^ }}", []string{}, []string{}, errors.New("[Bad ] is no valid template context for helm")}, + {"Test completion on {{ n^ }}", []string{"not"}, []string{}, nil}, + {"Test completion on {{ .Values.^ }}", []string{"replicaCount"}, []string{}, nil}, + {"Test completion on {{ .Release.N^ }}", []string{"Name"}, []string{}, nil}, + {"Test completion on {{ .Capabilities.KubeVersion.^ }}", []string{"Minor"}, []string{}, nil}, + {"Test completion on {{ .Capabilities.KubeVersion.Mi^ }}", []string{"nor"}, []string{}, nil}, + {`Test completion on {{ define "test" }} T1 {{ end }} {{ include "te^"}}`, []string{"test"}, []string{}, nil}, + } + + for _, tt := range testCases { + t.Run(tt.templateWithMark, func(t *testing.T) { + // seen chars up to ^ + col := strings.Index(tt.templateWithMark, "^") + buf := strings.Replace(tt.templateWithMark, "^", "", 1) + pos := protocol.Position{Line: 0, Character: uint32(col)} + + documents := lsplocal.NewDocumentStore() + + // to get the correct values file + path := "../../testdata/example/templates/completion-test.yaml" + fileURI := uri.File(path) + + d := lsp.DidOpenTextDocumentParams{ + TextDocument: lsp.TextDocumentItem{ + URI: fileURI, + LanguageID: "", + Version: 0, + Text: buf, + }, + } + documents.DidOpen(&d, util.DefaultConfig) + h := &langHandler{ + chartStore: charts.NewChartStore(uri.File("."), charts.NewChart), + documents: documents, + yamllsConnector: &yamlls.Connector{}, + } + result, err := h.Completion(context.Background(), &lsp.CompletionParams{ + TextDocumentPositionParams: lsp.TextDocumentPositionParams{ + TextDocument: lsp.TextDocumentIdentifier{ + URI: fileURI, + }, + Position: pos, + }, + }) + assert.NotNil(t, result) + assert.Equal(t, tt.err, err) + + if result == nil { + return + } + + insertTexts := []string{} + for _, item := range result.Items { + insertTexts = append(insertTexts, item.InsertText) + } + for _, expectedInsertText := range tt.expectedInsertTexts { + assert.Contains(t, insertTexts, expectedInsertText) + } + + for _, notExpectedInsertText := range tt.notExpectedInsertTexts { + assert.NotContains(t, insertTexts, notExpectedInsertText) + } + }) + } +} diff --git a/internal/handler/references.go b/internal/handler/references.go index bc3978ec..f41b3a14 100644 --- a/internal/handler/references.go +++ b/internal/handler/references.go @@ -8,7 +8,7 @@ import ( lsp "go.lsp.dev/protocol" ) -func (h *langHandler) References(ctx context.Context, params *lsp.ReferenceParams) (result []lsp.Location, err error) { +func (h *langHandler) References(_ context.Context, params *lsp.ReferenceParams) (result []lsp.Location, err error) { genericDocumentUseCase, err := h.NewGenericDocumentUseCase(params.TextDocumentPositionParams, lsplocal.NodeAtPosition) if err != nil { return nil, err diff --git a/internal/language_features/template_context_completion.go b/internal/language_features/template_context_completion.go index d37ba686..815539d1 100644 --- a/internal/language_features/template_context_completion.go +++ b/internal/language_features/template_context_completion.go @@ -1,6 +1,9 @@ package languagefeatures import ( + "fmt" + "strings" + helmdocs "github.com/mrjosh/helm-ls/internal/documentation/helm" lsplocal "github.com/mrjosh/helm-ls/internal/lsp" "github.com/mrjosh/helm-ls/internal/protocol" @@ -8,7 +11,7 @@ import ( lsp "go.lsp.dev/protocol" ) -func (f *TemplateContextFeature) Completion() (result *lsp.CompletionList, err error) { +func (f *TemplateContextFeature) Completion() (*lsp.CompletionList, error) { templateContext, err := f.getTemplateContext() if err != nil { return nil, err @@ -25,21 +28,31 @@ func (f *TemplateContextFeature) Completion() (result *lsp.CompletionList, err e } return protocol.CompletionResults{}.WithDocs(result, lsp.CompletionItemKindValue).ToList(), nil } - - switch templateContext[0] { - case "Values": + if templateContext[0] == "Values" { return f.valuesCompletion(templateContext) - case "Chart", "Release", "Files", "Capabilities", "Template": - // TODO: make this more fine, by checking the length - result, ok := helmdocs.BuiltInOjectVals[templateContext[0]] - if !ok { - return protocol.CompletionResults{}.WithDocs(helmdocs.BuiltInObjects, lsp.CompletionItemKindConstant).ToList(), nil + } + nestedDocs, ok := helmdocs.BuiltInOjectVals[templateContext[0]] + if ok { + if len(templateContext) < 3 { + return protocol.CompletionResults{}.WithDocs(nestedDocs, lsp.CompletionItemKindValue).ToList(), nil } - return protocol.CompletionResults{}.WithDocs(result, lsp.CompletionItemKindValue).ToList(), nil + adjustedDocs := trimPrefixForNestedDocs(nestedDocs, templateContext) + return protocol.CompletionResults{}.WithDocs(adjustedDocs, lsp.CompletionItemKindValue).ToList(), nil } + return protocol.CompletionResults{}.ToList(), fmt.Errorf("%s is no valid template context for helm", templateContext) +} - return nil, nil +// handels the completion for .Capabilities.KubeVersion.^ where the result should not contain KubeVersion again +func trimPrefixForNestedDocs(nestedDocs []helmdocs.HelmDocumentation, templateContext lsplocal.TemplateContext) []helmdocs.HelmDocumentation { + adjustedDocs := []helmdocs.HelmDocumentation{} + for _, v := range nestedDocs { + if strings.HasPrefix(v.Name, templateContext.Tail().Format()) { + v.Name = strings.TrimPrefix(v.Name, templateContext.Tail().Format()) + adjustedDocs = append(adjustedDocs, v) + } + } + return adjustedDocs } func (f *TemplateContextFeature) valuesCompletion(templateContext lsplocal.TemplateContext) (*lsp.CompletionList, error) { diff --git a/internal/util/values.go b/internal/util/values.go index 3171f741..ac4e28bd 100644 --- a/internal/util/values.go +++ b/internal/util/values.go @@ -31,7 +31,6 @@ func GetValueCompletion(values chartutil.Values, splittedVar []string) []lsp.Com ) if len(splittedVar) > 0 { - localValues, err = values.Table(tableName) if err != nil { if len(splittedVar) > 1 { @@ -47,17 +46,16 @@ func GetValueCompletion(values chartutil.Values, splittedVar []string) []lsp.Com } else { values = localValues } - } for variable, value := range values { - items = setItem(items, value, variable) + items = append(items, builCompletionItem(value, variable)) } return items } -func setItem(items []lsp.CompletionItem, value interface{}, variable string) []lsp.CompletionItem { +func builCompletionItem(value interface{}, variable string) lsp.CompletionItem { var ( itemKind = lsp.CompletionItemKindVariable valueOf = reflect.ValueOf(value) @@ -80,13 +78,13 @@ func setItem(items []lsp.CompletionItem, value interface{}, variable string) []l itemKind = lsp.CompletionItemKindField } - return append(items, lsp.CompletionItem{ + return lsp.CompletionItem{ Label: variable, InsertText: variable, Documentation: documentation, Detail: valueOf.Kind().String(), Kind: itemKind, - }) + } } func FormatToYAML(field reflect.Value, fieldName string) string {