From e2ac2b6c73e6f80377b41510d9cc2f28ab45b45b Mon Sep 17 00:00:00 2001 From: qvalentin Date: Sat, 13 Jan 2024 13:48:14 +0100 Subject: [PATCH 01/35] feat(workspaces): support editing multiple charts --- .gitignore | 3 + Makefile | 3 + internal/charts/chart.go | 36 ++++++ internal/charts/chart_for_document.go | 72 +++++++++++ internal/charts/chart_for_document_test.go | 86 +++++++++++++ internal/charts/chart_store.go | 38 ++++++ internal/charts/chart_test.go | 90 +++++++++++++ internal/charts/metadata.go | 40 ++++++ internal/charts/parent_chart.go | 33 +++++ internal/charts/values_file.go | 34 +++++ internal/charts/values_file_test.go | 34 +++++ internal/charts/values_files.go | 62 +++++++++ internal/charts/values_files_test.go | 52 ++++++++ internal/handler/completion.go | 23 +++- internal/handler/completion_values_test.go | 70 +++++++++- internal/handler/definition.go | 83 +++++++----- internal/handler/definition_test.go | 136 ++++++++++++++++---- internal/handler/handler.go | 19 +-- internal/handler/hover.go | 69 +++++++--- internal/handler/hover_test.go | 143 +++++++++++++++++++++ internal/handler/initialization.go | 29 +---- internal/util/strings.go | 2 + internal/util/yaml.go | 6 +- internal/util/yaml_test.go | 6 +- 24 files changed, 1051 insertions(+), 118 deletions(-) create mode 100644 internal/charts/chart.go create mode 100644 internal/charts/chart_for_document.go create mode 100644 internal/charts/chart_for_document_test.go create mode 100644 internal/charts/chart_store.go create mode 100644 internal/charts/chart_test.go create mode 100644 internal/charts/metadata.go create mode 100644 internal/charts/parent_chart.go create mode 100644 internal/charts/values_file.go create mode 100644 internal/charts/values_file_test.go create mode 100644 internal/charts/values_files.go create mode 100644 internal/charts/values_files_test.go create mode 100644 internal/handler/hover_test.go diff --git a/.gitignore b/.gitignore index 21d8609b..a66957a3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ /bin /dist +__debug_bin* +.vscode +.coverage diff --git a/Makefile b/Makefile index 260f3e0e..44eca004 100644 --- a/Makefile +++ b/Makefile @@ -57,6 +57,9 @@ install-metalinter: test: @$(GO) test ./... -v -race +coverage: + @$(GO) test -coverprofile=.coverage ./internal/... && go tool cover -html=.coverage + .PHONY: build-release build-release: @docker run \ diff --git a/internal/charts/chart.go b/internal/charts/chart.go new file mode 100644 index 00000000..6b496e85 --- /dev/null +++ b/internal/charts/chart.go @@ -0,0 +1,36 @@ +package charts + +import ( + "github.com/mrjosh/helm-ls/internal/log" + "go.lsp.dev/uri" +) + +var logger = log.GetLogger() + +type Chart struct { + ValuesFiles *ValuesFiles + ChartMetadata *ChartMetadata + RootURI uri.URI + ParentChart ParentChart +} + +func NewChart(rootURI uri.URI) *Chart { + return &Chart{ + ValuesFiles: NewValuesFiles(rootURI, "values.yaml", "values*.yaml"), + ChartMetadata: NewChartMetadata(rootURI), + RootURI: rootURI, + ParentChart: getParentChart(rootURI), + } +} + +// ResolveValueFiles returns a list of all values files in the chart +// and all parent charts if the query tries to access global values +func (c *Chart) ResolveValueFiles(query []string, chartStore *ChartStore) []*ValuesFiles { + if len(query) > 0 && query[0] == "global" { + parentChart := c.ParentChart.GetParentChart(chartStore) + if parentChart != nil { + return append([]*ValuesFiles{c.ValuesFiles}, parentChart.ResolveValueFiles(query, chartStore)...) + } + } + return []*ValuesFiles{c.ValuesFiles} +} diff --git a/internal/charts/chart_for_document.go b/internal/charts/chart_for_document.go new file mode 100644 index 00000000..ce56dcd4 --- /dev/null +++ b/internal/charts/chart_for_document.go @@ -0,0 +1,72 @@ +package charts + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/mrjosh/helm-ls/pkg/chartutil" + lsp "go.lsp.dev/protocol" + "go.lsp.dev/uri" +) + +func (s *ChartStore) GetChartForDoc(uri lsp.DocumentURI) (*Chart, error) { + chart := s.getChartFromCache(uri) + if chart != nil { + return chart, nil + } + + chart = s.getChartFromFilesystemForTemplates(uri.Filename()) + + if chart != nil { + s.Charts[chart.RootURI] = chart + return chart, nil + } + + return nil, ErrChartNotFound{ + URI: uri, + } +} + +func (s *ChartStore) getChartFromCache(uri lsp.DocumentURI) *Chart { + for chartURI, chart := range s.Charts { + if strings.HasPrefix(uri.Filename(), filepath.Join(chartURI.Filename(), "template")) { + return chart + } + } + return nil +} + +func (s *ChartStore) getChartFromFilesystemForTemplates(path string) *Chart { + directory := filepath.Dir(path) + if filepath.Base(directory) == "templates" { + templatesDir := directory + expectedChartDir := filepath.Dir(templatesDir) + + // check if Chart.yaml exists + if isChartDirectory(expectedChartDir) { + return s.newChart(uri.New("file://" + expectedChartDir)) + } + } + + rootDirectory := s.RootURI.Filename() + if directory == rootDirectory { + return nil + } + + return s.getChartFromFilesystemForTemplates(directory) +} + +func isChartDirectory(expectedChartDir string) bool { + _, err := os.Stat(filepath.Join(expectedChartDir, chartutil.ChartfileName)) + return err == nil +} + +type ErrChartNotFound struct { + URI lsp.DocumentURI +} + +func (e ErrChartNotFound) Error() string { + return fmt.Sprintf("Chart not found for file: %s", e.URI) +} diff --git a/internal/charts/chart_for_document_test.go b/internal/charts/chart_for_document_test.go new file mode 100644 index 00000000..7543da6f --- /dev/null +++ b/internal/charts/chart_for_document_test.go @@ -0,0 +1,86 @@ +package charts_test + +import ( + "os" + "path/filepath" + "testing" + + "github.com/mrjosh/helm-ls/internal/charts" + "github.com/stretchr/testify/assert" + "go.lsp.dev/uri" +) + +func TestGetChartForDocumentWorksForAlreadyAddedCharts(t *testing.T) { + var chartStore = charts.NewChartStore("file:///tmp", func(_ uri.URI) *charts.Chart { + return &charts.Chart{} + }) + + chart := &charts.Chart{} + chartStore.Charts["file:///tmp/chart"] = chart + subchart := &charts.Chart{} + chartStore.Charts["file:///tmp/chart/charts/subchart"] = subchart + otherchart := &charts.Chart{} + chartStore.Charts["file:///tmp/otherChart"] = otherchart + + result1, error := chartStore.GetChartForDoc("file:///tmp/chart/templates/deployment.yaml") + assert.NoError(t, error) + assert.Same(t, chart, result1) + + result2, error := chartStore.GetChartForDoc("file:///tmp/chart/templates/directory/deployment.yaml") + assert.NoError(t, error) + assert.Same(t, chart, result2) + + result3, error := chartStore.GetChartForDoc("file:///tmp/chart/charts/subchart/templates/directory/deployment.yaml") + assert.NoError(t, error) + assert.Same(t, subchart, result3) + + result4, error := chartStore.GetChartForDoc("file:///tmp/otherChart/templates/directory/deployment.yaml") + assert.NoError(t, error) + assert.Same(t, otherchart, result4) + + result5, error := chartStore.GetChartForDoc("file:///tmp/directory/deployment.yaml") + assert.Error(t, error) + assert.Nil(t, result5) +} + +func TestGetChartForDocumentWorksForNewToAddChart(t *testing.T) { + var ( + rootDir = t.TempDir() + expectedChartDirectory = filepath.Join(rootDir, "chart") + expectedChart = &charts.Chart{ + RootURI: uri.New("file://" + expectedChartDirectory), + } + newChartFunc = func(_ uri.URI) *charts.Chart { return expectedChart } + chartStore = charts.NewChartStore(uri.New("file://"+rootDir), newChartFunc) + err = os.MkdirAll(expectedChartDirectory, 0755) + ) + assert.NoError(t, err) + _, _ = os.Create(filepath.Join(expectedChartDirectory, "Chart.yaml")) + + result1, error := chartStore.GetChartForDoc(uri.New("file://" + filepath.Join(expectedChartDirectory, "templates", "deployment.yaml"))) + assert.NoError(t, error) + assert.Same(t, expectedChart, result1) + + assert.Same(t, expectedChart, chartStore.Charts[uri.New("file://"+expectedChartDirectory)]) +} + +func TestGetChartForDocumentWorksForNewToAddChartWithNestedFile(t *testing.T) { + var ( + rootDir = t.TempDir() + expectedChartDirectory = filepath.Join(rootDir, "chart") + expectedChart = &charts.Chart{ + RootURI: uri.New("file://" + expectedChartDirectory), + } + newChartFunc = func(_ uri.URI) *charts.Chart { return expectedChart } + chartStore = charts.NewChartStore(uri.New("file://"+rootDir), newChartFunc) + err = os.MkdirAll(expectedChartDirectory, 0755) + ) + assert.NoError(t, err) + _, _ = os.Create(filepath.Join(expectedChartDirectory, "Chart.yaml")) + + result1, error := chartStore.GetChartForDoc(uri.New("file://" + filepath.Join(expectedChartDirectory, "templates", "nested", "deployment.yaml"))) + assert.NoError(t, error) + assert.Same(t, expectedChart, result1) + + assert.Same(t, expectedChart, chartStore.Charts[uri.New("file://"+expectedChartDirectory)]) +} diff --git a/internal/charts/chart_store.go b/internal/charts/chart_store.go new file mode 100644 index 00000000..d759d72f --- /dev/null +++ b/internal/charts/chart_store.go @@ -0,0 +1,38 @@ +package charts + +import "go.lsp.dev/uri" + +type ChartStore struct { + Charts map[uri.URI]*Chart + RootURI uri.URI + newChart func(uri.URI) *Chart +} + +func NewChartStore(rootURI uri.URI, newChart func(uri.URI) *Chart) *ChartStore { + return &ChartStore{ + Charts: map[uri.URI]*Chart{}, + RootURI: rootURI, + newChart: newChart, + } +} + +func (s *ChartStore) GetChartForURI(fileURI uri.URI) (*Chart, error) { + if chart, ok := s.Charts[fileURI]; ok { + return chart, nil + } + + var chart *Chart + expectedChartDir := fileURI.Filename() + if isChartDirectory(expectedChartDir) { + chart = s.newChart(uri.New("file://" + expectedChartDir)) + } + + if chart != nil { + s.Charts[chart.RootURI] = chart + return chart, nil + } + + return nil, ErrChartNotFound{ + URI: fileURI, + } +} diff --git a/internal/charts/chart_test.go b/internal/charts/chart_test.go new file mode 100644 index 00000000..cf645c6e --- /dev/null +++ b/internal/charts/chart_test.go @@ -0,0 +1,90 @@ +package charts_test + +import ( + "os" + "path/filepath" + "testing" + + "github.com/mrjosh/helm-ls/internal/charts" + "go.lsp.dev/uri" + + "github.com/stretchr/testify/assert" +) + +func TestNewChartsLoadsMetadata(t *testing.T) { + tempDir := t.TempDir() + + chartYaml := ` +apiVersion: v2 +name: hello-world +description: A Helm chart for Kubernetes +type: application` + chartFile := filepath.Join(tempDir, "Chart.yaml") + _ = os.WriteFile(chartFile, []byte(chartYaml), 0644) + + chart := charts.NewChart(uri.New("file://" + tempDir)) + assert.Equal(t, "hello-world", chart.ChartMetadata.Metadata.Name) +} + +func TestNewChartsLoadsDefaultMetadataOnError(t *testing.T) { + tempDir := t.TempDir() + + chartYaml := `invalidYaml` + chartFile := filepath.Join(tempDir, "Chart.yaml") + _ = os.WriteFile(chartFile, []byte(chartYaml), 0644) + + chart := charts.NewChart(uri.New("file://" + tempDir)) + assert.Equal(t, "", chart.ChartMetadata.Metadata.Name) +} + +func TestNewChartsSetsParentChartURI(t *testing.T) { + tempDir := t.TempDir() + + chartFile := filepath.Join(tempDir, "Chart.yaml") + _ = os.WriteFile(chartFile, []byte{}, 0644) + + chart := charts.NewChart(uri.New("file://" + filepath.Join(tempDir, "charts", "subchart"))) + assert.Equal(t, tempDir, chart.ParentChart.ParentChartURI.Filename()) +} + +func TestNewChartsSetsParentChartURIToDefault(t *testing.T) { + tempDir := t.TempDir() + + chartFile := filepath.Join(tempDir, "Chart.yaml") + _ = os.WriteFile(chartFile, []byte{}, 0644) + + chart := charts.NewChart(uri.New("file://" + tempDir)) + assert.False(t, chart.ParentChart.HasParent) +} + +func TestResolvesValuesFileOfParent(t *testing.T) { + + tempDir := t.TempDir() + + chartFile := filepath.Join(tempDir, "Chart.yaml") + _ = os.WriteFile(chartFile, []byte{}, 0644) + valuesFile := filepath.Join(tempDir, "values.yaml") + _ = os.WriteFile(valuesFile, []byte{}, 0644) + + subChartValuesFile := filepath.Join(tempDir, "charts", "subchart", "values.yaml") + subChartChartFile := filepath.Join(tempDir, "charts", "subchart", "Chart.yaml") + var err = os.MkdirAll(filepath.Dir(subChartValuesFile), 0755) + assert.NoError(t, err) + err = os.WriteFile(subChartValuesFile, []byte{}, 0644) + assert.NoError(t, err) + err = os.WriteFile(subChartChartFile, []byte{}, 0644) + assert.NoError(t, err) + + chart := charts.NewChart(uri.New("file://" + filepath.Join(tempDir, "charts", "subchart"))) + + expectedChart := &charts.Chart{ + RootURI: uri.New("file://" + tempDir), + } + newChartFunc := func(_ uri.URI) *charts.Chart { return expectedChart } + chartStore := charts.NewChartStore(uri.New("file://"+tempDir), newChartFunc) + + valueFiles := chart.ResolveValueFiles([]string{"global", "foo"}, chartStore) + + assert.Equal(t, 2, len(valueFiles)) + +} diff --git a/internal/charts/metadata.go b/internal/charts/metadata.go new file mode 100644 index 00000000..f13d9e8d --- /dev/null +++ b/internal/charts/metadata.go @@ -0,0 +1,40 @@ +package charts + +import ( + "path/filepath" + + "github.com/mrjosh/helm-ls/internal/util" + "github.com/mrjosh/helm-ls/pkg/chart" + "github.com/mrjosh/helm-ls/pkg/chartutil" + "go.lsp.dev/uri" + "gopkg.in/yaml.v3" +) + +type ChartMetadata struct { + YamlNode yaml.Node + Metadata chart.Metadata + URI uri.URI +} + +func NewChartMetadata(rootURI uri.URI) *ChartMetadata { + filePath := filepath.Join(rootURI.Filename(), chartutil.ChartfileName) + chartNode, err := chartutil.ReadYamlFileToNode(filePath) + if err != nil { + logger.Error("Error loading Chart.yaml file", rootURI, err) + } + + return &ChartMetadata{ + Metadata: loadChartMetadata(filePath), + YamlNode: chartNode, + URI: uri.New(util.FileURIScheme + filePath), + } +} + +func loadChartMetadata(filePath string) chart.Metadata { + chartMetadata, err := chartutil.LoadChartfile(filePath) + if err != nil { + logger.Error("Error loading Chart.yaml file", filePath, err) + return chart.Metadata{} + } + return *chartMetadata +} diff --git a/internal/charts/parent_chart.go b/internal/charts/parent_chart.go new file mode 100644 index 00000000..39c397aa --- /dev/null +++ b/internal/charts/parent_chart.go @@ -0,0 +1,33 @@ +package charts + +import ( + "path/filepath" + + "github.com/mrjosh/helm-ls/internal/util" + "go.lsp.dev/uri" +) + +type ParentChart struct { + ParentChartURI uri.URI + HasParent bool +} + +func getParentChart(rootURI uri.URI) ParentChart { + directory := filepath.Dir(rootURI.Filename()) + if filepath.Base(directory) == "charts" && isChartDirectory(filepath.Dir(directory)) { + return ParentChart{uri.New(util.FileURIScheme + filepath.Dir(directory)), true} + } + return ParentChart{} +} + +func (p *ParentChart) GetParentChart(chartStore *ChartStore) *Chart { + if !p.HasParent { + return nil + } + chart, err := chartStore.GetChartForURI(p.ParentChartURI) + if err != nil { + logger.Error("Error getting parent chart ", err) + return nil + } + return chart +} diff --git a/internal/charts/values_file.go b/internal/charts/values_file.go new file mode 100644 index 00000000..b092ce88 --- /dev/null +++ b/internal/charts/values_file.go @@ -0,0 +1,34 @@ +package charts + +import ( + "github.com/mrjosh/helm-ls/internal/util" + "github.com/mrjosh/helm-ls/pkg/chartutil" + "go.lsp.dev/uri" + + "gopkg.in/yaml.v3" +) + +type ValuesFile struct { + Values chartutil.Values + ValueNode yaml.Node + URI uri.URI +} + +func NewValuesFile(filePath string) *ValuesFile { + + vals, err := chartutil.ReadValuesFile(filePath) + if err != nil { + logger.Error("Error loading values file ", filePath, err) + } + + valueNodes, err := chartutil.ReadYamlFileToNode(filePath) + if err != nil { + logger.Error("Error loading values file ", filePath, err) + } + + return &ValuesFile{ + ValueNode: valueNodes, + Values: vals, + URI: uri.New(util.FileURIScheme + filePath), + } +} diff --git a/internal/charts/values_file_test.go b/internal/charts/values_file_test.go new file mode 100644 index 00000000..998830ca --- /dev/null +++ b/internal/charts/values_file_test.go @@ -0,0 +1,34 @@ +package charts_test + +import ( + "os" + "path/filepath" + "testing" + + "github.com/mrjosh/helm-ls/internal/charts" + "github.com/mrjosh/helm-ls/pkg/chartutil" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" +) + +func TestNewValuesFile(t *testing.T) { + tempDir := t.TempDir() + + valuesContent := `foo: bar` + + _ = os.WriteFile(filepath.Join(tempDir, "values.yaml"), []byte(valuesContent), 0644) + valuesFile := charts.NewValuesFile(filepath.Join(tempDir, "values.yaml")) + + assert.Equal(t, "bar", valuesFile.Values["foo"]) + assert.NotEqual(t, yaml.Node{}, valuesFile.ValueNode) + +} +func TestNewValuesFileFileNotFound(t *testing.T) { + tempDir := t.TempDir() + + valuesFile := charts.NewValuesFile(filepath.Join(tempDir, "values.yaml")) + + assert.Equal(t, chartutil.Values{}, valuesFile.Values) + assert.Equal(t, yaml.Node{}, valuesFile.ValueNode) + +} diff --git a/internal/charts/values_files.go b/internal/charts/values_files.go new file mode 100644 index 00000000..c1401636 --- /dev/null +++ b/internal/charts/values_files.go @@ -0,0 +1,62 @@ +package charts + +import ( + "path/filepath" + + "github.com/mrjosh/helm-ls/internal/util" + lsp "go.lsp.dev/protocol" + + "go.lsp.dev/uri" +) + +type ValuesFiles struct { + MainValuesFile *ValuesFile + AdditionalValuesFiles []*ValuesFile +} + +func NewValuesFiles(rootURI uri.URI, mainValuesFileName string, additionalValuesFilesGlob string) *ValuesFiles { + additionalValuesFiles := getAdditionalValuesFiles(additionalValuesFilesGlob, rootURI, mainValuesFileName) + + return &ValuesFiles{ + MainValuesFile: NewValuesFile(filepath.Join(rootURI.Filename(), mainValuesFileName)), + AdditionalValuesFiles: additionalValuesFiles, + } +} + +func getAdditionalValuesFiles(additionalValuesFilesGlob string, rootURI uri.URI, mainValuesFileName string) []*ValuesFile { + additionalValuesFiles := []*ValuesFile{} + if additionalValuesFilesGlob != "" { + + matches, err := filepath.Glob(filepath.Join(rootURI.Filename(), additionalValuesFilesGlob)) + if err != nil { + logger.Error("Error loading additional values files with glob pattern", additionalValuesFilesGlob, err) + } else { + + for _, match := range matches { + if match == filepath.Join(rootURI.Filename(), mainValuesFileName) { + continue + } + additionalValuesFiles = append(additionalValuesFiles, NewValuesFile(match)) + } + } + } + return additionalValuesFiles +} + +func (v *ValuesFiles) AllValuesFiles() []*ValuesFile { + return append([]*ValuesFile{v.MainValuesFile}, v.AdditionalValuesFiles...) +} + +func (v *ValuesFiles) GetPositionsForValue(query []string) []lsp.Location { + var result = []lsp.Location{} + for _, value := range v.AllValuesFiles() { + pos, err := util.GetPositionOfNode(&value.ValueNode, query) + if err != nil { + logger.Error("Error getting position for value", value, query, err) + continue + } + result = append(result, lsp.Location{URI: value.URI, Range: lsp.Range{Start: pos, End: pos}}) + } + + return result +} diff --git a/internal/charts/values_files_test.go b/internal/charts/values_files_test.go new file mode 100644 index 00000000..abe9a719 --- /dev/null +++ b/internal/charts/values_files_test.go @@ -0,0 +1,52 @@ +package charts_test + +import ( + "os" + "path/filepath" + "testing" + + "github.com/mrjosh/helm-ls/internal/charts" + "github.com/stretchr/testify/assert" + lsp "go.lsp.dev/protocol" + "go.lsp.dev/uri" +) + +func TestNewValuesFiles(t *testing.T) { + tempDir := t.TempDir() + + valuesContent := `foo: bar` + additionalValuesContent := `bar: baz` + + _ = os.WriteFile(filepath.Join(tempDir, "values.yaml"), []byte(valuesContent), 0644) + _ = os.WriteFile(filepath.Join(tempDir, "values-additional.yaml"), []byte(additionalValuesContent), 0644) + + valuesFiles := charts.NewValuesFiles(uri.New("file://"+tempDir), "values.yaml", "values*.yaml") + + assert.Equal(t, "bar", valuesFiles.MainValuesFile.Values["foo"]) + assert.Equal(t, "baz", valuesFiles.AdditionalValuesFiles[0].Values["bar"]) + assert.Equal(t, 1, len(valuesFiles.AdditionalValuesFiles)) +} + +func TestGetPositionsForValue(t *testing.T) { + tempDir := t.TempDir() + + valuesContent := `foo: bar` + additionalValuesContent := ` +other: value +foo: baz` + + _ = os.WriteFile(filepath.Join(tempDir, "values.yaml"), []byte(valuesContent), 0644) + _ = os.WriteFile(filepath.Join(tempDir, "values-additional.yaml"), []byte(additionalValuesContent), 0644) + + valuesFiles := charts.NewValuesFiles(uri.New("file://"+tempDir), "values.yaml", "values*.yaml") + + assert.Equal(t, []lsp.Location{ + { + URI: uri.New("file://" + filepath.Join(tempDir, "values.yaml")), + Range: lsp.Range{Start: lsp.Position{Line: 0, Character: 0}, End: lsp.Position{Line: 0, Character: 0}}, + }, + { + URI: uri.New("file://" + filepath.Join(tempDir, "values-additional.yaml")), + Range: lsp.Range{Start: lsp.Position{Line: 2, Character: 0}, End: lsp.Position{Line: 2, Character: 0}}, + }}, valuesFiles.GetPositionsForValue([]string{"foo"})) +} diff --git a/internal/handler/completion.go b/internal/handler/completion.go index bce97f80..db8aff00 100644 --- a/internal/handler/completion.go +++ b/internal/handler/completion.go @@ -9,6 +9,7 @@ import ( "strings" + "github.com/mrjosh/helm-ls/internal/charts" lsplocal "github.com/mrjosh/helm-ls/internal/lsp" gotemplate "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" "github.com/mrjosh/helm-ls/pkg/chartutil" @@ -48,6 +49,10 @@ func (h *langHandler) handleTextDocumentCompletion(ctx context.Context, reply js if !ok { return errors.New("Could not get document: " + params.TextDocument.URI.Filename()) } + chart, err := h.chartStore.GetChartForDoc(params.TextDocument.URI) + if err != nil { + logger.Error("Error getting chart info for file", params.TextDocument.URI, err) + } word, isTextNode := completionAstParsing(doc, params.Position) @@ -89,7 +94,7 @@ func (h *langHandler) handleTextDocumentCompletion(ctx context.Context, reply js case "Chart": items = getVariableCompletionItems(chartVals) case "Values": - items = h.getValue(h.values, variableSplitted[1:]) + items = h.getValuesCompletions(chart, variableSplitted[1:]) case "Release": items = getVariableCompletionItems(releaseVals) case "Files": @@ -139,8 +144,22 @@ func completionAstParsing(doc *lsplocal.Document, position lsp.Position) (string return word, false } -func (h *langHandler) getValue(values chartutil.Values, splittedVar []string) []lsp.CompletionItem { +func (h *langHandler) getValuesCompletions(chart *charts.Chart, splittedVar []string) (result []lsp.CompletionItem) { + m := make(map[string]lsp.CompletionItem) + for _, valuesFile := range chart.ValuesFiles.AllValuesFiles() { + for _, item := range h.getValue(valuesFile.Values, splittedVar) { + m[item.InsertText] = item + } + } + + for _, item := range m { + result = append(result, item) + } + return result +} + +func (h *langHandler) getValue(values chartutil.Values, splittedVar []string) []lsp.CompletionItem { var ( err error tableName = strings.Join(splittedVar, ".") diff --git a/internal/handler/completion_values_test.go b/internal/handler/completion_values_test.go index e7df2152..a0ed8c22 100644 --- a/internal/handler/completion_values_test.go +++ b/internal/handler/completion_values_test.go @@ -3,8 +3,11 @@ package handler import ( "testing" + "github.com/mrjosh/helm-ls/internal/charts" lsplocal "github.com/mrjosh/helm-ls/internal/lsp" + "github.com/stretchr/testify/assert" "go.lsp.dev/protocol" + "gopkg.in/yaml.v3" ) func TestEmptyValues(t *testing.T) { @@ -12,7 +15,6 @@ func TestEmptyValues(t *testing.T) { linterName: "helm-lint", connPool: nil, documents: nil, - values: make(map[string]interface{}), } var result = handler.getValue(make(map[string]interface{}), []string{"global"}) @@ -32,7 +34,6 @@ func TestValues(t *testing.T) { linterName: "helm-lint", connPool: nil, documents: nil, - values: make(map[string]interface{}), } var nested = map[string]interface{}{"nested": "value"} var values = map[string]interface{}{"global": nested} @@ -60,7 +61,6 @@ func TestWrongValues(t *testing.T) { linterName: "helm-lint", connPool: nil, documents: nil, - values: make(map[string]interface{}), } var nested = map[string]interface{}{"nested": 1} var values = map[string]interface{}{"global": nested} @@ -104,3 +104,67 @@ func TestCompletionAstParsing(t *testing.T) { } } + +func TestGetValuesCompletions(t *testing.T) { + handler := &langHandler{ + linterName: "helm-lint", + connPool: nil, + documents: nil, + } + var nested = map[string]interface{}{"nested": "value"} + var valuesMain = map[string]interface{}{"global": nested} + var valuesAdditional = map[string]interface{}{"glob": nested} + chart := &charts.Chart{ + ValuesFiles: &charts.ValuesFiles{ + MainValuesFile: &charts.ValuesFile{ + Values: valuesMain, + ValueNode: yaml.Node{}, + URI: "", + }, + AdditionalValuesFiles: []*charts.ValuesFile{ + { + Values: valuesAdditional, + ValueNode: yaml.Node{}, + URI: "", + }, + }, + }, + RootURI: "", + } + + result := handler.getValuesCompletions(chart, []string{"g"}) + assert.Equal(t, 2, len(result)) + + result = handler.getValuesCompletions(chart, []string{"something", "different"}) + assert.Empty(t, result) +} + +func TestGetValuesCompletionsContainsNoDupliactes(t *testing.T) { + handler := &langHandler{ + linterName: "helm-lint", + connPool: nil, + documents: nil, + } + var nested = map[string]interface{}{"nested": "value"} + var valuesMain = map[string]interface{}{"global": nested} + var valuesAdditional = map[string]interface{}{"global": nested} + chart := &charts.Chart{ + ValuesFiles: &charts.ValuesFiles{ + MainValuesFile: &charts.ValuesFile{ + Values: valuesMain, + ValueNode: yaml.Node{}, + URI: "", + }, + AdditionalValuesFiles: []*charts.ValuesFile{ + { + Values: valuesAdditional, + URI: "", + }, + }, + }, + RootURI: "", + } + + result := handler.getValuesCompletions(chart, []string{"g"}) + assert.Equal(t, 1, len(result)) +} diff --git a/internal/handler/definition.go b/internal/handler/definition.go index 35ec0b1a..4c511b6e 100644 --- a/internal/handler/definition.go +++ b/internal/handler/definition.go @@ -7,12 +7,14 @@ import ( "fmt" "strings" + "github.com/mrjosh/helm-ls/internal/charts" lsplocal "github.com/mrjosh/helm-ls/internal/lsp" gotemplate "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" "github.com/mrjosh/helm-ls/internal/util" sitter "github.com/smacker/go-tree-sitter" "go.lsp.dev/jsonrpc2" lsp "go.lsp.dev/protocol" + "gopkg.in/yaml.v3" ) func (h *langHandler) handleDefinition(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) (err error) { @@ -29,7 +31,12 @@ func (h *langHandler) handleDefinition(ctx context.Context, reply jsonrpc2.Repli if !ok { return errors.New("Could not get document: " + params.TextDocument.URI.Filename()) } - result, err := h.definitionAstParsing(doc, params.Position) + chart, err := h.chartStore.GetChartForDoc(params.TextDocument.URI) + if err != nil { + logger.Error("Error getting chart info for file", params.TextDocument.URI, err) + } + + result, err := h.definitionAstParsing(chart, doc, params.Position) if err != nil { // suppress errors for clients @@ -41,7 +48,7 @@ func (h *langHandler) handleDefinition(ctx context.Context, reply jsonrpc2.Repli return reply(ctx, result, err) } -func (h *langHandler) definitionAstParsing(doc *lsplocal.Document, position lsp.Position) (lsp.Location, error) { +func (h *langHandler) definitionAstParsing(chart *charts.Chart, doc *lsplocal.Document, position lsp.Position) ([]lsp.Location, error) { var ( currentNode = lsplocal.NodeAtPosition(doc.Ast, position) pointToLoopUp = sitter.Point{ @@ -56,65 +63,77 @@ func (h *langHandler) definitionAstParsing(doc *lsplocal.Document, position lsp. if relevantChildNode.Parent().Type() == gotemplate.NodeTypeVariable { return h.getDefinitionForVariable(relevantChildNode, doc) } - return h.getDefinitionForFixedIdentifier(relevantChildNode, doc) + return h.getDefinitionForFixedIdentifier(chart, relevantChildNode, doc) case gotemplate.NodeTypeDot, gotemplate.NodeTypeDotSymbol, gotemplate.NodeTypeFieldIdentifier: - return h.getDefinitionForValue(relevantChildNode, doc) + return h.getDefinitionForValue(chart, relevantChildNode, doc) } - return lsp.Location{}, fmt.Errorf("Definition not implemented for node type %s", relevantChildNode.Type()) + return []lsp.Location{}, fmt.Errorf("Definition not implemented for node type %s", relevantChildNode.Type()) } -func (h *langHandler) getDefinitionForVariable(node *sitter.Node, doc *lsplocal.Document) (lsp.Location, error) { +func (h *langHandler) getDefinitionForVariable(node *sitter.Node, doc *lsplocal.Document) ([]lsp.Location, error) { variableName := node.Content([]byte(doc.Content)) var defintionNode = lsplocal.GetVariableDefinition(variableName, node.Parent(), doc.Content) if defintionNode == nil { - return lsp.Location{}, fmt.Errorf("Could not find definition for %s", variableName) + return []lsp.Location{}, fmt.Errorf("Could not find definition for %s", variableName) } - return lsp.Location{URI: doc.URI, Range: lsp.Range{Start: util.PointToPosition(defintionNode.StartPoint())}}, nil + return []lsp.Location{{URI: doc.URI, Range: lsp.Range{Start: util.PointToPosition(defintionNode.StartPoint())}}}, nil } // getDefinitionForFixedIdentifier checks if the current identifier has a constant definition and returns it -func (h *langHandler) getDefinitionForFixedIdentifier(node *sitter.Node, doc *lsplocal.Document) (lsp.Location, error) { +func (h *langHandler) getDefinitionForFixedIdentifier(chart *charts.Chart, node *sitter.Node, doc *lsplocal.Document) ([]lsp.Location, error) { var name = node.Content([]byte(doc.Content)) switch name { case "Values": - return lsp.Location{ - URI: h.projectFiles.GetValuesFileURI()}, nil + result := []lsp.Location{} + + for _, valueFile := range chart.ValuesFiles.AllValuesFiles() { + result = append(result, lsp.Location{URI: valueFile.URI}) + } + return result, nil + case "Chart": - return lsp.Location{ - URI: h.projectFiles.GetChartFileURI()}, nil + return []lsp.Location{ + {URI: chart.ChartMetadata.URI}}, + nil } - return lsp.Location{}, fmt.Errorf("Could not find definition for %s", name) + return []lsp.Location{}, fmt.Errorf("Could not find definition for %s", name) } -func (h *langHandler) getDefinitionForValue(node *sitter.Node, doc *lsplocal.Document) (lsp.Location, error) { +func (h *langHandler) getDefinitionForValue(chart *charts.Chart, node *sitter.Node, doc *lsplocal.Document) ([]lsp.Location, error) { var ( yamlPathString = getYamlPath(node, doc) yamlPath, err = util.NewYamlPath(yamlPathString) definitionFileURI lsp.DocumentURI - position lsp.Position + positions []lsp.Position ) if err != nil { - return lsp.Location{}, err + return []lsp.Location{}, err } if yamlPath.IsValuesPath() { - definitionFileURI = h.projectFiles.GetValuesFileURI() - position, err = h.getValueDefinition(yamlPath.GetTail()) + return h.getValueDefinition(chart, yamlPath.GetTail()), nil } if yamlPath.IsChartPath() { - definitionFileURI = h.projectFiles.GetChartFileURI() - position, err = h.getChartDefinition(yamlPath.GetTail()) + definitionFileURI = chart.ChartMetadata.URI + position, err := h.getChartDefinition(&chart.ChartMetadata.YamlNode, yamlPath.GetTail()) + if err == nil { + positions = append(positions, position) + } } if err == nil && definitionFileURI != "" { - return lsp.Location{ - URI: definitionFileURI, - Range: lsp.Range{Start: position}, - }, nil + locations := []lsp.Location{} + for _, position := range positions { + locations = append(locations, lsp.Location{ + URI: definitionFileURI, + Range: lsp.Range{Start: position}, + }) + } + return locations, nil } - return lsp.Location{}, fmt.Errorf("Could not find definition for %s", yamlPath) + return []lsp.Location{}, fmt.Errorf("Could not find definition for %s", yamlPath) } func getYamlPath(node *sitter.Node, doc *lsplocal.Document) string { @@ -128,11 +147,15 @@ func getYamlPath(node *sitter.Node, doc *lsplocal.Document) string { } } -func (h *langHandler) getValueDefinition(splittedVar []string) (lsp.Position, error) { - return util.GetPositionOfNode(h.valueNode, splittedVar) +func (h *langHandler) getValueDefinition(chart *charts.Chart, splittedVar []string) []lsp.Location { + locations := []lsp.Location{} + for _, value := range chart.ResolveValueFiles(splittedVar, h.chartStore) { + locations = append(locations, value.GetPositionsForValue(splittedVar)...) + } + return locations } -func (h *langHandler) getChartDefinition(splittedVar []string) (lsp.Position, error) { +func (h *langHandler) getChartDefinition(chartNode *yaml.Node, splittedVar []string) (lsp.Position, error) { modifyedVar := make([]string, 0) // for Charts, we make the first letter lowercase for _, value := range splittedVar { @@ -143,5 +166,5 @@ func (h *langHandler) getChartDefinition(splittedVar []string) (lsp.Position, er firstLetterLowercase := strings.ToLower(string(value[0])) + restOfString modifyedVar = append(modifyedVar, firstLetterLowercase) } - return util.GetPositionOfNode(h.chartNode, modifyedVar) + return util.GetPositionOfNode(chartNode, modifyedVar) } diff --git a/internal/handler/definition_test.go b/internal/handler/definition_test.go index 9b9ab57b..9cd2ef29 100644 --- a/internal/handler/definition_test.go +++ b/internal/handler/definition_test.go @@ -6,6 +6,7 @@ import ( "reflect" "testing" + "github.com/mrjosh/helm-ls/internal/charts" 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" @@ -28,13 +29,14 @@ var testFileContent = ` var testDocumentTemplateURI = uri.URI("file:///test.yaml") var testValuesURI = uri.URI("file:///values.yaml") +var testOtherValuesURI = uri.URI("file:///values.other.yaml") var valuesContent = ` foo: bar something: nested: false ` -func genericDefinitionTest(t *testing.T, position lsp.Position, expectedLocation lsp.Location, expectedError error) { +func genericDefinitionTest(t *testing.T, position lsp.Position, expectedLocations []lsp.Location, expectedError error) { var node yamlv3.Node var err = yamlv3.Unmarshal([]byte(valuesContent), &node) if err != nil { @@ -44,12 +46,6 @@ func genericDefinitionTest(t *testing.T, position lsp.Position, expectedLocation linterName: "helm-lint", connPool: nil, documents: nil, - values: make(map[string]interface{}), - valueNode: node, - projectFiles: ProjectFiles{ - ValuesFile: "/values.yaml", - ChartFile: "", - }, } parser := sitter.NewParser() @@ -61,14 +57,24 @@ func genericDefinitionTest(t *testing.T, position lsp.Position, expectedLocation Ast: tree, } - location, err := handler.definitionAstParsing(doc, position) + location, err := handler.definitionAstParsing(&charts.Chart{ + ValuesFiles: &charts.ValuesFiles{ + MainValuesFile: &charts.ValuesFile{ + Values: make(map[string]interface{}), + ValueNode: node, + URI: testValuesURI, + }, + AdditionalValuesFiles: []*charts.ValuesFile{}, + }, + RootURI: "", + }, doc, position) if err != nil && err.Error() != expectedError.Error() { t.Errorf("expected %v, got %v", expectedError, err) } - if reflect.DeepEqual(location, expectedLocation) == false { - t.Errorf("expected %v, got %v", expectedLocation, location) + if reflect.DeepEqual(location, expectedLocations) == false { + t.Errorf("expected %v, got %v", expectedLocations, location) } } @@ -76,21 +82,21 @@ func genericDefinitionTest(t *testing.T, position lsp.Position, expectedLocation // {{ $variable }} # line 2 // -----| # this line incides the coursor position for the test func TestDefinitionVariable(t *testing.T) { - genericDefinitionTest(t, lsp.Position{Line: 2, Character: 8}, lsp.Location{ - URI: testDocumentTemplateURI, - Range: lsp.Range{ - Start: lsp.Position{ - Line: 1, - Character: 3, + genericDefinitionTest(t, lsp.Position{Line: 2, Character: 8}, []lsp.Location{ + { + URI: testDocumentTemplateURI, + Range: lsp.Range{ + Start: lsp.Position{ + Line: 1, + Character: 3, + }, }, }, }, nil) } func TestDefinitionNotImplemented(t *testing.T) { - genericDefinitionTest(t, lsp.Position{Line: 1, Character: 1}, lsp.Location{ - Range: lsp.Range{}, - }, + genericDefinitionTest(t, lsp.Position{Line: 1, Character: 1}, []lsp.Location{}, fmt.Errorf("Definition not implemented for node type %s", "{{")) } @@ -101,7 +107,7 @@ func TestDefinitionNotImplemented(t *testing.T) { // {{ range $index, $element := pipeline }}{{ $index }}{{ $element }}{{ end }} # line 7 // -----------------| func TestDefinitionRange(t *testing.T) { - genericDefinitionTest(t, lsp.Position{Line: 7, Character: 60}, lsp.Location{ + genericDefinitionTest(t, lsp.Position{Line: 7, Character: 60}, []lsp.Location{{ URI: testDocumentTemplateURI, Range: lsp.Range{ Start: lsp.Position{ @@ -109,6 +115,7 @@ func TestDefinitionRange(t *testing.T) { Character: 17, }, }, + }, }, nil) } @@ -116,14 +123,19 @@ func TestDefinitionRange(t *testing.T) { // {{ .Values.foo }} # line 8 // ------------| func TestDefinitionValue(t *testing.T) { - genericDefinitionTest(t, lsp.Position{Line: 8, Character: 13}, lsp.Location{ + genericDefinitionTest(t, lsp.Position{Line: 8, Character: 13}, []lsp.Location{{ URI: testValuesURI, Range: lsp.Range{ Start: lsp.Position{ Line: 1, Character: 0, }, + End: lsp.Position{ + Line: 1, + Character: 0, + }, }, + }, }, nil) } @@ -131,21 +143,26 @@ func TestDefinitionValue(t *testing.T) { // {{ .Values.something.nested }} # line 9 // ----------------------| func TestDefinitionValueNested(t *testing.T) { - genericDefinitionTest(t, lsp.Position{Line: 9, Character: 26}, lsp.Location{ + genericDefinitionTest(t, lsp.Position{Line: 9, Character: 26}, []lsp.Location{{ URI: testValuesURI, Range: lsp.Range{ Start: lsp.Position{ Line: 3, Character: 2, }, + End: lsp.Position{ + Line: 3, + Character: 2, + }, }, + }, }, nil) } // {{ .Values.foo }} # line 8 // ------| func TestDefinitionValueFile(t *testing.T) { - genericDefinitionTest(t, lsp.Position{Line: 8, Character: 7}, lsp.Location{ + genericDefinitionTest(t, lsp.Position{Line: 8, Character: 7}, []lsp.Location{{ URI: testValuesURI, Range: lsp.Range{ Start: lsp.Position{ @@ -153,5 +170,78 @@ func TestDefinitionValueFile(t *testing.T) { Character: 0, }, }, + }, + }, nil) +} + +func genericDefinitionTestMultipleValuesFiles(t *testing.T, position lsp.Position, expectedLocations []lsp.Location, expectedError error) { + var node yamlv3.Node + var err = yamlv3.Unmarshal([]byte(valuesContent), &node) + if err != nil { + t.Fatal(err) + } + handler := &langHandler{ + linterName: "helm-lint", + connPool: nil, + documents: nil, + } + + parser := sitter.NewParser() + parser.SetLanguage(gotemplate.GetLanguage()) + tree, _ := parser.ParseCtx(context.Background(), nil, []byte(testFileContent)) + doc := &lsplocal.Document{ + Content: testFileContent, + URI: testDocumentTemplateURI, + Ast: tree, + } + + location, err := handler.definitionAstParsing(&charts.Chart{ + ValuesFiles: &charts.ValuesFiles{ + MainValuesFile: &charts.ValuesFile{ + Values: make(map[string]interface{}), + ValueNode: node, + URI: testValuesURI, + }, + AdditionalValuesFiles: []*charts.ValuesFile{ + { + Values: make(map[string]interface{}), + ValueNode: node, + URI: testOtherValuesURI, + }, + }, + }, + RootURI: "", + }, doc, position) + + if err != nil && err.Error() != expectedError.Error() { + t.Errorf("expected %v, got %v", expectedError, err) + } + + if reflect.DeepEqual(location, expectedLocations) == false { + t.Errorf("expected %v, got %v", expectedLocations, location) + } +} + +// {{ .Values.foo }} # line 8 +// ------| +func TestDefinitionValueFileMulitpleValues(t *testing.T) { + genericDefinitionTestMultipleValuesFiles(t, lsp.Position{Line: 8, Character: 7}, []lsp.Location{ + { + URI: testValuesURI, + Range: lsp.Range{ + Start: lsp.Position{ + Line: 0, + Character: 0, + }, + }, + }, { + URI: testOtherValuesURI, + Range: lsp.Range{ + Start: lsp.Position{ + Line: 0, + Character: 0, + }, + }, + }, }, nil) } diff --git a/internal/handler/handler.go b/internal/handler/handler.go index 88dcf191..fb100769 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -7,13 +7,11 @@ import ( "github.com/mrjosh/helm-ls/internal/adapter/fs" "github.com/mrjosh/helm-ls/internal/adapter/yamlls" + "github.com/mrjosh/helm-ls/internal/charts" lsplocal "github.com/mrjosh/helm-ls/internal/lsp" "github.com/mrjosh/helm-ls/internal/util" - "github.com/mrjosh/helm-ls/pkg/chart" - "github.com/mrjosh/helm-ls/pkg/chartutil" "go.lsp.dev/jsonrpc2" lsp "go.lsp.dev/protocol" - yamlv3 "gopkg.in/yaml.v3" "github.com/mrjosh/helm-ls/internal/log" ) @@ -24,11 +22,7 @@ type langHandler struct { connPool jsonrpc2.Conn linterName string documents *lsplocal.DocumentStore - projectFiles ProjectFiles - values chartutil.Values - chartMetadata chart.Metadata - valueNode yamlv3.Node - chartNode yamlv3.Node + chartStore *charts.ChartStore yamllsConnector *yamlls.Connector helmlsConfig util.HelmlsConfiguration } @@ -39,10 +33,6 @@ func NewHandler(connPool jsonrpc2.Conn) jsonrpc2.Handler { handler := &langHandler{ linterName: "helm-lint", connPool: connPool, - projectFiles: ProjectFiles{}, - values: make(map[string]interface{}), - valueNode: yamlv3.Node{}, - chartNode: yamlv3.Node{}, documents: documents, helmlsConfig: util.DefaultConfig, yamllsConnector: &yamlls.Connector{}, @@ -104,6 +94,11 @@ func (h *langHandler) handleTextDocumentDidOpen(ctx context.Context, reply jsonr h.yamllsConnector.DocumentDidOpen(doc.Ast, params) + _, err = h.chartStore.GetChartForDoc(doc.URI) + if err != nil { + logger.Error("Error getting chart info for file", doc.URI, err) + } + doc, ok := h.documents.Get(params.TextDocument.URI) if !ok { return errors.New("Could not get document: " + params.TextDocument.URI.Filename()) diff --git a/internal/handler/hover.go b/internal/handler/hover.go index 1bbf1d38..a0fcfa1d 100644 --- a/internal/handler/hover.go +++ b/internal/handler/hover.go @@ -5,9 +5,12 @@ import ( "encoding/json" "errors" "fmt" + "path/filepath" "reflect" + "sort" "strings" + "github.com/mrjosh/helm-ls/internal/charts" lspinternal "github.com/mrjosh/helm-ls/internal/lsp" "github.com/mrjosh/helm-ls/internal/util" @@ -15,6 +18,7 @@ import ( "github.com/mrjosh/helm-ls/pkg/chartutil" "go.lsp.dev/jsonrpc2" lsp "go.lsp.dev/protocol" + "go.lsp.dev/uri" ) func (h *langHandler) handleHover(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) (err error) { @@ -32,6 +36,10 @@ func (h *langHandler) handleHover(ctx context.Context, reply jsonrpc2.Replier, r if !ok { return errors.New("Could not get document: " + params.TextDocument.URI.Filename()) } + chart, err := h.chartStore.GetChartForDoc(params.TextDocument.URI) + if err != nil { + logger.Error("Error getting chart info for file", params.TextDocument.URI, err) + } var ( currentNode = lspinternal.NodeAtPosition(doc.Ast, params.Position) @@ -91,9 +99,9 @@ func (h *langHandler) handleHover(ctx context.Context, reply jsonrpc2.Replier, r if len(variableSplitted) > 1 { switch variableSplitted[0] { case "Values": - value, err = h.getValueHover(variableSplitted[1:]) + value, err = h.getValueHover(chart, variableSplitted[1:]) case "Chart": - value, err = h.getChartMetadataHover(variableSplitted[1]) + value, err = h.getChartMetadataHover(&chart.ChartMetadata.Metadata, variableSplitted[1]) case "Release": value, err = h.getBuiltInObjectsHover(releaseVals, variableSplitted[1]) case "Files": @@ -131,13 +139,13 @@ func (h *langHandler) handleHover(ctx context.Context, reply jsonrpc2.Replier, r return reply(ctx, lsp.Hover{}, err) } -func (h *langHandler) getChartMetadataHover(key string) (string, error) { +func (h *langHandler) getChartMetadataHover(metadata *chart.Metadata, key string) (string, error) { for _, completionItem := range chartVals { if key == completionItem.Name { logger.Println("Getting metadatafield of " + key) documentation := completionItem.Doc - value := h.getMetadataField(&h.chartMetadata, key) + value := h.getMetadataField(metadata, key) return fmt.Sprintf("%s\n\n%s\n", documentation, value), nil } @@ -145,25 +153,54 @@ func (h *langHandler) getChartMetadataHover(key string) (string, error) { return "", fmt.Errorf("%s was no known Chart Metadata property", key) } -func (h *langHandler) getValueHover(splittedVar []string) (string, error) { +func (h *langHandler) getValueHover(chart *charts.Chart, splittedVar []string) (result string, err error) { var ( - values = h.values - tableName = strings.Join(splittedVar, ".") - err error - localValues chartutil.Values - value interface{} + valuesFiles = chart.ResolveValueFiles(splittedVar, h.chartStore) + selector = strings.Join(splittedVar, ".") + results = map[uri.URI]string{} ) - if len(splittedVar) > 0 { - localValues, err = values.Table(tableName) + for _, valuesFiles := range valuesFiles { + for _, valuesFile := range valuesFiles.AllValuesFiles() { + result, err := getTableOrValueForSelector(valuesFile.Values, selector) + if err == nil { + results[valuesFile.URI] = result + } + } + } + + keys := make([]string, 0, len(results)) + for u := range results { + keys = append(keys, string(u)) + } + + sort.Sort(sort.Reverse(sort.StringSlice(keys))) + + for _, key := range keys { + uriKey := uri.New(key) + value := results[uriKey] + if value == "" { + value = "\"\"" + } + filepath, err := filepath.Rel(h.chartStore.RootURI.Filename(), uriKey.Filename()) if err != nil { - logger.Println(err) - logger.Println("values.PathValue(tableName)") - value, err = values.PathValue(tableName) + filepath = uriKey.Filename() + } + result += fmt.Sprintf("### %s\n%s\n\n", filepath, value) + } + return result, nil +} + +func getTableOrValueForSelector(values chartutil.Values, selector string) (string, error) { + if len(selector) > 0 { + var localValues, err = values.Table(selector) + if err != nil { + logger.Debug("values.PathValue(tableName) because of error", err) + var value, err = values.PathValue(selector) return fmt.Sprint(value), err } + logger.Debug("converting to YAML", localValues) return localValues.YAML() - } return values.YAML() } diff --git a/internal/handler/hover_test.go b/internal/handler/hover_test.go new file mode 100644 index 00000000..25185c1e --- /dev/null +++ b/internal/handler/hover_test.go @@ -0,0 +1,143 @@ +package handler + +import ( + "testing" + + "github.com/mrjosh/helm-ls/internal/charts" + "github.com/stretchr/testify/assert" + "go.lsp.dev/uri" +) + +func Test_langHandler_getValueHover(t *testing.T) { + type args struct { + chart *charts.Chart + parentChart *charts.Chart + splittedVar []string + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "single values file", + args: args{ + chart: &charts.Chart{ + ValuesFiles: &charts.ValuesFiles{ + MainValuesFile: &charts.ValuesFile{ + Values: map[string]interface{}{ + "key": "value", + }, + URI: "file://tmp/values.yaml"}, + }, + }, + splittedVar: []string{"key"}, + }, + want: `### values.yaml +value + +`, + wantErr: false, + }, + { + name: "multiple values files", + args: args{ + chart: &charts.Chart{ + ValuesFiles: &charts.ValuesFiles{ + MainValuesFile: &charts.ValuesFile{Values: map[string]interface{}{"key": "value"}, URI: "file://tmp/values.yaml"}, + AdditionalValuesFiles: []*charts.ValuesFile{{Values: map[string]interface{}{"key": ""}, URI: "file://tmp/values.other.yaml"}}, + }, + }, + splittedVar: []string{"key"}, + }, + want: `### values.yaml +value + +### values.other.yaml +"" + +`, + wantErr: false, + }, + { + name: "yaml result", + args: args{ + chart: &charts.Chart{ + ValuesFiles: &charts.ValuesFiles{ + MainValuesFile: &charts.ValuesFile{Values: map[string]interface{}{"key": map[string]interface{}{"nested": "value"}}, URI: "file://tmp/values.yaml"}, + }, + }, + splittedVar: []string{"key"}, + }, + want: `### values.yaml +nested: value + + +`, + wantErr: false, + }, + { + name: "yaml result as list", + args: args{ + chart: &charts.Chart{ + ValuesFiles: &charts.ValuesFiles{ + MainValuesFile: &charts.ValuesFile{Values: map[string]interface{}{"key": []map[string]interface{}{{"nested": "value"}}}, URI: "file://tmp/values.yaml"}, + }, + }, + splittedVar: []string{"key"}, + }, + want: `### values.yaml +[map[nested:value]] + +`, + wantErr: false, + }, + { + name: "subchart includes parent values", + args: args{ + chart: &charts.Chart{ + ValuesFiles: &charts.ValuesFiles{ + MainValuesFile: &charts.ValuesFile{Values: map[string]interface{}{"global": map[string]interface{}{"key": "value"}}, URI: "file://tmp/charts/subchart/values.yaml"}, + }, + ParentChart: charts.ParentChart{ + ParentChartURI: uri.New("file://tmp/"), + HasParent: true, + }, + }, + parentChart: &charts.Chart{ + ValuesFiles: &charts.ValuesFiles{ + MainValuesFile: &charts.ValuesFile{Values: map[string]interface{}{"global": map[string]interface{}{"key": "parentValue"}}, URI: "file://tmp/values.yaml"}, + }, + }, + splittedVar: []string{"global", "key"}, + }, + want: `### values.yaml +parentValue + +### charts/subchart/values.yaml +value + +`, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + h := &langHandler{ + chartStore: &charts.ChartStore{ + RootURI: uri.New("file://tmp/"), + Charts: map[uri.URI]*charts.Chart{ + uri.New("file://tmp/"): tt.args.parentChart, + }, + }, + } + got, err := h.getValueHover(tt.args.chart, tt.args.splittedVar) + if (err != nil) != tt.wantErr { + t.Errorf("langHandler.getValueHover() error = %v, wantErr %v", err, tt.wantErr) + return + } + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/internal/handler/initialization.go b/internal/handler/initialization.go index 08ff7b56..1b4384fa 100644 --- a/internal/handler/initialization.go +++ b/internal/handler/initialization.go @@ -7,8 +7,8 @@ import ( "os" "github.com/mrjosh/helm-ls/internal/adapter/yamlls" + "github.com/mrjosh/helm-ls/internal/charts" "github.com/mrjosh/helm-ls/internal/util" - "github.com/mrjosh/helm-ls/pkg/chartutil" "github.com/sirupsen/logrus" "go.lsp.dev/jsonrpc2" lsp "go.lsp.dev/protocol" @@ -33,30 +33,7 @@ func (h *langHandler) handleInitialize(ctx context.Context, reply jsonrpc2.Repli } h.yamllsConnector.CallInitialize(workspaceURI) - h.projectFiles = NewProjectFiles(workspaceURI, "") - - vals, err := chartutil.ReadValuesFile(h.projectFiles.ValuesFile) - if err != nil { - logger.Error("Error loading values.yaml file", err) - } - h.values = vals - - chartMetadata, err := chartutil.LoadChartfile(h.projectFiles.ChartFile) - if err != nil { - logger.Error("Error loading Chart.yaml file", err) - } - h.chartMetadata = *chartMetadata - valueNodes, err := chartutil.ReadYamlFileToNode(h.projectFiles.ValuesFile) - if err != nil { - logger.Error("Error loading values.yaml file", err) - } - h.valueNode = valueNodes - - chartNode, err := chartutil.ReadYamlFileToNode(h.projectFiles.ChartFile) - if err != nil { - logger.Error("Error loading Chart.yaml file", err) - } - h.chartNode = chartNode + h.chartStore = charts.NewChartStore(workspaceURI, charts.NewChart) return reply(ctx, lsp.InitializeResult{ Capabilities: lsp.ServerCapabilities{ @@ -86,7 +63,7 @@ func configureYamlls(h *langHandler) { config := h.helmlsConfig if config.YamllsConfiguration.Enabled { h.yamllsConnector = yamlls.NewConnector(config.YamllsConfiguration, h.connPool, h.documents) - h.yamllsConnector.CallInitialize(h.projectFiles.RootURI) + h.yamllsConnector.CallInitialize(h.chartStore.RootURI) h.yamllsConnector.InitiallySyncOpenDocuments(h.documents.GetAllDocs()) } } diff --git a/internal/util/strings.go b/internal/util/strings.go index e8381923..a7671838 100644 --- a/internal/util/strings.go +++ b/internal/util/strings.go @@ -11,6 +11,8 @@ import ( "go.lsp.dev/uri" ) +const FileURIScheme = "file://" + var logger = log.GetLogger() var wordRegex = regexp.MustCompile(`[^ \t\n\f\r,;\[\]\"\']+`) diff --git a/internal/util/yaml.go b/internal/util/yaml.go index 140b6ecd..dc40ca15 100644 --- a/internal/util/yaml.go +++ b/internal/util/yaml.go @@ -7,14 +7,14 @@ import ( yamlv3 "gopkg.in/yaml.v3" ) -func GetPositionOfNode(node yamlv3.Node, query []string) (lsp.Position, error) { +func GetPositionOfNode(node *yamlv3.Node, query []string) (lsp.Position, error) { if node.IsZero() { return lsp.Position{}, fmt.Errorf("could not find Position of %s in values.yaml. Node was zero", query) } for index, value := range node.Content { if value.Value == "" { - result, err := GetPositionOfNode(*value, query) + result, err := GetPositionOfNode(value, query) if err == nil { return result, nil } @@ -24,7 +24,7 @@ func GetPositionOfNode(node yamlv3.Node, query []string) (lsp.Position, error) { if len(node.Content) < index+1 { return lsp.Position{}, fmt.Errorf("could not find Position of %s in values.yaml", query) } - return GetPositionOfNode(*node.Content[index+1], query[1:]) + return GetPositionOfNode(node.Content[index+1], query[1:]) } return lsp.Position{Line: uint32(value.Line) - 1, Character: uint32(value.Column) - 1}, nil } diff --git a/internal/util/yaml_test.go b/internal/util/yaml_test.go index 01b3a867..b8796c4e 100644 --- a/internal/util/yaml_test.go +++ b/internal/util/yaml_test.go @@ -25,7 +25,7 @@ func TestGetPositionOfNode(t *testing.T) { t.Errorf("error yml parsing") } - result, err := GetPositionOfNode(node, []string{"replicaCount"}) + result, err := GetPositionOfNode(&node, []string{"replicaCount"}) expected := lsp.Position{Line: 5, Character: 0} if err != nil { t.Errorf("Result had error: %s", err) @@ -34,7 +34,7 @@ func TestGetPositionOfNode(t *testing.T) { t.Errorf("Result was not expected Position %v but was %v", expected, result) } - result, err = GetPositionOfNode(node, []string{"image", "repository"}) + result, err = GetPositionOfNode(&node, []string{"image", "repository"}) expected = lsp.Position{Line: 8, Character: 2} if err != nil { t.Errorf("Result had error: %s", err) @@ -43,7 +43,7 @@ func TestGetPositionOfNode(t *testing.T) { t.Errorf("Result was not expected Position %v but was %v", expected, result) } - result, err = GetPositionOfNode(node, []string{"service", "test", "nested", "value"}) + result, err = GetPositionOfNode(&node, []string{"service", "test", "nested", "value"}) expected = lsp.Position{Line: 30, Character: 6} if err != nil { t.Errorf("Result had error: %s", err) From 7be12c703cfb1310a84686d0234f1fd14bd57616 Mon Sep 17 00:00:00 2001 From: qvalentin Date: Sat, 20 Jan 2024 15:51:26 +0100 Subject: [PATCH 02/35] feat(lint): support for multiple charts --- cmds/lint.go | 11 ++++- internal/charts/chart.go | 4 +- internal/charts/chart_for_document.go | 4 ++ internal/charts/parent_chart.go | 2 +- internal/charts/values_files.go | 28 +++++++++++-- internal/charts/values_files_test.go | 57 +++++++++++++++++++++---- internal/handler/handler.go | 12 +++++- internal/lsp/lint.go | 60 +++++---------------------- 8 files changed, 112 insertions(+), 66 deletions(-) diff --git a/cmds/lint.go b/cmds/lint.go index 584acfd1..a44ae6be 100644 --- a/cmds/lint.go +++ b/cmds/lint.go @@ -4,7 +4,9 @@ import ( "fmt" "os" + "github.com/mrjosh/helm-ls/internal/charts" locallsp "github.com/mrjosh/helm-ls/internal/lsp" + "github.com/mrjosh/helm-ls/internal/util" "github.com/spf13/cobra" "go.lsp.dev/uri" ) @@ -19,7 +21,14 @@ func newLintCmd() *cobra.Command { args = append(args, os.Getenv("PWD")) } - msgs, err := locallsp.GetDiagnostics(uri.New(args[0])) + rootPath := uri.New(util.FileURIScheme + args[0]) + chartStore := charts.NewChartStore(rootPath, charts.NewChart) + chart, err := chartStore.GetChartForURI(rootPath) + if err != nil { + return err + } + + msgs, err := locallsp.GetDiagnostics(uri.New(args[0]), chart.ValuesFiles.MainValuesFile.Values) if err != nil { return err } diff --git a/internal/charts/chart.go b/internal/charts/chart.go index 6b496e85..a0ef8bfe 100644 --- a/internal/charts/chart.go +++ b/internal/charts/chart.go @@ -16,10 +16,10 @@ type Chart struct { func NewChart(rootURI uri.URI) *Chart { return &Chart{ - ValuesFiles: NewValuesFiles(rootURI, "values.yaml", "values*.yaml"), + ValuesFiles: NewValuesFiles(rootURI, "values.yaml", "", "values*.yaml"), ChartMetadata: NewChartMetadata(rootURI), RootURI: rootURI, - ParentChart: getParentChart(rootURI), + ParentChart: newParentChart(rootURI), } } diff --git a/internal/charts/chart_for_document.go b/internal/charts/chart_for_document.go index ce56dcd4..d217ee73 100644 --- a/internal/charts/chart_for_document.go +++ b/internal/charts/chart_for_document.go @@ -55,6 +55,10 @@ func (s *ChartStore) getChartFromFilesystemForTemplates(path string) *Chart { return nil } + if directory == path { + return nil + } + return s.getChartFromFilesystemForTemplates(directory) } diff --git a/internal/charts/parent_chart.go b/internal/charts/parent_chart.go index 39c397aa..a89c7ff2 100644 --- a/internal/charts/parent_chart.go +++ b/internal/charts/parent_chart.go @@ -12,7 +12,7 @@ type ParentChart struct { HasParent bool } -func getParentChart(rootURI uri.URI) ParentChart { +func newParentChart(rootURI uri.URI) ParentChart { directory := filepath.Dir(rootURI.Filename()) if filepath.Base(directory) == "charts" && isChartDirectory(filepath.Dir(directory)) { return ParentChart{uri.New(util.FileURIScheme + filepath.Dir(directory)), true} diff --git a/internal/charts/values_files.go b/internal/charts/values_files.go index c1401636..561513f9 100644 --- a/internal/charts/values_files.go +++ b/internal/charts/values_files.go @@ -11,18 +11,41 @@ import ( type ValuesFiles struct { MainValuesFile *ValuesFile + OverlayValuesFile *ValuesFile AdditionalValuesFiles []*ValuesFile } -func NewValuesFiles(rootURI uri.URI, mainValuesFileName string, additionalValuesFilesGlob string) *ValuesFiles { +func NewValuesFiles(rootURI uri.URI, mainValuesFileName string, lintOverlayValuesFile string, additionalValuesFilesGlob string) *ValuesFiles { additionalValuesFiles := getAdditionalValuesFiles(additionalValuesFilesGlob, rootURI, mainValuesFileName) + var overlayValuesFile *ValuesFile + + overlayValuesFile = getLintOverlayValuesFile(lintOverlayValuesFile, additionalValuesFiles, overlayValuesFile, rootURI) return &ValuesFiles{ MainValuesFile: NewValuesFile(filepath.Join(rootURI.Filename(), mainValuesFileName)), + OverlayValuesFile: overlayValuesFile, AdditionalValuesFiles: additionalValuesFiles, } } +func getLintOverlayValuesFile(lintOverlayValuesFile string, additionalValuesFiles []*ValuesFile, overlayValuesFile *ValuesFile, rootURI uri.URI) *ValuesFile { + if lintOverlayValuesFile == "" && len(additionalValuesFiles) == 1 { + overlayValuesFile = additionalValuesFiles[0] + } + if lintOverlayValuesFile != "" { + for _, additionalValuesFile := range additionalValuesFiles { + if filepath.Base(additionalValuesFile.URI.Filename()) == lintOverlayValuesFile { + overlayValuesFile = additionalValuesFile + break + } + } + if overlayValuesFile == nil { + overlayValuesFile = NewValuesFile(filepath.Join(rootURI.Filename(), lintOverlayValuesFile)) + } + } + return overlayValuesFile +} + func getAdditionalValuesFiles(additionalValuesFilesGlob string, rootURI uri.URI, mainValuesFileName string) []*ValuesFile { additionalValuesFiles := []*ValuesFile{} if additionalValuesFilesGlob != "" { @@ -31,7 +54,6 @@ func getAdditionalValuesFiles(additionalValuesFilesGlob string, rootURI uri.URI, if err != nil { logger.Error("Error loading additional values files with glob pattern", additionalValuesFilesGlob, err) } else { - for _, match := range matches { if match == filepath.Join(rootURI.Filename(), mainValuesFileName) { continue @@ -48,7 +70,7 @@ func (v *ValuesFiles) AllValuesFiles() []*ValuesFile { } func (v *ValuesFiles) GetPositionsForValue(query []string) []lsp.Location { - var result = []lsp.Location{} + result := []lsp.Location{} for _, value := range v.AllValuesFiles() { pos, err := util.GetPositionOfNode(&value.ValueNode, query) if err != nil { diff --git a/internal/charts/values_files_test.go b/internal/charts/values_files_test.go index abe9a719..8efeb09f 100644 --- a/internal/charts/values_files_test.go +++ b/internal/charts/values_files_test.go @@ -17,10 +17,10 @@ func TestNewValuesFiles(t *testing.T) { valuesContent := `foo: bar` additionalValuesContent := `bar: baz` - _ = os.WriteFile(filepath.Join(tempDir, "values.yaml"), []byte(valuesContent), 0644) - _ = os.WriteFile(filepath.Join(tempDir, "values-additional.yaml"), []byte(additionalValuesContent), 0644) + _ = os.WriteFile(filepath.Join(tempDir, "values.yaml"), []byte(valuesContent), 0o644) + _ = os.WriteFile(filepath.Join(tempDir, "values-additional.yaml"), []byte(additionalValuesContent), 0o644) - valuesFiles := charts.NewValuesFiles(uri.New("file://"+tempDir), "values.yaml", "values*.yaml") + valuesFiles := charts.NewValuesFiles(uri.New("file://"+tempDir), "values.yaml", "", "values*.yaml") assert.Equal(t, "bar", valuesFiles.MainValuesFile.Values["foo"]) assert.Equal(t, "baz", valuesFiles.AdditionalValuesFiles[0].Values["bar"]) @@ -35,10 +35,10 @@ func TestGetPositionsForValue(t *testing.T) { other: value foo: baz` - _ = os.WriteFile(filepath.Join(tempDir, "values.yaml"), []byte(valuesContent), 0644) - _ = os.WriteFile(filepath.Join(tempDir, "values-additional.yaml"), []byte(additionalValuesContent), 0644) + _ = os.WriteFile(filepath.Join(tempDir, "values.yaml"), []byte(valuesContent), 0o644) + _ = os.WriteFile(filepath.Join(tempDir, "values-additional.yaml"), []byte(additionalValuesContent), 0o644) - valuesFiles := charts.NewValuesFiles(uri.New("file://"+tempDir), "values.yaml", "values*.yaml") + valuesFiles := charts.NewValuesFiles(uri.New("file://"+tempDir), "values.yaml", "", "values*.yaml") assert.Equal(t, []lsp.Location{ { @@ -48,5 +48,48 @@ foo: baz` { URI: uri.New("file://" + filepath.Join(tempDir, "values-additional.yaml")), Range: lsp.Range{Start: lsp.Position{Line: 2, Character: 0}, End: lsp.Position{Line: 2, Character: 0}}, - }}, valuesFiles.GetPositionsForValue([]string{"foo"})) + }, + }, valuesFiles.GetPositionsForValue([]string{"foo"})) +} + +func TestNewValuesFileForLintOverlay(t *testing.T) { + tempDir := t.TempDir() + + valuesContent := `foo: bar` + additionalValuesContent := `bar: baz` + + _ = os.WriteFile(filepath.Join(tempDir, "values.yaml"), []byte(valuesContent), 0o644) + _ = os.WriteFile(filepath.Join(tempDir, "values-additional.yaml"), []byte(additionalValuesContent), 0o644) + + valuesFiles := charts.NewValuesFiles(uri.New("file://"+tempDir), "values.yaml", "values-additional.yaml", "values*.yaml") + + assert.Equal(t, "baz", valuesFiles.OverlayValuesFile.Values["bar"]) +} + +func TestNewValuesFileForLintOverlayNewFile(t *testing.T) { + tempDir := t.TempDir() + + valuesContent := `foo: bar` + additionalValuesContent := `bar: baz` + + _ = os.WriteFile(filepath.Join(tempDir, "values.yaml"), []byte(valuesContent), 0o644) + _ = os.WriteFile(filepath.Join(tempDir, "values-additional.yaml"), []byte(additionalValuesContent), 0o644) + + valuesFiles := charts.NewValuesFiles(uri.New("file://"+tempDir), "values.yaml", "values-additional.yaml", "") + + assert.Equal(t, "baz", valuesFiles.OverlayValuesFile.Values["bar"]) +} + +func TestNewValuesFileForLintOverlayPicksFirst(t *testing.T) { + tempDir := t.TempDir() + + valuesContent := `foo: bar` + additionalValuesContent := `bar: baz` + + _ = os.WriteFile(filepath.Join(tempDir, "values.yaml"), []byte(valuesContent), 0o644) + _ = os.WriteFile(filepath.Join(tempDir, "values-additional.yaml"), []byte(additionalValuesContent), 0o644) + + valuesFiles := charts.NewValuesFiles(uri.New("file://"+tempDir), "values.yaml", "", "values*.yaml") + + assert.Equal(t, "baz", valuesFiles.OverlayValuesFile.Values["bar"]) } diff --git a/internal/handler/handler.go b/internal/handler/handler.go index fb100769..b39aace6 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -103,7 +103,11 @@ func (h *langHandler) handleTextDocumentDidOpen(ctx context.Context, reply jsonr if !ok { return errors.New("Could not get document: " + params.TextDocument.URI.Filename()) } - notification, err := lsplocal.NotifcationFromLint(ctx, h.connPool, doc) + chart, err := h.chartStore.GetChartForDoc(doc.URI) + if err != nil { + logger.Error("Error getting chart info for file", doc.URI, err) + } + notification, err := lsplocal.NotifcationFromLint(ctx, h.connPool, chart, doc) return reply(ctx, notification, err) } @@ -129,8 +133,12 @@ func (h *langHandler) handleTextDocumentDidSave(ctx context.Context, reply jsonr if !ok { return errors.New("Could not get document: " + params.TextDocument.URI.Filename()) } + chart, err := h.chartStore.GetChartForDoc(doc.URI) + if err != nil { + logger.Error("Error getting chart info for file", doc.URI, err) + } h.yamllsConnector.DocumentDidSave(doc, params) - notification, err := lsplocal.NotifcationFromLint(ctx, h.connPool, doc) + notification, err := lsplocal.NotifcationFromLint(ctx, h.connPool, chart, doc) return reply(ctx, notification, err) } diff --git a/internal/lsp/lint.go b/internal/lsp/lint.go index 293b4d97..db217bfc 100644 --- a/internal/lsp/lint.go +++ b/internal/lsp/lint.go @@ -3,17 +3,16 @@ package lsp import ( "context" "fmt" - "os" "strconv" "strings" + "github.com/mrjosh/helm-ls/internal/charts" "github.com/mrjosh/helm-ls/internal/log" "github.com/mrjosh/helm-ls/internal/util" "github.com/mrjosh/helm-ls/pkg/action" "github.com/mrjosh/helm-ls/pkg/chartutil" "github.com/mrjosh/helm-ls/pkg/lint/support" "github.com/pkg/errors" - yaml "gopkg.in/yaml.v3" // nolint "github.com/mrjosh/helm-ls/pkg/lint/rules" @@ -24,9 +23,13 @@ import ( var logger = log.GetLogger() -func NotifcationFromLint(ctx context.Context, conn jsonrpc2.Conn, doc *Document) (*jsonrpc2.Notification, error) { - diagnostics, err := GetDiagnostics(doc.URI) +func NotifcationFromLint(ctx context.Context, conn jsonrpc2.Conn, chart *charts.Chart, doc *Document) (*jsonrpc2.Notification, error) { + vals := chart.ValuesFiles.MainValuesFile.Values + if chart.ValuesFiles.OverlayValuesFile != nil { + vals = chartutil.CoalesceTables(chart.ValuesFiles.OverlayValuesFile.Values, chart.ValuesFiles.MainValuesFile.Values) + } + diagnostics, err := GetDiagnostics(doc.URI, vals) if err != nil { return nil, err } @@ -42,38 +45,9 @@ func NotifcationFromLint(ctx context.Context, conn jsonrpc2.Conn, doc *Document) ) } -// loadValues will load the values files into a map[string]interface{} -// the filename arg default is values.yaml -func loadValues(dir string, filename ...string) (map[string]interface{}, error) { - - vals := make(map[string]interface{}) - if len(filename) == 0 { - filename = append(filename, chartutil.ValuesfileName) - } - - if len(filename) > 1 { - return vals, errors.New("filename should be a single string") - } - - file, err := os.Open(fmt.Sprintf("%s/%s", dir, filename[0])) - if err != nil { - return vals, err - } - - if err := yaml.NewDecoder(file).Decode(&vals); err != nil { - return vals, err - } - - logger.Println(fmt.Sprintf("%s file loaded successfully", file.Name())) - logger.Debug(vals) - - return vals, nil -} - -// GetDiagnostics will run helm linter against the currect document URI +// GetDiagnostics will run helm linter against the currect document URI using the given values // and converts the helm.support.Message to lsp.Diagnostics -func GetDiagnostics(uri uri.URI) ([]lsp.Diagnostic, error) { - +func GetDiagnostics(uri uri.URI, vals chartutil.Values) ([]lsp.Diagnostic, error) { var ( filename = uri.Filename() paths = strings.Split(filename, "/") @@ -93,20 +67,8 @@ func GetDiagnostics(uri uri.URI) ([]lsp.Diagnostic, error) { logger.Println(dir) client := action.NewLint() - vals, err := loadValues(dir) - if err != nil { - - logger.Println(errors.Wrap(err, "could not load values.yaml, trying to load values.yml instead")) - - vals, err = loadValues(dir, "values.yml") - if err != nil { - logger.Println(errors.Wrap(err, "could not load values.yml, ignoring values")) - } - - } - result := client.Run([]string{dir}, vals) - logger.Println("helm lint: result:", result.Messages) + logger.Println(fmt.Sprintf("helm lint: result for file %s : %s", uri, result.Messages)) for _, msg := range result.Messages { d, filename, err := GetDiagnosticFromLinterErr(msg) @@ -123,7 +85,6 @@ func GetDiagnostics(uri uri.URI) ([]lsp.Diagnostic, error) { } func GetDiagnosticFromLinterErr(supMsg support.Message) (*lsp.Diagnostic, string, error) { - var ( err error msg string @@ -185,7 +146,6 @@ func GetDiagnosticFromLinterErr(supMsg support.Message) (*lsp.Diagnostic, string } func getFilePathFromLinterErr(msg support.Message) string { - var ( filename string fileLine = util.BetweenStrings(msg.Error(), "(", ")") From 46c5f65eaad11a2f89d1dd8fa7e63590294b8884 Mon Sep 17 00:00:00 2001 From: qvalentin Date: Sat, 20 Jan 2024 18:47:06 +0100 Subject: [PATCH 03/35] feat(configuration): make values files configureable third commit with my new neo65 keyboard :D --- README.md | 42 +++++--- cmds/lint.go | 1 - internal/charts/chart.go | 25 ++++- internal/charts/chart_for_document.go | 2 +- internal/charts/chart_for_document_test.go | 11 +- internal/charts/chart_store.go | 36 +++++-- internal/charts/chart_store_test.go | 75 ++++++++++++++ internal/charts/chart_test.go | 75 ++++++++++---- internal/charts/values_file_test.go | 6 +- internal/handler/completion.go | 13 ++- internal/handler/completion_values_test.go | 31 +++--- internal/handler/definition.go | 10 +- internal/handler/definition_test.go | 91 +++++++++-------- internal/handler/hover.go | 14 ++- internal/handler/hover_test.go | 111 +++++++++++++++++++-- internal/handler/initialization.go | 2 +- internal/util/config.go | 12 +++ 17 files changed, 411 insertions(+), 146 deletions(-) create mode 100644 internal/charts/chart_store_test.go diff --git a/README.md b/README.md index 3149573d..ed3bb784 100644 --- a/README.md +++ b/README.md @@ -18,20 +18,21 @@ Helm-ls is a [helm](https://github.com/helm/helm) language server protocol [LSP] * [Demo](#demo) * [Getting Started](#getting-started) - * [Installation with a package manager](#installation-with-a-package-manager) - * [Download](#download) - * [Make it executable](#make-it-executable) - * [Integration with yaml-language-server](#integration-with-yaml-language-server) + * [Installation with a package manager](#installation-with-a-package-manager) + * [Download](#download) + * [Make it executable](#make-it-executable) + * [Integration with yaml-language-server](#integration-with-yaml-language-server) * [Configuration options](#configuration-options) - * [LSP Server](#lsp-server) - * [yaml-language-server config](#yaml-language-server-config) - * [Default Configuration](#default-configuration) + * [General](#general) + * [Values Files](#values-files) + * [yaml-language-server config](#yaml-language-server-config) + * [Default Configuration](#default-configuration) * [Editor Config examples](#editor-config-examples) - * [Neovim (using nvim-lspconfig)](#neovim-using-nvim-lspconfig) - * [Vim Helm Plugin](#vim-helm-plugin) - * [Setup laguage server](#setup-laguage-server) - * [VSCode](#vscode) - * [Emacs eglot setup](#emacs-eglot-setup) + * [Neovim (using nvim-lspconfig)](#neovim-using-nvim-lspconfig) + * [Vim Helm Plugin](#vim-helm-plugin) + * [Setup laguage server](#setup-laguage-server) + * [VSCode](#vscode) + * [Emacs eglot setup](#emacs-eglot-setup) * [Contributing](#contributing) * [License](#license) @@ -93,10 +94,16 @@ kind: ScaledObject You can configure helm-ls with lsp workspace configurations. -### LSP Server +### General - **Log Level**: Adjust log verbosity. +### Values Files + +- **Main Values File**: Path to the main values file (values.yaml per default) +- **Lint Overlay Values File**: Path to the lint overlay values file, which will be merged with the main values file for linting +- **Additional Values Files Glob Pattern**: Pattern for additional values files, which will be shown for completion and hover + ### yaml-language-server config - **Enable yaml-language-server**: Toggle support of this feature. @@ -116,7 +123,12 @@ You can configure helm-ls with lsp workspace configurations. ```lua settings = { ['helm-ls'] = { - logLevel = "debug", + logLevel = "info", + valuesFiles = { + mainValuesFile = "values.yaml", + lintOverlayValuesFile = "values.lint.yaml", + additionalValuesFilesGlobPattern = "values*.yaml" + }, yamlls = { enabled = true, diagnosticsLimit = 50, @@ -128,7 +140,7 @@ settings = { }, completion = true, hover = true, - -- any other config: https://github.com/redhat-developer/yaml-language-server#language-server-settings + -- any other config from https://github.com/redhat-developer/yaml-language-server#language-server-settings } } } diff --git a/cmds/lint.go b/cmds/lint.go index a44ae6be..88e20f74 100644 --- a/cmds/lint.go +++ b/cmds/lint.go @@ -16,7 +16,6 @@ func newLintCmd() *cobra.Command { Use: "lint", Short: "Lint a helm project", RunE: func(cmd *cobra.Command, args []string) error { - if len(args) == 0 { args = append(args, os.Getenv("PWD")) } diff --git a/internal/charts/chart.go b/internal/charts/chart.go index a0ef8bfe..2c38ff12 100644 --- a/internal/charts/chart.go +++ b/internal/charts/chart.go @@ -2,6 +2,7 @@ package charts import ( "github.com/mrjosh/helm-ls/internal/log" + "github.com/mrjosh/helm-ls/internal/util" "go.lsp.dev/uri" ) @@ -14,23 +15,37 @@ type Chart struct { ParentChart ParentChart } -func NewChart(rootURI uri.URI) *Chart { +func NewChart(rootURI uri.URI, valuesFilesConfig util.ValuesFilesConfig) *Chart { return &Chart{ - ValuesFiles: NewValuesFiles(rootURI, "values.yaml", "", "values*.yaml"), + ValuesFiles: NewValuesFiles(rootURI, valuesFilesConfig.MainValuesFileName, valuesFilesConfig.LintOverlayValuesFileName, valuesFilesConfig.AdditionalValuesFilesGlobPattern), ChartMetadata: NewChartMetadata(rootURI), RootURI: rootURI, ParentChart: newParentChart(rootURI), } } +type QueriedValuesFiles struct { + Selector []string + ValuesFiles *ValuesFiles +} + // ResolveValueFiles returns a list of all values files in the chart // and all parent charts if the query tries to access global values -func (c *Chart) ResolveValueFiles(query []string, chartStore *ChartStore) []*ValuesFiles { +func (c *Chart) ResolveValueFiles(query []string, chartStore *ChartStore) []*QueriedValuesFiles { if len(query) > 0 && query[0] == "global" { parentChart := c.ParentChart.GetParentChart(chartStore) if parentChart != nil { - return append([]*ValuesFiles{c.ValuesFiles}, parentChart.ResolveValueFiles(query, chartStore)...) + return append([]*QueriedValuesFiles{{Selector: query, ValuesFiles: c.ValuesFiles}}, parentChart.ResolveValueFiles(query, chartStore)...) + } + } + chartName := c.ChartMetadata.Metadata.Name + if len(query) > 0 { + parentChart := c.ParentChart.GetParentChart(chartStore) + if parentChart != nil { + extendedQuery := append([]string{chartName}, query...) + return append([]*QueriedValuesFiles{{Selector: query, ValuesFiles: c.ValuesFiles}}, + parentChart.ResolveValueFiles(extendedQuery, chartStore)...) } } - return []*ValuesFiles{c.ValuesFiles} + return []*QueriedValuesFiles{{Selector: query, ValuesFiles: c.ValuesFiles}} } diff --git a/internal/charts/chart_for_document.go b/internal/charts/chart_for_document.go index d217ee73..b297129b 100644 --- a/internal/charts/chart_for_document.go +++ b/internal/charts/chart_for_document.go @@ -46,7 +46,7 @@ func (s *ChartStore) getChartFromFilesystemForTemplates(path string) *Chart { // check if Chart.yaml exists if isChartDirectory(expectedChartDir) { - return s.newChart(uri.New("file://" + expectedChartDir)) + return s.newChart(uri.New("file://"+expectedChartDir), s.valuesFilesConfig) } } diff --git a/internal/charts/chart_for_document_test.go b/internal/charts/chart_for_document_test.go index 7543da6f..f0474ce1 100644 --- a/internal/charts/chart_for_document_test.go +++ b/internal/charts/chart_for_document_test.go @@ -6,12 +6,13 @@ import ( "testing" "github.com/mrjosh/helm-ls/internal/charts" + "github.com/mrjosh/helm-ls/internal/util" "github.com/stretchr/testify/assert" "go.lsp.dev/uri" ) func TestGetChartForDocumentWorksForAlreadyAddedCharts(t *testing.T) { - var chartStore = charts.NewChartStore("file:///tmp", func(_ uri.URI) *charts.Chart { + chartStore := charts.NewChartStore("file:///tmp", func(_ uri.URI, _ util.ValuesFilesConfig) *charts.Chart { return &charts.Chart{} }) @@ -50,9 +51,9 @@ func TestGetChartForDocumentWorksForNewToAddChart(t *testing.T) { expectedChart = &charts.Chart{ RootURI: uri.New("file://" + expectedChartDirectory), } - newChartFunc = func(_ uri.URI) *charts.Chart { return expectedChart } + newChartFunc = func(_ uri.URI, _ util.ValuesFilesConfig) *charts.Chart { return expectedChart } chartStore = charts.NewChartStore(uri.New("file://"+rootDir), newChartFunc) - err = os.MkdirAll(expectedChartDirectory, 0755) + err = os.MkdirAll(expectedChartDirectory, 0o755) ) assert.NoError(t, err) _, _ = os.Create(filepath.Join(expectedChartDirectory, "Chart.yaml")) @@ -71,9 +72,9 @@ func TestGetChartForDocumentWorksForNewToAddChartWithNestedFile(t *testing.T) { expectedChart = &charts.Chart{ RootURI: uri.New("file://" + expectedChartDirectory), } - newChartFunc = func(_ uri.URI) *charts.Chart { return expectedChart } + newChartFunc = func(_ uri.URI, _ util.ValuesFilesConfig) *charts.Chart { return expectedChart } chartStore = charts.NewChartStore(uri.New("file://"+rootDir), newChartFunc) - err = os.MkdirAll(expectedChartDirectory, 0755) + err = os.MkdirAll(expectedChartDirectory, 0o755) ) assert.NoError(t, err) _, _ = os.Create(filepath.Join(expectedChartDirectory, "Chart.yaml")) diff --git a/internal/charts/chart_store.go b/internal/charts/chart_store.go index d759d72f..174d6d3f 100644 --- a/internal/charts/chart_store.go +++ b/internal/charts/chart_store.go @@ -1,18 +1,36 @@ package charts -import "go.lsp.dev/uri" +import ( + "github.com/mrjosh/helm-ls/internal/util" + "go.lsp.dev/uri" +) type ChartStore struct { - Charts map[uri.URI]*Chart - RootURI uri.URI - newChart func(uri.URI) *Chart + Charts map[uri.URI]*Chart + RootURI uri.URI + newChart func(uri.URI, util.ValuesFilesConfig) *Chart + valuesFilesConfig util.ValuesFilesConfig } -func NewChartStore(rootURI uri.URI, newChart func(uri.URI) *Chart) *ChartStore { +func NewChartStore(rootURI uri.URI, newChart func(uri.URI, util.ValuesFilesConfig) *Chart) *ChartStore { return &ChartStore{ - Charts: map[uri.URI]*Chart{}, - RootURI: rootURI, - newChart: newChart, + Charts: map[uri.URI]*Chart{}, + RootURI: rootURI, + newChart: newChart, + valuesFilesConfig: util.DefaultConfig.ValuesFilesConfig, + } +} + +func (s *ChartStore) SetValuesFilesConfig(valuesFilesConfig util.ValuesFilesConfig) { + logger.Debug("SetValuesFilesConfig", valuesFilesConfig) + if valuesFilesConfig.MainValuesFileName == s.valuesFilesConfig.MainValuesFileName && + valuesFilesConfig.AdditionalValuesFilesGlobPattern == s.valuesFilesConfig.AdditionalValuesFilesGlobPattern && + valuesFilesConfig.LintOverlayValuesFileName == s.valuesFilesConfig.LintOverlayValuesFileName { + return + } + s.valuesFilesConfig = valuesFilesConfig + for _, chart := range s.Charts { + chart.ValuesFiles = NewValuesFiles(chart.RootURI, valuesFilesConfig.MainValuesFileName, valuesFilesConfig.LintOverlayValuesFileName, valuesFilesConfig.AdditionalValuesFilesGlobPattern) } } @@ -24,7 +42,7 @@ func (s *ChartStore) GetChartForURI(fileURI uri.URI) (*Chart, error) { var chart *Chart expectedChartDir := fileURI.Filename() if isChartDirectory(expectedChartDir) { - chart = s.newChart(uri.New("file://" + expectedChartDir)) + chart = s.newChart(uri.New("file://"+expectedChartDir), s.valuesFilesConfig) } if chart != nil { diff --git a/internal/charts/chart_store_test.go b/internal/charts/chart_store_test.go new file mode 100644 index 00000000..740b72ff --- /dev/null +++ b/internal/charts/chart_store_test.go @@ -0,0 +1,75 @@ +package charts + +import ( + "os" + "path/filepath" + "testing" + + "github.com/mrjosh/helm-ls/internal/util" + "github.com/mrjosh/helm-ls/pkg/chartutil" + "github.com/stretchr/testify/assert" + "go.lsp.dev/uri" +) + +func TestSetValuesFilesConfigOverwrites(t *testing.T) { + valuesFilesConfig := util.ValuesFilesConfig{ + MainValuesFileName: "value.yaml", + AdditionalValuesFilesGlobPattern: "values*.yaml", + LintOverlayValuesFileName: "something.yaml", + } + tempDir := t.TempDir() + valuesContent := `foo: bar` + + _ = os.WriteFile(filepath.Join(tempDir, "Chart.yaml"), []byte(""), 0o644) + _ = os.WriteFile(filepath.Join(tempDir, "value.yaml"), []byte("foo: main"), 0o644) + _ = os.WriteFile(filepath.Join(tempDir, "something.yaml"), []byte(valuesContent), 0o644) + _ = os.WriteFile(filepath.Join(tempDir, "values.other.yaml"), []byte(valuesContent), 0o644) + s := NewChartStore(uri.New(util.FileURIScheme+tempDir), NewChart) + + chart, err := s.GetChartForURI(uri.New(util.FileURIScheme + tempDir)) + assert.Equal(t, chartutil.Values{}, chart.ValuesFiles.MainValuesFile.Values) + + s.SetValuesFilesConfig(valuesFilesConfig) + assert.NoError(t, err) + assert.NotEqual(t, chartutil.Values{}, chart.ValuesFiles.MainValuesFile.Values) + assert.Equal(t, valuesFilesConfig.MainValuesFileName, filepath.Base(chart.ValuesFiles.MainValuesFile.URI.Filename())) + assert.Equal(t, valuesFilesConfig.LintOverlayValuesFileName, filepath.Base(chart.ValuesFiles.OverlayValuesFile.URI.Filename())) +} + +func TestSetValuesFilesConfigDoesNotOverwrite(t *testing.T) { + valuesFilesConfig := util.ValuesFilesConfig{ + MainValuesFileName: "values.yaml", + AdditionalValuesFilesGlobPattern: "values*.yaml", + LintOverlayValuesFileName: "values.lint.yaml", + } + tempDir := t.TempDir() + valuesContent := `foo: bar` + + _ = os.WriteFile(filepath.Join(tempDir, "Chart.yaml"), []byte(""), 0o644) + _ = os.WriteFile(filepath.Join(tempDir, "values.yaml"), []byte("foo: main"), 0o644) + _ = os.WriteFile(filepath.Join(tempDir, "something.yaml"), []byte(valuesContent), 0o644) + _ = os.WriteFile(filepath.Join(tempDir, "values.lint.yaml"), []byte(valuesContent), 0o644) + _ = os.WriteFile(filepath.Join(tempDir, "values.other.yaml"), []byte(valuesContent), 0o644) + s := NewChartStore(uri.New(util.FileURIScheme+tempDir), NewChart) + + chart, err := s.GetChartForURI(uri.New(util.FileURIScheme + tempDir)) + assert.NoError(t, err) + assert.NotEqual(t, chartutil.Values{}, chart.ValuesFiles.MainValuesFile.Values) + + s.SetValuesFilesConfig(valuesFilesConfig) + chart, err = s.GetChartForURI(uri.New(util.FileURIScheme + tempDir)) + assert.NoError(t, err) + assert.Equal(t, valuesFilesConfig.MainValuesFileName, filepath.Base(chart.ValuesFiles.MainValuesFile.URI.Filename())) + assert.Equal(t, valuesFilesConfig.LintOverlayValuesFileName, filepath.Base(chart.ValuesFiles.OverlayValuesFile.URI.Filename())) +} + +func TestGetChartForURIWhenChartYamlDoesNotExist(t *testing.T) { + tempDir := t.TempDir() + + _ = os.WriteFile(filepath.Join(tempDir, "values.yaml"), []byte("foo: main"), 0o644) + s := NewChartStore(uri.New(util.FileURIScheme+tempDir), NewChart) + + chart, err := s.GetChartForURI(uri.New(util.FileURIScheme + tempDir)) + assert.Error(t, err) + assert.Nil(t, chart) +} diff --git a/internal/charts/chart_test.go b/internal/charts/chart_test.go index cf645c6e..710bad04 100644 --- a/internal/charts/chart_test.go +++ b/internal/charts/chart_test.go @@ -6,6 +6,8 @@ import ( "testing" "github.com/mrjosh/helm-ls/internal/charts" + "github.com/mrjosh/helm-ls/internal/util" + "github.com/mrjosh/helm-ls/pkg/chart" "go.lsp.dev/uri" "github.com/stretchr/testify/assert" @@ -20,9 +22,9 @@ name: hello-world description: A Helm chart for Kubernetes type: application` chartFile := filepath.Join(tempDir, "Chart.yaml") - _ = os.WriteFile(chartFile, []byte(chartYaml), 0644) + _ = os.WriteFile(chartFile, []byte(chartYaml), 0o644) - chart := charts.NewChart(uri.New("file://" + tempDir)) + chart := charts.NewChart(uri.New("file://"+tempDir), util.ValuesFilesConfig{}) assert.Equal(t, "hello-world", chart.ChartMetadata.Metadata.Name) } @@ -31,9 +33,9 @@ func TestNewChartsLoadsDefaultMetadataOnError(t *testing.T) { chartYaml := `invalidYaml` chartFile := filepath.Join(tempDir, "Chart.yaml") - _ = os.WriteFile(chartFile, []byte(chartYaml), 0644) + _ = os.WriteFile(chartFile, []byte(chartYaml), 0o644) - chart := charts.NewChart(uri.New("file://" + tempDir)) + chart := charts.NewChart(uri.New("file://"+tempDir), util.ValuesFilesConfig{}) assert.Equal(t, "", chart.ChartMetadata.Metadata.Name) } @@ -41,9 +43,9 @@ func TestNewChartsSetsParentChartURI(t *testing.T) { tempDir := t.TempDir() chartFile := filepath.Join(tempDir, "Chart.yaml") - _ = os.WriteFile(chartFile, []byte{}, 0644) + _ = os.WriteFile(chartFile, []byte{}, 0o644) - chart := charts.NewChart(uri.New("file://" + filepath.Join(tempDir, "charts", "subchart"))) + chart := charts.NewChart(uri.New("file://"+filepath.Join(tempDir, "charts", "subchart")), util.ValuesFilesConfig{}) assert.Equal(t, tempDir, chart.ParentChart.ParentChartURI.Filename()) } @@ -51,40 +53,79 @@ func TestNewChartsSetsParentChartURIToDefault(t *testing.T) { tempDir := t.TempDir() chartFile := filepath.Join(tempDir, "Chart.yaml") - _ = os.WriteFile(chartFile, []byte{}, 0644) + _ = os.WriteFile(chartFile, []byte{}, 0o644) - chart := charts.NewChart(uri.New("file://" + tempDir)) + chart := charts.NewChart(uri.New("file://"+tempDir), util.ValuesFilesConfig{}) assert.False(t, chart.ParentChart.HasParent) } func TestResolvesValuesFileOfParent(t *testing.T) { - tempDir := t.TempDir() chartFile := filepath.Join(tempDir, "Chart.yaml") - _ = os.WriteFile(chartFile, []byte{}, 0644) + _ = os.WriteFile(chartFile, []byte{}, 0o644) valuesFile := filepath.Join(tempDir, "values.yaml") - _ = os.WriteFile(valuesFile, []byte{}, 0644) + _ = os.WriteFile(valuesFile, []byte{}, 0o644) subChartValuesFile := filepath.Join(tempDir, "charts", "subchart", "values.yaml") subChartChartFile := filepath.Join(tempDir, "charts", "subchart", "Chart.yaml") - var err = os.MkdirAll(filepath.Dir(subChartValuesFile), 0755) + err := os.MkdirAll(filepath.Dir(subChartValuesFile), 0o755) assert.NoError(t, err) - err = os.WriteFile(subChartValuesFile, []byte{}, 0644) + err = os.WriteFile(subChartValuesFile, []byte{}, 0o644) assert.NoError(t, err) - err = os.WriteFile(subChartChartFile, []byte{}, 0644) + err = os.WriteFile(subChartChartFile, []byte{}, 0o644) assert.NoError(t, err) - chart := charts.NewChart(uri.New("file://" + filepath.Join(tempDir, "charts", "subchart"))) + chart := charts.NewChart(uri.New("file://"+filepath.Join(tempDir, "charts", "subchart")), util.ValuesFilesConfig{}) expectedChart := &charts.Chart{ - RootURI: uri.New("file://" + tempDir), + RootURI: uri.New("file://" + tempDir), + ChartMetadata: &charts.ChartMetadata{}, } - newChartFunc := func(_ uri.URI) *charts.Chart { return expectedChart } + newChartFunc := func(_ uri.URI, _ util.ValuesFilesConfig) *charts.Chart { return expectedChart } chartStore := charts.NewChartStore(uri.New("file://"+tempDir), newChartFunc) valueFiles := chart.ResolveValueFiles([]string{"global", "foo"}, chartStore) assert.Equal(t, 2, len(valueFiles)) +} + +func TestResolvesValuesFileOfParentByName(t *testing.T) { + tempDir := t.TempDir() + + chartFile := filepath.Join(tempDir, "Chart.yaml") + _ = os.WriteFile(chartFile, []byte{}, 0o644) + valuesFile := filepath.Join(tempDir, "values.yaml") + _ = os.WriteFile(valuesFile, []byte{}, 0o644) + + subChartValuesFile := filepath.Join(tempDir, "charts", "subchart", "values.yaml") + subChartChartFile := filepath.Join(tempDir, "charts", "subchart", "Chart.yaml") + err := os.MkdirAll(filepath.Dir(subChartValuesFile), 0o755) + assert.NoError(t, err) + err = os.WriteFile(subChartValuesFile, []byte{}, 0o644) + assert.NoError(t, err) + err = os.WriteFile(subChartChartFile, []byte{}, 0o644) + assert.NoError(t, err) + + subchart := charts.NewChart(uri.New("file://"+filepath.Join(tempDir, "charts", "subchart")), util.ValuesFilesConfig{}) + subchart.ChartMetadata.Metadata.Name = "subchart" + + expectedChart := &charts.Chart{ + RootURI: uri.New("file://" + tempDir), + ChartMetadata: &charts.ChartMetadata{ + Metadata: chart.Metadata{ + Name: "parent", + }, + }, + } + newChartFunc := func(_ uri.URI, _ util.ValuesFilesConfig) *charts.Chart { return expectedChart } + chartStore := charts.NewChartStore(uri.New("file://"+tempDir), newChartFunc) + + valueFiles := subchart.ResolveValueFiles([]string{"foo"}, chartStore) + parentChart, err := chartStore.GetChartForURI(uri.New("file://" + tempDir)) + assert.NoError(t, err) + + assert.Equal(t, 2, len(valueFiles)) + assert.Contains(t, valueFiles, &charts.QueriedValuesFiles{Selector: []string{"subchart", "foo"}, ValuesFiles: parentChart.ValuesFiles}) } diff --git a/internal/charts/values_file_test.go b/internal/charts/values_file_test.go index 998830ca..899e6a3a 100644 --- a/internal/charts/values_file_test.go +++ b/internal/charts/values_file_test.go @@ -15,14 +15,13 @@ func TestNewValuesFile(t *testing.T) { tempDir := t.TempDir() valuesContent := `foo: bar` - - _ = os.WriteFile(filepath.Join(tempDir, "values.yaml"), []byte(valuesContent), 0644) + _ = os.WriteFile(filepath.Join(tempDir, "values.yaml"), []byte(valuesContent), 0o644) valuesFile := charts.NewValuesFile(filepath.Join(tempDir, "values.yaml")) assert.Equal(t, "bar", valuesFile.Values["foo"]) assert.NotEqual(t, yaml.Node{}, valuesFile.ValueNode) - } + func TestNewValuesFileFileNotFound(t *testing.T) { tempDir := t.TempDir() @@ -30,5 +29,4 @@ func TestNewValuesFileFileNotFound(t *testing.T) { assert.Equal(t, chartutil.Values{}, valuesFile.Values) assert.Equal(t, yaml.Node{}, valuesFile.ValueNode) - } diff --git a/internal/handler/completion.go b/internal/handler/completion.go index db8aff00..4fccc320 100644 --- a/internal/handler/completion.go +++ b/internal/handler/completion.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "reflect" - "strings" "github.com/mrjosh/helm-ls/internal/charts" @@ -35,7 +34,6 @@ func init() { } func (h *langHandler) handleTextDocumentCompletion(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) (err error) { - if req.Params() == nil { return &jsonrpc2.Error{Code: jsonrpc2.InvalidParams} } @@ -57,7 +55,7 @@ func (h *langHandler) handleTextDocumentCompletion(ctx context.Context, reply js word, isTextNode := completionAstParsing(doc, params.Position) if isTextNode { - var result = make([]lsp.CompletionItem, 0) + result := make([]lsp.CompletionItem, 0) result = append(result, textCompletionsItems...) result = append(result, yamllsCompletions(ctx, h, params)...) return reply(ctx, result, err) @@ -146,9 +144,11 @@ func completionAstParsing(doc *lsplocal.Document, position lsp.Position) (string func (h *langHandler) getValuesCompletions(chart *charts.Chart, splittedVar []string) (result []lsp.CompletionItem) { m := make(map[string]lsp.CompletionItem) - for _, valuesFile := range chart.ValuesFiles.AllValuesFiles() { - for _, item := range h.getValue(valuesFile.Values, splittedVar) { - m[item.InsertText] = item + for _, queriedValuesFiles := range chart.ResolveValueFiles(splittedVar, h.chartStore) { + for _, valuesFile := range queriedValuesFiles.ValuesFiles.AllValuesFiles() { + for _, item := range h.getValue(valuesFile.Values, queriedValuesFiles.Selector) { + m[item.InsertText] = item + } } } @@ -197,7 +197,6 @@ func (h *langHandler) getValue(values chartutil.Values, splittedVar []string) [] } func (h *langHandler) setItem(items []lsp.CompletionItem, value interface{}, variable string) []lsp.CompletionItem { - var ( itemKind = lsp.CompletionItemKindVariable valueOf = reflect.ValueOf(value) diff --git a/internal/handler/completion_values_test.go b/internal/handler/completion_values_test.go index a0ed8c22..5775b833 100644 --- a/internal/handler/completion_values_test.go +++ b/internal/handler/completion_values_test.go @@ -5,6 +5,7 @@ import ( "github.com/mrjosh/helm-ls/internal/charts" lsplocal "github.com/mrjosh/helm-ls/internal/lsp" + "github.com/mrjosh/helm-ls/pkg/chart" "github.com/stretchr/testify/assert" "go.lsp.dev/protocol" "gopkg.in/yaml.v3" @@ -17,7 +18,7 @@ func TestEmptyValues(t *testing.T) { documents: nil, } - var result = handler.getValue(make(map[string]interface{}), []string{"global"}) + result := handler.getValue(make(map[string]interface{}), []string{"global"}) if len(result) != 0 { t.Errorf("Length of result was not zero.") @@ -35,8 +36,8 @@ func TestValues(t *testing.T) { connPool: nil, documents: nil, } - var nested = map[string]interface{}{"nested": "value"} - var values = map[string]interface{}{"global": nested} + nested := map[string]interface{}{"nested": "value"} + values := map[string]interface{}{"global": nested} result := handler.getValue(values, []string{"g"}) @@ -62,8 +63,8 @@ func TestWrongValues(t *testing.T) { connPool: nil, documents: nil, } - var nested = map[string]interface{}{"nested": 1} - var values = map[string]interface{}{"global": nested} + nested := map[string]interface{}{"nested": 1} + values := map[string]interface{}{"global": nested} result := handler.getValue(values, []string{"some", "wrong", "values"}) if len(result) != 0 { @@ -87,7 +88,6 @@ func TestWrongValues(t *testing.T) { } func TestCompletionAstParsing(t *testing.T) { - documentText := `{{ .Values.global. }}` expectedWord := ".Values.global." doc := &lsplocal.Document{ @@ -102,7 +102,6 @@ func TestCompletionAstParsing(t *testing.T) { if expectedWord != word { t.Errorf("Expected word '%s', but got '%s'", expectedWord, word) } - } func TestGetValuesCompletions(t *testing.T) { @@ -111,10 +110,11 @@ func TestGetValuesCompletions(t *testing.T) { connPool: nil, documents: nil, } - var nested = map[string]interface{}{"nested": "value"} - var valuesMain = map[string]interface{}{"global": nested} - var valuesAdditional = map[string]interface{}{"glob": nested} + nested := map[string]interface{}{"nested": "value"} + valuesMain := map[string]interface{}{"global": nested} + valuesAdditional := map[string]interface{}{"glob": nested} chart := &charts.Chart{ + ChartMetadata: &charts.ChartMetadata{Metadata: chart.Metadata{Name: "test"}}, ValuesFiles: &charts.ValuesFiles{ MainValuesFile: &charts.ValuesFile{ Values: valuesMain, @@ -145,10 +145,11 @@ func TestGetValuesCompletionsContainsNoDupliactes(t *testing.T) { connPool: nil, documents: nil, } - var nested = map[string]interface{}{"nested": "value"} - var valuesMain = map[string]interface{}{"global": nested} - var valuesAdditional = map[string]interface{}{"global": nested} - chart := &charts.Chart{ + nested := map[string]interface{}{"nested": "value"} + valuesMain := map[string]interface{}{"global": nested} + valuesAdditional := map[string]interface{}{"global": nested} + testChart := &charts.Chart{ + ChartMetadata: &charts.ChartMetadata{Metadata: chart.Metadata{Name: "test"}}, ValuesFiles: &charts.ValuesFiles{ MainValuesFile: &charts.ValuesFile{ Values: valuesMain, @@ -165,6 +166,6 @@ func TestGetValuesCompletionsContainsNoDupliactes(t *testing.T) { RootURI: "", } - result := handler.getValuesCompletions(chart, []string{"g"}) + result := handler.getValuesCompletions(testChart, []string{"g"}) assert.Equal(t, 1, len(result)) } diff --git a/internal/handler/definition.go b/internal/handler/definition.go index 4c511b6e..00c42901 100644 --- a/internal/handler/definition.go +++ b/internal/handler/definition.go @@ -37,7 +37,6 @@ func (h *langHandler) handleDefinition(ctx context.Context, reply jsonrpc2.Repli } result, err := h.definitionAstParsing(chart, doc, params.Position) - if err != nil { // suppress errors for clients // otherwise using go-to-definition on words that have no definition @@ -73,7 +72,7 @@ func (h *langHandler) definitionAstParsing(chart *charts.Chart, doc *lsplocal.Do func (h *langHandler) getDefinitionForVariable(node *sitter.Node, doc *lsplocal.Document) ([]lsp.Location, error) { variableName := node.Content([]byte(doc.Content)) - var defintionNode = lsplocal.GetVariableDefinition(variableName, node.Parent(), doc.Content) + defintionNode := lsplocal.GetVariableDefinition(variableName, node.Parent(), doc.Content) if defintionNode == nil { return []lsp.Location{}, fmt.Errorf("Could not find definition for %s", variableName) } @@ -82,7 +81,7 @@ func (h *langHandler) getDefinitionForVariable(node *sitter.Node, doc *lsplocal. // getDefinitionForFixedIdentifier checks if the current identifier has a constant definition and returns it func (h *langHandler) getDefinitionForFixedIdentifier(chart *charts.Chart, node *sitter.Node, doc *lsplocal.Document) ([]lsp.Location, error) { - var name = node.Content([]byte(doc.Content)) + name := node.Content([]byte(doc.Content)) switch name { case "Values": result := []lsp.Location{} @@ -94,7 +93,8 @@ func (h *langHandler) getDefinitionForFixedIdentifier(chart *charts.Chart, node case "Chart": return []lsp.Location{ - {URI: chart.ChartMetadata.URI}}, + {URI: chart.ChartMetadata.URI}, + }, nil } @@ -150,7 +150,7 @@ func getYamlPath(node *sitter.Node, doc *lsplocal.Document) string { func (h *langHandler) getValueDefinition(chart *charts.Chart, splittedVar []string) []lsp.Location { locations := []lsp.Location{} for _, value := range chart.ResolveValueFiles(splittedVar, h.chartStore) { - locations = append(locations, value.GetPositionsForValue(splittedVar)...) + locations = append(locations, value.ValuesFiles.GetPositionsForValue(value.Selector)...) } return locations } diff --git a/internal/handler/definition_test.go b/internal/handler/definition_test.go index 9cd2ef29..812c53e0 100644 --- a/internal/handler/definition_test.go +++ b/internal/handler/definition_test.go @@ -27,18 +27,20 @@ var testFileContent = ` {{ .Values.something.nested }} # line 9 ` -var testDocumentTemplateURI = uri.URI("file:///test.yaml") -var testValuesURI = uri.URI("file:///values.yaml") -var testOtherValuesURI = uri.URI("file:///values.other.yaml") -var valuesContent = ` +var ( + testDocumentTemplateURI = uri.URI("file:///test.yaml") + testValuesURI = uri.URI("file:///values.yaml") + testOtherValuesURI = uri.URI("file:///values.other.yaml") + valuesContent = ` foo: bar something: nested: false ` +) func genericDefinitionTest(t *testing.T, position lsp.Position, expectedLocations []lsp.Location, expectedError error) { var node yamlv3.Node - var err = yamlv3.Unmarshal([]byte(valuesContent), &node) + err := yamlv3.Unmarshal([]byte(valuesContent), &node) if err != nil { t.Fatal(err) } @@ -58,6 +60,7 @@ func genericDefinitionTest(t *testing.T, position lsp.Position, expectedLocation } location, err := handler.definitionAstParsing(&charts.Chart{ + ChartMetadata: &charts.ChartMetadata{}, ValuesFiles: &charts.ValuesFiles{ MainValuesFile: &charts.ValuesFile{ Values: make(map[string]interface{}), @@ -107,15 +110,16 @@ func TestDefinitionNotImplemented(t *testing.T) { // {{ range $index, $element := pipeline }}{{ $index }}{{ $element }}{{ end }} # line 7 // -----------------| func TestDefinitionRange(t *testing.T) { - genericDefinitionTest(t, lsp.Position{Line: 7, Character: 60}, []lsp.Location{{ - URI: testDocumentTemplateURI, - Range: lsp.Range{ - Start: lsp.Position{ - Line: 7, - Character: 17, + genericDefinitionTest(t, lsp.Position{Line: 7, Character: 60}, []lsp.Location{ + { + URI: testDocumentTemplateURI, + Range: lsp.Range{ + Start: lsp.Position{ + Line: 7, + Character: 17, + }, }, }, - }, }, nil) } @@ -123,19 +127,20 @@ func TestDefinitionRange(t *testing.T) { // {{ .Values.foo }} # line 8 // ------------| func TestDefinitionValue(t *testing.T) { - genericDefinitionTest(t, lsp.Position{Line: 8, Character: 13}, []lsp.Location{{ - URI: testValuesURI, - Range: lsp.Range{ - Start: lsp.Position{ - Line: 1, - Character: 0, - }, - End: lsp.Position{ - Line: 1, - Character: 0, + genericDefinitionTest(t, lsp.Position{Line: 8, Character: 13}, []lsp.Location{ + { + URI: testValuesURI, + Range: lsp.Range{ + Start: lsp.Position{ + Line: 1, + Character: 0, + }, + End: lsp.Position{ + Line: 1, + Character: 0, + }, }, }, - }, }, nil) } @@ -143,40 +148,42 @@ func TestDefinitionValue(t *testing.T) { // {{ .Values.something.nested }} # line 9 // ----------------------| func TestDefinitionValueNested(t *testing.T) { - genericDefinitionTest(t, lsp.Position{Line: 9, Character: 26}, []lsp.Location{{ - URI: testValuesURI, - Range: lsp.Range{ - Start: lsp.Position{ - Line: 3, - Character: 2, - }, - End: lsp.Position{ - Line: 3, - Character: 2, + genericDefinitionTest(t, lsp.Position{Line: 9, Character: 26}, []lsp.Location{ + { + URI: testValuesURI, + Range: lsp.Range{ + Start: lsp.Position{ + Line: 3, + Character: 2, + }, + End: lsp.Position{ + Line: 3, + Character: 2, + }, }, }, - }, }, nil) } // {{ .Values.foo }} # line 8 // ------| func TestDefinitionValueFile(t *testing.T) { - genericDefinitionTest(t, lsp.Position{Line: 8, Character: 7}, []lsp.Location{{ - URI: testValuesURI, - Range: lsp.Range{ - Start: lsp.Position{ - Line: 0, - Character: 0, + genericDefinitionTest(t, lsp.Position{Line: 8, Character: 7}, []lsp.Location{ + { + URI: testValuesURI, + Range: lsp.Range{ + Start: lsp.Position{ + Line: 0, + Character: 0, + }, }, }, - }, }, nil) } func genericDefinitionTestMultipleValuesFiles(t *testing.T, position lsp.Position, expectedLocations []lsp.Location, expectedError error) { var node yamlv3.Node - var err = yamlv3.Unmarshal([]byte(valuesContent), &node) + err := yamlv3.Unmarshal([]byte(valuesContent), &node) if err != nil { t.Fatal(err) } diff --git a/internal/handler/hover.go b/internal/handler/hover.go index a0fcfa1d..c9b2cb2f 100644 --- a/internal/handler/hover.go +++ b/internal/handler/hover.go @@ -22,7 +22,6 @@ import ( ) func (h *langHandler) handleHover(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) (err error) { - if req.Params() == nil { return &jsonrpc2.Error{Code: jsonrpc2.InvalidParams} } @@ -56,11 +55,11 @@ func (h *langHandler) handleHover(ctx context.Context, reply jsonrpc2.Replier, r pt := parent.Type() ct := currentNode.Type() if ct == "text" { - var word = doc.WordAt(params.Position) + word := doc.WordAt(params.Position) if len(word) > 2 && string(word[len(word)-1]) == ":" { word = word[0 : len(word)-1] } - var response, err = h.yamllsConnector.CallHover(ctx, params, word) + response, err := h.yamllsConnector.CallHover(ctx, params, word) return reply(ctx, response, err) } if pt == "function_call" && ct == "identifier" { @@ -156,13 +155,12 @@ func (h *langHandler) getChartMetadataHover(metadata *chart.Metadata, key string func (h *langHandler) getValueHover(chart *charts.Chart, splittedVar []string) (result string, err error) { var ( valuesFiles = chart.ResolveValueFiles(splittedVar, h.chartStore) - selector = strings.Join(splittedVar, ".") results = map[uri.URI]string{} ) for _, valuesFiles := range valuesFiles { - for _, valuesFile := range valuesFiles.AllValuesFiles() { - result, err := getTableOrValueForSelector(valuesFile.Values, selector) + for _, valuesFile := range valuesFiles.ValuesFiles.AllValuesFiles() { + result, err := getTableOrValueForSelector(valuesFile.Values, strings.Join(valuesFiles.Selector, ".")) if err == nil { results[valuesFile.URI] = result } @@ -193,10 +191,10 @@ func (h *langHandler) getValueHover(chart *charts.Chart, splittedVar []string) ( func getTableOrValueForSelector(values chartutil.Values, selector string) (string, error) { if len(selector) > 0 { - var localValues, err = values.Table(selector) + localValues, err := values.Table(selector) if err != nil { logger.Debug("values.PathValue(tableName) because of error", err) - var value, err = values.PathValue(selector) + value, err := values.PathValue(selector) return fmt.Sprint(value), err } logger.Debug("converting to YAML", localValues) diff --git a/internal/handler/hover_test.go b/internal/handler/hover_test.go index 25185c1e..31e999c7 100644 --- a/internal/handler/hover_test.go +++ b/internal/handler/hover_test.go @@ -4,15 +4,16 @@ import ( "testing" "github.com/mrjosh/helm-ls/internal/charts" + "github.com/mrjosh/helm-ls/pkg/chart" "github.com/stretchr/testify/assert" "go.lsp.dev/uri" ) func Test_langHandler_getValueHover(t *testing.T) { type args struct { - chart *charts.Chart - parentChart *charts.Chart - splittedVar []string + chart *charts.Chart + parentCharts map[uri.URI]*charts.Chart + splittedVar []string } tests := []struct { name string @@ -24,12 +25,14 @@ func Test_langHandler_getValueHover(t *testing.T) { name: "single values file", args: args{ chart: &charts.Chart{ + ChartMetadata: &charts.ChartMetadata{}, ValuesFiles: &charts.ValuesFiles{ MainValuesFile: &charts.ValuesFile{ Values: map[string]interface{}{ "key": "value", }, - URI: "file://tmp/values.yaml"}, + URI: "file://tmp/values.yaml", + }, }, }, splittedVar: []string{"key"}, @@ -44,6 +47,7 @@ value name: "multiple values files", args: args{ chart: &charts.Chart{ + ChartMetadata: &charts.ChartMetadata{}, ValuesFiles: &charts.ValuesFiles{ MainValuesFile: &charts.ValuesFile{Values: map[string]interface{}{"key": "value"}, URI: "file://tmp/values.yaml"}, AdditionalValuesFiles: []*charts.ValuesFile{{Values: map[string]interface{}{"key": ""}, URI: "file://tmp/values.other.yaml"}}, @@ -64,6 +68,7 @@ value name: "yaml result", args: args{ chart: &charts.Chart{ + ChartMetadata: &charts.ChartMetadata{}, ValuesFiles: &charts.ValuesFiles{ MainValuesFile: &charts.ValuesFile{Values: map[string]interface{}{"key": map[string]interface{}{"nested": "value"}}, URI: "file://tmp/values.yaml"}, }, @@ -81,6 +86,7 @@ nested: value name: "yaml result as list", args: args{ chart: &charts.Chart{ + ChartMetadata: &charts.ChartMetadata{}, ValuesFiles: &charts.ValuesFiles{ MainValuesFile: &charts.ValuesFile{Values: map[string]interface{}{"key": []map[string]interface{}{{"nested": "value"}}}, URI: "file://tmp/values.yaml"}, }, @@ -94,9 +100,10 @@ nested: value wantErr: false, }, { - name: "subchart includes parent values", + name: "subchart includes parent values global", args: args{ chart: &charts.Chart{ + ChartMetadata: &charts.ChartMetadata{}, ValuesFiles: &charts.ValuesFiles{ MainValuesFile: &charts.ValuesFile{Values: map[string]interface{}{"global": map[string]interface{}{"key": "value"}}, URI: "file://tmp/charts/subchart/values.yaml"}, }, @@ -105,9 +112,12 @@ nested: value HasParent: true, }, }, - parentChart: &charts.Chart{ - ValuesFiles: &charts.ValuesFiles{ - MainValuesFile: &charts.ValuesFile{Values: map[string]interface{}{"global": map[string]interface{}{"key": "parentValue"}}, URI: "file://tmp/values.yaml"}, + parentCharts: map[uri.URI]*charts.Chart{ + uri.New("file://tmp/"): { + ChartMetadata: &charts.ChartMetadata{}, + ValuesFiles: &charts.ValuesFiles{ + MainValuesFile: &charts.ValuesFile{Values: map[string]interface{}{"global": map[string]interface{}{"key": "parentValue"}}, URI: "file://tmp/values.yaml"}, + }, }, }, splittedVar: []string{"global", "key"}, @@ -118,6 +128,87 @@ parentValue ### charts/subchart/values.yaml value +`, + wantErr: false, + }, + { + name: "subchart includes parent values by chart name", + args: args{ + chart: &charts.Chart{ + ChartMetadata: &charts.ChartMetadata{ + Metadata: chart.Metadata{Name: "subchart"}, + }, + ValuesFiles: &charts.ValuesFiles{ + MainValuesFile: &charts.ValuesFile{Values: map[string]interface{}{"key": "value"}, URI: "file://tmp/charts/subchart/values.yaml"}, + }, + ParentChart: charts.ParentChart{ + ParentChartURI: uri.New("file://tmp/"), + HasParent: true, + }, + }, + parentCharts: map[uri.URI]*charts.Chart{ + uri.New("file://tmp/"): { + ChartMetadata: &charts.ChartMetadata{}, + ValuesFiles: &charts.ValuesFiles{ + MainValuesFile: &charts.ValuesFile{Values: map[string]interface{}{"subchart": map[string]interface{}{"key": "parentValue"}}, URI: "file://tmp/values.yaml"}, + }, + }, + }, + splittedVar: []string{"key"}, + }, + want: `### values.yaml +parentValue + +### charts/subchart/values.yaml +value + +`, + wantErr: false, + }, + { + name: "subsubchart includes parent values by chart name", + args: args{ + chart: &charts.Chart{ + ChartMetadata: &charts.ChartMetadata{Metadata: chart.Metadata{Name: "subsubchart"}}, + ValuesFiles: &charts.ValuesFiles{ + MainValuesFile: &charts.ValuesFile{Values: map[string]interface{}{"key": "value"}, URI: "file://tmp/charts/subchart/charts/subsubchart/values.yaml"}, + }, + ParentChart: charts.ParentChart{ + ParentChartURI: uri.New("file://tmp/charts/subchart"), + HasParent: true, + }, + }, + parentCharts: map[uri.URI]*charts.Chart{ + uri.New("file://tmp/charts/subchart"): { + ChartMetadata: &charts.ChartMetadata{Metadata: chart.Metadata{Name: "subchart"}}, + ValuesFiles: &charts.ValuesFiles{ + MainValuesFile: &charts.ValuesFile{Values: map[string]interface{}{"subsubchart": map[string]interface{}{"key": "middleValue"}}, URI: "file://tmp/charts/subchart/values.yaml"}, + }, + ParentChart: charts.ParentChart{ + ParentChartURI: uri.New("file://tmp/"), + HasParent: true, + }, + }, + uri.New("file://tmp/"): { + ChartMetadata: &charts.ChartMetadata{ + Metadata: chart.Metadata{Name: "parent"}, + }, + ValuesFiles: &charts.ValuesFiles{ + MainValuesFile: &charts.ValuesFile{Values: map[string]interface{}{"subchart": map[string]interface{}{"subsubchart": map[string]interface{}{"key": "parentValue"}}}, URI: "file://tmp/values.yaml"}, + }, + }, + }, + splittedVar: []string{"key"}, + }, + want: `### values.yaml +parentValue + +### charts/subchart/values.yaml +middleValue + +### charts/subchart/charts/subsubchart/values.yaml +value + `, wantErr: false, }, @@ -127,9 +218,7 @@ value h := &langHandler{ chartStore: &charts.ChartStore{ RootURI: uri.New("file://tmp/"), - Charts: map[uri.URI]*charts.Chart{ - uri.New("file://tmp/"): tt.args.parentChart, - }, + Charts: tt.args.parentCharts, }, } got, err := h.getValueHover(tt.args.chart, tt.args.splittedVar) diff --git a/internal/handler/initialization.go b/internal/handler/initialization.go index 1b4384fa..771be2ef 100644 --- a/internal/handler/initialization.go +++ b/internal/handler/initialization.go @@ -16,7 +16,6 @@ import ( ) func (h *langHandler) handleInitialize(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error { - var params lsp.InitializeParams if err := json.Unmarshal(req.Params(), ¶ms); err != nil { return err @@ -56,6 +55,7 @@ func (h *langHandler) handleInitialize(ctx context.Context, reply jsonrpc2.Repli func (h *langHandler) initializationWithConfig() { configureLogLevel(h.helmlsConfig) + h.chartStore.SetValuesFilesConfig(h.helmlsConfig.ValuesFilesConfig) configureYamlls(h) } diff --git a/internal/util/config.go b/internal/util/config.go index 5955dee5..879ab42c 100644 --- a/internal/util/config.go +++ b/internal/util/config.go @@ -2,9 +2,16 @@ package util type HelmlsConfiguration struct { YamllsConfiguration YamllsConfiguration `json:"yamlls,omitempty"` + ValuesFilesConfig ValuesFilesConfig `json:"valuesFiles,omitempty"` LogLevel string `json:"logLevel,omitempty"` } +type ValuesFilesConfig struct { + MainValuesFileName string `json:"mainValuesFile,omitempty"` + LintOverlayValuesFileName string `json:"lintOverlayValuesFile,omitempty"` + AdditionalValuesFilesGlobPattern string `json:"additionalValuesFilesGlobPattern,omitempty"` +} + type YamllsConfiguration struct { Enabled bool `json:"enabled,omitempty"` Path string `json:"path,omitempty"` @@ -19,6 +26,11 @@ type YamllsConfiguration struct { var DefaultConfig = HelmlsConfiguration{ LogLevel: "info", + ValuesFilesConfig: ValuesFilesConfig{ + MainValuesFileName: "values.yaml", + LintOverlayValuesFileName: "values.lint.yaml", + AdditionalValuesFilesGlobPattern: "values*.yaml", + }, YamllsConfiguration: YamllsConfiguration{ Enabled: true, Path: "yaml-language-server", From 0a96b2bdb7222854d51cf1484d5f7b7810a14a59 Mon Sep 17 00:00:00 2001 From: qvalentin Date: Sun, 21 Jan 2024 14:51:36 +0100 Subject: [PATCH 04/35] feat(values): reload values files on changes --- internal/charts/chart_store.go | 20 ++++++++- internal/charts/chart_store_test.go | 33 +++++++++++++- internal/charts/values_file.go | 24 +++++++--- internal/charts/values_file_test.go | 15 +++++++ internal/charts/values_files.go | 5 +-- internal/handler/handler.go | 3 +- internal/handler/initialization.go | 2 +- internal/handler/watched_files.go | 68 +++++++++++++++++++++++++++++ 8 files changed, 155 insertions(+), 15 deletions(-) create mode 100644 internal/handler/watched_files.go diff --git a/internal/charts/chart_store.go b/internal/charts/chart_store.go index 174d6d3f..625bcf1e 100644 --- a/internal/charts/chart_store.go +++ b/internal/charts/chart_store.go @@ -1,6 +1,8 @@ package charts import ( + "path/filepath" + "github.com/mrjosh/helm-ls/internal/util" "go.lsp.dev/uri" ) @@ -29,8 +31,8 @@ func (s *ChartStore) SetValuesFilesConfig(valuesFilesConfig util.ValuesFilesConf return } s.valuesFilesConfig = valuesFilesConfig - for _, chart := range s.Charts { - chart.ValuesFiles = NewValuesFiles(chart.RootURI, valuesFilesConfig.MainValuesFileName, valuesFilesConfig.LintOverlayValuesFileName, valuesFilesConfig.AdditionalValuesFilesGlobPattern) + for uri := range s.Charts { + s.Charts[uri] = s.newChart(uri, valuesFilesConfig) } } @@ -54,3 +56,17 @@ func (s *ChartStore) GetChartForURI(fileURI uri.URI) (*Chart, error) { URI: fileURI, } } + +func (s *ChartStore) ReloadValuesFile(file uri.URI) { + logger.Println("Reloading values file", file) + chart, err := s.GetChartForURI(uri.URI(util.FileURIScheme + filepath.Dir(file.Filename()))) + if err != nil { + logger.Error("Error reloading values file", file, err) + return + } + for _, valuesFile := range chart.ValuesFiles.AllValuesFiles() { + if valuesFile.URI == file { + valuesFile.Reload() + } + } +} diff --git a/internal/charts/chart_store_test.go b/internal/charts/chart_store_test.go index 740b72ff..75bea663 100644 --- a/internal/charts/chart_store_test.go +++ b/internal/charts/chart_store_test.go @@ -9,6 +9,7 @@ import ( "github.com/mrjosh/helm-ls/pkg/chartutil" "github.com/stretchr/testify/assert" "go.lsp.dev/uri" + "gopkg.in/yaml.v3" ) func TestSetValuesFilesConfigOverwrites(t *testing.T) { @@ -26,10 +27,13 @@ func TestSetValuesFilesConfigOverwrites(t *testing.T) { _ = os.WriteFile(filepath.Join(tempDir, "values.other.yaml"), []byte(valuesContent), 0o644) s := NewChartStore(uri.New(util.FileURIScheme+tempDir), NewChart) - chart, err := s.GetChartForURI(uri.New(util.FileURIScheme + tempDir)) - assert.Equal(t, chartutil.Values{}, chart.ValuesFiles.MainValuesFile.Values) + chartOld, err := s.GetChartForURI(uri.New(util.FileURIScheme + tempDir)) + assert.Equal(t, chartutil.Values{}, chartOld.ValuesFiles.MainValuesFile.Values) s.SetValuesFilesConfig(valuesFilesConfig) + chart, err := s.GetChartForURI(uri.New(util.FileURIScheme + tempDir)) + assert.NoError(t, err) + assert.NotSame(t, chartOld, chart) assert.NoError(t, err) assert.NotEqual(t, chartutil.Values{}, chart.ValuesFiles.MainValuesFile.Values) assert.Equal(t, valuesFilesConfig.MainValuesFileName, filepath.Base(chart.ValuesFiles.MainValuesFile.URI.Filename())) @@ -73,3 +77,28 @@ func TestGetChartForURIWhenChartYamlDoesNotExist(t *testing.T) { assert.Error(t, err) assert.Nil(t, chart) } + +func TestReloadValuesFiles(t *testing.T) { + tempDir := t.TempDir() + chart := &Chart{ + ValuesFiles: &ValuesFiles{MainValuesFile: &ValuesFile{ + Values: map[string]interface{}{"foo": "bar"}, + ValueNode: yaml.Node{}, + URI: uri.New(util.FileURIScheme + filepath.Join(tempDir, "values.yaml")), + }, OverlayValuesFile: &ValuesFile{}, AdditionalValuesFiles: []*ValuesFile{}}, + ChartMetadata: &ChartMetadata{}, + RootURI: uri.New("file://" + tempDir), + ParentChart: ParentChart{}, + } + s := NewChartStore(uri.New(util.FileURIScheme+tempDir), NewChart) + s.Charts[chart.RootURI] = chart + + assert.Equal(t, "bar", chart.ValuesFiles.MainValuesFile.Values["foo"]) + os.WriteFile(filepath.Join(tempDir, "values.yaml"), []byte("foo: new"), 0o644) + + s.ReloadValuesFile(uri.New(util.FileURIScheme + filepath.Join(tempDir, "values.yaml"))) + assert.Equal(t, "new", chart.ValuesFiles.MainValuesFile.Values["foo"]) + + s.ReloadValuesFile(uri.New(util.FileURIScheme + filepath.Join(tempDir, "notfound.yaml"))) + s.ReloadValuesFile(uri.New(util.FileURIScheme + "/notFound.yaml")) +} diff --git a/internal/charts/values_file.go b/internal/charts/values_file.go index b092ce88..5e710738 100644 --- a/internal/charts/values_file.go +++ b/internal/charts/values_file.go @@ -15,7 +15,24 @@ type ValuesFile struct { } func NewValuesFile(filePath string) *ValuesFile { + vals, valueNodes := readInValuesFile(filePath) + return &ValuesFile{ + ValueNode: valueNodes, + Values: vals, + URI: uri.New(util.FileURIScheme + filePath), + } +} + +func (v *ValuesFile) Reload() { + vals, valueNodes := readInValuesFile(v.URI.Filename()) + + logger.Debug("Reloading values file", v.URI.Filename(), vals) + v.Values = vals + v.ValueNode = valueNodes +} + +func readInValuesFile(filePath string) (chartutil.Values, yaml.Node) { vals, err := chartutil.ReadValuesFile(filePath) if err != nil { logger.Error("Error loading values file ", filePath, err) @@ -25,10 +42,5 @@ func NewValuesFile(filePath string) *ValuesFile { if err != nil { logger.Error("Error loading values file ", filePath, err) } - - return &ValuesFile{ - ValueNode: valueNodes, - Values: vals, - URI: uri.New(util.FileURIScheme + filePath), - } + return vals, valueNodes } diff --git a/internal/charts/values_file_test.go b/internal/charts/values_file_test.go index 899e6a3a..58efbe56 100644 --- a/internal/charts/values_file_test.go +++ b/internal/charts/values_file_test.go @@ -30,3 +30,18 @@ func TestNewValuesFileFileNotFound(t *testing.T) { assert.Equal(t, chartutil.Values{}, valuesFile.Values) assert.Equal(t, yaml.Node{}, valuesFile.ValueNode) } + +func TestReload(t *testing.T) { + tempDir := t.TempDir() + valuesContent := `foo: bar` + _ = os.WriteFile(filepath.Join(tempDir, "values.yaml"), []byte(valuesContent), 0o644) + valuesFile := charts.NewValuesFile(filepath.Join(tempDir, "values.yaml")) + + assert.Equal(t, "bar", valuesFile.Values["foo"]) + assert.NotEqual(t, yaml.Node{}, valuesFile.ValueNode) + + _ = os.WriteFile(filepath.Join(tempDir, "values.yaml"), []byte("foo: baz"), 0o644) + valuesFile.Reload() + assert.Equal(t, "baz", valuesFile.Values["foo"]) + assert.NotEqual(t, yaml.Node{}, valuesFile.ValueNode) +} diff --git a/internal/charts/values_files.go b/internal/charts/values_files.go index 561513f9..ba9bf9b5 100644 --- a/internal/charts/values_files.go +++ b/internal/charts/values_files.go @@ -17,9 +17,8 @@ type ValuesFiles struct { func NewValuesFiles(rootURI uri.URI, mainValuesFileName string, lintOverlayValuesFile string, additionalValuesFilesGlob string) *ValuesFiles { additionalValuesFiles := getAdditionalValuesFiles(additionalValuesFilesGlob, rootURI, mainValuesFileName) - var overlayValuesFile *ValuesFile - overlayValuesFile = getLintOverlayValuesFile(lintOverlayValuesFile, additionalValuesFiles, overlayValuesFile, rootURI) + overlayValuesFile := getLintOverlayValuesFile(lintOverlayValuesFile, additionalValuesFiles, rootURI) return &ValuesFiles{ MainValuesFile: NewValuesFile(filepath.Join(rootURI.Filename(), mainValuesFileName)), @@ -28,7 +27,7 @@ func NewValuesFiles(rootURI uri.URI, mainValuesFileName string, lintOverlayValue } } -func getLintOverlayValuesFile(lintOverlayValuesFile string, additionalValuesFiles []*ValuesFile, overlayValuesFile *ValuesFile, rootURI uri.URI) *ValuesFile { +func getLintOverlayValuesFile(lintOverlayValuesFile string, additionalValuesFiles []*ValuesFile, rootURI uri.URI) (overlayValuesFile *ValuesFile) { if lintOverlayValuesFile == "" && len(additionalValuesFiles) == 1 { overlayValuesFile = additionalValuesFiles[0] } diff --git a/internal/handler/handler.go b/internal/handler/handler.go index b39aace6..f7bc804a 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -68,6 +68,8 @@ func (h *langHandler) handle(ctx context.Context, reply jsonrpc2.Replier, req js return h.handleHover(ctx, reply, req) case lsp.MethodWorkspaceDidChangeConfiguration: return h.handleWorkspaceDidChangeConfiguration(ctx, reply, req) + case lsp.MethodWorkspaceDidChangeWatchedFiles: + return h.handleDidChangeWatchedFiles(ctx, reply, req) default: logger.Debug("Unsupported method", req.Method()) } @@ -80,7 +82,6 @@ func (h *langHandler) handleShutdown(_ context.Context, _ jsonrpc2.Replier, _ js } func (h *langHandler) handleTextDocumentDidOpen(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) (err error) { - var params lsp.DidOpenTextDocumentParams if err := json.Unmarshal(req.Params(), ¶ms); err != nil { return reply(ctx, nil, err) diff --git a/internal/handler/initialization.go b/internal/handler/initialization.go index 771be2ef..5e1bf782 100644 --- a/internal/handler/initialization.go +++ b/internal/handler/initialization.go @@ -32,7 +32,7 @@ func (h *langHandler) handleInitialize(ctx context.Context, reply jsonrpc2.Repli } h.yamllsConnector.CallInitialize(workspaceURI) - h.chartStore = charts.NewChartStore(workspaceURI, charts.NewChart) + h.chartStore = charts.NewChartStore(workspaceURI, h.NewChartWithWatchedFiles) return reply(ctx, lsp.InitializeResult{ Capabilities: lsp.ServerCapabilities{ diff --git a/internal/handler/watched_files.go b/internal/handler/watched_files.go new file mode 100644 index 00000000..906ad594 --- /dev/null +++ b/internal/handler/watched_files.go @@ -0,0 +1,68 @@ +package handler + +import ( + "context" + "encoding/json" + + "github.com/mrjosh/helm-ls/internal/charts" + "github.com/mrjosh/helm-ls/internal/util" + lsp "go.lsp.dev/protocol" + "go.lsp.dev/uri" + + "go.lsp.dev/jsonrpc2" +) + +func (h *langHandler) NewChartWithWatchedFiles(rootURI uri.URI, valuesFilesConfig util.ValuesFilesConfig) *charts.Chart { + logger.Debug("NewChartWithWatchedFiles", rootURI, valuesFilesConfig) + chart := charts.NewChart(rootURI, valuesFilesConfig) + + uris := make([]uri.URI, 0) + for _, valuesFile := range chart.ValuesFiles.AllValuesFiles() { + uris = append(uris, valuesFile.URI) + } + + go h.RegisterWatchedFiles(context.Background(), h.connPool, uris) + return chart +} + +func (h *langHandler) RegisterWatchedFiles(ctx context.Context, conn jsonrpc2.Conn, files []uri.URI) { + watchers := make([]lsp.FileSystemWatcher, 0) + + for _, file := range files { + watchers = append(watchers, lsp.FileSystemWatcher{ + GlobPattern: file.Filename(), + }) + } + + var result any + _, err := conn.Call(ctx, "client/registerCapability", lsp.RegistrationParams{ + Registrations: []lsp.Registration{ + { + Method: "workspace/didChangeWatchedFiles", + RegisterOptions: lsp.DidChangeWatchedFilesRegistrationOptions{ + Watchers: watchers, + }, + }, + }, + }, result) + if err != nil { + logger.Error("Error registering watched files", err) + } +} + +func (h *langHandler) handleDidChangeWatchedFiles(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) (err error) { + if req.Params() == nil { + return &jsonrpc2.Error{Code: jsonrpc2.InvalidParams} + } + + var params lsp.DidChangeWatchedFilesParams + if err := json.Unmarshal(req.Params(), ¶ms); err != nil { + return err + } + + for _, change := range params.Changes { + h.chartStore.ReloadValuesFile(change.URI) + } + + return reply(ctx, nil, nil) +} From 970d1958bc767a9dedee88d18e48a8ec2150730f Mon Sep 17 00:00:00 2001 From: qvalentin Date: Sat, 27 Jan 2024 16:24:36 +0100 Subject: [PATCH 05/35] fix(windows): support windows --- .github/workflows/artifacts.yml | 2 +- .github/workflows/lint.yml | 4 +- .github/workflows/tests.yml | 32 ++- Makefile | 1 + cmds/lint.go | 5 +- internal/adapter/fs/fs.go | 1 - internal/adapter/yamlls/trimTemplate_test.go | 6 +- internal/charts/chart.go | 36 +-- internal/charts/chart_for_document.go | 30 +-- internal/charts/chart_for_document_test.go | 35 +-- internal/charts/chart_store.go | 4 +- internal/charts/chart_store_test.go | 28 +-- internal/charts/chart_test.go | 22 +- internal/charts/metadata.go | 3 +- internal/charts/parent_chart.go | 3 +- internal/charts/values_file.go | 3 +- internal/charts/values_files_test.go | 14 +- internal/handler/completion_values_test.go | 2 +- internal/handler/handler.go | 8 +- internal/handler/hover_test.go | 9 +- internal/handler/initialization.go | 27 ++- internal/log/log.go | 7 +- internal/lsp/ast.go | 6 +- internal/lsp/ast_diagnostics_test.go | 3 +- internal/lsp/ast_field_identifier_test.go | 8 +- internal/lsp/document.go | 224 ++----------------- internal/lsp/document_test.go | 37 +++ internal/lsp/fs.go | 36 --- internal/lsp/lint.go | 2 +- internal/util/strings.go | 55 +---- internal/util/strings_test.go | 61 ----- 31 files changed, 229 insertions(+), 485 deletions(-) create mode 100644 internal/lsp/document_test.go delete mode 100644 internal/lsp/fs.go diff --git a/.github/workflows/artifacts.yml b/.github/workflows/artifacts.yml index 1540058a..d868b5e6 100644 --- a/.github/workflows/artifacts.yml +++ b/.github/workflows/artifacts.yml @@ -27,7 +27,7 @@ jobs: with: go-version: ${{ matrix.go-version }} - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: build run: | make build-release diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index dbd639c1..d895464f 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -18,12 +18,12 @@ jobs: steps: - name: Install Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v4 with: go-version: ${{ matrix.go-version }} - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Lint run: make lint diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 93789e15..7430185a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -5,6 +5,7 @@ on: branches: - "master" pull_request: + types: [synchronize] jobs: @@ -18,12 +19,39 @@ jobs: steps: - name: Install Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v4 with: go-version: ${{ matrix.go-version }} - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Run tests run: make test + + tests-windows: + name: tests + strategy: + matrix: + go-version: [1.19.1] + os: [windows-2019] + runs-on: ${{ matrix.os }} + steps: + + - name: Install Go + uses: actions/setup-go@v4 + with: + go-version: ${{ matrix.go-version }} + + - name: Set up MinGW + uses: egor-tensin/setup-mingw@v2 + with: + version: 12.2.0 + + - name: Checkout code + uses: actions/checkout@v4 + + - name: Run tests + run: make test + env: + CC: "cc" diff --git a/Makefile b/Makefile index 44eca004..2f3f03fa 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ export GOBIN?=$(BIN) export GO=$(shell which go) export PACKAGE_NAME=github.com/mrjosh/helm-ls export GOLANG_CROSS_VERSION=v1.20.6 +export CGO_ENABLED=1 $(eval GIT_COMMIT=$(shell git rev-parse --short HEAD)) $(eval BRANCH_NAME=$(shell git symbolic-ref -q --short HEAD || git describe --tags --exact-match)) diff --git a/cmds/lint.go b/cmds/lint.go index 88e20f74..6319aa0f 100644 --- a/cmds/lint.go +++ b/cmds/lint.go @@ -6,7 +6,6 @@ import ( "github.com/mrjosh/helm-ls/internal/charts" locallsp "github.com/mrjosh/helm-ls/internal/lsp" - "github.com/mrjosh/helm-ls/internal/util" "github.com/spf13/cobra" "go.lsp.dev/uri" ) @@ -20,14 +19,14 @@ func newLintCmd() *cobra.Command { args = append(args, os.Getenv("PWD")) } - rootPath := uri.New(util.FileURIScheme + args[0]) + rootPath := uri.File(args[0]) chartStore := charts.NewChartStore(rootPath, charts.NewChart) chart, err := chartStore.GetChartForURI(rootPath) if err != nil { return err } - msgs, err := locallsp.GetDiagnostics(uri.New(args[0]), chart.ValuesFiles.MainValuesFile.Values) + msgs, err := locallsp.GetDiagnostics(rootPath, chart.ValuesFiles.MainValuesFile.Values) if err != nil { return err } diff --git a/internal/adapter/fs/fs.go b/internal/adapter/fs/fs.go index 7446ac0c..7bf35c98 100644 --- a/internal/adapter/fs/fs.go +++ b/internal/adapter/fs/fs.go @@ -66,7 +66,6 @@ func (fs *FileStorage) Canonical(path string) string { } func (fs *FileStorage) FileExists(path string) (bool, error) { - fi, err := fs.fileInfo(path) if err != nil { return false, err diff --git a/internal/adapter/yamlls/trimTemplate_test.go b/internal/adapter/yamlls/trimTemplate_test.go index 4343951c..fd35c77a 100644 --- a/internal/adapter/yamlls/trimTemplate_test.go +++ b/internal/adapter/yamlls/trimTemplate_test.go @@ -307,12 +307,12 @@ func TestTrimTemplate(t *testing.T) { func testTrimTemplateWithTestData(t *testing.T, testData TrimTemplateTestData) { doc := &lsplocal.Document{ Content: testData.documentText, - Ast: lsplocal.ParseAst(testData.documentText), + Ast: lsplocal.ParseAst(nil, testData.documentText), } - var trimmed = trimTemplateForYamllsFromAst(doc.Ast, testData.documentText) + trimmed := trimTemplateForYamllsFromAst(doc.Ast, testData.documentText) - var result = trimmed == testData.trimmedText + result := trimmed == testData.trimmedText if !result { t.Errorf("Trimmed templated was not as expected but was %s ", trimmed) diff --git a/internal/charts/chart.go b/internal/charts/chart.go index 2c38ff12..66177e3e 100644 --- a/internal/charts/chart.go +++ b/internal/charts/chart.go @@ -17,7 +17,10 @@ type Chart struct { func NewChart(rootURI uri.URI, valuesFilesConfig util.ValuesFilesConfig) *Chart { return &Chart{ - ValuesFiles: NewValuesFiles(rootURI, valuesFilesConfig.MainValuesFileName, valuesFilesConfig.LintOverlayValuesFileName, valuesFilesConfig.AdditionalValuesFilesGlobPattern), + ValuesFiles: NewValuesFiles(rootURI, + valuesFilesConfig.MainValuesFileName, + valuesFilesConfig.LintOverlayValuesFileName, + valuesFilesConfig.AdditionalValuesFilesGlobPattern), ChartMetadata: NewChartMetadata(rootURI), RootURI: rootURI, ParentChart: newParentChart(rootURI), @@ -32,20 +35,23 @@ type QueriedValuesFiles struct { // ResolveValueFiles returns a list of all values files in the chart // and all parent charts if the query tries to access global values func (c *Chart) ResolveValueFiles(query []string, chartStore *ChartStore) []*QueriedValuesFiles { - if len(query) > 0 && query[0] == "global" { - parentChart := c.ParentChart.GetParentChart(chartStore) - if parentChart != nil { - return append([]*QueriedValuesFiles{{Selector: query, ValuesFiles: c.ValuesFiles}}, parentChart.ResolveValueFiles(query, chartStore)...) - } + ownResult := []*QueriedValuesFiles{{Selector: query, ValuesFiles: c.ValuesFiles}} + if len(query) == 0 { + return ownResult } - chartName := c.ChartMetadata.Metadata.Name - if len(query) > 0 { - parentChart := c.ParentChart.GetParentChart(chartStore) - if parentChart != nil { - extendedQuery := append([]string{chartName}, query...) - return append([]*QueriedValuesFiles{{Selector: query, ValuesFiles: c.ValuesFiles}}, - parentChart.ResolveValueFiles(extendedQuery, chartStore)...) - } + + parentChart := c.ParentChart.GetParentChart(chartStore) + if parentChart == nil { + return ownResult + } + + if query[0] == "global" { + return append(ownResult, + parentChart.ResolveValueFiles(query, chartStore)...) } - return []*QueriedValuesFiles{{Selector: query, ValuesFiles: c.ValuesFiles}} + + chartName := c.ChartMetadata.Metadata.Name + extendedQuery := append([]string{chartName}, query...) + return append(ownResult, + parentChart.ResolveValueFiles(extendedQuery, chartStore)...) } diff --git a/internal/charts/chart_for_document.go b/internal/charts/chart_for_document.go index b297129b..362d4991 100644 --- a/internal/charts/chart_for_document.go +++ b/internal/charts/chart_for_document.go @@ -17,16 +17,14 @@ func (s *ChartStore) GetChartForDoc(uri lsp.DocumentURI) (*Chart, error) { return chart, nil } - chart = s.getChartFromFilesystemForTemplates(uri.Filename()) - - if chart != nil { - s.Charts[chart.RootURI] = chart - return chart, nil - } - - return nil, ErrChartNotFound{ - URI: uri, + chart, err := s.getChartFromFilesystemForTemplates(uri.Filename()) + s.Charts[chart.RootURI] = chart + if err != nil { + return chart, ErrChartNotFound{ + URI: uri, + } } + return chart, nil } func (s *ChartStore) getChartFromCache(uri lsp.DocumentURI) *Chart { @@ -38,7 +36,7 @@ func (s *ChartStore) getChartFromCache(uri lsp.DocumentURI) *Chart { return nil } -func (s *ChartStore) getChartFromFilesystemForTemplates(path string) *Chart { +func (s *ChartStore) getChartFromFilesystemForTemplates(path string) (*Chart, error) { directory := filepath.Dir(path) if filepath.Base(directory) == "templates" { templatesDir := directory @@ -46,17 +44,13 @@ func (s *ChartStore) getChartFromFilesystemForTemplates(path string) *Chart { // check if Chart.yaml exists if isChartDirectory(expectedChartDir) { - return s.newChart(uri.New("file://"+expectedChartDir), s.valuesFilesConfig) + return s.newChart(uri.File(expectedChartDir), s.valuesFilesConfig), nil } } rootDirectory := s.RootURI.Filename() - if directory == rootDirectory { - return nil - } - - if directory == path { - return nil + if directory == rootDirectory || directory == path { + return s.newChart(uri.File(directory), s.valuesFilesConfig), ErrChartNotFound{} } return s.getChartFromFilesystemForTemplates(directory) @@ -72,5 +66,5 @@ type ErrChartNotFound struct { } func (e ErrChartNotFound) Error() string { - return fmt.Sprintf("Chart not found for file: %s", e.URI) + return fmt.Sprintf("Chart not found for file: %s. Using fallback", e.URI) } diff --git a/internal/charts/chart_for_document_test.go b/internal/charts/chart_for_document_test.go index f0474ce1..e0589c08 100644 --- a/internal/charts/chart_for_document_test.go +++ b/internal/charts/chart_for_document_test.go @@ -3,6 +3,7 @@ package charts_test import ( "os" "path/filepath" + "runtime" "testing" "github.com/mrjosh/helm-ls/internal/charts" @@ -12,8 +13,8 @@ import ( ) func TestGetChartForDocumentWorksForAlreadyAddedCharts(t *testing.T) { - chartStore := charts.NewChartStore("file:///tmp", func(_ uri.URI, _ util.ValuesFilesConfig) *charts.Chart { - return &charts.Chart{} + chartStore := charts.NewChartStore("file:///tmp", func(uri uri.URI, _ util.ValuesFilesConfig) *charts.Chart { + return &charts.Chart{RootURI: uri} }) chart := &charts.Chart{} @@ -41,47 +42,55 @@ func TestGetChartForDocumentWorksForAlreadyAddedCharts(t *testing.T) { result5, error := chartStore.GetChartForDoc("file:///tmp/directory/deployment.yaml") assert.Error(t, error) - assert.Nil(t, result5) + assert.Equal(t, &charts.Chart{RootURI: uri.File("/tmp")}, result5) } func TestGetChartForDocumentWorksForNewToAddChart(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("Skipping test on windows because of https://github.com/golang/go/issues/51442") + } var ( rootDir = t.TempDir() expectedChartDirectory = filepath.Join(rootDir, "chart") expectedChart = &charts.Chart{ - RootURI: uri.New("file://" + expectedChartDirectory), + RootURI: uri.File(expectedChartDirectory), } newChartFunc = func(_ uri.URI, _ util.ValuesFilesConfig) *charts.Chart { return expectedChart } - chartStore = charts.NewChartStore(uri.New("file://"+rootDir), newChartFunc) + chartStore = charts.NewChartStore(uri.File(rootDir), newChartFunc) err = os.MkdirAll(expectedChartDirectory, 0o755) ) assert.NoError(t, err) - _, _ = os.Create(filepath.Join(expectedChartDirectory, "Chart.yaml")) + chartFile := filepath.Join(expectedChartDirectory, "Chart.yaml") + _, _ = os.Create(chartFile) - result1, error := chartStore.GetChartForDoc(uri.New("file://" + filepath.Join(expectedChartDirectory, "templates", "deployment.yaml"))) + result1, error := chartStore.GetChartForDoc(uri.File(filepath.Join(expectedChartDirectory, "templates", "deployment.yaml"))) assert.NoError(t, error) assert.Same(t, expectedChart, result1) - assert.Same(t, expectedChart, chartStore.Charts[uri.New("file://"+expectedChartDirectory)]) + assert.Same(t, expectedChart, chartStore.Charts[uri.File(expectedChartDirectory)]) } func TestGetChartForDocumentWorksForNewToAddChartWithNestedFile(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("Skipping test on windows because of https://github.com/golang/go/issues/51442") + } var ( rootDir = t.TempDir() expectedChartDirectory = filepath.Join(rootDir, "chart") expectedChart = &charts.Chart{ - RootURI: uri.New("file://" + expectedChartDirectory), + RootURI: uri.File(expectedChartDirectory), } newChartFunc = func(_ uri.URI, _ util.ValuesFilesConfig) *charts.Chart { return expectedChart } - chartStore = charts.NewChartStore(uri.New("file://"+rootDir), newChartFunc) + chartStore = charts.NewChartStore(uri.File(rootDir), newChartFunc) err = os.MkdirAll(expectedChartDirectory, 0o755) ) assert.NoError(t, err) - _, _ = os.Create(filepath.Join(expectedChartDirectory, "Chart.yaml")) + chartFile := filepath.Join(expectedChartDirectory, "Chart.yaml") + _, _ = os.Create(chartFile) - result1, error := chartStore.GetChartForDoc(uri.New("file://" + filepath.Join(expectedChartDirectory, "templates", "nested", "deployment.yaml"))) + result1, error := chartStore.GetChartForDoc(uri.File(filepath.Join(expectedChartDirectory, "templates", "nested", "deployment.yaml"))) assert.NoError(t, error) assert.Same(t, expectedChart, result1) - assert.Same(t, expectedChart, chartStore.Charts[uri.New("file://"+expectedChartDirectory)]) + assert.Same(t, expectedChart, chartStore.Charts[uri.File(expectedChartDirectory)]) } diff --git a/internal/charts/chart_store.go b/internal/charts/chart_store.go index 625bcf1e..8e336592 100644 --- a/internal/charts/chart_store.go +++ b/internal/charts/chart_store.go @@ -44,7 +44,7 @@ func (s *ChartStore) GetChartForURI(fileURI uri.URI) (*Chart, error) { var chart *Chart expectedChartDir := fileURI.Filename() if isChartDirectory(expectedChartDir) { - chart = s.newChart(uri.New("file://"+expectedChartDir), s.valuesFilesConfig) + chart = s.newChart(uri.File(expectedChartDir), s.valuesFilesConfig) } if chart != nil { @@ -59,7 +59,7 @@ func (s *ChartStore) GetChartForURI(fileURI uri.URI) (*Chart, error) { func (s *ChartStore) ReloadValuesFile(file uri.URI) { logger.Println("Reloading values file", file) - chart, err := s.GetChartForURI(uri.URI(util.FileURIScheme + filepath.Dir(file.Filename()))) + chart, err := s.GetChartForURI(uri.File(filepath.Dir(file.Filename()))) if err != nil { logger.Error("Error reloading values file", file, err) return diff --git a/internal/charts/chart_store_test.go b/internal/charts/chart_store_test.go index 75bea663..49646a78 100644 --- a/internal/charts/chart_store_test.go +++ b/internal/charts/chart_store_test.go @@ -25,13 +25,13 @@ func TestSetValuesFilesConfigOverwrites(t *testing.T) { _ = os.WriteFile(filepath.Join(tempDir, "value.yaml"), []byte("foo: main"), 0o644) _ = os.WriteFile(filepath.Join(tempDir, "something.yaml"), []byte(valuesContent), 0o644) _ = os.WriteFile(filepath.Join(tempDir, "values.other.yaml"), []byte(valuesContent), 0o644) - s := NewChartStore(uri.New(util.FileURIScheme+tempDir), NewChart) + s := NewChartStore(uri.File(tempDir), NewChart) - chartOld, err := s.GetChartForURI(uri.New(util.FileURIScheme + tempDir)) + chartOld, err := s.GetChartForURI(uri.File(tempDir)) assert.Equal(t, chartutil.Values{}, chartOld.ValuesFiles.MainValuesFile.Values) s.SetValuesFilesConfig(valuesFilesConfig) - chart, err := s.GetChartForURI(uri.New(util.FileURIScheme + tempDir)) + chart, err := s.GetChartForURI(uri.File(tempDir)) assert.NoError(t, err) assert.NotSame(t, chartOld, chart) assert.NoError(t, err) @@ -54,14 +54,14 @@ func TestSetValuesFilesConfigDoesNotOverwrite(t *testing.T) { _ = os.WriteFile(filepath.Join(tempDir, "something.yaml"), []byte(valuesContent), 0o644) _ = os.WriteFile(filepath.Join(tempDir, "values.lint.yaml"), []byte(valuesContent), 0o644) _ = os.WriteFile(filepath.Join(tempDir, "values.other.yaml"), []byte(valuesContent), 0o644) - s := NewChartStore(uri.New(util.FileURIScheme+tempDir), NewChart) + s := NewChartStore(uri.File(tempDir), NewChart) - chart, err := s.GetChartForURI(uri.New(util.FileURIScheme + tempDir)) + chart, err := s.GetChartForURI(uri.File(tempDir)) assert.NoError(t, err) assert.NotEqual(t, chartutil.Values{}, chart.ValuesFiles.MainValuesFile.Values) s.SetValuesFilesConfig(valuesFilesConfig) - chart, err = s.GetChartForURI(uri.New(util.FileURIScheme + tempDir)) + chart, err = s.GetChartForURI(uri.File(tempDir)) assert.NoError(t, err) assert.Equal(t, valuesFilesConfig.MainValuesFileName, filepath.Base(chart.ValuesFiles.MainValuesFile.URI.Filename())) assert.Equal(t, valuesFilesConfig.LintOverlayValuesFileName, filepath.Base(chart.ValuesFiles.OverlayValuesFile.URI.Filename())) @@ -71,9 +71,9 @@ func TestGetChartForURIWhenChartYamlDoesNotExist(t *testing.T) { tempDir := t.TempDir() _ = os.WriteFile(filepath.Join(tempDir, "values.yaml"), []byte("foo: main"), 0o644) - s := NewChartStore(uri.New(util.FileURIScheme+tempDir), NewChart) + s := NewChartStore(uri.File(tempDir), NewChart) - chart, err := s.GetChartForURI(uri.New(util.FileURIScheme + tempDir)) + chart, err := s.GetChartForURI(uri.File(tempDir)) assert.Error(t, err) assert.Nil(t, chart) } @@ -84,21 +84,21 @@ func TestReloadValuesFiles(t *testing.T) { ValuesFiles: &ValuesFiles{MainValuesFile: &ValuesFile{ Values: map[string]interface{}{"foo": "bar"}, ValueNode: yaml.Node{}, - URI: uri.New(util.FileURIScheme + filepath.Join(tempDir, "values.yaml")), + URI: uri.File(filepath.Join(tempDir, "values.yaml")), }, OverlayValuesFile: &ValuesFile{}, AdditionalValuesFiles: []*ValuesFile{}}, ChartMetadata: &ChartMetadata{}, - RootURI: uri.New("file://" + tempDir), + RootURI: uri.File(tempDir), ParentChart: ParentChart{}, } - s := NewChartStore(uri.New(util.FileURIScheme+tempDir), NewChart) + s := NewChartStore(uri.File(tempDir), NewChart) s.Charts[chart.RootURI] = chart assert.Equal(t, "bar", chart.ValuesFiles.MainValuesFile.Values["foo"]) os.WriteFile(filepath.Join(tempDir, "values.yaml"), []byte("foo: new"), 0o644) - s.ReloadValuesFile(uri.New(util.FileURIScheme + filepath.Join(tempDir, "values.yaml"))) + s.ReloadValuesFile(uri.File(filepath.Join(tempDir, "values.yaml"))) assert.Equal(t, "new", chart.ValuesFiles.MainValuesFile.Values["foo"]) - s.ReloadValuesFile(uri.New(util.FileURIScheme + filepath.Join(tempDir, "notfound.yaml"))) - s.ReloadValuesFile(uri.New(util.FileURIScheme + "/notFound.yaml")) + s.ReloadValuesFile(uri.File(filepath.Join(tempDir, "notfound.yaml"))) + s.ReloadValuesFile(uri.File("/notFound.yaml")) } diff --git a/internal/charts/chart_test.go b/internal/charts/chart_test.go index 710bad04..d9f88998 100644 --- a/internal/charts/chart_test.go +++ b/internal/charts/chart_test.go @@ -24,7 +24,7 @@ type: application` chartFile := filepath.Join(tempDir, "Chart.yaml") _ = os.WriteFile(chartFile, []byte(chartYaml), 0o644) - chart := charts.NewChart(uri.New("file://"+tempDir), util.ValuesFilesConfig{}) + chart := charts.NewChart(uri.File(tempDir), util.ValuesFilesConfig{}) assert.Equal(t, "hello-world", chart.ChartMetadata.Metadata.Name) } @@ -35,7 +35,7 @@ func TestNewChartsLoadsDefaultMetadataOnError(t *testing.T) { chartFile := filepath.Join(tempDir, "Chart.yaml") _ = os.WriteFile(chartFile, []byte(chartYaml), 0o644) - chart := charts.NewChart(uri.New("file://"+tempDir), util.ValuesFilesConfig{}) + chart := charts.NewChart(uri.File(tempDir), util.ValuesFilesConfig{}) assert.Equal(t, "", chart.ChartMetadata.Metadata.Name) } @@ -45,7 +45,7 @@ func TestNewChartsSetsParentChartURI(t *testing.T) { chartFile := filepath.Join(tempDir, "Chart.yaml") _ = os.WriteFile(chartFile, []byte{}, 0o644) - chart := charts.NewChart(uri.New("file://"+filepath.Join(tempDir, "charts", "subchart")), util.ValuesFilesConfig{}) + chart := charts.NewChart(uri.File(filepath.Join(tempDir, "charts", "subchart")), util.ValuesFilesConfig{}) assert.Equal(t, tempDir, chart.ParentChart.ParentChartURI.Filename()) } @@ -55,7 +55,7 @@ func TestNewChartsSetsParentChartURIToDefault(t *testing.T) { chartFile := filepath.Join(tempDir, "Chart.yaml") _ = os.WriteFile(chartFile, []byte{}, 0o644) - chart := charts.NewChart(uri.New("file://"+tempDir), util.ValuesFilesConfig{}) + chart := charts.NewChart(uri.File(tempDir), util.ValuesFilesConfig{}) assert.False(t, chart.ParentChart.HasParent) } @@ -76,14 +76,14 @@ func TestResolvesValuesFileOfParent(t *testing.T) { err = os.WriteFile(subChartChartFile, []byte{}, 0o644) assert.NoError(t, err) - chart := charts.NewChart(uri.New("file://"+filepath.Join(tempDir, "charts", "subchart")), util.ValuesFilesConfig{}) + chart := charts.NewChart(uri.File(filepath.Join(tempDir, "charts", "subchart")), util.ValuesFilesConfig{}) expectedChart := &charts.Chart{ - RootURI: uri.New("file://" + tempDir), + RootURI: uri.File(tempDir), ChartMetadata: &charts.ChartMetadata{}, } newChartFunc := func(_ uri.URI, _ util.ValuesFilesConfig) *charts.Chart { return expectedChart } - chartStore := charts.NewChartStore(uri.New("file://"+tempDir), newChartFunc) + chartStore := charts.NewChartStore(uri.File(tempDir), newChartFunc) valueFiles := chart.ResolveValueFiles([]string{"global", "foo"}, chartStore) @@ -107,11 +107,11 @@ func TestResolvesValuesFileOfParentByName(t *testing.T) { err = os.WriteFile(subChartChartFile, []byte{}, 0o644) assert.NoError(t, err) - subchart := charts.NewChart(uri.New("file://"+filepath.Join(tempDir, "charts", "subchart")), util.ValuesFilesConfig{}) + subchart := charts.NewChart(uri.File(filepath.Join(tempDir, "charts", "subchart")), util.ValuesFilesConfig{}) subchart.ChartMetadata.Metadata.Name = "subchart" expectedChart := &charts.Chart{ - RootURI: uri.New("file://" + tempDir), + RootURI: uri.File(tempDir), ChartMetadata: &charts.ChartMetadata{ Metadata: chart.Metadata{ Name: "parent", @@ -119,11 +119,11 @@ func TestResolvesValuesFileOfParentByName(t *testing.T) { }, } newChartFunc := func(_ uri.URI, _ util.ValuesFilesConfig) *charts.Chart { return expectedChart } - chartStore := charts.NewChartStore(uri.New("file://"+tempDir), newChartFunc) + chartStore := charts.NewChartStore(uri.File(tempDir), newChartFunc) valueFiles := subchart.ResolveValueFiles([]string{"foo"}, chartStore) - parentChart, err := chartStore.GetChartForURI(uri.New("file://" + tempDir)) + parentChart, err := chartStore.GetChartForURI(uri.File(tempDir)) assert.NoError(t, err) assert.Equal(t, 2, len(valueFiles)) diff --git a/internal/charts/metadata.go b/internal/charts/metadata.go index f13d9e8d..53c47ab4 100644 --- a/internal/charts/metadata.go +++ b/internal/charts/metadata.go @@ -3,7 +3,6 @@ package charts import ( "path/filepath" - "github.com/mrjosh/helm-ls/internal/util" "github.com/mrjosh/helm-ls/pkg/chart" "github.com/mrjosh/helm-ls/pkg/chartutil" "go.lsp.dev/uri" @@ -26,7 +25,7 @@ func NewChartMetadata(rootURI uri.URI) *ChartMetadata { return &ChartMetadata{ Metadata: loadChartMetadata(filePath), YamlNode: chartNode, - URI: uri.New(util.FileURIScheme + filePath), + URI: uri.File(filePath), } } diff --git a/internal/charts/parent_chart.go b/internal/charts/parent_chart.go index a89c7ff2..5925826a 100644 --- a/internal/charts/parent_chart.go +++ b/internal/charts/parent_chart.go @@ -3,7 +3,6 @@ package charts import ( "path/filepath" - "github.com/mrjosh/helm-ls/internal/util" "go.lsp.dev/uri" ) @@ -15,7 +14,7 @@ type ParentChart struct { func newParentChart(rootURI uri.URI) ParentChart { directory := filepath.Dir(rootURI.Filename()) if filepath.Base(directory) == "charts" && isChartDirectory(filepath.Dir(directory)) { - return ParentChart{uri.New(util.FileURIScheme + filepath.Dir(directory)), true} + return ParentChart{uri.File(filepath.Dir(directory)), true} } return ParentChart{} } diff --git a/internal/charts/values_file.go b/internal/charts/values_file.go index 5e710738..812882dc 100644 --- a/internal/charts/values_file.go +++ b/internal/charts/values_file.go @@ -1,7 +1,6 @@ package charts import ( - "github.com/mrjosh/helm-ls/internal/util" "github.com/mrjosh/helm-ls/pkg/chartutil" "go.lsp.dev/uri" @@ -20,7 +19,7 @@ func NewValuesFile(filePath string) *ValuesFile { return &ValuesFile{ ValueNode: valueNodes, Values: vals, - URI: uri.New(util.FileURIScheme + filePath), + URI: uri.File(filePath), } } diff --git a/internal/charts/values_files_test.go b/internal/charts/values_files_test.go index 8efeb09f..4a4802e6 100644 --- a/internal/charts/values_files_test.go +++ b/internal/charts/values_files_test.go @@ -20,7 +20,7 @@ func TestNewValuesFiles(t *testing.T) { _ = os.WriteFile(filepath.Join(tempDir, "values.yaml"), []byte(valuesContent), 0o644) _ = os.WriteFile(filepath.Join(tempDir, "values-additional.yaml"), []byte(additionalValuesContent), 0o644) - valuesFiles := charts.NewValuesFiles(uri.New("file://"+tempDir), "values.yaml", "", "values*.yaml") + valuesFiles := charts.NewValuesFiles(uri.File(tempDir), "values.yaml", "", "values*.yaml") assert.Equal(t, "bar", valuesFiles.MainValuesFile.Values["foo"]) assert.Equal(t, "baz", valuesFiles.AdditionalValuesFiles[0].Values["bar"]) @@ -38,15 +38,15 @@ foo: baz` _ = os.WriteFile(filepath.Join(tempDir, "values.yaml"), []byte(valuesContent), 0o644) _ = os.WriteFile(filepath.Join(tempDir, "values-additional.yaml"), []byte(additionalValuesContent), 0o644) - valuesFiles := charts.NewValuesFiles(uri.New("file://"+tempDir), "values.yaml", "", "values*.yaml") + valuesFiles := charts.NewValuesFiles(uri.File(tempDir), "values.yaml", "", "values*.yaml") assert.Equal(t, []lsp.Location{ { - URI: uri.New("file://" + filepath.Join(tempDir, "values.yaml")), + URI: uri.File(filepath.Join(tempDir, "values.yaml")), Range: lsp.Range{Start: lsp.Position{Line: 0, Character: 0}, End: lsp.Position{Line: 0, Character: 0}}, }, { - URI: uri.New("file://" + filepath.Join(tempDir, "values-additional.yaml")), + URI: uri.File(filepath.Join(tempDir, "values-additional.yaml")), Range: lsp.Range{Start: lsp.Position{Line: 2, Character: 0}, End: lsp.Position{Line: 2, Character: 0}}, }, }, valuesFiles.GetPositionsForValue([]string{"foo"})) @@ -61,7 +61,7 @@ func TestNewValuesFileForLintOverlay(t *testing.T) { _ = os.WriteFile(filepath.Join(tempDir, "values.yaml"), []byte(valuesContent), 0o644) _ = os.WriteFile(filepath.Join(tempDir, "values-additional.yaml"), []byte(additionalValuesContent), 0o644) - valuesFiles := charts.NewValuesFiles(uri.New("file://"+tempDir), "values.yaml", "values-additional.yaml", "values*.yaml") + valuesFiles := charts.NewValuesFiles(uri.File(tempDir), "values.yaml", "values-additional.yaml", "values*.yaml") assert.Equal(t, "baz", valuesFiles.OverlayValuesFile.Values["bar"]) } @@ -75,7 +75,7 @@ func TestNewValuesFileForLintOverlayNewFile(t *testing.T) { _ = os.WriteFile(filepath.Join(tempDir, "values.yaml"), []byte(valuesContent), 0o644) _ = os.WriteFile(filepath.Join(tempDir, "values-additional.yaml"), []byte(additionalValuesContent), 0o644) - valuesFiles := charts.NewValuesFiles(uri.New("file://"+tempDir), "values.yaml", "values-additional.yaml", "") + valuesFiles := charts.NewValuesFiles(uri.File(tempDir), "values.yaml", "values-additional.yaml", "") assert.Equal(t, "baz", valuesFiles.OverlayValuesFile.Values["bar"]) } @@ -89,7 +89,7 @@ func TestNewValuesFileForLintOverlayPicksFirst(t *testing.T) { _ = os.WriteFile(filepath.Join(tempDir, "values.yaml"), []byte(valuesContent), 0o644) _ = os.WriteFile(filepath.Join(tempDir, "values-additional.yaml"), []byte(additionalValuesContent), 0o644) - valuesFiles := charts.NewValuesFiles(uri.New("file://"+tempDir), "values.yaml", "", "values*.yaml") + valuesFiles := charts.NewValuesFiles(uri.File(tempDir), "values.yaml", "", "values*.yaml") assert.Equal(t, "baz", valuesFiles.OverlayValuesFile.Values["bar"]) } diff --git a/internal/handler/completion_values_test.go b/internal/handler/completion_values_test.go index 5775b833..9c454cdf 100644 --- a/internal/handler/completion_values_test.go +++ b/internal/handler/completion_values_test.go @@ -92,7 +92,7 @@ func TestCompletionAstParsing(t *testing.T) { expectedWord := ".Values.global." doc := &lsplocal.Document{ Content: documentText, - Ast: lsplocal.ParseAst(documentText), + Ast: lsplocal.ParseAst(nil, documentText), } position := protocol.Position{ Line: 0, diff --git a/internal/handler/handler.go b/internal/handler/handler.go index f7bc804a..983b5a12 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -5,7 +5,6 @@ import ( "encoding/json" "errors" - "github.com/mrjosh/helm-ls/internal/adapter/fs" "github.com/mrjosh/helm-ls/internal/adapter/yamlls" "github.com/mrjosh/helm-ls/internal/charts" lsplocal "github.com/mrjosh/helm-ls/internal/lsp" @@ -28,8 +27,7 @@ type langHandler struct { } func NewHandler(connPool jsonrpc2.Conn) jsonrpc2.Handler { - fileStorage, _ := fs.NewFileStorage("") - documents := lsplocal.NewDocumentStore(fileStorage) + documents := lsplocal.NewDocumentStore() handler := &langHandler{ linterName: "helm-lint", connPool: connPool, @@ -108,7 +106,7 @@ func (h *langHandler) handleTextDocumentDidOpen(ctx context.Context, reply jsonr if err != nil { logger.Error("Error getting chart info for file", doc.URI, err) } - notification, err := lsplocal.NotifcationFromLint(ctx, h.connPool, chart, doc) + notification, err := lsplocal.NotificationFromLint(ctx, h.connPool, chart, doc) return reply(ctx, notification, err) } @@ -140,6 +138,6 @@ func (h *langHandler) handleTextDocumentDidSave(ctx context.Context, reply jsonr } h.yamllsConnector.DocumentDidSave(doc, params) - notification, err := lsplocal.NotifcationFromLint(ctx, h.connPool, chart, doc) + notification, err := lsplocal.NotificationFromLint(ctx, h.connPool, chart, doc) return reply(ctx, notification, err) } diff --git a/internal/handler/hover_test.go b/internal/handler/hover_test.go index 31e999c7..aabaed3c 100644 --- a/internal/handler/hover_test.go +++ b/internal/handler/hover_test.go @@ -1,6 +1,7 @@ package handler import ( + "path/filepath" "testing" "github.com/mrjosh/helm-ls/internal/charts" @@ -125,7 +126,7 @@ nested: value want: `### values.yaml parentValue -### charts/subchart/values.yaml +### ` + filepath.Join("charts", "subchart", "values.yaml") + ` value `, @@ -159,7 +160,7 @@ value want: `### values.yaml parentValue -### charts/subchart/values.yaml +### ` + filepath.Join("charts", "subchart", "values.yaml") + ` value `, @@ -203,10 +204,10 @@ value want: `### values.yaml parentValue -### charts/subchart/values.yaml +### ` + filepath.Join("charts", "subchart", "values.yaml") + ` middleValue -### charts/subchart/charts/subsubchart/values.yaml +### ` + filepath.Join("charts", "subchart", "charts", "subsubchart", "values.yaml") + ` value `, diff --git a/internal/handler/initialization.go b/internal/handler/initialization.go index 5e1bf782..bc38c36e 100644 --- a/internal/handler/initialization.go +++ b/internal/handler/initialization.go @@ -3,7 +3,6 @@ package handler import ( "context" "encoding/json" - "errors" "os" "github.com/mrjosh/helm-ls/internal/adapter/yamlls" @@ -16,24 +15,34 @@ import ( ) func (h *langHandler) handleInitialize(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error { - var params lsp.InitializeParams + var ( + params lsp.InitializeParams + workspaceURI uri.URI + err error + ) if err := json.Unmarshal(req.Params(), ¶ms); err != nil { return err } - if len(params.WorkspaceFolders) == 0 { - return errors.New("length WorkspaceFolders is 0") + logger.Debug("handleInitialize with params ", req.Params()) + if len(params.WorkspaceFolders) != 0 { + workspaceURI, err = uri.Parse(params.WorkspaceFolders[0].URI) + if err != nil { + logger.Error("Error parsing workspace URI", err) + return err + } + } else { + logger.Error("length WorkspaceFolders is 0, falling back to current working directory") + workspaceURI = uri.File(".") } - workspaceURI, err := uri.Parse(params.WorkspaceFolders[0].URI) - if err != nil { - logger.Error("Error parsing workspace URI", err) - return err - } + logger.Debug("Initializing yamllsConnector") h.yamllsConnector.CallInitialize(workspaceURI) + logger.Debug("Initializing chartStore") h.chartStore = charts.NewChartStore(workspaceURI, h.NewChartWithWatchedFiles) + logger.Debug("Initializing done") return reply(ctx, lsp.InitializeResult{ Capabilities: lsp.ServerCapabilities{ TextDocumentSync: lsp.TextDocumentSyncOptions{ diff --git a/internal/log/log.go b/internal/log/log.go index be35b07f..335c9f7a 100644 --- a/internal/log/log.go +++ b/internal/log/log.go @@ -14,8 +14,10 @@ type logger interface { SetLevel(level logrus.Level) } -var l logger -var once sync.Once +var ( + l logger + once sync.Once +) // start a new logger func GetLogger() logger { @@ -28,5 +30,6 @@ func GetLogger() logger { func createLogger() logger { logger := logrus.New() logger.SetFormatter(&logrus.JSONFormatter{}) + logger.SetLevel(logrus.DebugLevel) return logger } diff --git a/internal/lsp/ast.go b/internal/lsp/ast.go index 047d4a2d..44330c38 100644 --- a/internal/lsp/ast.go +++ b/internal/lsp/ast.go @@ -9,10 +9,10 @@ import ( lsp "go.lsp.dev/protocol" ) -func ParseAst(content string) *sitter.Tree { +func ParseAst(oldTree *sitter.Tree, content string) *sitter.Tree { parser := sitter.NewParser() parser.SetLanguage(gotemplate.GetLanguage()) - tree, _ := parser.ParseCtx(context.Background(), nil, []byte(content)) + tree, _ := parser.ParseCtx(context.Background(), oldTree, []byte(content)) return tree } @@ -106,7 +106,7 @@ func TraverseIdentifierPathUp(node *sitter.Node, doc *Document) string { } func (d *Document) ApplyChangesToAst(newContent string) { - d.Ast = ParseAst(newContent) + d.Ast = ParseAst(nil, newContent) } func GetLspRangeForNode(node *sitter.Node) lsp.Range { diff --git a/internal/lsp/ast_diagnostics_test.go b/internal/lsp/ast_diagnostics_test.go index 3b9e81be..c3354598 100644 --- a/internal/lsp/ast_diagnostics_test.go +++ b/internal/lsp/ast_diagnostics_test.go @@ -8,7 +8,7 @@ import ( func TestIsInElseBranch(t *testing.T) { template := `{{if pipeline}} t1 {{ else if pipeline }} t2 {{ else if pipeline2 }} t3 {{ else }} t4 {{ end }}` - var ast = ParseAst(template) + ast := ParseAst(nil, template) // (template [0, 0] - [1, 0] // (if_action [0, 0] - [0, 95] // condition: (function_call [0, 5] - [0, 13] @@ -52,5 +52,4 @@ func TestIsInElseBranch(t *testing.T) { if !IsInElseBranch(t4) { t.Errorf("t4 was incorrectly identified as not in else branch") } - } diff --git a/internal/lsp/ast_field_identifier_test.go b/internal/lsp/ast_field_identifier_test.go index b5064677..bb06b23d 100644 --- a/internal/lsp/ast_field_identifier_test.go +++ b/internal/lsp/ast_field_identifier_test.go @@ -11,7 +11,7 @@ import ( func TestGetFieldIdentifierPathSimple(t *testing.T) { template := `{{ .Values.test }}` - var ast = ParseAst(template) + ast := ParseAst(nil, template) // (template [0, 0] - [1, 0] // (selector_expression [0, 3] - [0, 15] // operand: (field [0, 3] - [0, 10] @@ -37,7 +37,7 @@ func TestGetFieldIdentifierPathSimple(t *testing.T) { func TestGetFieldIdentifierPathWith(t *testing.T) { template := `{{ with .Values }}{{ .test }} {{ end }}` - var ast = ParseAst(template) + ast := ParseAst(nil, template) // (template [0, 0] - [1, 0] // (with_action [0, 0] - [0, 39] // condition: (field [0, 8] - [0, 15] @@ -64,7 +64,7 @@ func TestGetFieldIdentifierPathWith(t *testing.T) { func TestGetFieldIdentifierPathFunction(t *testing.T) { template := `{{ and .Values.test1 .Values.test2 }}` - var ast = ParseAst(template) + ast := ParseAst(nil, template) // (template [0, 0] - [1, 0] // (function_call [0, 3] - [0, 35] // function: (identifier [0, 3] - [0, 6]) @@ -102,7 +102,7 @@ func TestGetFieldIdentifierPathFunctionForCompletion(t *testing.T) { template := `{{ and .Values.image .Values. }}` // | -> complete at dot - var ast = ParseAst(template) + ast := ParseAst(nil, template) var ( position = lsp.Position{Line: 0, Character: 29} diff --git a/internal/lsp/document.go b/internal/lsp/document.go index 0f68dd98..ba5f94a0 100644 --- a/internal/lsp/document.go +++ b/internal/lsp/document.go @@ -2,10 +2,10 @@ package lsp import ( "bytes" + "fmt" "strings" "github.com/mrjosh/helm-ls/internal/util" - "github.com/pkg/errors" sitter "github.com/smacker/go-tree-sitter" lsp "go.lsp.dev/protocol" "go.lsp.dev/uri" @@ -14,13 +14,11 @@ import ( // documentStore holds opened documents. type DocumentStore struct { documents map[string]*Document - fs FileStorage } -func NewDocumentStore(fs FileStorage) *DocumentStore { +func NewDocumentStore() *DocumentStore { return &DocumentStore{ documents: map[string]*Document{}, - fs: fs, } } @@ -32,50 +30,28 @@ func (s *DocumentStore) GetAllDocs() []*Document { return docs } -func (s *DocumentStore) DidOpen(params lsp.DidOpenTextDocumentParams,helmlsConfig util.HelmlsConfiguration) (*Document, error) { - //langID := params.TextDocument.LanguageID - //if langID != "markdown" && langID != "vimwiki" && langID != "pandoc" { - //return nil, nil - //} +func (s *DocumentStore) DidOpen(params lsp.DidOpenTextDocumentParams, helmlsConfig util.HelmlsConfiguration) (*Document, error) { + logger.Debug(fmt.Sprintf("Opening document %s with langID %s", params.TextDocument.URI, params.TextDocument.LanguageID)) uri := params.TextDocument.URI - path, err := s.normalizePath(uri) - if err != nil { - return nil, err - } + path := uri.Filename() doc := &Document{ - URI: uri, - Path: path, - Content: params.TextDocument.Text, - Ast: ParseAst(params.TextDocument.Text), + URI: uri, + Path: path, + Content: params.TextDocument.Text, + Ast: ParseAst(nil, params.TextDocument.Text), DiagnosticsCache: NewDiagnosticsCache(helmlsConfig), } s.documents[path] = doc return doc, nil } -func (s *DocumentStore) Close(uri lsp.DocumentURI) { - delete(s.documents, uri.Filename()) -} - func (s *DocumentStore) Get(docuri uri.URI) (*Document, bool) { - path, err := s.normalizePath(docuri) - if err != nil { - logger.Debug(err) - return nil, false - } + path := docuri.Filename() d, ok := s.documents[path] return d, ok } -func (s *DocumentStore) normalizePath(docuri uri.URI) (string, error) { - path, err := util.URIToPath(docuri) - if err != nil { - return "", errors.Wrapf(err, "unable to parse URI: %s", docuri) - } - return s.fs.Canonical(path), nil -} - // Document represents an opened file. type Document struct { URI lsp.DocumentURI @@ -84,12 +60,12 @@ type Document struct { Content string lines []string Ast *sitter.Tree - DiagnosticsCache DiagnosticsCache + DiagnosticsCache DiagnosticsCache } // ApplyChanges updates the content of the document from LSP textDocument/didChange events. func (d *Document) ApplyChanges(changes []lsp.TextDocumentContentChangeEvent) { - var content = []byte(d.Content) + content := []byte(d.Content) for _, change := range changes { start, end := util.PositionToIndex(change.Range.Start, content), util.PositionToIndex(change.Range.End, content) @@ -108,8 +84,7 @@ func (d *Document) ApplyChanges(changes []lsp.TextDocumentContentChangeEvent) { // WordAt returns the word found at the given location. func (d *Document) WordAt(pos lsp.Position) string { - - logger.Debug(pos) + logger.Debug(pos) line, ok := d.GetLine(int(pos.Line)) if !ok { @@ -118,187 +93,20 @@ func (d *Document) WordAt(pos lsp.Position) string { return util.WordAt(line, int(pos.Character)) } -func (d *Document) ValueAt(pos lsp.Position) string { - logger.Debug(pos) - - line, ok := d.GetLine(int(pos.Line)) - if !ok { - return "" - } - return util.ValueAt(line, int(pos.Character)) -} - -// ContentAtRange returns the document text at given range. -func (d *Document) ContentAtRange(rng lsp.Range) string { - return d.Content[rng.Start.Character:rng.End.Character] -} - // GetLine returns the line at the given index. func (d *Document) GetLine(index int) (string, bool) { - lines := d.GetLines() + lines := d.getLines() if index < 0 || index > len(lines) { return "", false } return lines[index], true } -// GetLines returns all the lines in the document. -func (d *Document) GetLines() []string { +// getLines returns all the lines in the document. +func (d *Document) getLines() []string { if d.lines == nil { // We keep \r on purpose, to avoid messing up position conversions. d.lines = strings.Split(d.Content, "\n") } return d.lines } - -// LookBehind returns the n characters before the given position, on the same line. -func (d *Document) LookBehind(pos lsp.Position, length int) string { - line, ok := d.GetLine(int(pos.Line)) - if !ok { - return "" - } - - charIdx := int(pos.Character) - if length > charIdx { - return line[0:charIdx] - } - return line[(charIdx - length):charIdx] -} - -// LookForward returns the n characters after the given position, on the same line. -func (d *Document) LookForward(pos lsp.Position, length int) string { - line, ok := d.GetLine(int(pos.Line)) - if !ok { - return "" - } - - lineLength := len(line) - charIdx := int(pos.Character) - if lineLength <= charIdx+length { - return line[charIdx:] - } - return line[charIdx:(charIdx + length)] -} - -// LinkFromRoot returns a Link to this document from the root of the given -// notebook. -//func (d *document) LinkFromRoot(nb *core.Notebook) (*documentLink, error) { - //href, err := nb.RelPath(d.Path) - //if err != nil { - //return nil, err - //} - //return &documentLink{ - //Href: href, - //RelativeToDir: nb.Path, - //}, nil -//} - -// DocumentLinkAt returns the internal or external link found in the document -// at the given position. -//func (d *Document) DocumentLinkAt(pos lsp.Position) (*documentLink, error) { - //links, err := d.DocumentLinks() - //if err != nil { - //return nil, err - //} - - //for _, link := range links { - //if positionInRange(d.Content, link.Range, pos) { - //return &link, nil - //} - //} - - //return nil, nil -//} - -// DocumentLinks returns all the internal and external links found in the -// document. -//func (d *document) DocumentLinks() ([]documentLink, error) { - //links := []documentLink{} - - //lines := d.GetLines() - //for lineIndex, line := range lines { - - //appendLink := func(href string, start, end int, hasTitle bool, isWikiLink bool) { - //if href == "" { - //return - //} - - //// Go regexes work with bytes, but the LSP client expects character indexes. - //start = strutil.ByteIndexToRuneIndex(line, start) - //end = strutil.ByteIndexToRuneIndex(line, end) - - //links = append(links, documentLink{ - //Href: href, - //RelativeToDir: filepath.Dir(d.Path), - //Range: protocol.Range{ - //Start: protocol.Position{ - //Line: protocol.UInteger(lineIndex), - //Character: protocol.UInteger(start), - //}, - //End: protocol.Position{ - //Line: protocol.UInteger(lineIndex), - //Character: protocol.UInteger(end), - //}, - //}, - //HasTitle: hasTitle, - //IsWikiLink: isWikiLink, - //}) - //} - - //for _, match := range markdownLinkRegex.FindAllStringSubmatchIndex(line, -1) { - //// Ignore embedded image, e.g. ![title](href.png) - //if match[0] > 0 && line[match[0]-1] == '!' { - //continue - //} - - //href := line[match[4]:match[5]] - //// Valid Markdown links are percent-encoded. - //if decodedHref, err := url.PathUnescape(href); err == nil { - //href = decodedHref - //} - //appendLink(href, match[0], match[1], false, false) - //} - - //for _, match := range wikiLinkRegex.FindAllStringSubmatchIndex(line, -1) { - //href := line[match[2]:match[3]] - //hasTitle := match[4] != -1 - //appendLink(href, match[0], match[1], hasTitle, true) - //} - //} - - //return links, nil -//} - -//// IsTagPosition returns whether the given caret position is inside a tag (YAML frontmatter, #hashtag, etc.). -//func (d *document) IsTagPosition(position protocol.Position, noteContentParser core.NoteContentParser) bool { - //lines := strutil.CopyList(d.GetLines()) - //lineIdx := int(position.Line) - //charIdx := int(position.Character) - //line := lines[lineIdx] - //// https://github.com/mickael-menu/zk/issues/144#issuecomment-1006108485 - //line = line[:charIdx] + "ZK_PLACEHOLDER" + line[charIdx:] - //lines[lineIdx] = line - //targetWord := strutil.WordAt(line, charIdx) - //if targetWord == "" { - //return false - //} - - //content := strings.Join(lines, "\n") - //note, err := noteContentParser.ParseNoteContent(content) - //if err != nil { - //return false - //} - //return strutil.Contains(note.Tags, targetWord) -//} - -//type documentLink struct { - //Href string - //RelativeToDir string - //Range lsp.Range - //// HasTitle indicates whether this link has a title information. For - //// example [[filename]] doesn't but [[filename|title]] does. - //HasTitle bool - //// IsWikiLink indicates whether this link is a [[WikiLink]] instead of a - //// regular Markdown link. - //IsWikiLink bool -//} diff --git a/internal/lsp/document_test.go b/internal/lsp/document_test.go new file mode 100644 index 00000000..ea0d84db --- /dev/null +++ b/internal/lsp/document_test.go @@ -0,0 +1,37 @@ +package lsp + +import ( + "testing" + + "github.com/mrjosh/helm-ls/internal/util" + "github.com/stretchr/testify/assert" + "go.lsp.dev/protocol" + "go.lsp.dev/uri" +) + +func TestDocumentStore(t *testing.T) { + assert := assert.New(t) + + sut := NewDocumentStore() + + assert.Empty(sut.GetAllDocs()) + + doc, ok := sut.Get(uri.File("test")) + assert.Nil(doc) + assert.False(ok) + + sut.DidOpen(protocol.DidOpenTextDocumentParams{ + TextDocument: protocol.TextDocumentItem{ + URI: uri.File("test.yaml"), + LanguageID: "helm", + Version: 0, + Text: "{{ .Values.test }}", + }, + }, util.DefaultConfig) + + assert.Len(sut.GetAllDocs(), 1) + + doc, ok = sut.Get(uri.File("test.yaml")) + assert.NotNil(doc) + assert.True(ok) +} diff --git a/internal/lsp/fs.go b/internal/lsp/fs.go deleted file mode 100644 index ca40a677..00000000 --- a/internal/lsp/fs.go +++ /dev/null @@ -1,36 +0,0 @@ -package lsp - -// FileStorage is a port providing read and write access to a file storage. -type FileStorage interface { - - // WorkingDir returns the current working directory. - WorkingDir() string - - // Abs makes the given file path absolute if needed, using the FileStorage - // working directory. - Abs(path string) (string, error) - - // Rel makes the given absolute file path relative to the current working - // directory. - Rel(path string) (string, error) - - // Canonical returns the canonical version of this path, resolving any - // symbolic link. - Canonical(path string) string - - // FileExists returns whether a file exists at the given file path. - FileExists(path string) (bool, error) - - // DirExists returns whether a directory exists at the given file path. - DirExists(path string) (bool, error) - - // IsDescendantOf returns whether the given path is dir or one of its descendants. - IsDescendantOf(dir string, path string) (bool, error) - - // Read returns the bytes content of the file at the given file path. - Read(path string) ([]byte, error) - - // Write creates or overwrite the content at the given file path, creating - // any intermediate directories if needed. - Write(path string, content []byte) error -} diff --git a/internal/lsp/lint.go b/internal/lsp/lint.go index db217bfc..7cb6378d 100644 --- a/internal/lsp/lint.go +++ b/internal/lsp/lint.go @@ -23,7 +23,7 @@ import ( var logger = log.GetLogger() -func NotifcationFromLint(ctx context.Context, conn jsonrpc2.Conn, chart *charts.Chart, doc *Document) (*jsonrpc2.Notification, error) { +func NotificationFromLint(ctx context.Context, conn jsonrpc2.Conn, chart *charts.Chart, doc *Document) (*jsonrpc2.Notification, error) { vals := chart.ValuesFiles.MainValuesFile.Values if chart.ValuesFiles.OverlayValuesFile != nil { vals = chartutil.CoalesceTables(chart.ValuesFiles.OverlayValuesFile.Values, chart.ValuesFiles.MainValuesFile.Values) diff --git a/internal/util/strings.go b/internal/util/strings.go index a7671838..03545cf6 100644 --- a/internal/util/strings.go +++ b/internal/util/strings.go @@ -1,20 +1,17 @@ package util import ( - "net/url" "regexp" - "runtime" "strings" "github.com/mrjosh/helm-ls/internal/log" "go.lsp.dev/protocol" - "go.lsp.dev/uri" ) -const FileURIScheme = "file://" - -var logger = log.GetLogger() -var wordRegex = regexp.MustCompile(`[^ \t\n\f\r,;\[\]\"\']+`) +var ( + logger = log.GetLogger() + wordRegex = regexp.MustCompile(`[^ \t\n\f\r,;\[\]\"\']+`) +) // BetweenStrings gets the substring between two strings. func BetweenStrings(value string, a string, b string) string { @@ -46,33 +43,9 @@ func AfterStrings(value string, a string) string { return value[adjustedPos:] } -func URIToPath(docuri uri.URI) (string, error) { - parsed, err := url.Parse(docuri.Filename()) - if err != nil { - return "", err - } - - logger.Printf("Go file uri %s, path: %s", parsed, parsed.Path) - if runtime.GOOS == "windows" { - - // In Windows "file:///c:/tmp/foo.md" is parsed to "/c:/tmp/foo.md". - // Strip the first character to get a valid path. - if strings.Contains(parsed.Path[1:], ":") { - // url.Parse() behaves differently with "file:///c:/..." and "file://c:/..." - return parsed.Path[1:], nil - } - - // if the windows drive is not included in Path it will be in Host - return parsed.Host + "/" + parsed.Path[1:], nil - } - - return parsed.Path, nil -} - // WordAt returns the word found at the given character position. // Credit https://github.com/aca/neuron-language-server/blob/450a7cff71c14e291ee85ff8a0614fa9d4dd5145/utils.go#L13 func WordAt(str string, index int) string { - wordIdxs := wordRegex.FindAllStringIndex(str, -1) for _, wordIdx := range wordIdxs { if wordIdx[0] <= index && index <= wordIdx[1] { @@ -83,26 +56,6 @@ func WordAt(str string, index int) string { return "" } -// ValueAt returns the value found at the given character position. -// It removes all content of the word after a "." right of the position. -func ValueAt(str string, index int) string { - - wordIdxs := wordRegex.FindAllStringIndex(str, -1) - for _, wordIdx := range wordIdxs { - if wordIdx[0] <= index && index+1 <= wordIdx[1] { - leftOfWord := str[wordIdx[0] : index+1] - rightOfWord := str[index+1 : wordIdx[1]] - rightOfWordEnd := strings.Index(rightOfWord, ".") - if rightOfWordEnd == -1 { - rightOfWordEnd = len(rightOfWord) - 1 - } - return leftOfWord + rightOfWord[0:rightOfWordEnd+1] - } - } - - return "" -} - func PositionToIndex(pos protocol.Position, content []byte) int { index := 0 for i := 0; i < int(pos.Line); i++ { diff --git a/internal/util/strings_test.go b/internal/util/strings_test.go index 907ff2d9..c7d86821 100644 --- a/internal/util/strings_test.go +++ b/internal/util/strings_test.go @@ -1,62 +1 @@ package util - -import ( - "testing" -) - -func TestValueAtRemovesValueAfterDot(t *testing.T) { - - input := "test.go" - expected := "test." - - result := ValueAt(input, 2) - - if expected != result { - t.Errorf("Expected %s but got %s.", expected, result) - } -} - -func TestValueAtKeepsValueAfterDot(t *testing.T) { - - input := "test.go" - expected := "test.go" - - result := ValueAt(input, 5) - - if expected != result { - t.Errorf("Expected %s but got %s.", expected, result) - } -} - -func TestValueAtWithMoreContext(t *testing.T) { - - input := "test $.Values.test.go ____--------" - expected := "$.Values.test." - - result := ValueAt(input, 15) - - if expected != result { - t.Errorf("Expected %s but got %s.", expected, result) - } -} - -func TestValueAtEmptyString(t *testing.T) { - - input := "test $.Values. ____--------" - expected := "$.Values." - - result := ValueAt(input, 13) - - if expected != result { - t.Errorf("Expected %s but got %s.", expected, result) - } - - input = " image: \"{{ $.Values.global }}:{{ .Values.image.pu | default .Chart.Maintainers }}\"" - expected = "" - - result = ValueAt(input, 89) - - if expected != result { - t.Errorf("Expected %s but got %s.", expected, result) - } -} From 534adde6d77f7a0375cfcd83e6db7599588bb227 Mon Sep 17 00:00:00 2001 From: qvalentin Date: Sat, 3 Feb 2024 21:53:59 +0100 Subject: [PATCH 06/35] fix(yamlls): use kubernetes schema only for templates --- README.md | 28 ++++++++++++++-------------- internal/util/config.go | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index ed3bb784..43a71f60 100644 --- a/README.md +++ b/README.md @@ -18,21 +18,21 @@ Helm-ls is a [helm](https://github.com/helm/helm) language server protocol [LSP] * [Demo](#demo) * [Getting Started](#getting-started) - * [Installation with a package manager](#installation-with-a-package-manager) - * [Download](#download) - * [Make it executable](#make-it-executable) - * [Integration with yaml-language-server](#integration-with-yaml-language-server) + * [Installation with a package manager](#installation-with-a-package-manager) + * [Download](#download) + * [Make it executable](#make-it-executable) + * [Integration with yaml-language-server](#integration-with-yaml-language-server) * [Configuration options](#configuration-options) - * [General](#general) - * [Values Files](#values-files) - * [yaml-language-server config](#yaml-language-server-config) - * [Default Configuration](#default-configuration) + * [General](#general) + * [Values Files](#values-files) + * [yaml-language-server config](#yaml-language-server-config) + * [Default Configuration](#default-configuration) * [Editor Config examples](#editor-config-examples) - * [Neovim (using nvim-lspconfig)](#neovim-using-nvim-lspconfig) - * [Vim Helm Plugin](#vim-helm-plugin) - * [Setup laguage server](#setup-laguage-server) - * [VSCode](#vscode) - * [Emacs eglot setup](#emacs-eglot-setup) + * [Neovim (using nvim-lspconfig)](#neovim-using-nvim-lspconfig) + * [Vim Helm Plugin](#vim-helm-plugin) + * [Setup laguage server](#setup-laguage-server) + * [VSCode](#vscode) + * [Emacs eglot setup](#emacs-eglot-setup) * [Contributing](#contributing) * [License](#license) @@ -136,7 +136,7 @@ settings = { path = "yaml-language-server", config = { schemas = { - kubernetes = "**", + kubernetes = "templates/**", }, completion = true, hover = true, diff --git a/internal/util/config.go b/internal/util/config.go index 879ab42c..55972196 100644 --- a/internal/util/config.go +++ b/internal/util/config.go @@ -47,7 +47,7 @@ type YamllsSettings struct { } var DefaultYamllsSettings = YamllsSettings{ - Schemas: map[string]string{"kubernetes": "**"}, + Schemas: map[string]string{"kubernetes": "templates/**"}, Completion: true, Hover: true, } From 7f32902938a3ab02da6e862c168632ce96462782 Mon Sep 17 00:00:00 2001 From: qvalentin Date: Sun, 4 Feb 2024 17:28:46 +0100 Subject: [PATCH 07/35] fix: don't always use debug log level --- internal/log/log.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/log/log.go b/internal/log/log.go index 335c9f7a..b90407f6 100644 --- a/internal/log/log.go +++ b/internal/log/log.go @@ -30,6 +30,5 @@ func GetLogger() logger { func createLogger() logger { logger := logrus.New() logger.SetFormatter(&logrus.JSONFormatter{}) - logger.SetLevel(logrus.DebugLevel) return logger } From 19d8135251afad4e5fde483985fa7ba1da18d9b9 Mon Sep 17 00:00:00 2001 From: valentin <36446499+qvalentin@users.noreply.github.com> Date: Mon, 26 Feb 2024 18:11:21 +0100 Subject: [PATCH 08/35] fix(yamlls): trim some templates better (#60) * fix(yamlls): trim some templates better * test(yamlls): add some trimTemplate tests * fix(yamlls): use copy of ast to avoid multithreading issues * fix(ci): run tests on pr always * fix(yamlls): trim single line ifs completely * fix(yamlls): ignore Block scalars with more-indented .. --- .github/workflows/tests.yml | 1 - internal/adapter/yamlls/diagnostics.go | 12 ++- internal/adapter/yamlls/trimTemplate.go | 22 +++-- internal/adapter/yamlls/trimTemplate_test.go | 96 +++++++++++++++++-- internal/tree-sitter/gotemplate/node-types.go | 1 + 5 files changed, 113 insertions(+), 19 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7430185a..864310ff 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -5,7 +5,6 @@ on: branches: - "master" pull_request: - types: [synchronize] jobs: diff --git a/internal/adapter/yamlls/diagnostics.go b/internal/adapter/yamlls/diagnostics.go index 0bf2b07b..29af3a1d 100644 --- a/internal/adapter/yamlls/diagnostics.go +++ b/internal/adapter/yamlls/diagnostics.go @@ -21,7 +21,7 @@ func (yamllsConnector *Connector) handleDiagnostics(req jsonrpc2.Request, client logger.Println("Error handling diagnostic. Could not get document: " + params.URI.Filename()) } - doc.DiagnosticsCache.SetYamlDiagnostics(filterDiagnostics(params.Diagnostics, doc.Ast, doc.Content)) + doc.DiagnosticsCache.SetYamlDiagnostics(filterDiagnostics(params.Diagnostics, doc.Ast.Copy(), doc.Content)) if doc.DiagnosticsCache.ShouldShowDiagnosticsOnNewYamlDiagnostics() { logger.Debug("Publishing yamlls diagnostics") params.Diagnostics = doc.DiagnosticsCache.GetMergedDiagnostics() @@ -50,7 +50,7 @@ func filterDiagnostics(diagnostics []lsp.Diagnostic, ast *sitter.Tree, content s } func diagnisticIsRelevant(diagnostic lsp.Diagnostic, node *sitter.Node) bool { - logger.Debug("Diagnostic", diagnostic.Message) + logger.Debug("Checking if diagnostic is relevant", diagnostic.Message) switch diagnostic.Message { case "Map keys must be unique": return !lsplocal.IsInElseBranch(node) @@ -60,9 +60,15 @@ func diagnisticIsRelevant(diagnostic lsp.Diagnostic, node *sitter.Node) bool { "A block sequence may not be used as an implicit map key": // TODO: could add a check if is is caused by includes return false + case "Block scalars with more-indented leading empty lines must use an explicit indentation indicator": + return false + // TODO: check if this is a false positive, probably requires parsing the yaml with tree-sitter injections + // smtp-password: | + // {{- if not .Values.existingSecret }} + // test: dsa + // {{- end }} default: return true } - } diff --git a/internal/adapter/yamlls/trimTemplate.go b/internal/adapter/yamlls/trimTemplate.go index 1827c4e0..9f5beb47 100644 --- a/internal/adapter/yamlls/trimTemplate.go +++ b/internal/adapter/yamlls/trimTemplate.go @@ -6,13 +6,13 @@ import ( ) func trimTemplateForYamllsFromAst(ast *sitter.Tree, text string) string { - var result = []byte(text) + result := []byte(text) prettyPrintNode(ast.RootNode(), []byte(text), result) return string(result) } func prettyPrintNode(node *sitter.Node, previous []byte, result []byte) { - var childCount = node.ChildCount() + childCount := node.ChildCount() switch node.Type() { case gotemplate.NodeTypeIfAction: @@ -23,7 +23,7 @@ func prettyPrintNode(node *sitter.Node, previous []byte, result []byte) { earaseTemplate(node, previous, result) case gotemplate.NodeTypeFunctionCall: trimFunctionCall(node, previous, result) - case gotemplate.NodeTypeComment, gotemplate.NodeTypeVariableDefinition: + case gotemplate.NodeTypeComment, gotemplate.NodeTypeVariableDefinition, gotemplate.NodeTypeAssignment: earaseTemplateAndSiblings(node, previous, result) default: for i := 0; i < int(childCount); i++ { @@ -37,6 +37,7 @@ func trimAction(childCount uint32, node *sitter.Node, previous []byte, result [] child := node.Child(i) switch child.Type() { case + gotemplate.NodeTypeAssignment, gotemplate.NodeTypeIf, gotemplate.NodeTypeSelectorExpression, gotemplate.NodeTypeElse, @@ -52,6 +53,7 @@ func trimAction(childCount uint32, node *sitter.Node, previous []byte, result [] gotemplate.NodeTypeInterpretedStringLiteral, gotemplate.NodeTypeBlock, gotemplate.NodeTypeVariableDefinition, + gotemplate.NodeTypeVariable, gotemplate.NodeTypeRangeVariableDefinition: earaseTemplate(child, previous, result) default: @@ -61,6 +63,11 @@ func trimAction(childCount uint32, node *sitter.Node, previous []byte, result [] } func trimIfAction(node *sitter.Node, previous []byte, result []byte) { + if node.StartPoint().Row == node.EndPoint().Row { + earaseTemplate(node, previous, result) + return + } + curser := sitter.NewTreeCursor(node) curser.GoToFirstChild() for curser.GoToNextSibling() { @@ -94,7 +101,7 @@ func trimFunctionCall(node *sitter.Node, previous []byte, result []byte) { func earaseTemplateAndSiblings(node *sitter.Node, previous []byte, result []byte) { earaseTemplate(node, previous, result) - var prevSibling, nextSibling = node.PrevSibling(), node.NextSibling() + prevSibling, nextSibling := node.PrevSibling(), node.NextSibling() if prevSibling != nil { earaseTemplate(prevSibling, previous, result) } @@ -107,8 +114,11 @@ func earaseTemplate(node *sitter.Node, previous []byte, result []byte) { if node == nil { return } - logger.Println("Content that is earased", node.Content(previous)) + logger.Debug("Content that is erased", node.Content(previous)) for i := range []byte(node.Content(previous)) { - result[int(node.StartByte())+i] = byte(' ') + index := int(node.StartByte()) + i + if result[index] != '\n' && result[index] != '\r' { + result[index] = byte(' ') + } } } diff --git a/internal/adapter/yamlls/trimTemplate_test.go b/internal/adapter/yamlls/trimTemplate_test.go index fd35c77a..cc86f1bc 100644 --- a/internal/adapter/yamlls/trimTemplate_test.go +++ b/internal/adapter/yamlls/trimTemplate_test.go @@ -1,9 +1,11 @@ package yamlls import ( + "fmt" "testing" lsplocal "github.com/mrjosh/helm-ls/internal/lsp" + "github.com/stretchr/testify/assert" ) type TrimTemplateTestData struct { @@ -240,9 +242,19 @@ metadata: `, }, { - // todo: Handle this case better documentText: `{{ if }}{{- end -}}`, - trimmedText: ` }} `, + trimmedText: ` `, + }, + { + // todo: Handle this case better + documentText: ` +{{ if }} + +{{- end -}}`, + trimmedText: ` + }} + + `, }, { documentText: `{{- $shards := $.Values.shards | int }}`, @@ -296,6 +308,78 @@ data: `, }, + { + documentText: ` +{{- /* +Copyright Some Company, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} +`, + trimmedText: ` + + + + +`, + }, + { + documentText: ` +{{- $namespaces := list .Release.Namespace }} +{{- $namespaces = .Values.controller.workflowNamespaces }} +`, + trimmedText: ` + + +`, + }, + { + documentText: ` +{{- range $namespaces }} +{{- end }} +`, + trimmedText: ` + + +`, + }, + { + documentText: ` +list: + - value: {{ join "," .Values.initialCluster | quote }} + - name: some +`, + trimmedText: ` +list: + - value: {{ join "," .Values.initialCluster | quote }} + - name: some +`, + }, + { + documentText: ` + - name: ELASTICSEARCH_NODE_ROLES + value: {{ join "," $roles | quote }} + - name: ELASTICSEARCH_TRANSPORT_PORT_NUMBER + value: {{ .Values.containerPorts.transport | quote }} + - name: ELASTICSEARCH_HTTP_PORT_NUMBER + value: {{ .Values.containerPorts.restAPI | quote }} +`, + trimmedText: ` + - name: ELASTICSEARCH_NODE_ROLES + value: {{ join "," $roles | quote }} + - name: ELASTICSEARCH_TRANSPORT_PORT_NUMBER + value: {{ .Values.containerPorts.transport | quote }} + - name: ELASTICSEARCH_HTTP_PORT_NUMBER + value: {{ .Values.containerPorts.restAPI | quote }} +`, + }, + { + documentText: ` +apiVersion: {{ if .Values.useStatefulSet }}{{ include "common.capabilities.statefulset.apiVersion" . }}{{- else }}{{ include "common.capabilities.deployment.apiVersion" . }}{{- end }} + `, + trimmedText: ` +apiVersion: + `, + }, } func TestTrimTemplate(t *testing.T) { @@ -312,11 +396,5 @@ func testTrimTemplateWithTestData(t *testing.T, testData TrimTemplateTestData) { trimmed := trimTemplateForYamllsFromAst(doc.Ast, testData.documentText) - result := trimmed == testData.trimmedText - - if !result { - t.Errorf("Trimmed templated was not as expected but was %s ", trimmed) - } else { - t.Log("Trimmed templated was as expected") - } + assert.Equal(t, testData.trimmedText, trimmed, fmt.Sprintf("AST was: %v", doc.Ast.RootNode().String())) } diff --git a/internal/tree-sitter/gotemplate/node-types.go b/internal/tree-sitter/gotemplate/node-types.go index dddaf9f1..b96e645d 100644 --- a/internal/tree-sitter/gotemplate/node-types.go +++ b/internal/tree-sitter/gotemplate/node-types.go @@ -1,6 +1,7 @@ package gotemplate const ( + NodeTypeAssignment = "assignment" NodeTypeBlock = "block" NodeTypeBlockAction = "block_action" NodeTypeChainedPipeline = "chained_pipeline" From 75e696f48d1e8f6493c29f3313eb04e9ba9e88c7 Mon Sep 17 00:00:00 2001 From: qvalentin <36446499+qvalentin@users.noreply.github.com> Date: Sat, 9 Mar 2024 21:37:46 +0100 Subject: [PATCH 09/35] chore(deps): update dependencies and go to 1.21 (#64) --- .github/workflows/artifacts.yml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/tests.yml | 4 +- Makefile | 2 +- go.mod | 54 ++-- go.sum | 453 +++++--------------------------- 6 files changed, 99 insertions(+), 418 deletions(-) diff --git a/.github/workflows/artifacts.yml b/.github/workflows/artifacts.yml index d868b5e6..72c2050a 100644 --- a/.github/workflows/artifacts.yml +++ b/.github/workflows/artifacts.yml @@ -18,7 +18,7 @@ jobs: name: artifacts strategy: matrix: - go-version: [1.19.1] + go-version: [1.21.5] os: [ubuntu-latest] runs-on: ${{ matrix.os }} steps: diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index d895464f..4b218166 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -12,7 +12,7 @@ jobs: name: lint strategy: matrix: - go-version: [1.19.1] + go-version: [1.21.5] os: [ubuntu-latest] runs-on: ${{ matrix.os }} steps: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 864310ff..29083558 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,7 +12,7 @@ jobs: name: tests strategy: matrix: - go-version: [1.19.1] + go-version: [1.21.5] os: [ubuntu-latest, macos-latest] runs-on: ${{ matrix.os }} steps: @@ -32,7 +32,7 @@ jobs: name: tests strategy: matrix: - go-version: [1.19.1] + go-version: [1.21.5] os: [windows-2019] runs-on: ${{ matrix.os }} steps: diff --git a/Makefile b/Makefile index 2f3f03fa..6cdf201d 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ export BIN=$(ROOT)/bin export GOBIN?=$(BIN) export GO=$(shell which go) export PACKAGE_NAME=github.com/mrjosh/helm-ls -export GOLANG_CROSS_VERSION=v1.20.6 +export GOLANG_CROSS_VERSION=v1.21.5 export CGO_ENABLED=1 $(eval GIT_COMMIT=$(shell git rev-parse --short HEAD)) diff --git a/go.mod b/go.mod index 97171a93..cbf7f193 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/mrjosh/helm-ls -go 1.19 +go 1.21 require ( github.com/BurntSushi/toml v1.3.2 @@ -11,38 +11,37 @@ require ( github.com/gobwas/glob v0.2.3 github.com/mitchellh/copystructure v1.2.0 github.com/pkg/errors v0.9.1 - github.com/sirupsen/logrus v1.9.2 - github.com/smacker/go-tree-sitter v0.0.0-20230501083651-a7d92773b3aa - github.com/spf13/cobra v1.7.0 - github.com/stretchr/testify v1.8.4 + github.com/sirupsen/logrus v1.9.3 + github.com/smacker/go-tree-sitter v0.0.0-20240214120134-1f283e24f560 + github.com/spf13/cobra v1.8.0 + github.com/stretchr/testify v1.9.0 github.com/xeipuuv/gojsonschema v1.2.0 go.lsp.dev/jsonrpc2 v0.10.0 go.lsp.dev/protocol v0.12.0 go.lsp.dev/uri v0.3.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/apiextensions-apiserver v0.27.3 - k8s.io/apimachinery v0.27.3 - k8s.io/apiserver v0.27.3 - k8s.io/client-go v0.27.3 + k8s.io/apiextensions-apiserver v0.29.2 + k8s.io/apimachinery v0.29.2 + k8s.io/apiserver v0.29.2 + k8s.io/client-go v0.29.2 k8s.io/helm v2.17.0+incompatible - sigs.k8s.io/yaml v1.3.0 + sigs.k8s.io/yaml v1.4.0 ) require ( github.com/Masterminds/goutils v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/emicklei/go-restful/v3 v3.9.0 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/frankban/quicktest v1.14.4 // indirect - github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/logr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect - github.com/go-openapi/jsonreference v0.20.1 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/go-cmp v0.5.9 // indirect - github.com/google/gofuzz v1.1.0 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.3.0 // indirect github.com/huandu/xstrings v1.3.3 // indirect github.com/imdario/mergo v0.3.11 // indirect @@ -63,23 +62,24 @@ require ( github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect go.lsp.dev/pkg v0.0.0-20210717090340-384b27a52fb2 // indirect - go.uber.org/atomic v1.9.0 // indirect - go.uber.org/multierr v1.8.0 // indirect + go.uber.org/atomic v1.10.0 // indirect + go.uber.org/goleak v1.2.1 // indirect + go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.24.0 // indirect golang.org/x/crypto v0.17.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect + golang.org/x/net v0.19.0 // indirect + golang.org/x/oauth2 v0.10.0 // indirect golang.org/x/sys v0.15.0 // indirect golang.org/x/term v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect + golang.org/x/time v0.3.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.28.1 // indirect + google.golang.org/protobuf v1.31.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - k8s.io/api v0.27.3 // indirect - k8s.io/klog/v2 v2.90.1 // indirect - k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect - k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect + k8s.io/api v0.29.2 // indirect + k8s.io/klog/v2 v2.110.1 // indirect + k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect + k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect ) diff --git a/go.sum b/go.sum index 0b5c0334..ec6baa01 100644 --- a/go.sum +++ b/go.sum @@ -1,40 +1,5 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= @@ -45,109 +10,53 @@ github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBa github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= -github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= -github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= -github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -156,12 +65,8 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -184,15 +89,18 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= +github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= +github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= +github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/segmentio/asm v1.1.3 h1:WM03sfUOENvvKexOLp+pCqgb/WDjsi7EK8gIsICtzhc= github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg= @@ -200,41 +108,37 @@ github.com/segmentio/encoding v0.3.4 h1:WM4IBnxH8B9TakiM2QD5LyNl9JSndh88QbHqVC+P github.com/segmentio/encoding v0.3.4/go.mod h1:n0JeuIqEQrQoPDGsjo8UNd1iA0U8d8+oHAA4E3G3OxM= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= -github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smacker/go-tree-sitter v0.0.0-20230501083651-a7d92773b3aa h1:exZ0FwfhblsYbgfqYH+W/3sFye821WD02NjBmc+ENhE= -github.com/smacker/go-tree-sitter v0.0.0-20230501083651-a7d92773b3aa/go.mod h1:q99oHDsbP0xRwmn7Vmob8gbSMNyvJ83OauXPSuHQuKE= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smacker/go-tree-sitter v0.0.0-20240214120134-1f283e24f560 h1:i1kygzBpj4bIXk+ztDJCnmywZrbpsRJG1QCMrMI0P3o= +github.com/smacker/go-tree-sitter v0.0.0-20240214120134-1f283e24f560/go.mod h1:q99oHDsbP0xRwmn7Vmob8gbSMNyvJ83OauXPSuHQuKE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.lsp.dev/jsonrpc2 v0.10.0 h1:Pr/YcXJoEOTMc/b6OTmcR1DPJ3mSWl/SWiU1Cct6VmI= @@ -245,139 +149,46 @@ go.lsp.dev/protocol v0.12.0 h1:tNprUI9klQW5FAFVM4Sa+AbPFuVQByWhP1ttNUAjIWg= go.lsp.dev/protocol v0.12.0/go.mod h1:Qb11/HgZQ72qQbeyPfJbu3hZBH23s1sr4st8czGeDMQ= go.lsp.dev/uri v0.3.0 h1:KcZJmh6nFIBeJzTugn5JTU6OOyG0lDOo3R9KwTxTYbo= go.lsp.dev/uri v0.3.0/go.mod h1:P5sbO1IQR+qySTWOCnhnK7phBx+W3zbLqSMDJNTw88I= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= -go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= -go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 h1:OSnWWcOd/CtWQC2cYSBgbTSJv3ciqd8r54ySIW2y3RE= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= +golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -389,153 +200,35 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= +golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -544,41 +237,29 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.27.3 h1:yR6oQXXnUEBWEWcvPWS0jQL575KoAboQPfJAuKNrw5Y= -k8s.io/api v0.27.3/go.mod h1:C4BNvZnQOF7JA/0Xed2S+aUyJSfTGkGFxLXz9MnpIpg= -k8s.io/apiextensions-apiserver v0.27.3 h1:xAwC1iYabi+TDfpRhxh4Eapl14Hs2OftM2DN5MpgKX4= -k8s.io/apiextensions-apiserver v0.27.3/go.mod h1:BH3wJ5NsB9XE1w+R6SSVpKmYNyIiyIz9xAmBl8Mb+84= -k8s.io/apimachinery v0.27.3 h1:Ubye8oBufD04l9QnNtW05idcOe9Z3GQN8+7PqmuVcUM= -k8s.io/apimachinery v0.27.3/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= -k8s.io/apiserver v0.27.3 h1:AxLvq9JYtveYWK+D/Dz/uoPCfz8JC9asR5z7+I/bbQ4= -k8s.io/apiserver v0.27.3/go.mod h1:Y61+EaBMVWUBJtxD5//cZ48cHZbQD+yIyV/4iEBhhNA= -k8s.io/client-go v0.27.3 h1:7dnEGHZEJld3lYwxvLl7WoehK6lAq7GvgjxpA3nv1E8= -k8s.io/client-go v0.27.3/go.mod h1:2MBEKuTo6V1lbKy3z1euEGnhPfGZLKTS9tiJ2xodM48= +k8s.io/api v0.29.2 h1:hBC7B9+MU+ptchxEqTNW2DkUosJpp1P+Wn6YncZ474A= +k8s.io/api v0.29.2/go.mod h1:sdIaaKuU7P44aoyyLlikSLayT6Vb7bvJNCX105xZXY0= +k8s.io/apiextensions-apiserver v0.29.2 h1:UK3xB5lOWSnhaCk0RFZ0LUacPZz9RY4wi/yt2Iu+btg= +k8s.io/apiextensions-apiserver v0.29.2/go.mod h1:aLfYjpA5p3OwtqNXQFkhJ56TB+spV8Gc4wfMhUA3/b8= +k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8= +k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU= +k8s.io/apiserver v0.29.2 h1:+Z9S0dSNr+CjnVXQePG8TcBWHr3Q7BmAr7NraHvsMiQ= +k8s.io/apiserver v0.29.2/go.mod h1:B0LieKVoyU7ykQvPFm7XSdIHaCHSzCzQWPFa5bqbeMQ= +k8s.io/client-go v0.29.2 h1:FEg85el1TeZp+/vYJM7hkDlSTFZ+c5nnK44DJ4FyoRg= +k8s.io/client-go v0.29.2/go.mod h1:knlvFZE58VpqbQpJNbCbctTVXcd35mMyAAwBdpt4jrA= k8s.io/helm v2.17.0+incompatible h1:Bpn6o1wKLYqKM3+Osh8e+1/K2g/GsQJ4F4yNF2+deao= k8s.io/helm v2.17.0+incompatible/go.mod h1:LZzlS4LQBHfciFOurYBFkCMTaZ0D1l+p0teMg7TSULI= -k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= -k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= -k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= -k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY= -k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= +k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= +k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= +k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= From 3a39e1961faab1379e3463e63c84f9ef50c9e009 Mon Sep 17 00:00:00 2001 From: qvalentin <36446499+qvalentin@users.noreply.github.com> Date: Sun, 10 Mar 2024 11:09:02 +0100 Subject: [PATCH 10/35] feat(yamlls): keep only text nodes when trimming yaml (#63) * feat(yamlls): keep only text nodes when trimming yaml and start testing it * test(yamlls): use bitnami charts for integration test * fix(concurrency): use sync.map --- .github/workflows/tests.yml | 29 +- .gitmodules | 3 + Makefile | 9 + internal/adapter/yamlls/diagnostics.go | 20 +- .../yamlls/diagnostics_integration_test.go | 164 +++++++ internal/adapter/yamlls/documentSync.go | 17 +- internal/adapter/yamlls/trimTemplate.go | 124 ------ internal/adapter/yamlls/trimTemplate_test.go | 400 ------------------ internal/lsp/document.go | 21 +- internal/lsp/yaml_ast.go | 62 +++ internal/lsp/yaml_ast_test.go | 162 +++++++ internal/util/config.go | 14 +- testdata/charts | 1 + 13 files changed, 448 insertions(+), 578 deletions(-) create mode 100644 .gitmodules create mode 100644 internal/adapter/yamlls/diagnostics_integration_test.go delete mode 100644 internal/adapter/yamlls/trimTemplate.go delete mode 100644 internal/adapter/yamlls/trimTemplate_test.go create mode 100644 internal/lsp/yaml_ast.go create mode 100644 internal/lsp/yaml_ast_test.go create mode 160000 testdata/charts diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 29083558..580bd33a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: go-version: [1.21.5] - os: [ubuntu-latest, macos-latest] + os: [ubuntu-latest, macos-latest, windows-2022] runs-on: ${{ matrix.os }} steps: @@ -27,30 +27,3 @@ jobs: - name: Run tests run: make test - - tests-windows: - name: tests - strategy: - matrix: - go-version: [1.21.5] - os: [windows-2019] - runs-on: ${{ matrix.os }} - steps: - - - name: Install Go - uses: actions/setup-go@v4 - with: - go-version: ${{ matrix.go-version }} - - - name: Set up MinGW - uses: egor-tensin/setup-mingw@v2 - with: - version: 12.2.0 - - - name: Checkout code - uses: actions/checkout@v4 - - - name: Run tests - run: make test - env: - CC: "cc" diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..83e55fdc --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "testdata/charts"] + path = testdata/charts + url = https://github.com/qvalentin/charts.git diff --git a/Makefile b/Makefile index 6cdf201d..f8a23ccc 100644 --- a/Makefile +++ b/Makefile @@ -55,7 +55,16 @@ install-metalinter: @$(GO) get -v github.com/golangci/golangci-lint/cmd/golangci-lint@v1.53.2 @$(GO) install -v github.com/golangci/golangci-lint/cmd/golangci-lint@v1.53.2 +install-yamlls: + npm install --global yaml-language-server + +integration-test-deps: + @YAMLLS_BIN=$$(command -v yaml-language-server) || { echo "yaml-language-server command not found! Installing..." && $(MAKE) install-yamlls; }; + git submodule init + git submodule update --depth 1 + test: + $(MAKE) integration-test-deps @$(GO) test ./... -v -race coverage: diff --git a/internal/adapter/yamlls/diagnostics.go b/internal/adapter/yamlls/diagnostics.go index 29af3a1d..01bb695e 100644 --- a/internal/adapter/yamlls/diagnostics.go +++ b/internal/adapter/yamlls/diagnostics.go @@ -54,20 +54,26 @@ func diagnisticIsRelevant(diagnostic lsp.Diagnostic, node *sitter.Node) bool { switch diagnostic.Message { case "Map keys must be unique": return !lsplocal.IsInElseBranch(node) - case "All mapping items must start at the same column", - "Implicit map keys need to be followed by map values", - "Implicit keys need to be on a single line", - "A block sequence may not be used as an implicit map key": - // TODO: could add a check if is is caused by includes + case "All mapping items must start at the same column": + // unknown what exactly this is, only causes one error in bitnami/charts return false + case "Implicit map keys need to be followed by map values", "A block sequence may not be used as an implicit map key", "Implicit keys need to be on a single line": + // still breaks with + // params: + // {{- range $key, $value := .params }} + // {{ $key }}: + // {{- range $value }} + // - {{ . | quote }} + // {{- end }} + // {{- end }} + return false && !lsplocal.IsInElseBranch(node) case "Block scalars with more-indented leading empty lines must use an explicit indentation indicator": - return false // TODO: check if this is a false positive, probably requires parsing the yaml with tree-sitter injections // smtp-password: | // {{- if not .Values.existingSecret }} // test: dsa // {{- end }} - + return false default: return true } diff --git a/internal/adapter/yamlls/diagnostics_integration_test.go b/internal/adapter/yamlls/diagnostics_integration_test.go new file mode 100644 index 00000000..3426d82e --- /dev/null +++ b/internal/adapter/yamlls/diagnostics_integration_test.go @@ -0,0 +1,164 @@ +package yamlls + +import ( + "encoding/json" + "fmt" + "log" + "os" + "path/filepath" + "regexp" + "strings" + "testing" + "time" + + lsplocal "github.com/mrjosh/helm-ls/internal/lsp" + "github.com/mrjosh/helm-ls/internal/util" + "github.com/stretchr/testify/assert" + "go.lsp.dev/jsonrpc2" + lsp "go.lsp.dev/protocol" + "go.lsp.dev/uri" +) + +// must be relative to this file +var TEST_DATA_DIR = "../../../testdata/charts/bitnami/" + +type jsonRpcDiagnostics struct { + Params lsp.PublishDiagnosticsParams `json:"params"` + Jsonrpc string `json:"jsonrpc"` + Method string `json:"method"` +} + +type readWriteCloseMock struct { + diagnosticsChan chan lsp.PublishDiagnosticsParams +} + +func (proc readWriteCloseMock) Read(p []byte) (int, error) { + return 1, nil +} + +func (proc readWriteCloseMock) Write(p []byte) (int, error) { + if strings.HasPrefix(string(p), "Content-Length: ") { + return 1, nil + } + var diagnostics jsonRpcDiagnostics + json.NewDecoder(strings.NewReader(string(p))).Decode(&diagnostics) + + proc.diagnosticsChan <- diagnostics.Params + return 1, nil +} + +func (proc readWriteCloseMock) Close() error { + return nil +} + +func readTestFiles(dir string, channel chan<- lsp.DidOpenTextDocumentParams, doneChan chan<- int) { + libRegEx, e := regexp.Compile(".*(/|\\\\)templates(/|\\\\).*\\.ya?ml") + if e != nil { + log.Fatal(e) + return + } + if _, err := os.Stat(dir); os.IsNotExist(err) { + log.Fatal(err) + return + } + + count := 0 + filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error { + if d.Type().IsRegular() && libRegEx.MatchString(path) { + contentBytes, _ := os.ReadFile(path) + count++ + channel <- lsp.DidOpenTextDocumentParams{ + TextDocument: lsp.TextDocumentItem{ + URI: uri.File(path), + LanguageID: "", + Version: 0, + Text: string(contentBytes), + }, + } + } + return nil + }) + doneChan <- count +} + +func sendTestFilesToYamlls(documents *lsplocal.DocumentStore, yamllsConnector *Connector, + doneReadingFilesChan <-chan int, + doneSendingFilesChan chan<- int, + filesChan <-chan lsp.DidOpenTextDocumentParams, +) { + ownCount := 0 + for { + select { + case d := <-filesChan: + documents.DidOpen(d, util.DefaultConfig) + tree := lsplocal.ParseAst(nil, d.TextDocument.Text) + yamllsConnector.DocumentDidOpen(tree, d) + ownCount++ + case count := <-doneReadingFilesChan: + if count != ownCount { + log.Fatal("Count mismatch: ", count, " != ", ownCount) + } + doneSendingFilesChan <- count + return + } + } +} + +func TestYamllsDiagnosticsIntegration(t *testing.T) { + diagnosticsChan := make(chan lsp.PublishDiagnosticsParams) + doneReadingFilesChan := make(chan int) + doneSendingFilesChan := make(chan int) + + dir := t.TempDir() + documents := lsplocal.NewDocumentStore() + con := jsonrpc2.NewConn(jsonrpc2.NewStream(readWriteCloseMock{diagnosticsChan})) + config := util.DefaultConfig.YamllsConfiguration + + yamllsSettings := util.DefaultYamllsSettings + // disabling yamlls schema store improves performance and + // removes all schema diagnostics that are not caused by the yaml trimming + yamllsSettings.Schemas = make(map[string]string) + yamllsSettings.YamllsSchemaStoreSettings = util.YamllsSchemaStoreSettings{ + Enable: false, + } + config.YamllsSettings = yamllsSettings + yamllsConnector := NewConnector(config, con, documents) + + if yamllsConnector.Conn == nil { + t.Fatal("Could not connect to yaml-language-server") + } + + yamllsConnector.CallInitialize(uri.File(dir)) + + didOpenChan := make(chan lsp.DidOpenTextDocumentParams) + go readTestFiles(TEST_DATA_DIR, didOpenChan, doneReadingFilesChan) + go sendTestFilesToYamlls(documents, + yamllsConnector, doneReadingFilesChan, doneSendingFilesChan, didOpenChan) + + sentCount, diagnosticsCount := 0, 0 + receivedDiagnostics := make(map[uri.URI]lsp.PublishDiagnosticsParams) + + afterCh := time.After(600 * time.Second) + for { + if sentCount != 0 && len(receivedDiagnostics) == sentCount { + fmt.Println("All files checked") + break + } + select { + case d := <-diagnosticsChan: + receivedDiagnostics[d.URI] = d + if len(d.Diagnostics) > 0 { + diagnosticsCount++ + fmt.Printf("Got diagnostic in %s diagnostics: %v \n", d.URI.Filename(), d.Diagnostics) + } + case <-afterCh: + t.Fatal("Timed out waiting for diagnostics") + case count := <-doneSendingFilesChan: + sentCount = count + } + } + + fmt.Printf("Checked %d files, found %d diagnostics\n", sentCount, diagnosticsCount) + assert.LessOrEqual(t, diagnosticsCount, 23) + assert.Equal(t, 2368, sentCount, "Count of files in test data not equal to actual count") +} diff --git a/internal/adapter/yamlls/documentSync.go b/internal/adapter/yamlls/documentSync.go index c32f3756..167d0f6c 100644 --- a/internal/adapter/yamlls/documentSync.go +++ b/internal/adapter/yamlls/documentSync.go @@ -24,11 +24,11 @@ func (yamllsConnector Connector) InitiallySyncOpenDocuments(docs []*lsplocal.Doc } func (yamllsConnector Connector) DocumentDidOpen(ast *sitter.Tree, params lsp.DidOpenTextDocumentParams) { - logger.Println("YamllsConnector DocumentDidOpen", params.TextDocument.URI) + logger.Debug("YamllsConnector DocumentDidOpen", params.TextDocument.URI) if yamllsConnector.Conn == nil { return } - params.TextDocument.Text = trimTemplateForYamllsFromAst(ast, params.TextDocument.Text) + params.TextDocument.Text = lsplocal.TrimTemplate(ast, params.TextDocument.Text) err := (*yamllsConnector.Conn).Notify(context.Background(), lsp.MethodTextDocumentDidOpen, params) if err != nil { @@ -40,16 +40,17 @@ func (yamllsConnector Connector) DocumentDidSave(doc *lsplocal.Document, params if yamllsConnector.Conn == nil { return } - params.Text = trimTemplateForYamllsFromAst(doc.Ast, doc.Content) + params.Text = lsplocal.TrimTemplate(doc.Ast, doc.Content) err := (*yamllsConnector.Conn).Notify(context.Background(), lsp.MethodTextDocumentDidSave, params) if err != nil { logger.Error("Error calling yamlls for didSave", err) } - yamllsConnector.DocumentDidChangeFullSync(doc, lsp.DidChangeTextDocumentParams{TextDocument: lsp.VersionedTextDocumentIdentifier{ - TextDocumentIdentifier: params.TextDocument, - }, + yamllsConnector.DocumentDidChangeFullSync(doc, lsp.DidChangeTextDocumentParams{ + TextDocument: lsp.VersionedTextDocumentIdentifier{ + TextDocumentIdentifier: params.TextDocument, + }, }) } @@ -57,7 +58,7 @@ func (yamllsConnector Connector) DocumentDidChange(doc *lsplocal.Document, param if yamllsConnector.Conn == nil { return } - var trimmedText = trimTemplateForYamllsFromAst(doc.Ast, doc.Content) + trimmedText := lsplocal.TrimTemplate(doc.Ast, doc.Content) logger.Debug("Sending DocumentDidChange previous", params) for i, change := range params.ContentChanges { @@ -88,7 +89,7 @@ func (yamllsConnector Connector) DocumentDidChangeFullSync(doc *lsplocal.Documen } logger.Println("Sending DocumentDidChange with full sync, current content:", doc.Content) - var trimmedText = trimTemplateForYamllsFromAst(doc.Ast.Copy(), doc.Content) + trimmedText := lsplocal.TrimTemplate(doc.Ast.Copy(), doc.Content) params.ContentChanges = []lsp.TextDocumentContentChangeEvent{ { diff --git a/internal/adapter/yamlls/trimTemplate.go b/internal/adapter/yamlls/trimTemplate.go deleted file mode 100644 index 9f5beb47..00000000 --- a/internal/adapter/yamlls/trimTemplate.go +++ /dev/null @@ -1,124 +0,0 @@ -package yamlls - -import ( - "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" - sitter "github.com/smacker/go-tree-sitter" -) - -func trimTemplateForYamllsFromAst(ast *sitter.Tree, text string) string { - result := []byte(text) - prettyPrintNode(ast.RootNode(), []byte(text), result) - return string(result) -} - -func prettyPrintNode(node *sitter.Node, previous []byte, result []byte) { - childCount := node.ChildCount() - - switch node.Type() { - case gotemplate.NodeTypeIfAction: - trimIfAction(node, previous, result) - case gotemplate.NodeTypeBlockAction, gotemplate.NodeTypeWithAction, gotemplate.NodeTypeRangeAction: - trimAction(childCount, node, previous, result) - case gotemplate.NodeTypeDefineAction: - earaseTemplate(node, previous, result) - case gotemplate.NodeTypeFunctionCall: - trimFunctionCall(node, previous, result) - case gotemplate.NodeTypeComment, gotemplate.NodeTypeVariableDefinition, gotemplate.NodeTypeAssignment: - earaseTemplateAndSiblings(node, previous, result) - default: - for i := 0; i < int(childCount); i++ { - prettyPrintNode(node.Child(i), previous, result) - } - } -} - -func trimAction(childCount uint32, node *sitter.Node, previous []byte, result []byte) { - for i := 0; i < int(childCount); i++ { - child := node.Child(i) - switch child.Type() { - case - gotemplate.NodeTypeAssignment, - gotemplate.NodeTypeIf, - gotemplate.NodeTypeSelectorExpression, - gotemplate.NodeTypeElse, - gotemplate.NodeTypeRange, - gotemplate.NodeTypeFunctionCall, - gotemplate.NodeTypeWith, - gotemplate.NodeTypeDefine, - gotemplate.NodeTypeOpenBraces, - gotemplate.NodeTypeOpenBracesDash, - gotemplate.NodeTypeCloseBraces, - gotemplate.NodeTypeCloseBracesDash, - gotemplate.NodeTypeEnd, - gotemplate.NodeTypeInterpretedStringLiteral, - gotemplate.NodeTypeBlock, - gotemplate.NodeTypeVariableDefinition, - gotemplate.NodeTypeVariable, - gotemplate.NodeTypeRangeVariableDefinition: - earaseTemplate(child, previous, result) - default: - prettyPrintNode(child, previous, result) - } - } -} - -func trimIfAction(node *sitter.Node, previous []byte, result []byte) { - if node.StartPoint().Row == node.EndPoint().Row { - earaseTemplate(node, previous, result) - return - } - - curser := sitter.NewTreeCursor(node) - curser.GoToFirstChild() - for curser.GoToNextSibling() { - if curser.CurrentFieldName() == gotemplate.FieldNameCondition { - earaseTemplate(curser.CurrentNode(), previous, result) - earaseTemplate(curser.CurrentNode().NextSibling(), previous, result) - continue - } - switch curser.CurrentNode().Type() { - case gotemplate.NodeTypeIf, gotemplate.NodeTypeElseIf: - earaseTemplate(curser.CurrentNode(), previous, result) - earaseTemplate(curser.CurrentNode().PrevSibling(), previous, result) - case gotemplate.NodeTypeEnd, gotemplate.NodeTypeElse: - earaseTemplateAndSiblings(curser.CurrentNode(), previous, result) - default: - prettyPrintNode(curser.CurrentNode(), previous, result) - } - } - curser.Close() -} - -func trimFunctionCall(node *sitter.Node, previous []byte, result []byte) { - functionName := node.ChildByFieldName(gotemplate.FieldNameFunction) - if functionName.Content(previous) == "include" { - parent := node.Parent() - if parent != nil && parent.Type() == gotemplate.NodeTypeChainedPipeline { - earaseTemplateAndSiblings(parent, previous, result) - } - } -} - -func earaseTemplateAndSiblings(node *sitter.Node, previous []byte, result []byte) { - earaseTemplate(node, previous, result) - prevSibling, nextSibling := node.PrevSibling(), node.NextSibling() - if prevSibling != nil { - earaseTemplate(prevSibling, previous, result) - } - if nextSibling != nil { - earaseTemplate(nextSibling, previous, result) - } -} - -func earaseTemplate(node *sitter.Node, previous []byte, result []byte) { - if node == nil { - return - } - logger.Debug("Content that is erased", node.Content(previous)) - for i := range []byte(node.Content(previous)) { - index := int(node.StartByte()) + i - if result[index] != '\n' && result[index] != '\r' { - result[index] = byte(' ') - } - } -} diff --git a/internal/adapter/yamlls/trimTemplate_test.go b/internal/adapter/yamlls/trimTemplate_test.go deleted file mode 100644 index cc86f1bc..00000000 --- a/internal/adapter/yamlls/trimTemplate_test.go +++ /dev/null @@ -1,400 +0,0 @@ -package yamlls - -import ( - "fmt" - "testing" - - lsplocal "github.com/mrjosh/helm-ls/internal/lsp" - "github.com/stretchr/testify/assert" -) - -type TrimTemplateTestData struct { - documentText string - trimmedText string -} - -var testTrimTemplateTestData = []TrimTemplateTestData{ - { - documentText: ` -{{ .Values.global. }} -yaml: test - -{{block "name"}} T1 {{end}} -`, - trimmedText: ` -{{ .Values.global. }} -yaml: test - - T1 -`, - }, - { - documentText: ` -{{ .Values.global. }} -yaml: test - -{{block "name"}} T1 {{end}} -`, - - trimmedText: ` -{{ .Values.global. }} -yaml: test - - T1 -`, - }, - { - documentText: ` -{{ if eq .Values.service.myParameter "true" }} -apiVersion: keda.sh/v1alpha1 -kind: ScaledObject -metadata: - name: prometheus-scaledobject - namespace: default -spec: - scaleTargetRef: - name: hasd - triggers: - - type: prometheus - metadata: - serverAdress: http://:9090 - metricName: http_requests_total # DEPRECATED: This parameter is deprecated as of KEDA v2.10 and will be removed in version 2.12 - threshold: '100' - query: sum(rate(http_requests_total{deployment="my-deployment"}[2m])) -# yaml-language-server: $schema=https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/keda.sh/scaledobject_v1alpha1.json -{{ end }} -`, - trimmedText: ` - -apiVersion: keda.sh/v1alpha1 -kind: ScaledObject -metadata: - name: prometheus-scaledobject - namespace: default -spec: - scaleTargetRef: - name: hasd - triggers: - - type: prometheus - metadata: - serverAdress: http://:9090 - metricName: http_requests_total # DEPRECATED: This parameter is deprecated as of KEDA v2.10 and will be removed in version 2.12 - threshold: '100' - query: sum(rate(http_requests_total{deployment="my-deployment"}[2m])) -# yaml-language-server: $schema=https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/keda.sh/scaledobject_v1alpha1.json - -`, - }, - { - documentText: ` -{{ if eq .Values.service.myParameter "true" }} -{{ if eq .Values.service.second "true" }} -apiVersion: keda.sh/v1alpha1 -{{ end }} -{{ end }} -`, - trimmedText: ` - - -apiVersion: keda.sh/v1alpha1 - - -`, - }, - { - documentText: ` -{{- if .Values.ingress.enabled }} -apiVersion: apps/v1 -kind: Ingress -{{- end }} -`, - - trimmedText: ` - -apiVersion: apps/v1 -kind: Ingress - -`, - }, - { - documentText: ` -{{- if .Values.ingress.enabled }} -apiVersion: apps/v1 -{{- else }} -apiVersion: apps/v2 -{{- end }} -`, - - trimmedText: ` - -apiVersion: apps/v1 - -apiVersion: apps/v2 - -`, - }, - { - documentText: ` -apiVersion: {{ include "common.capabilities.ingress.apiVersion" . }} -kind: Ingress -metadata: - name: {{ include "common.names.fullname" . }} - namespace: {{ .Release.Namespace | quote }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - app.kubernetes.io/component: grafana - annotations: - {{- if .Values.ingress.certManager }} - kubernetes.io/tls-acme: "true" - {{- end }} - {{- if .Values.ingress.annotations }} - {{- include "common.tplvalues.render" (dict "value" .Values.ingress.annotations "context" $) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - `, - trimmedText: ` -apiVersion: {{ include "common.capabilities.ingress.apiVersion" . }} -kind: Ingress -metadata: - name: {{ include "common.names.fullname" . }} - namespace: {{ .Release.Namespace | quote }} - labels: - - - - app.kubernetes.io/component: grafana - annotations: - - kubernetes.io/tls-acme: "true" - - - - - - - - `, - }, - {documentText: `{{- $x := "test" -}}`, trimmedText: ` `}, - {documentText: `{{ $x := "test" }}`, trimmedText: ` `}, - {documentText: `{{ /* comment */ }}`, trimmedText: ` `}, - {documentText: `{{define "name"}} T1 {{end}}`, trimmedText: ` `}, - { - documentText: ` - {{- if .Values.controller.customStartupProbe }} - startupProbe: {} - {{- else if .Values.controller.startupProbe.enabled }} - startupProbe: - httpGet: - path: /healthz - port: {{ .Values.controller.containerPorts.controller }} - {{- end }} - `, - trimmedText: ` - - startupProbe: {} - - startupProbe: - httpGet: - path: /healthz - port: {{ .Values.controller.containerPorts.controller }} - - `, - }, - { - documentText: ` - {{ if eq .Values.replicaCout 1 }} - {{- $kube := "" -}} - apiVersion: v1 - kind: Service - bka: dsa - metadata: - name: {{ include "hello-world.fullname" . }} - labels: - {{- include "hello-world.labels" . | nindent 4 }} - spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.port }} - targetPort: http - {{ end }} - `, - trimmedText: ` - - - apiVersion: v1 - kind: Service - bka: dsa - metadata: - name: {{ include "hello-world.fullname" . }} - labels: - - spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.port }} - targetPort: http - - `, - }, - { - documentText: `{{ if }}{{- end -}}`, - trimmedText: ` `, - }, - { - // todo: Handle this case better - documentText: ` -{{ if }} - -{{- end -}}`, - trimmedText: ` - }} - - `, - }, - { - documentText: `{{- $shards := $.Values.shards | int }}`, - trimmedText: ` `, - }, - { - documentText: ` -{{- if $.Values.externalAccess.enabled }} -{{- $shards := $.Values.shards | int }} -{{- $replicas := $.Values.replicaCount | int }} -{{- $totalNodes := mul $shards $replicas }} -{{- range $shard, $e := until $shards }} -{{- range $i, $_e := until $replicas }} -{{- $targetPod := printf "%s-shard%d-%d" (include "common.names.fullname" $) $shard $i }} -{{- end }} -{{- end }} -{{- end }} - `, - trimmedText: ` - - - - - - - - - - - `, - }, - { - documentText: ` -data: - pod_template.yaml: |- - {{- if .Values.worker.podTemplate }} - {{- include "common.tplvalues.render" (dict "value" .Values.worker.podTemplate "context" $) | nindent 4 }} - {{- else }} - apiVersion: v1 - kind: Pod - {{ end }} -`, - trimmedText: ` -data: - pod_template.yaml: |- - - - - apiVersion: v1 - kind: Pod - -`, - }, - { - documentText: ` -{{- /* -Copyright Some Company, Inc. -SPDX-License-Identifier: APACHE-2.0 -*/}} -`, - trimmedText: ` - - - - -`, - }, - { - documentText: ` -{{- $namespaces := list .Release.Namespace }} -{{- $namespaces = .Values.controller.workflowNamespaces }} -`, - trimmedText: ` - - -`, - }, - { - documentText: ` -{{- range $namespaces }} -{{- end }} -`, - trimmedText: ` - - -`, - }, - { - documentText: ` -list: - - value: {{ join "," .Values.initialCluster | quote }} - - name: some -`, - trimmedText: ` -list: - - value: {{ join "," .Values.initialCluster | quote }} - - name: some -`, - }, - { - documentText: ` - - name: ELASTICSEARCH_NODE_ROLES - value: {{ join "," $roles | quote }} - - name: ELASTICSEARCH_TRANSPORT_PORT_NUMBER - value: {{ .Values.containerPorts.transport | quote }} - - name: ELASTICSEARCH_HTTP_PORT_NUMBER - value: {{ .Values.containerPorts.restAPI | quote }} -`, - trimmedText: ` - - name: ELASTICSEARCH_NODE_ROLES - value: {{ join "," $roles | quote }} - - name: ELASTICSEARCH_TRANSPORT_PORT_NUMBER - value: {{ .Values.containerPorts.transport | quote }} - - name: ELASTICSEARCH_HTTP_PORT_NUMBER - value: {{ .Values.containerPorts.restAPI | quote }} -`, - }, - { - documentText: ` -apiVersion: {{ if .Values.useStatefulSet }}{{ include "common.capabilities.statefulset.apiVersion" . }}{{- else }}{{ include "common.capabilities.deployment.apiVersion" . }}{{- end }} - `, - trimmedText: ` -apiVersion: - `, - }, -} - -func TestTrimTemplate(t *testing.T) { - for _, testData := range testTrimTemplateTestData { - testTrimTemplateWithTestData(t, testData) - } -} - -func testTrimTemplateWithTestData(t *testing.T, testData TrimTemplateTestData) { - doc := &lsplocal.Document{ - Content: testData.documentText, - Ast: lsplocal.ParseAst(nil, testData.documentText), - } - - trimmed := trimTemplateForYamllsFromAst(doc.Ast, testData.documentText) - - assert.Equal(t, testData.trimmedText, trimmed, fmt.Sprintf("AST was: %v", doc.Ast.RootNode().String())) -} diff --git a/internal/lsp/document.go b/internal/lsp/document.go index ba5f94a0..0d9c1a7b 100644 --- a/internal/lsp/document.go +++ b/internal/lsp/document.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "strings" + "sync" "github.com/mrjosh/helm-ls/internal/util" sitter "github.com/smacker/go-tree-sitter" @@ -13,20 +14,21 @@ import ( // documentStore holds opened documents. type DocumentStore struct { - documents map[string]*Document + documents sync.Map } func NewDocumentStore() *DocumentStore { return &DocumentStore{ - documents: map[string]*Document{}, + documents: sync.Map{}, } } func (s *DocumentStore) GetAllDocs() []*Document { var docs []*Document - for _, doc := range s.documents { - docs = append(docs, doc) - } + s.documents.Range(func(_, v interface{}) bool { + docs = append(docs, v.(*Document)) + return true + }) return docs } @@ -42,14 +44,17 @@ func (s *DocumentStore) DidOpen(params lsp.DidOpenTextDocumentParams, helmlsConf Ast: ParseAst(nil, params.TextDocument.Text), DiagnosticsCache: NewDiagnosticsCache(helmlsConfig), } - s.documents[path] = doc + s.documents.Store(path, doc) return doc, nil } func (s *DocumentStore) Get(docuri uri.URI) (*Document, bool) { path := docuri.Filename() - d, ok := s.documents[path] - return d, ok + d, ok := s.documents.Load(path) + if !ok { + return nil, false + } + return d.(*Document), ok } // Document represents an opened file. diff --git a/internal/lsp/yaml_ast.go b/internal/lsp/yaml_ast.go new file mode 100644 index 00000000..974ce90b --- /dev/null +++ b/internal/lsp/yaml_ast.go @@ -0,0 +1,62 @@ +package lsp + +import ( + "context" + + "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" + sitter "github.com/smacker/go-tree-sitter" + "github.com/smacker/go-tree-sitter/yaml" +) + +func getRangeForNode(node *sitter.Node) sitter.Range { + return sitter.Range{ + StartPoint: node.StartPoint(), + EndPoint: node.EndPoint(), + StartByte: node.StartByte(), + EndByte: node.EndByte(), + } +} + +func getTextNodeRanges(gotemplateNode *sitter.Node) []sitter.Range { + textNodes := []sitter.Range{} + + for i := 0; i < int(gotemplateNode.ChildCount()); i++ { + child := gotemplateNode.Child(i) + if child.Type() == gotemplate.NodeTypeText { + textNodes = append(textNodes, getRangeForNode(child)) + } else { + textNodes = append(textNodes, getTextNodeRanges(child)...) + } + } + return textNodes +} + +func ParseYamlAst(gotemplateTree *sitter.Tree, content string) *sitter.Tree { + parser := sitter.NewParser() + parser.SetLanguage(yaml.GetLanguage()) + parser.SetIncludedRanges(getTextNodeRanges(gotemplateTree.RootNode())) + + tree, _ := parser.ParseCtx(context.Background(), nil, []byte(content)) + return tree +} + +// TrimTemplate removes all template nodes. +// This is done by keeping only the text nodes +// which is easier then removing the template nodes +// since template nodes could contain other nodes +func TrimTemplate(gotemplateTree *sitter.Tree, content string) string { + ranges := getTextNodeRanges(gotemplateTree.RootNode()) + result := make([]byte, len(content)) + for i := range result { + if content[i] == '\n' || content[i] == '\r' { + result[i] = content[i] + continue + } + result[i] = byte(' ') + } + for _, yamlRange := range ranges { + copy(result[yamlRange.StartByte:yamlRange.EndByte], + content[yamlRange.StartByte:yamlRange.EndByte]) + } + return string(result) +} diff --git a/internal/lsp/yaml_ast_test.go b/internal/lsp/yaml_ast_test.go new file mode 100644 index 00000000..fc8b8eab --- /dev/null +++ b/internal/lsp/yaml_ast_test.go @@ -0,0 +1,162 @@ +package lsp + +import ( + "testing" + + sitter "github.com/smacker/go-tree-sitter" + "github.com/stretchr/testify/assert" +) + +func Test_getTextNodeRanges(t *testing.T) { + type args struct { + gotemplateString string + } + tests := []struct { + name string + args args + want []sitter.Range + }{ + { + name: "no text nodes", + args: args{ + "{{ with .Values }}{{ .test }}{{ end }}", + }, + want: []sitter.Range{}, + }, + { + name: "simple text node", + args: args{ + "a: {{ .test }}", + }, + want: []sitter.Range{ + { + StartPoint: sitter.Point{Row: 0, Column: 0}, + EndPoint: sitter.Point{Row: 0, Column: 2}, + StartByte: 0, + EndByte: 2, + }, + }, + }, + { + name: "to simple text nodes", + args: args{ + ` +a: {{ .test }} +b: not`, + }, + want: []sitter.Range{ + {StartPoint: sitter.Point{ + Row: 0x1, Column: 0x0, + }, EndPoint: sitter.Point{ + Row: 0x1, Column: 0x2, + }, StartByte: 0x1, EndByte: 0x3}, + { + StartPoint: sitter.Point{ + Row: 0x2, + Column: 0x0, + }, + EndPoint: sitter.Point{ + Row: 0x2, + Column: 0x2, + }, + StartByte: 0x10, + EndByte: 0x12, + }, + { + StartPoint: sitter.Point{ + Row: 0x2, + Column: 0x2, + }, EndPoint: sitter.Point{ + Row: 0x2, + Column: 0x6, + }, + StartByte: 0x12, + EndByte: 0x16, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := getTextNodeRanges(ParseAst(nil, tt.args.gotemplateString).RootNode()) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestParseYamlAst(t *testing.T) { + type args struct { + content string + } + tests := []struct { + name string + args args + wantSexpr string + }{ + { + name: "simple template node", + args: args{ + "a: {{ .test }}", + }, + wantSexpr: "(stream (document (block_node (block_mapping (block_mapping_pair key: (flow_node (plain_scalar (string_scalar))))))))", + }, + { + name: "key value", + args: args{ + "a: value", + }, + wantSexpr: "(stream (document (block_node (block_mapping (block_mapping_pair key: (flow_node (plain_scalar (string_scalar))) value: (flow_node (plain_scalar (string_scalar))))))))", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotemplateTree := ParseAst(nil, tt.args.content) + got := ParseYamlAst(gotemplateTree, tt.args.content) + assert.Equal(t, tt.wantSexpr, got.RootNode().String()) + }) + } +} + +func TestTrimTemplate(t *testing.T) { + tests := []struct { + documentText string + trimmedText string + }{ + { + documentText: ` +{{ .Values.global. }} +yaml: test + +{{block "name"}} T1 {{end}} +`, + trimmedText: ` + +yaml: test + + T1 +`, + }, + { + documentText: ` +{{ .Values.global. }} +yaml: test + +{{block "name"}} T1 {{end}} +`, + + trimmedText: ` + +yaml: test + + T1 +`, + }, + } + for _, tt := range tests { + t.Run(tt.documentText, func(t *testing.T) { + gotemplateTree := ParseAst(nil, tt.documentText) + got := TrimTemplate(gotemplateTree, tt.documentText) + assert.Equal(t, tt.trimmedText, got) + }) + } +} diff --git a/internal/util/config.go b/internal/util/config.go index 55972196..b71bd94a 100644 --- a/internal/util/config.go +++ b/internal/util/config.go @@ -40,14 +40,22 @@ var DefaultConfig = HelmlsConfiguration{ }, } +type YamllsSchemaStoreSettings struct { + Enable bool `json:"enable"` +} + type YamllsSettings struct { - Schemas map[string]string `json:"schemas"` - Completion bool `json:"completion"` - Hover bool `json:"hover"` + Schemas map[string]string `json:"schemas"` + Completion bool `json:"completion"` + Hover bool `json:"hover"` + YamllsSchemaStoreSettings YamllsSchemaStoreSettings `json:"schemaStore"` } var DefaultYamllsSettings = YamllsSettings{ Schemas: map[string]string{"kubernetes": "templates/**"}, Completion: true, Hover: true, + YamllsSchemaStoreSettings: YamllsSchemaStoreSettings{ + Enable: true, + }, } diff --git a/testdata/charts b/testdata/charts new file mode 160000 index 00000000..3b84d7dc --- /dev/null +++ b/testdata/charts @@ -0,0 +1 @@ +Subproject commit 3b84d7dc269f76064e18ba4e21937cc78e77deda From 3fa321eaf73e88b9417ba9b9efca339289891ede Mon Sep 17 00:00:00 2001 From: qvalentin <36446499+qvalentin@users.noreply.github.com> Date: Sun, 10 Mar 2024 19:12:42 +0100 Subject: [PATCH 11/35] fix(yamlls): add missing schemastore url (#66) --- internal/util/config.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/util/config.go b/internal/util/config.go index b71bd94a..f0615ee4 100644 --- a/internal/util/config.go +++ b/internal/util/config.go @@ -41,7 +41,8 @@ var DefaultConfig = HelmlsConfiguration{ } type YamllsSchemaStoreSettings struct { - Enable bool `json:"enable"` + Enable bool `json:"enable"` + Url string `json:"url"` } type YamllsSettings struct { @@ -57,5 +58,6 @@ var DefaultYamllsSettings = YamllsSettings{ Hover: true, YamllsSchemaStoreSettings: YamllsSchemaStoreSettings{ Enable: true, + Url: "https://www.schemastore.org/api/json/catalog.json", }, } From 876297aa7f2c4e69788398e0311f351bed90453b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 16 Mar 2024 16:01:10 +0000 Subject: [PATCH 12/35] build(deps): bump google.golang.org/protobuf from 1.31.0 to 1.33.0 (#67) Bumps google.golang.org/protobuf from 1.31.0 to 1.33.0. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index cbf7f193..56ec369a 100644 --- a/go.mod +++ b/go.mod @@ -74,7 +74,7 @@ require ( golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.31.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect k8s.io/api v0.29.2 // indirect k8s.io/klog/v2 v2.110.1 // indirect diff --git a/go.sum b/go.sum index ec6baa01..b4280ed2 100644 --- a/go.sum +++ b/go.sum @@ -224,8 +224,8 @@ google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6 google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= From 89f77e357a3b2fa4c262527eb15dc0853a922e29 Mon Sep 17 00:00:00 2001 From: qvalentin Date: Sun, 10 Mar 2024 17:27:47 +0100 Subject: [PATCH 13/35] refactor(lsp): use lsp server and client interfaces refactor(yamlls): use protocol client and server test(yamlls): more integration tests add example chart to testdata test(yamlls): clean up diagnostics tests with schema fix: add integration test tags fix: remove flaky test fix: os agnostic filepath fix(tests): test works differently on windows fix: configuration is working again test(yamlls): test yamlls completion refactor(configuration): extract config parsing test(hover): test many hover cases fix(lints): cleanup --- .mockery.yaml | 10 + Makefile | 4 +- cmds/lint.go | 5 +- cmds/serve.go | 11 +- examples/nvim/init.lua | 42 ++ go.mod | 3 +- go.sum | 2 + internal/adapter/yamlls/client.go | 57 ++ internal/adapter/yamlls/completion.go | 18 +- .../yamlls/completion_integration_test.go | 96 +++ internal/adapter/yamlls/configuration.go | 21 +- internal/adapter/yamlls/diagnostics.go | 16 +- .../yamlls/diagnostics_integration_test.go | 121 ++-- internal/adapter/yamlls/documentSync.go | 18 +- internal/adapter/yamlls/handler.go | 26 - internal/adapter/yamlls/hover.go | 33 +- .../adapter/yamlls/hover_integration_test.go | 79 +++ internal/adapter/yamlls/initization.go | 24 +- .../adapter/yamlls/integration_test_utils.go | 86 +++ internal/adapter/yamlls/yamlls.go | 34 +- internal/handler/completion.go | 39 +- internal/handler/configuration.go | 47 +- internal/handler/configuration_test.go | 106 +++ internal/handler/definition.go | 21 +- internal/handler/handler.go | 332 ++++++--- internal/handler/hover.go | 31 +- internal/handler/hover_main_test.go | 127 ++++ internal/handler/initialization.go | 41 +- internal/handler/text.go | 48 -- internal/handler/text_document.go | 99 +++ internal/handler/watched_files.go | 14 +- internal/lsp/document.go | 3 +- internal/lsp/document_test.go | 2 +- internal/lsp/lint.go | 27 +- internal/util/config.go | 4 +- internal/util/lsp.go | 4 +- main.go | 4 +- mocks/go.lsp.dev/protocol/mock_Client.go | 646 ++++++++++++++++++ testdata/example/.helmignore | 23 + testdata/example/Chart.yaml | 24 + testdata/example/templates/NOTES.txt | 22 + testdata/example/templates/_helpers.tpl | 62 ++ .../templates/deployment-no-templates.yaml | 21 + testdata/example/templates/deployment.yaml | 71 ++ testdata/example/templates/hpa.yaml | 32 + testdata/example/templates/ingress.yaml | 61 ++ testdata/example/templates/service.yaml | 16 + .../example/templates/serviceaccount.yaml | 13 + .../templates/tests/test-connection.yaml | 15 + testdata/example/values.yaml | 107 +++ 50 files changed, 2304 insertions(+), 464 deletions(-) create mode 100644 .mockery.yaml create mode 100644 internal/adapter/yamlls/client.go create mode 100644 internal/adapter/yamlls/completion_integration_test.go delete mode 100644 internal/adapter/yamlls/handler.go create mode 100644 internal/adapter/yamlls/hover_integration_test.go create mode 100644 internal/adapter/yamlls/integration_test_utils.go create mode 100644 internal/handler/configuration_test.go create mode 100644 internal/handler/hover_main_test.go create mode 100644 internal/handler/text_document.go create mode 100644 mocks/go.lsp.dev/protocol/mock_Client.go create mode 100644 testdata/example/.helmignore create mode 100644 testdata/example/Chart.yaml create mode 100644 testdata/example/templates/NOTES.txt create mode 100644 testdata/example/templates/_helpers.tpl create mode 100644 testdata/example/templates/deployment-no-templates.yaml create mode 100644 testdata/example/templates/deployment.yaml create mode 100644 testdata/example/templates/hpa.yaml create mode 100644 testdata/example/templates/ingress.yaml create mode 100644 testdata/example/templates/service.yaml create mode 100644 testdata/example/templates/serviceaccount.yaml create mode 100644 testdata/example/templates/tests/test-connection.yaml create mode 100644 testdata/example/values.yaml diff --git a/.mockery.yaml b/.mockery.yaml new file mode 100644 index 00000000..45e1571a --- /dev/null +++ b/.mockery.yaml @@ -0,0 +1,10 @@ +with-expecter: true +packages: + go.lsp.dev/protocol: + # place your package-specific config here + config: + interfaces: + # select the interfaces you want mocked + Client: + # Modify package-level config for this specific interface (if applicable) + config: diff --git a/Makefile b/Makefile index f8a23ccc..9880237e 100644 --- a/Makefile +++ b/Makefile @@ -65,10 +65,10 @@ integration-test-deps: test: $(MAKE) integration-test-deps - @$(GO) test ./... -v -race + @$(GO) test ./... -v -race -tags=integration coverage: - @$(GO) test -coverprofile=.coverage ./internal/... && go tool cover -html=.coverage + @$(GO) test -coverprofile=.coverage -tags=integration ./internal/... && go tool cover -html=.coverage .PHONY: build-release build-release: diff --git a/cmds/lint.go b/cmds/lint.go index 6319aa0f..03ba3f05 100644 --- a/cmds/lint.go +++ b/cmds/lint.go @@ -26,10 +26,7 @@ func newLintCmd() *cobra.Command { return err } - msgs, err := locallsp.GetDiagnostics(rootPath, chart.ValuesFiles.MainValuesFile.Values) - if err != nil { - return err - } + msgs := locallsp.GetDiagnostics(rootPath, chart.ValuesFiles.MainValuesFile.Values) for _, msg := range msgs { fmt.Println(msg) diff --git a/cmds/serve.go b/cmds/serve.go index 3a756d6e..3629af0c 100644 --- a/cmds/serve.go +++ b/cmds/serve.go @@ -1,25 +1,18 @@ package cmds import ( - "context" "os" "github.com/mrjosh/helm-ls/internal/handler" "github.com/spf13/cobra" - "go.lsp.dev/jsonrpc2" ) func newServeCmd() *cobra.Command { cmd := &cobra.Command{ Use: "serve", Short: "Start helm lint language server", - RunE: func(cmd *cobra.Command, args []string) error { - - conn := jsonrpc2.NewConn(jsonrpc2.NewStream(stdrwc{})) - handler := handler.NewHandler(conn) - handlerSrv := jsonrpc2.HandlerServer(handler) - - return handlerSrv.ServeStream(context.Background(), conn) + Run: func(cmd *cobra.Command, args []string) { + handler.StartHandler(stdrwc{}) }, } diff --git a/examples/nvim/init.lua b/examples/nvim/init.lua index b7613095..73579ecd 100644 --- a/examples/nvim/init.lua +++ b/examples/nvim/init.lua @@ -39,3 +39,45 @@ lspconfig.helm_ls.setup { -- setup yamlls lspconfig.yamlls.setup {} + + +-- below are keymapping as recommended by nvim-lspconfig + +-- Global mappings. +-- See `:help vim.diagnostic.*` for documentation on any of the below functions +vim.keymap.set('n', 'e', vim.diagnostic.open_float) +vim.keymap.set('n', '[d', vim.diagnostic.goto_prev) +vim.keymap.set('n', ']d', vim.diagnostic.goto_next) +vim.keymap.set('n', 'q', vim.diagnostic.setloclist) + +-- Use LspAttach autocommand to only map the following keys +-- after the language server attaches to the current buffer +vim.api.nvim_create_autocmd('LspAttach', { + group = vim.api.nvim_create_augroup('UserLspConfig', {}), + callback = function(ev) + -- Enable completion triggered by + vim.bo[ev.buf].omnifunc = 'v:lua.vim.lsp.omnifunc' + + -- Buffer local mappings. + -- See `:help vim.lsp.*` for documentation on any of the below functions + local opts = { buffer = ev.buf } + vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, opts) + vim.keymap.set('n', 'gd', vim.lsp.buf.definition, opts) + vim.keymap.set('n', 'K', vim.lsp.buf.hover, opts) + vim.keymap.set('n', 'gi', vim.lsp.buf.implementation, opts) + vim.keymap.set('n', '', vim.lsp.buf.signature_help, opts) + vim.keymap.set('n', 'wa', vim.lsp.buf.add_workspace_folder, opts) + vim.keymap.set('n', 'wr', vim.lsp.buf.remove_workspace_folder, opts) + vim.keymap.set('n', 'wl', function() + print(vim.inspect(vim.lsp.buf.list_workspace_folders())) + end, opts) + vim.keymap.set('n', 'D', vim.lsp.buf.type_definition, opts) + vim.keymap.set('n', 'rn', vim.lsp.buf.rename, opts) + vim.keymap.set({ 'n', 'v' }, 'ca', vim.lsp.buf.code_action, opts) + vim.keymap.set('n', 'gr', vim.lsp.buf.references, opts) + vim.keymap.set('n', 'f', function() + vim.lsp.buf.format { async = true } + end, opts) + end, +}) + diff --git a/go.mod b/go.mod index 56ec369a..9b3ad0ea 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( go.lsp.dev/jsonrpc2 v0.10.0 go.lsp.dev/protocol v0.12.0 go.lsp.dev/uri v0.3.0 + go.uber.org/zap v1.24.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 k8s.io/apiextensions-apiserver v0.29.2 @@ -59,13 +60,13 @@ require ( github.com/shopspring/decimal v1.2.0 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect go.lsp.dev/pkg v0.0.0-20210717090340-384b27a52fb2 // indirect go.uber.org/atomic v1.10.0 // indirect go.uber.org/goleak v1.2.1 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.24.0 // indirect golang.org/x/crypto v0.17.0 // indirect golang.org/x/net v0.19.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect diff --git a/go.sum b/go.sum index b4280ed2..ca232526 100644 --- a/go.sum +++ b/go.sum @@ -122,6 +122,8 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= diff --git a/internal/adapter/yamlls/client.go b/internal/adapter/yamlls/client.go new file mode 100644 index 00000000..529ca7ad --- /dev/null +++ b/internal/adapter/yamlls/client.go @@ -0,0 +1,57 @@ +package yamlls + +import ( + "context" + + "go.lsp.dev/protocol" +) + +// ApplyEdit implements protocol.Client. +func (y Connector) ApplyEdit(ctx context.Context, params *protocol.ApplyWorkspaceEditParams) (result bool, err error) { + return true, nil +} + +// LogMessage implements protocol.Client. +func (y Connector) LogMessage(ctx context.Context, params *protocol.LogMessageParams) (err error) { + return nil +} + +// Progress implements protocol.Client. +func (y Connector) Progress(ctx context.Context, params *protocol.ProgressParams) (err error) { + return nil +} + +// RegisterCapability implements protocol.Client. +func (y Connector) RegisterCapability(ctx context.Context, params *protocol.RegistrationParams) (err error) { + return nil +} + +// ShowMessage implements protocol.Client. +func (y Connector) ShowMessage(ctx context.Context, params *protocol.ShowMessageParams) (err error) { + return y.client.ShowMessage(ctx, params) +} + +// ShowMessageRequest implements protocol.Client. +func (y Connector) ShowMessageRequest(ctx context.Context, params *protocol.ShowMessageRequestParams) (result *protocol.MessageActionItem, err error) { + return nil, nil +} + +// Telemetry implements protocol.Client. +func (y Connector) Telemetry(ctx context.Context, params interface{}) (err error) { + return nil +} + +// UnregisterCapability implements protocol.Client. +func (y Connector) UnregisterCapability(ctx context.Context, params *protocol.UnregistrationParams) (err error) { + return nil +} + +// WorkDoneProgressCreate implements protocol.Client. +func (y Connector) WorkDoneProgressCreate(ctx context.Context, params *protocol.WorkDoneProgressCreateParams) (err error) { + return nil +} + +// WorkspaceFolders implements protocol.Client. +func (y Connector) WorkspaceFolders(ctx context.Context) (result []protocol.WorkspaceFolder, err error) { + return nil, nil +} diff --git a/internal/adapter/yamlls/completion.go b/internal/adapter/yamlls/completion.go index 8e73eb9a..b3459259 100644 --- a/internal/adapter/yamlls/completion.go +++ b/internal/adapter/yamlls/completion.go @@ -2,24 +2,14 @@ package yamlls import ( "context" - "reflect" lsp "go.lsp.dev/protocol" ) -func (yamllsConnector Connector) CallCompletion(ctx context.Context, params lsp.CompletionParams) *lsp.CompletionList { - if yamllsConnector.Conn == nil { - return &lsp.CompletionList{} +func (yamllsConnector Connector) CallCompletion(ctx context.Context, params *lsp.CompletionParams) (*lsp.CompletionList, error) { + if yamllsConnector.server == nil { + return &lsp.CompletionList{}, nil } - logger.Println("Calling yamlls for completions") - var response = reflect.New(reflect.TypeOf(lsp.CompletionList{})).Interface() - _, err := (*yamllsConnector.Conn).Call(ctx, lsp.MethodTextDocumentCompletion, params, response) - if err != nil { - logger.Error("Error Calling yamlls for completions", err) - return &lsp.CompletionList{} - } - - logger.Debug("Got completions from yamlls", response) - return response.(*lsp.CompletionList) + return yamllsConnector.server.Completion(ctx, params) } diff --git a/internal/adapter/yamlls/completion_integration_test.go b/internal/adapter/yamlls/completion_integration_test.go new file mode 100644 index 00000000..4b73c2f4 --- /dev/null +++ b/internal/adapter/yamlls/completion_integration_test.go @@ -0,0 +1,96 @@ +//go:build integration + +package yamlls + +import ( + "context" + "testing" + "time" + + "github.com/mrjosh/helm-ls/internal/util" + "github.com/stretchr/testify/assert" + lsp "go.lsp.dev/protocol" + "go.lsp.dev/uri" +) + +func TestYamllsCompletionIntegration(t *testing.T) { + config := util.DefaultConfig.YamllsConfiguration + + testCases := []struct { + desc string + file string + position lsp.Position + + expected []lsp.CompletionItem + }{ + { + desc: "test hover on deployment.yaml", + file: "../../../testdata/example/templates/deployment.yaml", + position: lsp.Position{ + Line: 42, + Character: 14, + }, + expected: []lsp.CompletionItem{ + { + Command: nil, + CommitCharacters: nil, + Tags: nil, + Data: nil, + Deprecated: false, + Documentation: "What host IP to bind the external port to.", + FilterText: "", + InsertText: "hostIP: ", + InsertTextFormat: 2, + Kind: 10, + Label: "hostIP", + SortText: "", + TextEdit: nil, + }, + { + Command: nil, + CommitCharacters: nil, + Tags: nil, + Data: nil, + Deprecated: false, + Documentation: "Number of port to expose on the host. If specified, this must be a valid port number, 0 < x < 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.", + FilterText: "", + InsertText: "hostPort: ${1:0}", + InsertTextFormat: 2, + InsertTextMode: 0, + Kind: 10, + Label: "hostPort", + TextEdit: nil, + }, + }, + }, + } + for _, tt1 := range testCases { + tt := tt1 + t.Run(tt.desc, func(t *testing.T) { + t.Parallel() + yamllsConnector, documents, _ := getYamlLsConnector(t, config) + openFile(t, documents, tt.file, yamllsConnector) + + assert.EventuallyWithT(t, func(c *assert.CollectT) { + result, _ := yamllsConnector.CallCompletion(context.Background(), &lsp.CompletionParams{ + TextDocumentPositionParams: lsp.TextDocumentPositionParams{ + TextDocument: lsp.TextDocumentIdentifier{ + URI: uri.File(tt.file), + }, + Position: tt.position, + }, + }) + assert.NotNil(c, result) + if result == nil { + return + } + for i := 0; i < len(result.Items); i++ { + result.Items[i].TextEdit = nil + } + for _, v := range tt.expected { + assert.Contains(c, result.Items, v) + } + }, time.Second*10, time.Second/2) + }) + } +} diff --git a/internal/adapter/yamlls/configuration.go b/internal/adapter/yamlls/configuration.go index 94e4f4b9..cf4e1575 100644 --- a/internal/adapter/yamlls/configuration.go +++ b/internal/adapter/yamlls/configuration.go @@ -1,18 +1,17 @@ package yamlls import ( - "encoding/json" + "context" - "go.lsp.dev/jsonrpc2" - lsp "go.lsp.dev/protocol" + "go.lsp.dev/protocol" ) -func (yamllsConnector Connector) handleConfiguration(req jsonrpc2.Request) []interface{} { - var params lsp.ConfigurationParams - if err := json.Unmarshal(req.Params(), ¶ms); err != nil { - logger.Error("Error parsing configuration request from yamlls", err) - } - logger.Debug("Yamlls ConfigurationParams", params) - settings := []interface{}{yamllsConnector.config.YamllsSettings} - return settings +// Configuration implements protocol.Client. +func (y Connector) Configuration(_ context.Context, _ *protocol.ConfigurationParams) (result []interface{}, err error) { + settings := []interface{}{y.config.YamllsSettings} + return settings, nil +} + +func (y Connector) DidChangeConfiguration(ctx context.Context) (err error) { + return y.server.DidChangeConfiguration(ctx, &protocol.DidChangeConfigurationParams{}) } diff --git a/internal/adapter/yamlls/diagnostics.go b/internal/adapter/yamlls/diagnostics.go index 01bb695e..7b92f6f6 100644 --- a/internal/adapter/yamlls/diagnostics.go +++ b/internal/adapter/yamlls/diagnostics.go @@ -2,21 +2,15 @@ package yamlls import ( "context" - "encoding/json" lsplocal "github.com/mrjosh/helm-ls/internal/lsp" sitter "github.com/smacker/go-tree-sitter" - "go.lsp.dev/jsonrpc2" + "go.lsp.dev/protocol" lsp "go.lsp.dev/protocol" ) -func (yamllsConnector *Connector) handleDiagnostics(req jsonrpc2.Request, clientConn jsonrpc2.Conn, documents *lsplocal.DocumentStore) { - var params lsp.PublishDiagnosticsParams - if err := json.Unmarshal(req.Params(), ¶ms); err != nil { - logger.Println("Error handling diagnostic", err) - } - - doc, ok := documents.Get(params.URI) +func (c Connector) PublishDiagnostics(ctx context.Context, params *protocol.PublishDiagnosticsParams) (err error) { + doc, ok := c.documents.Get(params.URI) if !ok { logger.Println("Error handling diagnostic. Could not get document: " + params.URI.Filename()) } @@ -25,11 +19,13 @@ func (yamllsConnector *Connector) handleDiagnostics(req jsonrpc2.Request, client if doc.DiagnosticsCache.ShouldShowDiagnosticsOnNewYamlDiagnostics() { logger.Debug("Publishing yamlls diagnostics") params.Diagnostics = doc.DiagnosticsCache.GetMergedDiagnostics() - err := clientConn.Notify(context.Background(), lsp.MethodTextDocumentPublishDiagnostics, ¶ms) + err := c.client.PublishDiagnostics(ctx, params) if err != nil { logger.Println("Error calling yamlls for diagnostics", err) } } + + return nil } func filterDiagnostics(diagnostics []lsp.Diagnostic, ast *sitter.Tree, content string) (filtered []lsp.Diagnostic) { diff --git a/internal/adapter/yamlls/diagnostics_integration_test.go b/internal/adapter/yamlls/diagnostics_integration_test.go index 3426d82e..15446b45 100644 --- a/internal/adapter/yamlls/diagnostics_integration_test.go +++ b/internal/adapter/yamlls/diagnostics_integration_test.go @@ -1,20 +1,20 @@ +//go:build integration + package yamlls import ( - "encoding/json" "fmt" "log" "os" "path/filepath" "regexp" - "strings" "testing" "time" lsplocal "github.com/mrjosh/helm-ls/internal/lsp" "github.com/mrjosh/helm-ls/internal/util" "github.com/stretchr/testify/assert" - "go.lsp.dev/jsonrpc2" + "go.lsp.dev/protocol" lsp "go.lsp.dev/protocol" "go.lsp.dev/uri" ) @@ -22,36 +22,7 @@ import ( // must be relative to this file var TEST_DATA_DIR = "../../../testdata/charts/bitnami/" -type jsonRpcDiagnostics struct { - Params lsp.PublishDiagnosticsParams `json:"params"` - Jsonrpc string `json:"jsonrpc"` - Method string `json:"method"` -} - -type readWriteCloseMock struct { - diagnosticsChan chan lsp.PublishDiagnosticsParams -} - -func (proc readWriteCloseMock) Read(p []byte) (int, error) { - return 1, nil -} - -func (proc readWriteCloseMock) Write(p []byte) (int, error) { - if strings.HasPrefix(string(p), "Content-Length: ") { - return 1, nil - } - var diagnostics jsonRpcDiagnostics - json.NewDecoder(strings.NewReader(string(p))).Decode(&diagnostics) - - proc.diagnosticsChan <- diagnostics.Params - return 1, nil -} - -func (proc readWriteCloseMock) Close() error { - return nil -} - -func readTestFiles(dir string, channel chan<- lsp.DidOpenTextDocumentParams, doneChan chan<- int) { +func readTestFiles(dir string, channel chan<- string, doneChan chan<- int) { libRegEx, e := regexp.Compile(".*(/|\\\\)templates(/|\\\\).*\\.ya?ml") if e != nil { log.Fatal(e) @@ -65,16 +36,8 @@ func readTestFiles(dir string, channel chan<- lsp.DidOpenTextDocumentParams, don count := 0 filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error { if d.Type().IsRegular() && libRegEx.MatchString(path) { - contentBytes, _ := os.ReadFile(path) count++ - channel <- lsp.DidOpenTextDocumentParams{ - TextDocument: lsp.TextDocumentItem{ - URI: uri.File(path), - LanguageID: "", - Version: 0, - Text: string(contentBytes), - }, - } + channel <- path } return nil }) @@ -84,15 +47,13 @@ func readTestFiles(dir string, channel chan<- lsp.DidOpenTextDocumentParams, don func sendTestFilesToYamlls(documents *lsplocal.DocumentStore, yamllsConnector *Connector, doneReadingFilesChan <-chan int, doneSendingFilesChan chan<- int, - filesChan <-chan lsp.DidOpenTextDocumentParams, + filesChan <-chan string, ) { ownCount := 0 for { select { case d := <-filesChan: - documents.DidOpen(d, util.DefaultConfig) - tree := lsplocal.ParseAst(nil, d.TextDocument.Text) - yamllsConnector.DocumentDidOpen(tree, d) + openFile(&testing.T{}, documents, d, yamllsConnector) ownCount++ case count := <-doneReadingFilesChan: if count != ownCount { @@ -105,13 +66,9 @@ func sendTestFilesToYamlls(documents *lsplocal.DocumentStore, yamllsConnector *C } func TestYamllsDiagnosticsIntegration(t *testing.T) { - diagnosticsChan := make(chan lsp.PublishDiagnosticsParams) doneReadingFilesChan := make(chan int) doneSendingFilesChan := make(chan int) - dir := t.TempDir() - documents := lsplocal.NewDocumentStore() - con := jsonrpc2.NewConn(jsonrpc2.NewStream(readWriteCloseMock{diagnosticsChan})) config := util.DefaultConfig.YamllsConfiguration yamllsSettings := util.DefaultYamllsSettings @@ -122,15 +79,9 @@ func TestYamllsDiagnosticsIntegration(t *testing.T) { Enable: false, } config.YamllsSettings = yamllsSettings - yamllsConnector := NewConnector(config, con, documents) - - if yamllsConnector.Conn == nil { - t.Fatal("Could not connect to yaml-language-server") - } - - yamllsConnector.CallInitialize(uri.File(dir)) + yamllsConnector, documents, diagnosticsChan := getYamlLsConnector(t, config) - didOpenChan := make(chan lsp.DidOpenTextDocumentParams) + didOpenChan := make(chan string) go readTestFiles(TEST_DATA_DIR, didOpenChan, doneReadingFilesChan) go sendTestFilesToYamlls(documents, yamllsConnector, doneReadingFilesChan, doneSendingFilesChan, didOpenChan) @@ -162,3 +113,57 @@ func TestYamllsDiagnosticsIntegration(t *testing.T) { assert.LessOrEqual(t, diagnosticsCount, 23) assert.Equal(t, 2368, sentCount, "Count of files in test data not equal to actual count") } + +func TestYamllsDiagnosticsIntegrationWithSchema(t *testing.T) { + t.Parallel() + diagnosticsChan := make(chan lsp.PublishDiagnosticsParams) + + config := util.DefaultConfig.YamllsConfiguration + yamllsConnector, documents, diagnosticsChan := getYamlLsConnector(t, config) + file := filepath.Join("..", "..", "..", "testdata", "example", "templates", "service.yaml") + openFile(t, documents, file, yamllsConnector) + + expected := lsp.Diagnostic{ + Range: protocol.Range{ + Start: protocol.Position{ + Line: 1.0, + Character: 0, + }, + End: protocol.Position{ + Line: 1, + Character: 5, + }, + }, + Severity: 1, + Code: 0.0, + CodeDescription: nil, + Source: "yaml-schema: https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.22.4-standalone-strict/_definitions.json", + Message: "Yamlls: Property wrong is not allowed.", + Tags: nil, + RelatedInformation: nil, + Data: map[string]interface{}{ + "properties": []interface{}{ + "status", + }, + "schemaUri": []interface{}{ + "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.22.4-standalone-strict/_definitions.json", + }, + }, + } + + diagnostic := []lsp.Diagnostic{} + afterCh := time.After(10 * time.Second) + for { + if len(diagnostic) > 0 { + break + } + select { + case d := <-diagnosticsChan: + diagnostic = append(diagnostic, d.Diagnostics...) + case <-afterCh: + t.Fatal("Timed out waiting for diagnostics") + } + } + + assert.Contains(t, diagnostic, expected) +} diff --git a/internal/adapter/yamlls/documentSync.go b/internal/adapter/yamlls/documentSync.go index 167d0f6c..02c9bae7 100644 --- a/internal/adapter/yamlls/documentSync.go +++ b/internal/adapter/yamlls/documentSync.go @@ -10,7 +10,7 @@ import ( ) func (yamllsConnector Connector) InitiallySyncOpenDocuments(docs []*lsplocal.Document) { - if yamllsConnector.Conn == nil { + if yamllsConnector.server == nil { return } for _, doc := range docs { @@ -25,24 +25,24 @@ func (yamllsConnector Connector) InitiallySyncOpenDocuments(docs []*lsplocal.Doc func (yamllsConnector Connector) DocumentDidOpen(ast *sitter.Tree, params lsp.DidOpenTextDocumentParams) { logger.Debug("YamllsConnector DocumentDidOpen", params.TextDocument.URI) - if yamllsConnector.Conn == nil { + if yamllsConnector.server == nil { return } params.TextDocument.Text = lsplocal.TrimTemplate(ast, params.TextDocument.Text) - err := (*yamllsConnector.Conn).Notify(context.Background(), lsp.MethodTextDocumentDidOpen, params) + err := yamllsConnector.server.DidOpen(context.Background(), ¶ms) if err != nil { logger.Error("Error calling yamlls for didOpen", err) } } func (yamllsConnector Connector) DocumentDidSave(doc *lsplocal.Document, params lsp.DidSaveTextDocumentParams) { - if yamllsConnector.Conn == nil { + if yamllsConnector.server == nil { return } params.Text = lsplocal.TrimTemplate(doc.Ast, doc.Content) - err := (*yamllsConnector.Conn).Notify(context.Background(), lsp.MethodTextDocumentDidSave, params) + err := yamllsConnector.server.DidSave(context.Background(), ¶ms) if err != nil { logger.Error("Error calling yamlls for didSave", err) } @@ -55,7 +55,7 @@ func (yamllsConnector Connector) DocumentDidSave(doc *lsplocal.Document, params } func (yamllsConnector Connector) DocumentDidChange(doc *lsplocal.Document, params lsp.DidChangeTextDocumentParams) { - if yamllsConnector.Conn == nil { + if yamllsConnector.server == nil { return } trimmedText := lsplocal.TrimTemplate(doc.Ast, doc.Content) @@ -77,14 +77,14 @@ func (yamllsConnector Connector) DocumentDidChange(doc *lsplocal.Document, param } logger.Debug("Sending DocumentDidChange", params) - err := (*yamllsConnector.Conn).Notify(context.Background(), lsp.MethodTextDocumentDidChange, params) + err := yamllsConnector.server.DidChange(context.Background(), ¶ms) if err != nil { logger.Println("Error calling yamlls for didChange", err) } } func (yamllsConnector Connector) DocumentDidChangeFullSync(doc *lsplocal.Document, params lsp.DidChangeTextDocumentParams) { - if yamllsConnector.Conn == nil { + if yamllsConnector.server == nil { return } @@ -98,7 +98,7 @@ func (yamllsConnector Connector) DocumentDidChangeFullSync(doc *lsplocal.Documen } logger.Println("Sending DocumentDidChange with full sync", params) - err := (*yamllsConnector.Conn).Notify(context.Background(), lsp.MethodTextDocumentDidChange, params) + err := yamllsConnector.server.DidChange(context.Background(), ¶ms) if err != nil { logger.Println("Error calling yamlls for didChange", err) } diff --git a/internal/adapter/yamlls/handler.go b/internal/adapter/yamlls/handler.go deleted file mode 100644 index 05416f5c..00000000 --- a/internal/adapter/yamlls/handler.go +++ /dev/null @@ -1,26 +0,0 @@ -package yamlls - -import ( - "context" - - lsplocal "github.com/mrjosh/helm-ls/internal/lsp" - "go.lsp.dev/jsonrpc2" - lsp "go.lsp.dev/protocol" -) - -func (yamllsConnector *Connector) yamllsHandler(clientConn jsonrpc2.Conn, documents *lsplocal.DocumentStore) jsonrpc2.Handler { - return func(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error { - - switch req.Method() { - case lsp.MethodTextDocumentPublishDiagnostics: - yamllsConnector.handleDiagnostics(req, clientConn, documents) - case lsp.MethodWorkspaceConfiguration: - settings := yamllsConnector.handleConfiguration(req) - return reply(ctx, settings, nil) - default: - logger.Debug("Method not handled by yamlls handler: ", req.Method()) - } - - return reply(ctx, true, nil) - } -} diff --git a/internal/adapter/yamlls/hover.go b/internal/adapter/yamlls/hover.go index 41627990..92b12453 100644 --- a/internal/adapter/yamlls/hover.go +++ b/internal/adapter/yamlls/hover.go @@ -2,7 +2,6 @@ package yamlls import ( "context" - "reflect" "github.com/mrjosh/helm-ls/internal/util" lsp "go.lsp.dev/protocol" @@ -11,49 +10,37 @@ import ( // Calls the Hover method of yamlls to get a fitting hover response // If hover returns nothing appropriate, calls yamlls for completions func (yamllsConnector Connector) CallHover(ctx context.Context, params lsp.HoverParams, word string) (*lsp.Hover, error) { - if yamllsConnector.Conn == nil { + if yamllsConnector.server == nil { return &lsp.Hover{}, nil } - hoverResponse, err := (yamllsConnector).getHoverFromHover(ctx, params) + hoverResponse, err := yamllsConnector.server.Hover(ctx, ¶ms) if err != nil { return hoverResponse, err } - if hoverResponse.Contents.Value != "" { + if hoverResponse != nil && hoverResponse.Contents.Value != "" { return hoverResponse, nil } return (yamllsConnector).getHoverFromCompletion(ctx, params, word) } -func (yamllsConnector Connector) getHoverFromHover(ctx context.Context, params lsp.HoverParams) (*lsp.Hover, error) { - - var hoverResponse = reflect.New(reflect.TypeOf(lsp.Hover{})).Interface() - _, err := (*yamllsConnector.Conn).Call(ctx, lsp.MethodTextDocumentHover, params, hoverResponse) - if err != nil { - logger.Error("Error calling yamlls for hover", err) - return &lsp.Hover{}, err - } - logger.Debug("Got hover from yamlls", hoverResponse.(*lsp.Hover).Contents.Value) - return hoverResponse.(*lsp.Hover), nil -} - func (yamllsConnector Connector) getHoverFromCompletion(ctx context.Context, params lsp.HoverParams, word string) (*lsp.Hover, error) { var ( - err error - documentation string - completionResponse = reflect.New(reflect.TypeOf(lsp.CompletionList{})).Interface() - completionParams = lsp.CompletionParams{ + err error + documentation string + completionParams = lsp.CompletionParams{ TextDocumentPositionParams: params.TextDocumentPositionParams, } ) - _, err = (*yamllsConnector.Conn).Call(ctx, lsp.MethodTextDocumentCompletion, completionParams, completionResponse) + + completionList, err := yamllsConnector.server.Completion(ctx, &completionParams) if err != nil { logger.Error("Error calling yamlls for Completion", err) return &lsp.Hover{}, err } - for _, completionItem := range completionResponse.(*lsp.CompletionList).Items { + for _, completionItem := range completionList.Items { if completionItem.InsertText == word { documentation = completionItem.Documentation.(string) break @@ -61,5 +48,5 @@ func (yamllsConnector Connector) getHoverFromCompletion(ctx context.Context, par } response := util.BuildHoverResponse(documentation, lsp.Range{}) - return &response, nil + return response, nil } diff --git a/internal/adapter/yamlls/hover_integration_test.go b/internal/adapter/yamlls/hover_integration_test.go new file mode 100644 index 00000000..c4b97754 --- /dev/null +++ b/internal/adapter/yamlls/hover_integration_test.go @@ -0,0 +1,79 @@ +//go:build integration + +package yamlls + +import ( + "context" + "strings" + "testing" + "time" + + "github.com/mrjosh/helm-ls/internal/util" + "github.com/stretchr/testify/assert" + lsp "go.lsp.dev/protocol" + "go.lsp.dev/uri" +) + +func TestYamllsHoverIntegration(t *testing.T) { + config := util.DefaultConfig.YamllsConfiguration + + testCases := []struct { + desc string + file string + position lsp.Position + + word string + expected string + }{ + { + desc: "test hover on deployment.yaml", + file: "../../../testdata/example/templates/deployment.yaml", + position: lsp.Position{ + Line: 0, + Character: 0, + }, + word: "apiVersion", + expected: "APIVersion defines the versioned schema of this representation of an object", + }, + { + desc: "test hover on deployment.yaml, template", + file: "../../../testdata/example/templates/deployment.yaml", + position: lsp.Position{ + Line: 13, + Character: 3, + }, + word: "template", + expected: "Template describes the pods that will be created", + }, + { + desc: "test hover on file without templates", + file: "../../../testdata/example/templates/deployment-no-templates.yaml", + position: lsp.Position{ + Line: 0, + Character: 1, + }, + word: "", + expected: "APIVersion defines the versioned schema of this representation of an object", + }, + } + for _, tt1 := range testCases { + tt := tt1 + t.Run(tt.desc, func(t *testing.T) { + t.Parallel() + yamllsConnector, documents, _ := getYamlLsConnector(t, config) + openFile(t, documents, tt.file, yamllsConnector) + + assert.Eventually(t, func() bool { + result, err := yamllsConnector.CallHover(context.Background(), lsp.HoverParams{ + TextDocumentPositionParams: lsp.TextDocumentPositionParams{ + TextDocument: lsp.TextDocumentIdentifier{ + URI: uri.File(tt.file), + }, + Position: tt.position, + }, + }, tt.word) + return err == nil && strings.Contains(result.Contents.Value, tt.expected) + }, time.Second*10, time.Second/2) + }) + } +} diff --git a/internal/adapter/yamlls/initization.go b/internal/adapter/yamlls/initization.go index 7d1a3ac6..396b6ae5 100644 --- a/internal/adapter/yamlls/initization.go +++ b/internal/adapter/yamlls/initization.go @@ -8,9 +8,9 @@ import ( "go.lsp.dev/uri" ) -func (yamllsConnector Connector) CallInitialize(workspaceURI uri.URI) { - if yamllsConnector.Conn == nil { - return +func (yamllsConnector Connector) CallInitialize(ctx context.Context, workspaceURI uri.URI) error { + if yamllsConnector.server == nil { + return nil } params := lsp.InitializeParams{ @@ -21,21 +21,13 @@ func (yamllsConnector Connector) CallInitialize(workspaceURI uri.URI) { }, } - var response interface{} - _, err := (*yamllsConnector.Conn).Call(context.Background(), lsp.MethodInitialize, params, response) + _, err := yamllsConnector.server.Initialize(ctx, ¶ms) if err != nil { - logger.Error("Error calling yamlls for initialize", err) - return + return err } - err = (*yamllsConnector.Conn).Notify(context.Background(), lsp.MethodInitialized, params) - - if err != nil { - logger.Error("Error calling yamlls for initialized", err) - } - - changeConfigurationParams := lsp.DidChangeConfigurationParams{} - err = (*yamllsConnector.Conn).Notify(context.Background(), lsp.MethodWorkspaceDidChangeConfiguration, changeConfigurationParams) + err = yamllsConnector.server.DidChangeConfiguration(ctx, &lsp.DidChangeConfigurationParams{}) if err != nil { - logger.Error("Error calling yamlls for didChangeConfiguration", err) + return err } + return yamllsConnector.server.Initialized(ctx, &lsp.InitializedParams{}) } diff --git a/internal/adapter/yamlls/integration_test_utils.go b/internal/adapter/yamlls/integration_test_utils.go new file mode 100644 index 00000000..33aae38f --- /dev/null +++ b/internal/adapter/yamlls/integration_test_utils.go @@ -0,0 +1,86 @@ +//go:build integration + +package yamlls + +import ( + "context" + "encoding/json" + "os" + "strings" + "testing" + + lsplocal "github.com/mrjosh/helm-ls/internal/lsp" + "github.com/mrjosh/helm-ls/internal/util" + "go.lsp.dev/jsonrpc2" + "go.lsp.dev/protocol" + lsp "go.lsp.dev/protocol" + "go.lsp.dev/uri" + "go.uber.org/zap" +) + +type jsonRpcDiagnostics struct { + Params lsp.PublishDiagnosticsParams `json:"params"` + Jsonrpc string `json:"jsonrpc"` + Method string `json:"method"` +} + +type readWriteCloseMock struct { + diagnosticsChan chan lsp.PublishDiagnosticsParams +} + +func (proc readWriteCloseMock) Read(p []byte) (int, error) { + return 1, nil +} + +func (proc readWriteCloseMock) Write(p []byte) (int, error) { + if strings.HasPrefix(string(p), "Content-Length: ") { + return 1, nil + } + var diagnostics jsonRpcDiagnostics + json.NewDecoder(strings.NewReader(string(p))).Decode(&diagnostics) + + proc.diagnosticsChan <- diagnostics.Params + return 1, nil +} + +func (proc readWriteCloseMock) Close() error { + return nil +} + +func getYamlLsConnector(t *testing.T, config util.YamllsConfiguration) (*Connector, *lsplocal.DocumentStore, chan lsp.PublishDiagnosticsParams) { + dir := t.TempDir() + documents := lsplocal.NewDocumentStore() + diagnosticsChan := make(chan lsp.PublishDiagnosticsParams) + con := jsonrpc2.NewConn(jsonrpc2.NewStream(readWriteCloseMock{diagnosticsChan})) + zapLogger, _ := zap.NewProduction() + client := protocol.ClientDispatcher(con, zapLogger) + + yamllsConnector := NewConnector(context.Background(), config, client, documents) + + if yamllsConnector.server == nil { + t.Fatal("Could not connect to yaml-language-server") + } + + yamllsConnector.CallInitialize(context.Background(), uri.File(dir)) + + return yamllsConnector, documents, diagnosticsChan +} + +func openFile(t *testing.T, documents *lsplocal.DocumentStore, path string, yamllsConnector *Connector) { + fileURI := uri.File(path) + + content, err := os.ReadFile(path) + if err != nil { + t.Fatal("Could not read test file", err) + } + d := lsp.DidOpenTextDocumentParams{ + TextDocument: lsp.TextDocumentItem{ + URI: fileURI, + LanguageID: "", + Version: 0, + Text: string(content), + }, + } + doc, err := documents.DidOpen(&d, util.DefaultConfig) + yamllsConnector.DocumentDidOpen(doc.Ast, d) +} diff --git a/internal/adapter/yamlls/yamlls.go b/internal/adapter/yamlls/yamlls.go index 3b6a6ed5..9349f0b2 100644 --- a/internal/adapter/yamlls/yamlls.go +++ b/internal/adapter/yamlls/yamlls.go @@ -2,22 +2,28 @@ package yamlls import ( "context" + "io" + "os" "os/exec" "github.com/mrjosh/helm-ls/internal/log" lsplocal "github.com/mrjosh/helm-ls/internal/lsp" "github.com/mrjosh/helm-ls/internal/util" "go.lsp.dev/jsonrpc2" + "go.lsp.dev/protocol" + "go.uber.org/zap" ) var logger = log.GetLogger() type Connector struct { - Conn *jsonrpc2.Conn - config util.YamllsConfiguration + config util.YamllsConfiguration + server protocol.Server + documents *lsplocal.DocumentStore + client protocol.Client } -func NewConnector(yamllsConfiguration util.YamllsConfiguration, clientConn jsonrpc2.Conn, documents *lsplocal.DocumentStore) *Connector { +func NewConnector(ctx context.Context, yamllsConfiguration util.YamllsConfiguration, client protocol.Client, documents *lsplocal.DocumentStore) *Connector { yamllsCmd := exec.Command(yamllsConfiguration.Path, "--stdio") stdin, err := yamllsCmd.StdinPipe() @@ -31,6 +37,12 @@ func NewConnector(yamllsConfiguration util.YamllsConfiguration, clientConn jsonr return &Connector{} } + strderr, err := yamllsCmd.StderrPipe() + if err != nil { + logger.Error("Could not connect to stderr of yaml-language-server, some features may be missing.") + return &Connector{} + } + readWriteCloser := readWriteCloseSubprocess{ stout, stdin, @@ -50,10 +62,16 @@ func NewConnector(yamllsConfiguration util.YamllsConfiguration, clientConn jsonr return &Connector{} } } - var yamllsConnector = Connector{} - conn := jsonrpc2.NewConn(jsonrpc2.NewStream(readWriteCloser)) - yamllsConnector.config = yamllsConfiguration - conn.Go(context.Background(), yamllsConnector.yamllsHandler(clientConn, documents)) - yamllsConnector.Conn = &conn + + go func() { + io.Copy(os.Stderr, strderr) + }() + + yamllsConnector := Connector{documents: documents, config: yamllsConfiguration, client: client} + + zapLogger, _ := zap.NewProduction() + _, _, server := protocol.NewClient(ctx, yamllsConnector, jsonrpc2.NewStream(readWriteCloser), zapLogger) + + yamllsConnector.server = server return &yamllsConnector } diff --git a/internal/handler/completion.go b/internal/handler/completion.go index 4fccc320..2838a32e 100644 --- a/internal/handler/completion.go +++ b/internal/handler/completion.go @@ -2,7 +2,6 @@ package handler import ( "context" - "encoding/json" "errors" "fmt" "reflect" @@ -13,7 +12,7 @@ import ( gotemplate "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" "github.com/mrjosh/helm-ls/pkg/chartutil" sitter "github.com/smacker/go-tree-sitter" - "go.lsp.dev/jsonrpc2" + "go.lsp.dev/protocol" lsp "go.lsp.dev/protocol" yaml "gopkg.in/yaml.v2" @@ -33,19 +32,10 @@ func init() { textCompletionsItems = append(textCompletionsItems, getTextCompletionItems(godocs.TextSnippets)...) } -func (h *langHandler) handleTextDocumentCompletion(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) (err error) { - if req.Params() == nil { - return &jsonrpc2.Error{Code: jsonrpc2.InvalidParams} - } - - var params lsp.CompletionParams - if err := json.Unmarshal(req.Params(), ¶ms); err != nil { - return err - } - +func (h *langHandler) Completion(ctx context.Context, params *lsp.CompletionParams) (result *lsp.CompletionList, err error) { doc, ok := h.documents.Get(params.TextDocument.URI) if !ok { - return errors.New("Could not get document: " + params.TextDocument.URI.Filename()) + return nil, errors.New("Could not get document: " + params.TextDocument.URI.Filename()) } chart, err := h.chartStore.GetChartForDoc(params.TextDocument.URI) if err != nil { @@ -58,7 +48,7 @@ func (h *langHandler) handleTextDocumentCompletion(ctx context.Context, reply js result := make([]lsp.CompletionItem, 0) result = append(result, textCompletionsItems...) result = append(result, yamllsCompletions(ctx, h, params)...) - return reply(ctx, result, err) + return &protocol.CompletionList{IsIncomplete: false, Items: result}, err } var ( @@ -78,8 +68,17 @@ func (h *langHandler) handleTextDocumentCompletion(ctx context.Context, reply js logger.Println(fmt.Sprintf("Word found for completions is < %s >", word)) + items = make([]lsp.CompletionItem, 0) + for _, v := range basicItems { + items = append(items, lsp.CompletionItem{ + Label: v.Name, + InsertText: v.Name, + Detail: v.Detail, + Documentation: v.Doc, + }) + } if len(variableSplitted) == 0 { - return reply(ctx, basicItems, err) + return &lsp.CompletionList{IsIncomplete: false, Items: items}, err } // $ always points to the root context so we can safely remove it @@ -104,11 +103,15 @@ func (h *langHandler) handleTextDocumentCompletion(ctx context.Context, reply js items = append(items, functionsCompletionItems...) } - return reply(ctx, items, err) + return &lsp.CompletionList{IsIncomplete: false, Items: items}, err } -func yamllsCompletions(ctx context.Context, h *langHandler, params lsp.CompletionParams) []lsp.CompletionItem { - response := *h.yamllsConnector.CallCompletion(ctx, params) +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 } diff --git a/internal/handler/configuration.go b/internal/handler/configuration.go index d5b088eb..a2602839 100644 --- a/internal/handler/configuration.go +++ b/internal/handler/configuration.go @@ -2,38 +2,57 @@ package handler import ( "context" + "encoding/json" + // "reflect" "github.com/mrjosh/helm-ls/internal/util" - "go.lsp.dev/jsonrpc2" lsp "go.lsp.dev/protocol" ) -func (h *langHandler) handleWorkspaceDidChangeConfiguration(ctx context.Context, reply jsonrpc2.Replier, _ jsonrpc2.Request) (err error) { +func (h *langHandler) DidChangeConfiguration(ctx context.Context, params *lsp.DidChangeConfigurationParams) (err error) { // go h.retrieveWorkspaceConfiguration(ctx) logger.Println("Changing workspace config is not implemented") - return reply(ctx, nil, nil) + return nil } func (h *langHandler) retrieveWorkspaceConfiguration(ctx context.Context) { - logger.Println("Calling workspace/configuration") - result := []util.HelmlsConfiguration{util.DefaultConfig} - - _, err := h.connPool.Call(ctx, lsp.MethodWorkspaceConfiguration, lsp.ConfigurationParams{ + logger.Debug("Calling workspace/configuration") + configurationParams := lsp.ConfigurationParams{ Items: []lsp.ConfigurationItem{{Section: "helm-ls"}}, - }, &result) + } + rawResult, err := h.client.Configuration(ctx, &configurationParams) if err != nil { logger.Println("Error calling workspace/configuration", err) - } else { - logger.Println("Workspace configuration:", result) + h.initializationWithConfig(ctx) + return } - if len(result) == 0 { + h.helmlsConfig = parseWorkspaceConfiguration(rawResult, h.helmlsConfig) + logger.Println("Workspace configuration:", h.helmlsConfig) + h.initializationWithConfig(ctx) +} + +func parseWorkspaceConfiguration(rawResult []interface{}, currentConfig util.HelmlsConfiguration) (result util.HelmlsConfiguration) { + logger.Debug("Raw Workspace configuration:", rawResult) + + if len(rawResult) == 0 { logger.Println("Workspace configuration is empty") - return + return currentConfig + } + + jsonResult, err := json.Marshal(rawResult[0]) + if err != nil { + logger.Println("Error marshalling workspace/configuration", err) + return currentConfig } - h.helmlsConfig = result[0] - h.initializationWithConfig() + result = currentConfig + err = json.Unmarshal(jsonResult, &result) + if err != nil { + logger.Println("Error unmarshalling workspace/configuration", err) + return currentConfig + } + return result } diff --git a/internal/handler/configuration_test.go b/internal/handler/configuration_test.go new file mode 100644 index 00000000..a3298d80 --- /dev/null +++ b/internal/handler/configuration_test.go @@ -0,0 +1,106 @@ +package handler + +import ( + "context" + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + + "github.com/mrjosh/helm-ls/internal/charts" + "github.com/mrjosh/helm-ls/internal/util" + mocks "github.com/mrjosh/helm-ls/mocks/go.lsp.dev/protocol" + lsp "go.lsp.dev/protocol" + "go.lsp.dev/uri" +) + +var configurationParams = lsp.ConfigurationParams{Items: []lsp.ConfigurationItem{{Section: "helm-ls"}}} + +func TestConfigurationWorks(t *testing.T) { + mockClient := mocks.NewMockClient(t) + handler := &langHandler{ + helmlsConfig: util.DefaultConfig, + chartStore: charts.NewChartStore(uri.File("/"), charts.NewChart), + } + handler.client = mockClient + + userConfig := []interface{}{map[string]interface{}{ + "LogLevel": "debug", + // disable yamlls to avoid configuring it in the test + "yamlls": map[string]interface{}{"enabled": false}, + }} + mockClient.EXPECT().Configuration(mock.Anything, &configurationParams).Return(userConfig, nil) + handler.retrieveWorkspaceConfiguration(context.Background()) + + expectedConfig := util.DefaultConfig + expectedConfig.LogLevel = "debug" + expectedConfig.YamllsConfiguration.Enabled = false + assert.Equal(t, expectedConfig, handler.helmlsConfig) +} + +func TestConfigurationWorksForEmptyConfig(t *testing.T) { + mockClient := mocks.NewMockClient(t) + handler := &langHandler{ + helmlsConfig: util.DefaultConfig, + chartStore: charts.NewChartStore(uri.File("/"), charts.NewChart), + } + handler.client = mockClient + // disable yamlls to avoid configuring it in the test + handler.helmlsConfig.YamllsConfiguration.Enabled = false + + userConfig := []interface{}{} + mockClient.EXPECT().Configuration(mock.Anything, &configurationParams).Return(userConfig, nil) + handler.retrieveWorkspaceConfiguration(context.Background()) + + expectedConfig := util.DefaultConfig + expectedConfig.YamllsConfiguration.Enabled = false + assert.Equal(t, expectedConfig, handler.helmlsConfig) +} + +func TestConfigurationWorksForError(t *testing.T) { + mockClient := mocks.NewMockClient(t) + handler := &langHandler{ + helmlsConfig: util.DefaultConfig, + chartStore: charts.NewChartStore(uri.File("/"), charts.NewChart), + } + handler.client = mockClient + + // disable yamlls to avoid configuring it in the test + handler.helmlsConfig.YamllsConfiguration.Enabled = false + + userConfig := []interface{}{map[string]interface{}{ + "LogLevel": "debug", + }} + mockClient.EXPECT().Configuration(mock.Anything, &configurationParams).Return(userConfig, errors.New("error")) + handler.retrieveWorkspaceConfiguration(context.Background()) + + expectedConfig := util.DefaultConfig + expectedConfig.YamllsConfiguration.Enabled = false + assert.Equal(t, expectedConfig, handler.helmlsConfig) +} + +func TestConfigurationWorksForJsonError(t *testing.T) { + mockClient := mocks.NewMockClient(t) + handler := &langHandler{ + helmlsConfig: util.DefaultConfig, + chartStore: charts.NewChartStore(uri.File("/"), charts.NewChart), + } + handler.client = mockClient + + // disable yamlls to avoid configuring it in the test + handler.helmlsConfig.YamllsConfiguration.Enabled = false + + userConfig := []interface{}{map[string]interface{}{ + "LogLevel": "debug", + "test": func() { + return + }, + }} + mockClient.EXPECT().Configuration(mock.Anything, &configurationParams).Return(userConfig, nil) + handler.retrieveWorkspaceConfiguration(context.Background()) + + expectedConfig := util.DefaultConfig + expectedConfig.YamllsConfiguration.Enabled = false + assert.Equal(t, expectedConfig, handler.helmlsConfig) +} diff --git a/internal/handler/definition.go b/internal/handler/definition.go index 00c42901..408b26af 100644 --- a/internal/handler/definition.go +++ b/internal/handler/definition.go @@ -2,7 +2,6 @@ package handler import ( "context" - "encoding/json" "errors" "fmt" "strings" @@ -12,39 +11,29 @@ import ( gotemplate "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" "github.com/mrjosh/helm-ls/internal/util" sitter "github.com/smacker/go-tree-sitter" - "go.lsp.dev/jsonrpc2" lsp "go.lsp.dev/protocol" "gopkg.in/yaml.v3" ) -func (h *langHandler) handleDefinition(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) (err error) { - if req.Params() == nil { - return &jsonrpc2.Error{Code: jsonrpc2.InvalidParams} - } - - var params lsp.DefinitionParams - if err := json.Unmarshal(req.Params(), ¶ms); err != nil { - return err - } - +func (h *langHandler) Definition(ctx context.Context, params *lsp.DefinitionParams) (result []lsp.Location, err error) { doc, ok := h.documents.Get(params.TextDocument.URI) if !ok { - return errors.New("Could not get document: " + params.TextDocument.URI.Filename()) + return nil, errors.New("Could not get document: " + params.TextDocument.URI.Filename()) } chart, err := h.chartStore.GetChartForDoc(params.TextDocument.URI) if err != nil { logger.Error("Error getting chart info for file", params.TextDocument.URI, err) } - result, err := h.definitionAstParsing(chart, doc, params.Position) + result, err = h.definitionAstParsing(chart, doc, params.Position) if err != nil { // suppress errors for clients // otherwise using go-to-definition on words that have no definition // will result in an error logger.Println("Error getting definitions", err) - return reply(ctx, nil, nil) + return nil, nil } - return reply(ctx, result, err) + return result, nil } func (h *langHandler) definitionAstParsing(chart *charts.Chart, doc *lsplocal.Document, position lsp.Position) ([]lsp.Location, error) { diff --git a/internal/handler/handler.go b/internal/handler/handler.go index 983b5a12..4898aa97 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -2,15 +2,16 @@ package handler import ( "context" - "encoding/json" - "errors" + "io" "github.com/mrjosh/helm-ls/internal/adapter/yamlls" "github.com/mrjosh/helm-ls/internal/charts" lsplocal "github.com/mrjosh/helm-ls/internal/lsp" "github.com/mrjosh/helm-ls/internal/util" "go.lsp.dev/jsonrpc2" + "go.lsp.dev/protocol" lsp "go.lsp.dev/protocol" + "go.uber.org/zap" "github.com/mrjosh/helm-ls/internal/log" ) @@ -18,6 +19,7 @@ import ( var logger = log.GetLogger() type langHandler struct { + client protocol.Client connPool jsonrpc2.Conn linterName string documents *lsplocal.DocumentStore @@ -26,9 +28,25 @@ type langHandler struct { helmlsConfig util.HelmlsConfiguration } -func NewHandler(connPool jsonrpc2.Conn) jsonrpc2.Handler { +func StartHandler(stream io.ReadWriteCloser) { + logger, _ := zap.NewProduction() + + server := newHandler(nil, nil) + _, conn, client := protocol.NewServer(context.Background(), + server, + jsonrpc2.NewStream(stream), + logger, + ) + server.connPool = conn + server.client = client + + <-conn.Done() +} + +func newHandler(connPool jsonrpc2.Conn, client protocol.Client) *langHandler { documents := lsplocal.NewDocumentStore() handler := &langHandler{ + client: client, linterName: "helm-lint", connPool: connPool, documents: documents, @@ -36,108 +54,236 @@ func NewHandler(connPool jsonrpc2.Conn) jsonrpc2.Handler { yamllsConnector: &yamlls.Connector{}, } logger.Printf("helm-lint-langserver: connections opened") - return jsonrpc2.ReplyHandler(handler.handle) -} - -func (h *langHandler) handle(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error { - logger.Debug("helm-lint-langserver: request method:", req.Method()) - - switch req.Method() { - case lsp.MethodInitialize: - return h.handleInitialize(ctx, reply, req) - case lsp.MethodInitialized: - go h.retrieveWorkspaceConfiguration(ctx) - return reply(ctx, nil, nil) - case lsp.MethodShutdown: - return h.handleShutdown(ctx, reply, req) - case lsp.MethodTextDocumentDidOpen: - return h.handleTextDocumentDidOpen(ctx, reply, req) - case lsp.MethodTextDocumentDidClose: - return h.handleTextDocumentDidClose(ctx, reply, req) - case lsp.MethodTextDocumentDidChange: - return h.handleTextDocumentDidChange(ctx, reply, req) - case lsp.MethodTextDocumentDidSave: - return h.handleTextDocumentDidSave(ctx, reply, req) - case lsp.MethodTextDocumentCompletion: - return h.handleTextDocumentCompletion(ctx, reply, req) - case lsp.MethodTextDocumentDefinition: - return h.handleDefinition(ctx, reply, req) - case lsp.MethodTextDocumentHover: - return h.handleHover(ctx, reply, req) - case lsp.MethodWorkspaceDidChangeConfiguration: - return h.handleWorkspaceDidChangeConfiguration(ctx, reply, req) - case lsp.MethodWorkspaceDidChangeWatchedFiles: - return h.handleDidChangeWatchedFiles(ctx, reply, req) - default: - logger.Debug("Unsupported method", req.Method()) - } - return jsonrpc2.MethodNotFoundHandler(ctx, reply, req) + return handler +} + +// CodeAction implements protocol.Server. +func (h *langHandler) CodeAction(ctx context.Context, params *lsp.CodeActionParams) (result []lsp.CodeAction, err error) { + panic("unimplemented") +} + +// CodeLens implements protocol.Server. +func (h *langHandler) CodeLens(ctx context.Context, params *lsp.CodeLensParams) (result []lsp.CodeLens, err error) { + panic("unimplemented") +} + +// CodeLensRefresh implements protocol.Server. +func (h *langHandler) CodeLensRefresh(ctx context.Context) (err error) { + panic("unimplemented") +} + +// CodeLensResolve implements protocol.Server. +func (h *langHandler) CodeLensResolve(ctx context.Context, params *lsp.CodeLens) (result *lsp.CodeLens, err error) { + panic("unimplemented") +} + +// ColorPresentation implements protocol.Server. +func (h *langHandler) ColorPresentation(ctx context.Context, params *lsp.ColorPresentationParams) (result []lsp.ColorPresentation, err error) { + panic("unimplemented") +} + +// CompletionResolve implements protocol.Server. +func (h *langHandler) CompletionResolve(ctx context.Context, params *lsp.CompletionItem) (result *lsp.CompletionItem, err error) { + panic("unimplemented") +} + +// Declaration implements protocol.Server. +func (h *langHandler) Declaration(ctx context.Context, params *lsp.DeclarationParams) (result []lsp.Location, err error) { + panic("unimplemented") +} + +// DidChangeWorkspaceFolders implements protocol.Server. +func (h *langHandler) DidChangeWorkspaceFolders(ctx context.Context, params *lsp.DidChangeWorkspaceFoldersParams) (err error) { + panic("unimplemented") +} + +// DocumentColor implements protocol.Server. +func (h *langHandler) DocumentColor(ctx context.Context, params *lsp.DocumentColorParams) (result []lsp.ColorInformation, err error) { + panic("unimplemented") +} + +// DocumentHighlight implements protocol.Server. +func (h *langHandler) DocumentHighlight(ctx context.Context, params *lsp.DocumentHighlightParams) (result []lsp.DocumentHighlight, err error) { + panic("unimplemented") +} + +// DocumentLink implements protocol.Server. +func (h *langHandler) DocumentLink(ctx context.Context, params *lsp.DocumentLinkParams) (result []lsp.DocumentLink, err error) { + panic("unimplemented") +} + +// DocumentLinkResolve implements protocol.Server. +func (h *langHandler) DocumentLinkResolve(ctx context.Context, params *lsp.DocumentLink) (result *lsp.DocumentLink, err error) { + panic("unimplemented") +} + +// DocumentSymbol implements protocol.Server. +func (h *langHandler) DocumentSymbol(ctx context.Context, params *lsp.DocumentSymbolParams) (result []interface{}, err error) { + panic("unimplemented") +} + +// ExecuteCommand implements protocol.Server. +func (h *langHandler) ExecuteCommand(ctx context.Context, params *lsp.ExecuteCommandParams) (result interface{}, err error) { + panic("unimplemented") +} + +// Exit implements protocol.Server. +func (h *langHandler) Exit(ctx context.Context) (err error) { + panic("unimplemented") +} + +// FoldingRanges implements protocol.Server. +func (h *langHandler) FoldingRanges(ctx context.Context, params *lsp.FoldingRangeParams) (result []lsp.FoldingRange, err error) { + panic("unimplemented") +} + +// Formatting implements protocol.Server. +func (h *langHandler) Formatting(ctx context.Context, params *lsp.DocumentFormattingParams) (result []lsp.TextEdit, err error) { + panic("unimplemented") +} + +// Implementation implements protocol.Server. +func (h *langHandler) Implementation(ctx context.Context, params *lsp.ImplementationParams) (result []lsp.Location, err error) { + panic("unimplemented") +} + +// IncomingCalls implements protocol.Server. +func (h *langHandler) IncomingCalls(ctx context.Context, params *lsp.CallHierarchyIncomingCallsParams) (result []lsp.CallHierarchyIncomingCall, err error) { + panic("unimplemented") +} + +// LinkedEditingRange implements protocol.Server. +func (h *langHandler) LinkedEditingRange(ctx context.Context, params *lsp.LinkedEditingRangeParams) (result *lsp.LinkedEditingRanges, err error) { + panic("unimplemented") +} + +// LogTrace implements protocol.Server. +func (h *langHandler) LogTrace(ctx context.Context, params *lsp.LogTraceParams) (err error) { + panic("unimplemented") } -func (h *langHandler) handleShutdown(_ context.Context, _ jsonrpc2.Replier, _ jsonrpc2.Request) (err error) { +// Moniker implements protocol.Server. +func (h *langHandler) Moniker(ctx context.Context, params *lsp.MonikerParams) (result []lsp.Moniker, err error) { + panic("unimplemented") +} + +// OnTypeFormatting implements protocol.Server. +func (h *langHandler) OnTypeFormatting(ctx context.Context, params *lsp.DocumentOnTypeFormattingParams) (result []lsp.TextEdit, err error) { + panic("unimplemented") +} + +// OutgoingCalls implements protocol.Server. +func (h *langHandler) OutgoingCalls(ctx context.Context, params *lsp.CallHierarchyOutgoingCallsParams) (result []lsp.CallHierarchyOutgoingCall, err error) { + panic("unimplemented") +} + +// PrepareCallHierarchy implements protocol.Server. +func (h *langHandler) PrepareCallHierarchy(ctx context.Context, params *lsp.CallHierarchyPrepareParams) (result []lsp.CallHierarchyItem, err error) { + panic("unimplemented") +} + +// PrepareRename implements protocol.Server. +func (h *langHandler) PrepareRename(ctx context.Context, params *lsp.PrepareRenameParams) (result *lsp.Range, err error) { + panic("unimplemented") +} + +// RangeFormatting implements protocol.Server. +func (h *langHandler) RangeFormatting(ctx context.Context, params *lsp.DocumentRangeFormattingParams) (result []lsp.TextEdit, err error) { + panic("unimplemented") +} + +// References implements protocol.Server. +func (h *langHandler) References(ctx context.Context, params *lsp.ReferenceParams) (result []lsp.Location, err error) { + panic("unimplemented") +} + +// Rename implements protocol.Server. +func (h *langHandler) Rename(ctx context.Context, params *lsp.RenameParams) (result *lsp.WorkspaceEdit, err error) { + panic("unimplemented") +} + +// Request implements protocol.Server. +func (h *langHandler) Request(ctx context.Context, method string, params interface{}) (result interface{}, err error) { + panic("unimplemented") +} + +// SemanticTokensFull implements protocol.Server. +func (h *langHandler) SemanticTokensFull(ctx context.Context, params *lsp.SemanticTokensParams) (result *lsp.SemanticTokens, err error) { + panic("unimplemented") +} + +// SemanticTokensFullDelta implements protocol.Server. +func (h *langHandler) SemanticTokensFullDelta(ctx context.Context, params *lsp.SemanticTokensDeltaParams) (result interface{}, err error) { + panic("unimplemented") +} + +// SemanticTokensRange implements protocol.Server. +func (h *langHandler) SemanticTokensRange(ctx context.Context, params *lsp.SemanticTokensRangeParams) (result *lsp.SemanticTokens, err error) { + panic("unimplemented") +} + +// SemanticTokensRefresh implements protocol.Server. +func (h *langHandler) SemanticTokensRefresh(ctx context.Context) (err error) { + panic("unimplemented") +} + +// SetTrace implements protocol.Server. +func (h *langHandler) SetTrace(ctx context.Context, params *lsp.SetTraceParams) (err error) { + panic("unimplemented") +} + +// ShowDocument implements protocol.Server. +func (h *langHandler) ShowDocument(ctx context.Context, params *lsp.ShowDocumentParams) (result *lsp.ShowDocumentResult, err error) { + panic("unimplemented") +} + +// Shutdown implements protocol.Server. +func (h *langHandler) Shutdown(ctx context.Context) (err error) { return h.connPool.Close() } -func (h *langHandler) handleTextDocumentDidOpen(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) (err error) { - var params lsp.DidOpenTextDocumentParams - if err := json.Unmarshal(req.Params(), ¶ms); err != nil { - return reply(ctx, nil, err) - } +// SignatureHelp implements protocol.Server. +func (h *langHandler) SignatureHelp(ctx context.Context, params *lsp.SignatureHelpParams) (result *lsp.SignatureHelp, err error) { + panic("unimplemented") +} - doc, err := h.documents.DidOpen(params, h.helmlsConfig) - if err != nil { - logger.Println(err) - return reply(ctx, nil, err) - } +// Symbols implements protocol.Server. +func (h *langHandler) Symbols(ctx context.Context, params *lsp.WorkspaceSymbolParams) (result []lsp.SymbolInformation, err error) { + panic("unimplemented") +} - h.yamllsConnector.DocumentDidOpen(doc.Ast, params) +// TypeDefinition implements protocol.Server. +func (h *langHandler) TypeDefinition(ctx context.Context, params *lsp.TypeDefinitionParams) (result []lsp.Location, err error) { + panic("unimplemented") +} - _, err = h.chartStore.GetChartForDoc(doc.URI) - if err != nil { - logger.Error("Error getting chart info for file", doc.URI, err) - } +// WillCreateFiles implements protocol.Server. +func (h *langHandler) WillCreateFiles(ctx context.Context, params *lsp.CreateFilesParams) (result *lsp.WorkspaceEdit, err error) { + panic("unimplemented") +} - doc, ok := h.documents.Get(params.TextDocument.URI) - if !ok { - return errors.New("Could not get document: " + params.TextDocument.URI.Filename()) - } - chart, err := h.chartStore.GetChartForDoc(doc.URI) - if err != nil { - logger.Error("Error getting chart info for file", doc.URI, err) - } - notification, err := lsplocal.NotificationFromLint(ctx, h.connPool, chart, doc) - return reply(ctx, notification, err) -} - -func (h *langHandler) handleTextDocumentDidClose(ctx context.Context, reply jsonrpc2.Replier, _ jsonrpc2.Request) (err error) { - return reply( - ctx, - h.connPool.Notify( - ctx, - lsp.MethodTextDocumentDidClose, - nil, - ), - nil, - ) +// WillDeleteFiles implements protocol.Server. +func (h *langHandler) WillDeleteFiles(ctx context.Context, params *lsp.DeleteFilesParams) (result *lsp.WorkspaceEdit, err error) { + panic("unimplemented") } -func (h *langHandler) handleTextDocumentDidSave(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) (err error) { - var params lsp.DidSaveTextDocumentParams - if err := json.Unmarshal(req.Params(), ¶ms); err != nil { - return err - } +// WillRenameFiles implements protocol.Server. +func (h *langHandler) WillRenameFiles(ctx context.Context, params *lsp.RenameFilesParams) (result *lsp.WorkspaceEdit, err error) { + panic("unimplemented") +} - doc, ok := h.documents.Get(params.TextDocument.URI) - if !ok { - return errors.New("Could not get document: " + params.TextDocument.URI.Filename()) - } - chart, err := h.chartStore.GetChartForDoc(doc.URI) - if err != nil { - logger.Error("Error getting chart info for file", doc.URI, err) - } +// WillSave implements protocol.Server. +func (h *langHandler) WillSave(ctx context.Context, params *lsp.WillSaveTextDocumentParams) (err error) { + panic("unimplemented") +} + +// WillSaveWaitUntil implements protocol.Server. +func (h *langHandler) WillSaveWaitUntil(ctx context.Context, params *lsp.WillSaveTextDocumentParams) (result []lsp.TextEdit, err error) { + panic("unimplemented") +} - h.yamllsConnector.DocumentDidSave(doc, params) - notification, err := lsplocal.NotificationFromLint(ctx, h.connPool, chart, doc) - return reply(ctx, notification, err) +// WorkDoneProgressCancel implements protocol.Server. +func (h *langHandler) WorkDoneProgressCancel(ctx context.Context, params *lsp.WorkDoneProgressCancelParams) (err error) { + panic("unimplemented") } diff --git a/internal/handler/hover.go b/internal/handler/hover.go index c9b2cb2f..3ae3553f 100644 --- a/internal/handler/hover.go +++ b/internal/handler/hover.go @@ -2,7 +2,6 @@ package handler import ( "context" - "encoding/json" "errors" "fmt" "path/filepath" @@ -16,24 +15,14 @@ import ( "github.com/mrjosh/helm-ls/internal/util" "github.com/mrjosh/helm-ls/pkg/chart" "github.com/mrjosh/helm-ls/pkg/chartutil" - "go.lsp.dev/jsonrpc2" lsp "go.lsp.dev/protocol" "go.lsp.dev/uri" ) -func (h *langHandler) handleHover(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) (err error) { - if req.Params() == nil { - return &jsonrpc2.Error{Code: jsonrpc2.InvalidParams} - } - - var params lsp.HoverParams - if err := json.Unmarshal(req.Params(), ¶ms); err != nil { - return err - } - +func (h *langHandler) Hover(ctx context.Context, params *lsp.HoverParams) (result *lsp.Hover, err error) { doc, ok := h.documents.Get(params.TextDocument.URI) if !ok { - return errors.New("Could not get document: " + params.TextDocument.URI.Filename()) + return nil, errors.New("Could not get document: " + params.TextDocument.URI.Filename()) } chart, err := h.chartStore.GetChartForDoc(params.TextDocument.URI) if err != nil { @@ -49,7 +38,7 @@ func (h *langHandler) handleHover(ctx context.Context, reply jsonrpc2.Replier, r if parent == nil { err = errors.New("Could not parse ast correctly") - return reply(ctx, nil, err) + return nil, err } pt := parent.Type() @@ -59,8 +48,8 @@ func (h *langHandler) handleHover(ctx context.Context, reply jsonrpc2.Replier, r if len(word) > 2 && string(word[len(word)-1]) == ":" { word = word[0 : len(word)-1] } - response, err := h.yamllsConnector.CallHover(ctx, params, word) - return reply(ctx, response, err) + response, err := h.yamllsConnector.CallHover(ctx, *params, word) + return response, err } if pt == "function_call" && ct == "identifier" { word = currentNode.Content([]byte(doc.Content)) @@ -79,7 +68,7 @@ func (h *langHandler) handleHover(ctx context.Context, reply jsonrpc2.Replier, r ) if word == "" { - return reply(ctx, nil, err) + return nil, err } for _, s := range splitted { @@ -114,9 +103,9 @@ func (h *langHandler) handleHover(ctx context.Context, reply jsonrpc2.Replier, r value = "\"\"" } result := util.BuildHoverResponse(value, wordRange) - return reply(ctx, result, err) + return result, err } - return reply(ctx, nil, err) + return nil, err } searchWord := variableSplitted[0] @@ -132,10 +121,10 @@ func (h *langHandler) handleHover(ctx context.Context, reply jsonrpc2.Replier, r for _, completionItem := range toSearch { if searchWord == completionItem.Name { result := util.BuildHoverResponse(fmt.Sprint(completionItem.Doc), wordRange) - return reply(ctx, result, err) + return result, err } } - return reply(ctx, lsp.Hover{}, err) + return nil, err } func (h *langHandler) getChartMetadataHover(metadata *chart.Metadata, key string) (string, error) { diff --git a/internal/handler/hover_main_test.go b/internal/handler/hover_main_test.go new file mode 100644 index 00000000..c986b45c --- /dev/null +++ b/internal/handler/hover_main_test.go @@ -0,0 +1,127 @@ +package handler + +import ( + "context" + "fmt" + "os" + "path/filepath" + "testing" + + "github.com/mrjosh/helm-ls/internal/adapter/yamlls" + "github.com/mrjosh/helm-ls/internal/charts" + lsplocal "github.com/mrjosh/helm-ls/internal/lsp" + "github.com/mrjosh/helm-ls/internal/util" + "github.com/stretchr/testify/assert" + lsp "go.lsp.dev/protocol" + "go.lsp.dev/uri" +) + +func TestHoverMain(t *testing.T) { + testCases := []struct { + desc string + position lsp.Position + expected string + expectedError error + }{ + { + desc: "Test hover on function", + position: lsp.Position{ + Line: 7, + Character: 10, + }, + expected: "negate the boolean value of $x", + expectedError: nil, + }, + { + desc: "Test hover on .Values", + position: lsp.Position{ + Line: 25, + Character: 18, + }, + expected: "The values made available through values.yaml, --set and -f.", + expectedError: nil, + }, + { + desc: "Test hover on empty array in .Values", + position: lsp.Position{ + Line: 25, + Character: 28, + }, + expected: fmt.Sprintf("### %s\n%s\n\n", filepath.Join("..", "..", "testdata", "example", "values.yaml"), "[]"), + expectedError: nil, + }, + { + desc: "Test hover on .Chart metadata", + position: lsp.Position{ + Line: 33, + Character: 28, + }, + expected: "Name of the chart\n\nexample\n", + expectedError: nil, + }, + { + desc: "Test hover on dot", + position: lsp.Position{ + Line: 17, + Character: 19, + }, + expected: fmt.Sprintf("### %s\n%s\n\n\n", filepath.Join("..", "..", "testdata", "example", "values.yaml"), "{}"), + expectedError: nil, + }, + { + desc: "Test hover on .Files function", + position: lsp.Position{ + Line: 68, + Character: 24, + }, + expected: "Returns a list of files whose names match the given shell glob pattern.\n", + expectedError: nil, + }, + { + desc: "Test hover on yaml text", + position: lsp.Position{ + Line: 0, + Character: 0, + }, + expected: "", + expectedError: nil, + }, + } + for _, tt := range testCases { + t.Run(tt.desc, func(t *testing.T) { + documents := lsplocal.NewDocumentStore() + + path := "../../testdata/example/templates/deployment.yaml" + fileURI := uri.File(path) + + content, err := os.ReadFile(path) + if err != nil { + t.Fatal("Could not read test file", err) + } + d := lsp.DidOpenTextDocumentParams{ + TextDocument: lsp.TextDocumentItem{ + URI: fileURI, + LanguageID: "", + Version: 0, + Text: string(content), + }, + } + documents.DidOpen(&d, util.DefaultConfig) + h := &langHandler{ + chartStore: charts.NewChartStore(uri.File("."), charts.NewChart), + documents: documents, + yamllsConnector: &yamlls.Connector{}, + } + result, err := h.Hover(context.Background(), &lsp.HoverParams{ + TextDocumentPositionParams: lsp.TextDocumentPositionParams{ + TextDocument: lsp.TextDocumentIdentifier{ + URI: fileURI, + }, + Position: tt.position, + }, + }) + assert.Equal(t, tt.expectedError, err) + assert.Equal(t, tt.expected, result.Contents.Value) + }) + } +} diff --git a/internal/handler/initialization.go b/internal/handler/initialization.go index bc38c36e..d1b7bd26 100644 --- a/internal/handler/initialization.go +++ b/internal/handler/initialization.go @@ -2,48 +2,35 @@ package handler import ( "context" - "encoding/json" "os" "github.com/mrjosh/helm-ls/internal/adapter/yamlls" "github.com/mrjosh/helm-ls/internal/charts" "github.com/mrjosh/helm-ls/internal/util" "github.com/sirupsen/logrus" - "go.lsp.dev/jsonrpc2" lsp "go.lsp.dev/protocol" "go.lsp.dev/uri" ) -func (h *langHandler) handleInitialize(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error { - var ( - params lsp.InitializeParams - workspaceURI uri.URI - err error - ) - if err := json.Unmarshal(req.Params(), ¶ms); err != nil { - return err - } +func (h *langHandler) Initialize(ctx context.Context, params *lsp.InitializeParams) (result *lsp.InitializeResult, err error) { + var workspaceURI uri.URI - logger.Debug("handleInitialize with params ", req.Params()) if len(params.WorkspaceFolders) != 0 { workspaceURI, err = uri.Parse(params.WorkspaceFolders[0].URI) if err != nil { logger.Error("Error parsing workspace URI", err) - return err + return nil, err } } else { logger.Error("length WorkspaceFolders is 0, falling back to current working directory") workspaceURI = uri.File(".") } - logger.Debug("Initializing yamllsConnector") - h.yamllsConnector.CallInitialize(workspaceURI) - logger.Debug("Initializing chartStore") h.chartStore = charts.NewChartStore(workspaceURI, h.NewChartWithWatchedFiles) logger.Debug("Initializing done") - return reply(ctx, lsp.InitializeResult{ + return &lsp.InitializeResult{ Capabilities: lsp.ServerCapabilities{ TextDocumentSync: lsp.TextDocumentSyncOptions{ Change: lsp.TextDocumentSyncKindIncremental, @@ -59,20 +46,28 @@ func (h *langHandler) handleInitialize(ctx context.Context, reply jsonrpc2.Repli HoverProvider: true, DefinitionProvider: true, }, - }, nil) + }, nil +} + +func (h *langHandler) Initialized(ctx context.Context, _ *lsp.InitializedParams) (err error) { + go h.retrieveWorkspaceConfiguration(ctx) + return nil } -func (h *langHandler) initializationWithConfig() { +func (h *langHandler) initializationWithConfig(ctx context.Context) { configureLogLevel(h.helmlsConfig) h.chartStore.SetValuesFilesConfig(h.helmlsConfig.ValuesFilesConfig) - configureYamlls(h) + configureYamlls(ctx, h) } -func configureYamlls(h *langHandler) { +func configureYamlls(ctx context.Context, h *langHandler) { config := h.helmlsConfig if config.YamllsConfiguration.Enabled { - h.yamllsConnector = yamlls.NewConnector(config.YamllsConfiguration, h.connPool, h.documents) - h.yamllsConnector.CallInitialize(h.chartStore.RootURI) + h.yamllsConnector = yamlls.NewConnector(ctx, config.YamllsConfiguration, h.client, h.documents) + err := h.yamllsConnector.CallInitialize(ctx, h.chartStore.RootURI) + if err != nil { + logger.Error("Error initializing yamlls", err) + } h.yamllsConnector.InitiallySyncOpenDocuments(h.documents.GetAllDocs()) } } diff --git a/internal/handler/text.go b/internal/handler/text.go index eeeebad4..abeebd16 100644 --- a/internal/handler/text.go +++ b/internal/handler/text.go @@ -1,49 +1 @@ package handler - -import ( - "context" - "encoding/json" - "errors" - - lspinternal "github.com/mrjosh/helm-ls/internal/lsp" - "go.lsp.dev/jsonrpc2" - lsp "go.lsp.dev/protocol" -) - -func (h *langHandler) handleTextDocumentDidChange(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) (err error) { - - if req.Params() == nil { - return &jsonrpc2.Error{Code: jsonrpc2.InvalidParams} - } - - var params lsp.DidChangeTextDocumentParams - if err := json.Unmarshal(req.Params(), ¶ms); err != nil { - return err - } - - doc, ok := h.documents.Get(params.TextDocument.URI) - if !ok { - return errors.New("Could not get document: " + params.TextDocument.URI.Filename()) - } - - var shouldSendFullUpdateToYamlls = false - - // Synchronise changes into the doc's ContentChanges - doc.ApplyChanges(params.ContentChanges) - - for _, change := range params.ContentChanges { - var node = lspinternal.NodeAtPosition(doc.Ast, change.Range.Start) - if node.Type() != "text" { - shouldSendFullUpdateToYamlls = true - break - } - } - - if shouldSendFullUpdateToYamlls { - h.yamllsConnector.DocumentDidChangeFullSync(doc, params) - } else { - h.yamllsConnector.DocumentDidChange(doc, params) - } - - return reply(ctx, nil, nil) -} diff --git a/internal/handler/text_document.go b/internal/handler/text_document.go new file mode 100644 index 00000000..53033d49 --- /dev/null +++ b/internal/handler/text_document.go @@ -0,0 +1,99 @@ +package handler + +import ( + "context" + "errors" + + lspinternal "github.com/mrjosh/helm-ls/internal/lsp" + lsplocal "github.com/mrjosh/helm-ls/internal/lsp" + lsp "go.lsp.dev/protocol" +) + +func (h *langHandler) DidOpen(ctx context.Context, params *lsp.DidOpenTextDocumentParams) (err error) { + doc, err := h.documents.DidOpen(params, h.helmlsConfig) + if err != nil { + logger.Error(err) + return err + } + + h.yamllsConnector.DocumentDidOpen(doc.Ast, *params) + + _, err = h.chartStore.GetChartForDoc(doc.URI) + if err != nil { + logger.Error("Error getting chart info for file", doc.URI, err) + } + + doc, ok := h.documents.Get(params.TextDocument.URI) + if !ok { + return errors.New("Could not get document: " + params.TextDocument.URI.Filename()) + } + chart, err := h.chartStore.GetChartForDoc(doc.URI) + if err != nil { + logger.Error("Error getting chart info for file", doc.URI, err) + } + notification := lsplocal.GetDiagnosticsNotification(chart, doc) + + return h.client.PublishDiagnostics(ctx, notification) +} + +func (h *langHandler) DidClose(ctx context.Context, params *lsp.DidCloseTextDocumentParams) (err error) { + return nil +} + +func (h *langHandler) DidSave(ctx context.Context, params *lsp.DidSaveTextDocumentParams) (err error) { + doc, ok := h.documents.Get(params.TextDocument.URI) + if !ok { + return errors.New("Could not get document: " + params.TextDocument.URI.Filename()) + } + chart, err := h.chartStore.GetChartForDoc(doc.URI) + if err != nil { + logger.Error("Error getting chart info for file", doc.URI, err) + } + + h.yamllsConnector.DocumentDidSave(doc, *params) + notification := lsplocal.GetDiagnosticsNotification(chart, doc) + + return h.client.PublishDiagnostics(ctx, notification) +} + +func (h *langHandler) DidChange(ctx context.Context, params *lsp.DidChangeTextDocumentParams) (err error) { + doc, ok := h.documents.Get(params.TextDocument.URI) + if !ok { + return errors.New("Could not get document: " + params.TextDocument.URI.Filename()) + } + + shouldSendFullUpdateToYamlls := false + + // Synchronise changes into the doc's ContentChanges + doc.ApplyChanges(params.ContentChanges) + + for _, change := range params.ContentChanges { + node := lspinternal.NodeAtPosition(doc.Ast, change.Range.Start) + if node.Type() != "text" { + shouldSendFullUpdateToYamlls = true + break + } + } + + if shouldSendFullUpdateToYamlls { + h.yamllsConnector.DocumentDidChangeFullSync(doc, *params) + } else { + h.yamllsConnector.DocumentDidChange(doc, *params) + } + + return nil +} + +func (h *langHandler) DidCreateFiles(ctx context.Context, params *lsp.CreateFilesParams) (err error) { + panic("unimplemented") +} + +// DidDeleteFiles implements protocol.Server. +func (h *langHandler) DidDeleteFiles(ctx context.Context, params *lsp.DeleteFilesParams) (err error) { + panic("unimplemented") +} + +// DidRenameFiles implements protocol.Server. +func (h *langHandler) DidRenameFiles(ctx context.Context, params *lsp.RenameFilesParams) (err error) { + panic("unimplemented") +} diff --git a/internal/handler/watched_files.go b/internal/handler/watched_files.go index 906ad594..74e7eb05 100644 --- a/internal/handler/watched_files.go +++ b/internal/handler/watched_files.go @@ -2,7 +2,6 @@ package handler import ( "context" - "encoding/json" "github.com/mrjosh/helm-ls/internal/charts" "github.com/mrjosh/helm-ls/internal/util" @@ -50,19 +49,10 @@ func (h *langHandler) RegisterWatchedFiles(ctx context.Context, conn jsonrpc2.Co } } -func (h *langHandler) handleDidChangeWatchedFiles(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) (err error) { - if req.Params() == nil { - return &jsonrpc2.Error{Code: jsonrpc2.InvalidParams} - } - - var params lsp.DidChangeWatchedFilesParams - if err := json.Unmarshal(req.Params(), ¶ms); err != nil { - return err - } - +func (h *langHandler) DidChangeWatchedFiles(ctx context.Context, params *lsp.DidChangeWatchedFilesParams) (err error) { for _, change := range params.Changes { h.chartStore.ReloadValuesFile(change.URI) } - return reply(ctx, nil, nil) + return nil } diff --git a/internal/lsp/document.go b/internal/lsp/document.go index 0d9c1a7b..8c263e93 100644 --- a/internal/lsp/document.go +++ b/internal/lsp/document.go @@ -32,7 +32,7 @@ func (s *DocumentStore) GetAllDocs() []*Document { return docs } -func (s *DocumentStore) DidOpen(params lsp.DidOpenTextDocumentParams, helmlsConfig util.HelmlsConfiguration) (*Document, error) { +func (s *DocumentStore) DidOpen(params *lsp.DidOpenTextDocumentParams, helmlsConfig util.HelmlsConfiguration) (*Document, error) { logger.Debug(fmt.Sprintf("Opening document %s with langID %s", params.TextDocument.URI, params.TextDocument.LanguageID)) uri := params.TextDocument.URI @@ -44,6 +44,7 @@ func (s *DocumentStore) DidOpen(params lsp.DidOpenTextDocumentParams, helmlsConf Ast: ParseAst(nil, params.TextDocument.Text), DiagnosticsCache: NewDiagnosticsCache(helmlsConfig), } + // logger.Println("Storing doc ", path, s.documents) s.documents.Store(path, doc) return doc, nil } diff --git a/internal/lsp/document_test.go b/internal/lsp/document_test.go index ea0d84db..c09d0ca3 100644 --- a/internal/lsp/document_test.go +++ b/internal/lsp/document_test.go @@ -20,7 +20,7 @@ func TestDocumentStore(t *testing.T) { assert.Nil(doc) assert.False(ok) - sut.DidOpen(protocol.DidOpenTextDocumentParams{ + sut.DidOpen(&protocol.DidOpenTextDocumentParams{ TextDocument: protocol.TextDocumentItem{ URI: uri.File("test.yaml"), LanguageID: "helm", diff --git a/internal/lsp/lint.go b/internal/lsp/lint.go index 7cb6378d..a297e787 100644 --- a/internal/lsp/lint.go +++ b/internal/lsp/lint.go @@ -1,7 +1,6 @@ package lsp import ( - "context" "fmt" "strconv" "strings" @@ -14,40 +13,31 @@ import ( "github.com/mrjosh/helm-ls/pkg/lint/support" "github.com/pkg/errors" - // nolint "github.com/mrjosh/helm-ls/pkg/lint/rules" - "go.lsp.dev/jsonrpc2" lsp "go.lsp.dev/protocol" "go.lsp.dev/uri" ) var logger = log.GetLogger() -func NotificationFromLint(ctx context.Context, conn jsonrpc2.Conn, chart *charts.Chart, doc *Document) (*jsonrpc2.Notification, error) { +func GetDiagnosticsNotification(chart *charts.Chart, doc *Document) *lsp.PublishDiagnosticsParams { vals := chart.ValuesFiles.MainValuesFile.Values if chart.ValuesFiles.OverlayValuesFile != nil { vals = chartutil.CoalesceTables(chart.ValuesFiles.OverlayValuesFile.Values, chart.ValuesFiles.MainValuesFile.Values) } - diagnostics, err := GetDiagnostics(doc.URI, vals) - if err != nil { - return nil, err - } + diagnostics := GetDiagnostics(doc.URI, vals) doc.DiagnosticsCache.HelmDiagnostics = diagnostics - return nil, conn.Notify( - ctx, - lsp.MethodTextDocumentPublishDiagnostics, - &lsp.PublishDiagnosticsParams{ - URI: doc.URI, - Diagnostics: doc.DiagnosticsCache.GetMergedDiagnostics(), - }, - ) + return &lsp.PublishDiagnosticsParams{ + URI: doc.URI, + Diagnostics: doc.DiagnosticsCache.GetMergedDiagnostics(), + } } // GetDiagnostics will run helm linter against the currect document URI using the given values // and converts the helm.support.Message to lsp.Diagnostics -func GetDiagnostics(uri uri.URI, vals chartutil.Values) ([]lsp.Diagnostic, error) { +func GetDiagnostics(uri uri.URI, vals chartutil.Values) []lsp.Diagnostic { var ( filename = uri.Filename() paths = strings.Split(filename, "/") @@ -64,7 +54,6 @@ func GetDiagnostics(uri uri.URI, vals chartutil.Values) ([]lsp.Diagnostic, error } } - logger.Println(dir) client := action.NewLint() result := client.Run([]string{dir}, vals) @@ -81,7 +70,7 @@ func GetDiagnostics(uri uri.URI, vals chartutil.Values) ([]lsp.Diagnostic, error diagnostics = append(diagnostics, *d) } - return diagnostics, nil + return diagnostics } func GetDiagnosticFromLinterErr(supMsg support.Message) (*lsp.Diagnostic, string, error) { diff --git a/internal/util/config.go b/internal/util/config.go index f0615ee4..5c65ff98 100644 --- a/internal/util/config.go +++ b/internal/util/config.go @@ -42,7 +42,7 @@ var DefaultConfig = HelmlsConfiguration{ type YamllsSchemaStoreSettings struct { Enable bool `json:"enable"` - Url string `json:"url"` + URL string `json:"url"` } type YamllsSettings struct { @@ -58,6 +58,6 @@ var DefaultYamllsSettings = YamllsSettings{ Hover: true, YamllsSchemaStoreSettings: YamllsSchemaStoreSettings{ Enable: true, - Url: "https://www.schemastore.org/api/json/catalog.json", + URL: "https://www.schemastore.org/api/json/catalog.json", }, } diff --git a/internal/util/lsp.go b/internal/util/lsp.go index ca6e694e..e1fd5932 100644 --- a/internal/util/lsp.go +++ b/internal/util/lsp.go @@ -2,7 +2,7 @@ package util import lsp "go.lsp.dev/protocol" -func BuildHoverResponse(value string, wordRange lsp.Range) lsp.Hover { +func BuildHoverResponse(value string, wordRange lsp.Range) *lsp.Hover { content := lsp.MarkupContent{ Kind: lsp.Markdown, Value: value, @@ -11,5 +11,5 @@ func BuildHoverResponse(value string, wordRange lsp.Range) lsp.Hover { Contents: content, Range: &wordRange, } - return result + return &result } diff --git a/main.go b/main.go index 6af9da2d..1c4abe80 100644 --- a/main.go +++ b/main.go @@ -19,7 +19,6 @@ var ( ) func main() { - rootCmd := &cobra.Command{ Use: "helm_ls", Long: ` @@ -27,7 +26,7 @@ func main() { / /_/ / _ \ | '_ ' _ \ / / / __| / __ / __/ | | | | | / /__\__ \ \/ /_/ \___|_|_| |_| |_\____/___/`, - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(cmd *cobra.Command, _ []string) error { return cmd.Help() }, } @@ -47,5 +46,4 @@ func main() { fmt.Fprintln(os.Stderr, err) os.Exit(1) } - } diff --git a/mocks/go.lsp.dev/protocol/mock_Client.go b/mocks/go.lsp.dev/protocol/mock_Client.go new file mode 100644 index 00000000..751f61fd --- /dev/null +++ b/mocks/go.lsp.dev/protocol/mock_Client.go @@ -0,0 +1,646 @@ +// Code generated by mockery v2.38.0. DO NOT EDIT. + +package protocol + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" + protocol "go.lsp.dev/protocol" +) + +// MockClient is an autogenerated mock type for the Client type +type MockClient struct { + mock.Mock +} + +type MockClient_Expecter struct { + mock *mock.Mock +} + +func (_m *MockClient) EXPECT() *MockClient_Expecter { + return &MockClient_Expecter{mock: &_m.Mock} +} + +// ApplyEdit provides a mock function with given fields: ctx, params +func (_m *MockClient) ApplyEdit(ctx context.Context, params *protocol.ApplyWorkspaceEditParams) (bool, error) { + ret := _m.Called(ctx, params) + + if len(ret) == 0 { + panic("no return value specified for ApplyEdit") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *protocol.ApplyWorkspaceEditParams) (bool, error)); ok { + return rf(ctx, params) + } + if rf, ok := ret.Get(0).(func(context.Context, *protocol.ApplyWorkspaceEditParams) bool); ok { + r0 = rf(ctx, params) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(context.Context, *protocol.ApplyWorkspaceEditParams) error); ok { + r1 = rf(ctx, params) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockClient_ApplyEdit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ApplyEdit' +type MockClient_ApplyEdit_Call struct { + *mock.Call +} + +// ApplyEdit is a helper method to define mock.On call +// - ctx context.Context +// - params *protocol.ApplyWorkspaceEditParams +func (_e *MockClient_Expecter) ApplyEdit(ctx interface{}, params interface{}) *MockClient_ApplyEdit_Call { + return &MockClient_ApplyEdit_Call{Call: _e.mock.On("ApplyEdit", ctx, params)} +} + +func (_c *MockClient_ApplyEdit_Call) Run(run func(ctx context.Context, params *protocol.ApplyWorkspaceEditParams)) *MockClient_ApplyEdit_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*protocol.ApplyWorkspaceEditParams)) + }) + return _c +} + +func (_c *MockClient_ApplyEdit_Call) Return(result bool, err error) *MockClient_ApplyEdit_Call { + _c.Call.Return(result, err) + return _c +} + +func (_c *MockClient_ApplyEdit_Call) RunAndReturn(run func(context.Context, *protocol.ApplyWorkspaceEditParams) (bool, error)) *MockClient_ApplyEdit_Call { + _c.Call.Return(run) + return _c +} + +// Configuration provides a mock function with given fields: ctx, params +func (_m *MockClient) Configuration(ctx context.Context, params *protocol.ConfigurationParams) ([]interface{}, error) { + ret := _m.Called(ctx, params) + + if len(ret) == 0 { + panic("no return value specified for Configuration") + } + + var r0 []interface{} + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *protocol.ConfigurationParams) ([]interface{}, error)); ok { + return rf(ctx, params) + } + if rf, ok := ret.Get(0).(func(context.Context, *protocol.ConfigurationParams) []interface{}); ok { + r0 = rf(ctx, params) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]interface{}) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *protocol.ConfigurationParams) error); ok { + r1 = rf(ctx, params) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockClient_Configuration_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Configuration' +type MockClient_Configuration_Call struct { + *mock.Call +} + +// Configuration is a helper method to define mock.On call +// - ctx context.Context +// - params *protocol.ConfigurationParams +func (_e *MockClient_Expecter) Configuration(ctx interface{}, params interface{}) *MockClient_Configuration_Call { + return &MockClient_Configuration_Call{Call: _e.mock.On("Configuration", ctx, params)} +} + +func (_c *MockClient_Configuration_Call) Run(run func(ctx context.Context, params *protocol.ConfigurationParams)) *MockClient_Configuration_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*protocol.ConfigurationParams)) + }) + return _c +} + +func (_c *MockClient_Configuration_Call) Return(result []interface{}, err error) *MockClient_Configuration_Call { + _c.Call.Return(result, err) + return _c +} + +func (_c *MockClient_Configuration_Call) RunAndReturn(run func(context.Context, *protocol.ConfigurationParams) ([]interface{}, error)) *MockClient_Configuration_Call { + _c.Call.Return(run) + return _c +} + +// LogMessage provides a mock function with given fields: ctx, params +func (_m *MockClient) LogMessage(ctx context.Context, params *protocol.LogMessageParams) error { + ret := _m.Called(ctx, params) + + if len(ret) == 0 { + panic("no return value specified for LogMessage") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *protocol.LogMessageParams) error); ok { + r0 = rf(ctx, params) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockClient_LogMessage_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LogMessage' +type MockClient_LogMessage_Call struct { + *mock.Call +} + +// LogMessage is a helper method to define mock.On call +// - ctx context.Context +// - params *protocol.LogMessageParams +func (_e *MockClient_Expecter) LogMessage(ctx interface{}, params interface{}) *MockClient_LogMessage_Call { + return &MockClient_LogMessage_Call{Call: _e.mock.On("LogMessage", ctx, params)} +} + +func (_c *MockClient_LogMessage_Call) Run(run func(ctx context.Context, params *protocol.LogMessageParams)) *MockClient_LogMessage_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*protocol.LogMessageParams)) + }) + return _c +} + +func (_c *MockClient_LogMessage_Call) Return(err error) *MockClient_LogMessage_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockClient_LogMessage_Call) RunAndReturn(run func(context.Context, *protocol.LogMessageParams) error) *MockClient_LogMessage_Call { + _c.Call.Return(run) + return _c +} + +// Progress provides a mock function with given fields: ctx, params +func (_m *MockClient) Progress(ctx context.Context, params *protocol.ProgressParams) error { + ret := _m.Called(ctx, params) + + if len(ret) == 0 { + panic("no return value specified for Progress") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *protocol.ProgressParams) error); ok { + r0 = rf(ctx, params) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockClient_Progress_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Progress' +type MockClient_Progress_Call struct { + *mock.Call +} + +// Progress is a helper method to define mock.On call +// - ctx context.Context +// - params *protocol.ProgressParams +func (_e *MockClient_Expecter) Progress(ctx interface{}, params interface{}) *MockClient_Progress_Call { + return &MockClient_Progress_Call{Call: _e.mock.On("Progress", ctx, params)} +} + +func (_c *MockClient_Progress_Call) Run(run func(ctx context.Context, params *protocol.ProgressParams)) *MockClient_Progress_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*protocol.ProgressParams)) + }) + return _c +} + +func (_c *MockClient_Progress_Call) Return(err error) *MockClient_Progress_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockClient_Progress_Call) RunAndReturn(run func(context.Context, *protocol.ProgressParams) error) *MockClient_Progress_Call { + _c.Call.Return(run) + return _c +} + +// PublishDiagnostics provides a mock function with given fields: ctx, params +func (_m *MockClient) PublishDiagnostics(ctx context.Context, params *protocol.PublishDiagnosticsParams) error { + ret := _m.Called(ctx, params) + + if len(ret) == 0 { + panic("no return value specified for PublishDiagnostics") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *protocol.PublishDiagnosticsParams) error); ok { + r0 = rf(ctx, params) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockClient_PublishDiagnostics_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PublishDiagnostics' +type MockClient_PublishDiagnostics_Call struct { + *mock.Call +} + +// PublishDiagnostics is a helper method to define mock.On call +// - ctx context.Context +// - params *protocol.PublishDiagnosticsParams +func (_e *MockClient_Expecter) PublishDiagnostics(ctx interface{}, params interface{}) *MockClient_PublishDiagnostics_Call { + return &MockClient_PublishDiagnostics_Call{Call: _e.mock.On("PublishDiagnostics", ctx, params)} +} + +func (_c *MockClient_PublishDiagnostics_Call) Run(run func(ctx context.Context, params *protocol.PublishDiagnosticsParams)) *MockClient_PublishDiagnostics_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*protocol.PublishDiagnosticsParams)) + }) + return _c +} + +func (_c *MockClient_PublishDiagnostics_Call) Return(err error) *MockClient_PublishDiagnostics_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockClient_PublishDiagnostics_Call) RunAndReturn(run func(context.Context, *protocol.PublishDiagnosticsParams) error) *MockClient_PublishDiagnostics_Call { + _c.Call.Return(run) + return _c +} + +// RegisterCapability provides a mock function with given fields: ctx, params +func (_m *MockClient) RegisterCapability(ctx context.Context, params *protocol.RegistrationParams) error { + ret := _m.Called(ctx, params) + + if len(ret) == 0 { + panic("no return value specified for RegisterCapability") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *protocol.RegistrationParams) error); ok { + r0 = rf(ctx, params) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockClient_RegisterCapability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RegisterCapability' +type MockClient_RegisterCapability_Call struct { + *mock.Call +} + +// RegisterCapability is a helper method to define mock.On call +// - ctx context.Context +// - params *protocol.RegistrationParams +func (_e *MockClient_Expecter) RegisterCapability(ctx interface{}, params interface{}) *MockClient_RegisterCapability_Call { + return &MockClient_RegisterCapability_Call{Call: _e.mock.On("RegisterCapability", ctx, params)} +} + +func (_c *MockClient_RegisterCapability_Call) Run(run func(ctx context.Context, params *protocol.RegistrationParams)) *MockClient_RegisterCapability_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*protocol.RegistrationParams)) + }) + return _c +} + +func (_c *MockClient_RegisterCapability_Call) Return(err error) *MockClient_RegisterCapability_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockClient_RegisterCapability_Call) RunAndReturn(run func(context.Context, *protocol.RegistrationParams) error) *MockClient_RegisterCapability_Call { + _c.Call.Return(run) + return _c +} + +// ShowMessage provides a mock function with given fields: ctx, params +func (_m *MockClient) ShowMessage(ctx context.Context, params *protocol.ShowMessageParams) error { + ret := _m.Called(ctx, params) + + if len(ret) == 0 { + panic("no return value specified for ShowMessage") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *protocol.ShowMessageParams) error); ok { + r0 = rf(ctx, params) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockClient_ShowMessage_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ShowMessage' +type MockClient_ShowMessage_Call struct { + *mock.Call +} + +// ShowMessage is a helper method to define mock.On call +// - ctx context.Context +// - params *protocol.ShowMessageParams +func (_e *MockClient_Expecter) ShowMessage(ctx interface{}, params interface{}) *MockClient_ShowMessage_Call { + return &MockClient_ShowMessage_Call{Call: _e.mock.On("ShowMessage", ctx, params)} +} + +func (_c *MockClient_ShowMessage_Call) Run(run func(ctx context.Context, params *protocol.ShowMessageParams)) *MockClient_ShowMessage_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*protocol.ShowMessageParams)) + }) + return _c +} + +func (_c *MockClient_ShowMessage_Call) Return(err error) *MockClient_ShowMessage_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockClient_ShowMessage_Call) RunAndReturn(run func(context.Context, *protocol.ShowMessageParams) error) *MockClient_ShowMessage_Call { + _c.Call.Return(run) + return _c +} + +// ShowMessageRequest provides a mock function with given fields: ctx, params +func (_m *MockClient) ShowMessageRequest(ctx context.Context, params *protocol.ShowMessageRequestParams) (*protocol.MessageActionItem, error) { + ret := _m.Called(ctx, params) + + if len(ret) == 0 { + panic("no return value specified for ShowMessageRequest") + } + + var r0 *protocol.MessageActionItem + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *protocol.ShowMessageRequestParams) (*protocol.MessageActionItem, error)); ok { + return rf(ctx, params) + } + if rf, ok := ret.Get(0).(func(context.Context, *protocol.ShowMessageRequestParams) *protocol.MessageActionItem); ok { + r0 = rf(ctx, params) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*protocol.MessageActionItem) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *protocol.ShowMessageRequestParams) error); ok { + r1 = rf(ctx, params) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockClient_ShowMessageRequest_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ShowMessageRequest' +type MockClient_ShowMessageRequest_Call struct { + *mock.Call +} + +// ShowMessageRequest is a helper method to define mock.On call +// - ctx context.Context +// - params *protocol.ShowMessageRequestParams +func (_e *MockClient_Expecter) ShowMessageRequest(ctx interface{}, params interface{}) *MockClient_ShowMessageRequest_Call { + return &MockClient_ShowMessageRequest_Call{Call: _e.mock.On("ShowMessageRequest", ctx, params)} +} + +func (_c *MockClient_ShowMessageRequest_Call) Run(run func(ctx context.Context, params *protocol.ShowMessageRequestParams)) *MockClient_ShowMessageRequest_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*protocol.ShowMessageRequestParams)) + }) + return _c +} + +func (_c *MockClient_ShowMessageRequest_Call) Return(result *protocol.MessageActionItem, err error) *MockClient_ShowMessageRequest_Call { + _c.Call.Return(result, err) + return _c +} + +func (_c *MockClient_ShowMessageRequest_Call) RunAndReturn(run func(context.Context, *protocol.ShowMessageRequestParams) (*protocol.MessageActionItem, error)) *MockClient_ShowMessageRequest_Call { + _c.Call.Return(run) + return _c +} + +// Telemetry provides a mock function with given fields: ctx, params +func (_m *MockClient) Telemetry(ctx context.Context, params interface{}) error { + ret := _m.Called(ctx, params) + + if len(ret) == 0 { + panic("no return value specified for Telemetry") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, interface{}) error); ok { + r0 = rf(ctx, params) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockClient_Telemetry_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Telemetry' +type MockClient_Telemetry_Call struct { + *mock.Call +} + +// Telemetry is a helper method to define mock.On call +// - ctx context.Context +// - params interface{} +func (_e *MockClient_Expecter) Telemetry(ctx interface{}, params interface{}) *MockClient_Telemetry_Call { + return &MockClient_Telemetry_Call{Call: _e.mock.On("Telemetry", ctx, params)} +} + +func (_c *MockClient_Telemetry_Call) Run(run func(ctx context.Context, params interface{})) *MockClient_Telemetry_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(interface{})) + }) + return _c +} + +func (_c *MockClient_Telemetry_Call) Return(err error) *MockClient_Telemetry_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockClient_Telemetry_Call) RunAndReturn(run func(context.Context, interface{}) error) *MockClient_Telemetry_Call { + _c.Call.Return(run) + return _c +} + +// UnregisterCapability provides a mock function with given fields: ctx, params +func (_m *MockClient) UnregisterCapability(ctx context.Context, params *protocol.UnregistrationParams) error { + ret := _m.Called(ctx, params) + + if len(ret) == 0 { + panic("no return value specified for UnregisterCapability") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *protocol.UnregistrationParams) error); ok { + r0 = rf(ctx, params) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockClient_UnregisterCapability_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UnregisterCapability' +type MockClient_UnregisterCapability_Call struct { + *mock.Call +} + +// UnregisterCapability is a helper method to define mock.On call +// - ctx context.Context +// - params *protocol.UnregistrationParams +func (_e *MockClient_Expecter) UnregisterCapability(ctx interface{}, params interface{}) *MockClient_UnregisterCapability_Call { + return &MockClient_UnregisterCapability_Call{Call: _e.mock.On("UnregisterCapability", ctx, params)} +} + +func (_c *MockClient_UnregisterCapability_Call) Run(run func(ctx context.Context, params *protocol.UnregistrationParams)) *MockClient_UnregisterCapability_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*protocol.UnregistrationParams)) + }) + return _c +} + +func (_c *MockClient_UnregisterCapability_Call) Return(err error) *MockClient_UnregisterCapability_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockClient_UnregisterCapability_Call) RunAndReturn(run func(context.Context, *protocol.UnregistrationParams) error) *MockClient_UnregisterCapability_Call { + _c.Call.Return(run) + return _c +} + +// WorkDoneProgressCreate provides a mock function with given fields: ctx, params +func (_m *MockClient) WorkDoneProgressCreate(ctx context.Context, params *protocol.WorkDoneProgressCreateParams) error { + ret := _m.Called(ctx, params) + + if len(ret) == 0 { + panic("no return value specified for WorkDoneProgressCreate") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *protocol.WorkDoneProgressCreateParams) error); ok { + r0 = rf(ctx, params) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockClient_WorkDoneProgressCreate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WorkDoneProgressCreate' +type MockClient_WorkDoneProgressCreate_Call struct { + *mock.Call +} + +// WorkDoneProgressCreate is a helper method to define mock.On call +// - ctx context.Context +// - params *protocol.WorkDoneProgressCreateParams +func (_e *MockClient_Expecter) WorkDoneProgressCreate(ctx interface{}, params interface{}) *MockClient_WorkDoneProgressCreate_Call { + return &MockClient_WorkDoneProgressCreate_Call{Call: _e.mock.On("WorkDoneProgressCreate", ctx, params)} +} + +func (_c *MockClient_WorkDoneProgressCreate_Call) Run(run func(ctx context.Context, params *protocol.WorkDoneProgressCreateParams)) *MockClient_WorkDoneProgressCreate_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*protocol.WorkDoneProgressCreateParams)) + }) + return _c +} + +func (_c *MockClient_WorkDoneProgressCreate_Call) Return(err error) *MockClient_WorkDoneProgressCreate_Call { + _c.Call.Return(err) + return _c +} + +func (_c *MockClient_WorkDoneProgressCreate_Call) RunAndReturn(run func(context.Context, *protocol.WorkDoneProgressCreateParams) error) *MockClient_WorkDoneProgressCreate_Call { + _c.Call.Return(run) + return _c +} + +// WorkspaceFolders provides a mock function with given fields: ctx +func (_m *MockClient) WorkspaceFolders(ctx context.Context) ([]protocol.WorkspaceFolder, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for WorkspaceFolders") + } + + var r0 []protocol.WorkspaceFolder + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) ([]protocol.WorkspaceFolder, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) []protocol.WorkspaceFolder); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]protocol.WorkspaceFolder) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockClient_WorkspaceFolders_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WorkspaceFolders' +type MockClient_WorkspaceFolders_Call struct { + *mock.Call +} + +// WorkspaceFolders is a helper method to define mock.On call +// - ctx context.Context +func (_e *MockClient_Expecter) WorkspaceFolders(ctx interface{}) *MockClient_WorkspaceFolders_Call { + return &MockClient_WorkspaceFolders_Call{Call: _e.mock.On("WorkspaceFolders", ctx)} +} + +func (_c *MockClient_WorkspaceFolders_Call) Run(run func(ctx context.Context)) *MockClient_WorkspaceFolders_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *MockClient_WorkspaceFolders_Call) Return(result []protocol.WorkspaceFolder, err error) *MockClient_WorkspaceFolders_Call { + _c.Call.Return(result, err) + return _c +} + +func (_c *MockClient_WorkspaceFolders_Call) RunAndReturn(run func(context.Context) ([]protocol.WorkspaceFolder, error)) *MockClient_WorkspaceFolders_Call { + _c.Call.Return(run) + return _c +} + +// NewMockClient creates a new instance of MockClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockClient(t interface { + mock.TestingT + Cleanup(func()) +}) *MockClient { + mock := &MockClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/testdata/example/.helmignore b/testdata/example/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/testdata/example/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/testdata/example/Chart.yaml b/testdata/example/Chart.yaml new file mode 100644 index 00000000..b4d972f3 --- /dev/null +++ b/testdata/example/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: example +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" diff --git a/testdata/example/templates/NOTES.txt b/testdata/example/templates/NOTES.txt new file mode 100644 index 00000000..863493f7 --- /dev/null +++ b/testdata/example/templates/NOTES.txt @@ -0,0 +1,22 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "example.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "example.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "example.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "example.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/testdata/example/templates/_helpers.tpl b/testdata/example/templates/_helpers.tpl new file mode 100644 index 00000000..16daf581 --- /dev/null +++ b/testdata/example/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "example.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "example.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "example.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "example.labels" -}} +helm.sh/chart: {{ include "example.chart" . }} +{{ include "example.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "example.selectorLabels" -}} +app.kubernetes.io/name: {{ include "example.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "example.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "example.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/testdata/example/templates/deployment-no-templates.yaml b/testdata/example/templates/deployment-no-templates.yaml new file mode 100644 index 00000000..685c17aa --- /dev/null +++ b/testdata/example/templates/deployment-no-templates.yaml @@ -0,0 +1,21 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-deployment + labels: + app: nginx +spec: + replicas: 3 + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:1.14.2 + ports: + - containerPort: 80 diff --git a/testdata/example/templates/deployment.yaml b/testdata/example/templates/deployment.yaml new file mode 100644 index 00000000..67807b0f --- /dev/null +++ b/testdata/example/templates/deployment.yaml @@ -0,0 +1,71 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "example.fullname" . }} + labels: + {{- include "example.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "example.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "example.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "example.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + + livenessProbe: + {{- toYaml .Values.livenessProbe | nindent 12 }} + readinessProbe: + {{- toYaml .Values.readinessProbe | nindent 12 }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- with .Values.volumeMounts }} + volumeMounts: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.volumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- if $.Files.Glob "files/dags/*.py" }} + {{- end }} + {{- end }} diff --git a/testdata/example/templates/hpa.yaml b/testdata/example/templates/hpa.yaml new file mode 100644 index 00000000..51992d33 --- /dev/null +++ b/testdata/example/templates/hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "example.fullname" . }} + labels: + {{- include "example.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "example.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/testdata/example/templates/ingress.yaml b/testdata/example/templates/ingress.yaml new file mode 100644 index 00000000..e9d7a371 --- /dev/null +++ b/testdata/example/templates/ingress.yaml @@ -0,0 +1,61 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "example.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} + {{- end }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "example.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/testdata/example/templates/service.yaml b/testdata/example/templates/service.yaml new file mode 100644 index 00000000..02f54eab --- /dev/null +++ b/testdata/example/templates/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +wrong: key +kind: Service +metadata: + name: {{ include "example.fullname" . }} + labels: + {{- include "example.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "example.selectorLabels" . | nindent 4 }} diff --git a/testdata/example/templates/serviceaccount.yaml b/testdata/example/templates/serviceaccount.yaml new file mode 100644 index 00000000..f8a96331 --- /dev/null +++ b/testdata/example/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "example.serviceAccountName" . }} + labels: + {{- include "example.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} +{{- end }} diff --git a/testdata/example/templates/tests/test-connection.yaml b/testdata/example/templates/tests/test-connection.yaml new file mode 100644 index 00000000..35f374ab --- /dev/null +++ b/testdata/example/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "example.fullname" . }}-test-connection" + labels: + {{- include "example.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "example.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/testdata/example/values.yaml b/testdata/example/values.yaml new file mode 100644 index 00000000..7e405f4a --- /dev/null +++ b/testdata/example/values.yaml @@ -0,0 +1,107 @@ +# Default values for example. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: nginx + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Automatically mount a ServiceAccount's API credentials? + automount: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +podAnnotations: {} +podLabels: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: false + className: "" + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - host: chart-example.local + paths: + - path: / + pathType: ImplementationSpecific + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +livenessProbe: + httpGet: + path: / + port: http +readinessProbe: + httpGet: + path: / + port: http + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +# Additional volumes on the output Deployment definition. +volumes: [] +# - name: foo +# secret: +# secretName: mysecret +# optional: false + +# Additional volumeMounts on the output Deployment definition. +volumeMounts: [] +# - name: foo +# mountPath: "/etc/foo" +# readOnly: true + +nodeSelector: {} + +tolerations: [] + +affinity: {} From 3237b017f803f90d0d34768e13955f8bf6f74ea8 Mon Sep 17 00:00:00 2001 From: qvalentin Date: Sat, 30 Mar 2024 14:04:31 +0100 Subject: [PATCH 14/35] fix: remove yaml ast parsing is currently not used. Maybe fixes https://github.com/mrjosh/helm-ls/issues/68 --- internal/lsp/yaml_ast.go | 12 ------------ internal/lsp/yaml_ast_test.go | 33 --------------------------------- 2 files changed, 45 deletions(-) diff --git a/internal/lsp/yaml_ast.go b/internal/lsp/yaml_ast.go index 974ce90b..a9c6a689 100644 --- a/internal/lsp/yaml_ast.go +++ b/internal/lsp/yaml_ast.go @@ -1,11 +1,8 @@ package lsp import ( - "context" - "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" sitter "github.com/smacker/go-tree-sitter" - "github.com/smacker/go-tree-sitter/yaml" ) func getRangeForNode(node *sitter.Node) sitter.Range { @@ -31,15 +28,6 @@ func getTextNodeRanges(gotemplateNode *sitter.Node) []sitter.Range { return textNodes } -func ParseYamlAst(gotemplateTree *sitter.Tree, content string) *sitter.Tree { - parser := sitter.NewParser() - parser.SetLanguage(yaml.GetLanguage()) - parser.SetIncludedRanges(getTextNodeRanges(gotemplateTree.RootNode())) - - tree, _ := parser.ParseCtx(context.Background(), nil, []byte(content)) - return tree -} - // TrimTemplate removes all template nodes. // This is done by keeping only the text nodes // which is easier then removing the template nodes diff --git a/internal/lsp/yaml_ast_test.go b/internal/lsp/yaml_ast_test.go index fc8b8eab..05b4bded 100644 --- a/internal/lsp/yaml_ast_test.go +++ b/internal/lsp/yaml_ast_test.go @@ -84,39 +84,6 @@ b: not`, } } -func TestParseYamlAst(t *testing.T) { - type args struct { - content string - } - tests := []struct { - name string - args args - wantSexpr string - }{ - { - name: "simple template node", - args: args{ - "a: {{ .test }}", - }, - wantSexpr: "(stream (document (block_node (block_mapping (block_mapping_pair key: (flow_node (plain_scalar (string_scalar))))))))", - }, - { - name: "key value", - args: args{ - "a: value", - }, - wantSexpr: "(stream (document (block_node (block_mapping (block_mapping_pair key: (flow_node (plain_scalar (string_scalar))) value: (flow_node (plain_scalar (string_scalar))))))))", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotemplateTree := ParseAst(nil, tt.args.content) - got := ParseYamlAst(gotemplateTree, tt.args.content) - assert.Equal(t, tt.wantSexpr, got.RootNode().String()) - }) - } -} - func TestTrimTemplate(t *testing.T) { tests := []struct { documentText string From 3b8315485a7c16e4e15e9ec9d980c2bb230c266a Mon Sep 17 00:00:00 2001 From: qvalentin Date: Sat, 30 Mar 2024 20:59:25 +0100 Subject: [PATCH 15/35] fix(linting): use parent chart for linting subchart files --- internal/charts/chart_for_document.go | 16 +++++++++ internal/charts/chart_for_document_test.go | 41 ++++++++++++++++++++++ internal/charts/parent_chart.go | 12 +++++++ internal/handler/text_document.go | 9 ++--- 4 files changed, 71 insertions(+), 7 deletions(-) diff --git a/internal/charts/chart_for_document.go b/internal/charts/chart_for_document.go index 362d4991..c6f46915 100644 --- a/internal/charts/chart_for_document.go +++ b/internal/charts/chart_for_document.go @@ -27,6 +27,22 @@ func (s *ChartStore) GetChartForDoc(uri lsp.DocumentURI) (*Chart, error) { return chart, nil } +func (s *ChartStore) GetChartOrParentForDoc(uri lsp.DocumentURI) (*Chart, error) { + chart, err := s.GetChartForDoc(uri) + if err != nil { + return chart, err + } + + if chart.ParentChart.HasParent { + parentChart := chart.ParentChart.GetParentChartRecursive(s) + if parentChart == nil { + return chart, err + } + return parentChart, nil + } + return chart, nil +} + func (s *ChartStore) getChartFromCache(uri lsp.DocumentURI) *Chart { for chartURI, chart := range s.Charts { if strings.HasPrefix(uri.Filename(), filepath.Join(chartURI.Filename(), "template")) { diff --git a/internal/charts/chart_for_document_test.go b/internal/charts/chart_for_document_test.go index e0589c08..d3996338 100644 --- a/internal/charts/chart_for_document_test.go +++ b/internal/charts/chart_for_document_test.go @@ -94,3 +94,44 @@ func TestGetChartForDocumentWorksForNewToAddChartWithNestedFile(t *testing.T) { assert.Same(t, expectedChart, chartStore.Charts[uri.File(expectedChartDirectory)]) } + +func TestGetChartOrParentForDocWorks(t *testing.T) { + chartStore := charts.NewChartStore("file:///tmp", func(uri uri.URI, _ util.ValuesFilesConfig) *charts.Chart { + return &charts.Chart{RootURI: uri} + }) + + chart := &charts.Chart{} + chartStore.Charts["file:///tmp/chart"] = chart + subchart := &charts.Chart{ + ValuesFiles: &charts.ValuesFiles{}, + ChartMetadata: &charts.ChartMetadata{}, + RootURI: "file:///tmp/chart/charts/subchart", + ParentChart: charts.ParentChart{ + ParentChartURI: "file:///tmp/chart", + HasParent: true, + }, + } + chartStore.Charts["file:///tmp/chart/charts/subchart"] = subchart + otherchart := &charts.Chart{} + chartStore.Charts["file:///tmp/otherChart"] = otherchart + + result1, error := chartStore.GetChartOrParentForDoc("file:///tmp/chart/templates/deployment.yaml") + assert.NoError(t, error) + assert.Same(t, chart, result1) + + result2, error := chartStore.GetChartOrParentForDoc("file:///tmp/chart/templates/directory/deployment.yaml") + assert.NoError(t, error) + assert.Same(t, chart, result2) + + result3, error := chartStore.GetChartOrParentForDoc("file:///tmp/chart/charts/subchart/templates/directory/deployment.yaml") + assert.NoError(t, error) + assert.Same(t, chart, result3) + + result4, error := chartStore.GetChartOrParentForDoc("file:///tmp/otherChart/templates/directory/deployment.yaml") + assert.NoError(t, error) + assert.Same(t, otherchart, result4) + + result5, error := chartStore.GetChartOrParentForDoc("file:///tmp/directory/deployment.yaml") + assert.Error(t, error) + assert.Equal(t, &charts.Chart{RootURI: uri.File("/tmp")}, result5) +} diff --git a/internal/charts/parent_chart.go b/internal/charts/parent_chart.go index 5925826a..d182e493 100644 --- a/internal/charts/parent_chart.go +++ b/internal/charts/parent_chart.go @@ -30,3 +30,15 @@ func (p *ParentChart) GetParentChart(chartStore *ChartStore) *Chart { } return chart } + +func (p *ParentChart) GetParentChartRecursive(chartStore *ChartStore) *Chart { + chart := p.GetParentChart(chartStore) + if chart == nil { + return nil + } + parentChart := chart.ParentChart.GetParentChartRecursive(chartStore) + if parentChart == nil { + return chart + } + return parentChart +} diff --git a/internal/handler/text_document.go b/internal/handler/text_document.go index 53033d49..d9752110 100644 --- a/internal/handler/text_document.go +++ b/internal/handler/text_document.go @@ -18,16 +18,11 @@ func (h *langHandler) DidOpen(ctx context.Context, params *lsp.DidOpenTextDocume h.yamllsConnector.DocumentDidOpen(doc.Ast, *params) - _, err = h.chartStore.GetChartForDoc(doc.URI) - if err != nil { - logger.Error("Error getting chart info for file", doc.URI, err) - } - doc, ok := h.documents.Get(params.TextDocument.URI) if !ok { return errors.New("Could not get document: " + params.TextDocument.URI.Filename()) } - chart, err := h.chartStore.GetChartForDoc(doc.URI) + chart, err := h.chartStore.GetChartOrParentForDoc(doc.URI) if err != nil { logger.Error("Error getting chart info for file", doc.URI, err) } @@ -45,7 +40,7 @@ func (h *langHandler) DidSave(ctx context.Context, params *lsp.DidSaveTextDocume if !ok { return errors.New("Could not get document: " + params.TextDocument.URI.Filename()) } - chart, err := h.chartStore.GetChartForDoc(doc.URI) + chart, err := h.chartStore.GetChartOrParentForDoc(doc.URI) if err != nil { logger.Error("Error getting chart info for file", doc.URI, err) } From c75f5b723702d6dcb447582ccfa39c1a23324a18 Mon Sep 17 00:00:00 2001 From: qvalentin Date: Sat, 30 Mar 2024 20:23:22 +0100 Subject: [PATCH 16/35] fix(go-to-definition): support range values test(definition): fix tests --- internal/handler/definition.go | 9 ++- internal/handler/definition_test.go | 48 +++++++++++- internal/handler/generic_usecase.go | 1 + internal/handler/hover.go | 3 +- internal/handler/hover_main_test.go | 22 +++++- internal/handler/hover_test.go | 4 +- internal/util/strings.go | 5 +- internal/util/yaml.go | 40 ++++++---- internal/util/yaml_test.go | 87 +++++++++++++++++----- internal/util/yaml_test_input.yaml | 6 +- testdata/example/templates/deployment.yaml | 1 + 11 files changed, 179 insertions(+), 47 deletions(-) create mode 100644 internal/handler/generic_usecase.go diff --git a/internal/handler/definition.go b/internal/handler/definition.go index 408b26af..b7fa372e 100644 --- a/internal/handler/definition.go +++ b/internal/handler/definition.go @@ -46,7 +46,8 @@ func (h *langHandler) definitionAstParsing(chart *charts.Chart, doc *lsplocal.Do relevantChildNode = lsplocal.FindRelevantChildNode(currentNode, pointToLoopUp) ) - switch relevantChildNode.Type() { + nodeType := relevantChildNode.Type() + switch nodeType { case gotemplate.NodeTypeIdentifier: if relevantChildNode.Parent().Type() == gotemplate.NodeTypeVariable { return h.getDefinitionForVariable(relevantChildNode, doc) @@ -63,7 +64,7 @@ func (h *langHandler) getDefinitionForVariable(node *sitter.Node, doc *lsplocal. variableName := node.Content([]byte(doc.Content)) defintionNode := lsplocal.GetVariableDefinition(variableName, node.Parent(), doc.Content) if defintionNode == nil { - return []lsp.Location{}, fmt.Errorf("Could not find definition for %s", variableName) + return []lsp.Location{}, fmt.Errorf("Could not find definition for %s. Variable definition not found", variableName) } return []lsp.Location{{URI: doc.URI, Range: lsp.Range{Start: util.PointToPosition(defintionNode.StartPoint())}}}, nil } @@ -87,7 +88,7 @@ func (h *langHandler) getDefinitionForFixedIdentifier(chart *charts.Chart, node nil } - return []lsp.Location{}, fmt.Errorf("Could not find definition for %s", name) + return []lsp.Location{}, fmt.Errorf("Could not find definition for %s. Fixed identifier not found", name) } func (h *langHandler) getDefinitionForValue(chart *charts.Chart, node *sitter.Node, doc *lsplocal.Document) ([]lsp.Location, error) { @@ -122,7 +123,7 @@ func (h *langHandler) getDefinitionForValue(chart *charts.Chart, node *sitter.No } return locations, nil } - return []lsp.Location{}, fmt.Errorf("Could not find definition for %s", yamlPath) + return []lsp.Location{}, fmt.Errorf("Could not find definition for %s. No definition found", yamlPath) } func getYamlPath(node *sitter.Node, doc *lsplocal.Document) string { diff --git a/internal/handler/definition_test.go b/internal/handler/definition_test.go index 812c53e0..2bb7616a 100644 --- a/internal/handler/definition_test.go +++ b/internal/handler/definition_test.go @@ -25,6 +25,10 @@ var testFileContent = ` {{ range $index, $element := pipeline }}{{ $index }}{{ $element }}{{ end }} # line 7 {{ .Values.foo }} # line 8 {{ .Values.something.nested }} # line 9 + +{{ range .Values.list }} +{{ . }} # line 12 +{{ end }} ` var ( @@ -35,6 +39,8 @@ var ( foo: bar something: nested: false +list: + - test ` ) @@ -145,7 +151,29 @@ func TestDefinitionValue(t *testing.T) { } // Input: -// {{ .Values.something.nested }} # line 9 +// {{ range .Values.list }} +// {{ . }} # line 12 +// ---| +func TestDefinitionValueInList(t *testing.T) { + genericDefinitionTest(t, lsp.Position{Line: 12, Character: 3}, []lsp.Location{ + { + URI: testValuesURI, + Range: lsp.Range{ + Start: lsp.Position{ + Line: 4, + Character: 0, + }, + End: lsp.Position{ + Line: 4, + Character: 0, + }, + }, + }, + }, nil) +} + +// Input: +// {{ . }} # line 9 // ----------------------| func TestDefinitionValueNested(t *testing.T) { genericDefinitionTest(t, lsp.Position{Line: 9, Character: 26}, []lsp.Location{ @@ -173,7 +201,11 @@ func TestDefinitionValueFile(t *testing.T) { URI: testValuesURI, Range: lsp.Range{ Start: lsp.Position{ - Line: 0, + Line: 1, + Character: 0, + }, + End: lsp.Position{ + Line: 1, Character: 0, }, }, @@ -237,7 +269,11 @@ func TestDefinitionValueFileMulitpleValues(t *testing.T) { URI: testValuesURI, Range: lsp.Range{ Start: lsp.Position{ - Line: 0, + Line: 1, + Character: 0, + }, + End: lsp.Position{ + Line: 1, Character: 0, }, }, @@ -245,7 +281,11 @@ func TestDefinitionValueFileMulitpleValues(t *testing.T) { URI: testOtherValuesURI, Range: lsp.Range{ Start: lsp.Position{ - Line: 0, + Line: 1, + Character: 0, + }, + End: lsp.Position{ + Line: 1, Character: 0, }, }, diff --git a/internal/handler/generic_usecase.go b/internal/handler/generic_usecase.go new file mode 100644 index 00000000..abeebd16 --- /dev/null +++ b/internal/handler/generic_usecase.go @@ -0,0 +1 @@ +package handler diff --git a/internal/handler/hover.go b/internal/handler/hover.go index 3ae3553f..a7323f75 100644 --- a/internal/handler/hover.go +++ b/internal/handler/hover.go @@ -11,6 +11,7 @@ import ( "github.com/mrjosh/helm-ls/internal/charts" lspinternal "github.com/mrjosh/helm-ls/internal/lsp" + "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" "github.com/mrjosh/helm-ls/internal/util" "github.com/mrjosh/helm-ls/pkg/chart" @@ -57,7 +58,7 @@ func (h *langHandler) Hover(ctx context.Context, params *lsp.HoverParams) (resul if (pt == "selector_expression" || pt == "field") && (ct == "identifier" || ct == "field_identifier") { word = lspinternal.GetFieldIdentifierPath(currentNode, doc) } - if ct == "dot" { + if ct == gotemplate.NodeTypeDot { word = lspinternal.TraverseIdentifierPathUp(currentNode, doc) } diff --git a/internal/handler/hover_main_test.go b/internal/handler/hover_main_test.go index c986b45c..632d9ab2 100644 --- a/internal/handler/hover_main_test.go +++ b/internal/handler/hover_main_test.go @@ -86,6 +86,24 @@ func TestHoverMain(t *testing.T) { expected: "", expectedError: nil, }, + { + desc: "Test hover values list", + position: lsp.Position{ + Line: 71, + Character: 35, + }, + expected: fmt.Sprintf("### %s\n%s\n\n", filepath.Join("..", "..", "testdata", "example", "values.yaml"), "ingress.hosts:\n- host: chart-example.local\n paths:\n - path: /\n pathType: ImplementationSpecific\n"), + expectedError: nil, + }, + { + desc: "Test not existing values list", + position: lsp.Position{ + Line: 101, + Character: 35, + }, + expected: "", + expectedError: fmt.Errorf("Could not parse ast correctly"), + }, } for _, tt := range testCases { t.Run(tt.desc, func(t *testing.T) { @@ -121,7 +139,9 @@ func TestHoverMain(t *testing.T) { }, }) assert.Equal(t, tt.expectedError, err) - assert.Equal(t, tt.expected, result.Contents.Value) + if err == nil { + assert.Equal(t, tt.expected, result.Contents.Value) + } }) } } diff --git a/internal/handler/hover_test.go b/internal/handler/hover_test.go index aabaed3c..301c0e0d 100644 --- a/internal/handler/hover_test.go +++ b/internal/handler/hover_test.go @@ -95,7 +95,9 @@ nested: value splittedVar: []string{"key"}, }, want: `### values.yaml -[map[nested:value]] +key: +- nested: value + `, wantErr: false, diff --git a/internal/util/strings.go b/internal/util/strings.go index 03545cf6..68f8b6f0 100644 --- a/internal/util/strings.go +++ b/internal/util/strings.go @@ -8,10 +8,7 @@ import ( "go.lsp.dev/protocol" ) -var ( - logger = log.GetLogger() - wordRegex = regexp.MustCompile(`[^ \t\n\f\r,;\[\]\"\']+`) -) +var wordRegex = regexp.MustCompile(`[^ \t\n\f\r,;\[\]\"\']+`) // BetweenStrings gets the substring between two strings. func BetweenStrings(value string, a string, b string) string { diff --git a/internal/util/yaml.go b/internal/util/yaml.go index dc40ca15..66f758ef 100644 --- a/internal/util/yaml.go +++ b/internal/util/yaml.go @@ -2,6 +2,7 @@ package util import ( "fmt" + "strings" lsp "go.lsp.dev/protocol" yamlv3 "gopkg.in/yaml.v3" @@ -12,23 +13,34 @@ func GetPositionOfNode(node *yamlv3.Node, query []string) (lsp.Position, error) return lsp.Position{}, fmt.Errorf("could not find Position of %s in values.yaml. Node was zero", query) } - for index, value := range node.Content { - if value.Value == "" { - result, err := GetPositionOfNode(value, query) - if err == nil { - return result, nil - } + if len(query) == 0 { + return lsp.Position{Line: uint32(node.Line) - 1, Character: uint32(node.Column) - 1}, nil + } + + query[0] = strings.TrimSuffix(query[0], "[0]") + + switch node.Kind { + case yamlv3.DocumentNode: + if len(node.Content) < 1 { + return lsp.Position{}, fmt.Errorf("could not find Position of %s in values.yaml. Document is empty", query) + } + return GetPositionOfNode(node.Content[0], query) + case yamlv3.SequenceNode: + if len(node.Content) > 0 { + return GetPositionOfNode(node.Content[0], query) } - if value.Value == query[0] { - if len(query) > 1 { - if len(node.Content) < index+1 { - return lsp.Position{}, fmt.Errorf("could not find Position of %s in values.yaml", query) - } - return GetPositionOfNode(node.Content[index+1], query[1:]) + } + + for index, nestedNode := range node.Content { + if nestedNode.Value == query[0] { + if len(query) == 1 { + return GetPositionOfNode(nestedNode, query[1:]) } - return lsp.Position{Line: uint32(value.Line) - 1, Character: uint32(value.Column) - 1}, nil + if len(node.Content) < index+1 { + return lsp.Position{}, fmt.Errorf("could not find Position of %s in values.yaml", query) + } + return GetPositionOfNode(node.Content[index+1], query[1:]) } } return lsp.Position{}, fmt.Errorf("could not find Position of %s in values.yaml. Found no match", query) - } diff --git a/internal/util/yaml_test.go b/internal/util/yaml_test.go index b8796c4e..56b5edb3 100644 --- a/internal/util/yaml_test.go +++ b/internal/util/yaml_test.go @@ -2,15 +2,16 @@ package util import ( "fmt" - lsp "go.lsp.dev/protocol" "os" "testing" + "github.com/stretchr/testify/assert" + lsp "go.lsp.dev/protocol" + "gopkg.in/yaml.v3" ) func TestGetPositionOfNode(t *testing.T) { - data, err := os.ReadFile("./yaml_test_input.yaml") if err != nil { print(fmt.Sprint(err)) @@ -19,7 +20,6 @@ func TestGetPositionOfNode(t *testing.T) { var node yaml.Node err = yaml.Unmarshal(data, &node) - if err != nil { print(fmt.Sprint(err)) t.Errorf("error yml parsing") @@ -27,28 +27,81 @@ func TestGetPositionOfNode(t *testing.T) { result, err := GetPositionOfNode(&node, []string{"replicaCount"}) expected := lsp.Position{Line: 5, Character: 0} - if err != nil { - t.Errorf("Result had error: %s", err) - } - if result != expected { - t.Errorf("Result was not expected Position %v but was %v", expected, result) - } + assert.NoError(t, err) + assert.Equal(t, expected, result) result, err = GetPositionOfNode(&node, []string{"image", "repository"}) expected = lsp.Position{Line: 8, Character: 2} + assert.NoError(t, err) + assert.Equal(t, expected, result) + + result, err = GetPositionOfNode(&node, []string{"service", "test", "nested", "value"}) + expected = lsp.Position{Line: 30, Character: 6} + assert.NoError(t, err) + assert.Equal(t, expected, result) + + result, err = GetPositionOfNode(&node, []string{"service", "test", "wrong", "value"}) + expected = lsp.Position{} + assert.Error(t, err) + assert.Equal(t, expected, result) +} + +func TestGetPositionOfNodeWithList(t *testing.T) { + data, err := os.ReadFile("./yaml_test_input.yaml") if err != nil { - t.Errorf("Result had error: %s", err) + print(fmt.Sprint(err)) + t.Errorf("error yml parsing") } - if result != expected { - t.Errorf("Result was not expected Position %v but was %v", expected, result) + + var node yaml.Node + err = yaml.Unmarshal(data, &node) + if err != nil { + print(fmt.Sprint(err)) + t.Errorf("error yml parsing") } - result, err = GetPositionOfNode(&node, []string{"service", "test", "nested", "value"}) - expected = lsp.Position{Line: 30, Character: 6} + result, err := GetPositionOfNode(&node, []string{"list[0]"}) + expected := lsp.Position{Line: 32, Character: 0} + + assert.NoError(t, err) + assert.Equal(t, expected, result) + + result, err = GetPositionOfNode(&node, []string{"list[0]", "first"}) + expected = lsp.Position{Line: 33, Character: 4} + + assert.NoError(t, err) + assert.Equal(t, expected, result) + + result, err = GetPositionOfNode(&node, []string{"notExistingList[0]", "first"}) + expected = lsp.Position{} + + assert.Error(t, err) + assert.Equal(t, expected, result) +} + +func TestGetPositionOfNodeInEmptyDocument(t *testing.T) { + var node yaml.Node + err := yaml.Unmarshal([]byte(""), &node) if err != nil { - t.Errorf("Result had error: %s", err) + print(fmt.Sprint(err)) + t.Errorf("error yml parsing") } - if result != expected { - t.Errorf("Result was not expected Position %v but was %v", expected, result) + + result, err := GetPositionOfNode(&node, []string{"list[0]"}) + expected := lsp.Position{} + + assert.Error(t, err) + assert.Equal(t, expected, result) + + err = yaml.Unmarshal([]byte(" "), &node) + if err != nil { + print(fmt.Sprint(err)) + t.Errorf("error yml parsing") } + + result, err = GetPositionOfNode(&node, []string{"list[0]"}) + expected = lsp.Position{} + + assert.Error(t, err) + assert.Equal(t, expected, result) } diff --git a/internal/util/yaml_test_input.yaml b/internal/util/yaml_test_input.yaml index 81ba0102..decd4ef8 100644 --- a/internal/util/yaml_test_input.yaml +++ b/internal/util/yaml_test_input.yaml @@ -30,4 +30,8 @@ service: nested: value: test - +list: + - first: + nested: 1 + - second: + nested: 2 diff --git a/testdata/example/templates/deployment.yaml b/testdata/example/templates/deployment.yaml index 67807b0f..6d2f3764 100644 --- a/testdata/example/templates/deployment.yaml +++ b/testdata/example/templates/deployment.yaml @@ -69,3 +69,4 @@ spec: {{- if $.Files.Glob "files/dags/*.py" }} {{- end }} {{- end }} + hosts: {{ .Values.ingress.hosts }} From f87c46c0278bc24a82040c307423c507ca0422fd Mon Sep 17 00:00:00 2001 From: qvalentin Date: Sat, 30 Mar 2024 22:09:13 +0100 Subject: [PATCH 17/35] fix(hover): format list as yaml --- internal/handler/completion.go | 2 +- internal/handler/definition.go | 11 ++++++++-- internal/handler/generic_usecase.go | 1 - internal/handler/hover.go | 21 ++++++++++++------- internal/handler/hover_main_test.go | 2 +- internal/tree-sitter/gotemplate/node-types.go | 1 + internal/util/strings.go | 1 - 7 files changed, 26 insertions(+), 13 deletions(-) delete mode 100644 internal/handler/generic_usecase.go diff --git a/internal/handler/completion.go b/internal/handler/completion.go index 2838a32e..f6a9feaa 100644 --- a/internal/handler/completion.go +++ b/internal/handler/completion.go @@ -206,7 +206,7 @@ func (h *langHandler) setItem(items []lsp.CompletionItem, value interface{}, var documentation = valueOf.String() ) - logger.Println("ValueKind: ", valueOf) + logger.Debug("ValueKind: ", valueOf) switch valueOf.Kind() { case reflect.Slice, reflect.Map: diff --git a/internal/handler/definition.go b/internal/handler/definition.go index b7fa372e..4116ceb5 100644 --- a/internal/handler/definition.go +++ b/internal/handler/definition.go @@ -49,9 +49,15 @@ func (h *langHandler) definitionAstParsing(chart *charts.Chart, doc *lsplocal.Do nodeType := relevantChildNode.Type() switch nodeType { case gotemplate.NodeTypeIdentifier: - if relevantChildNode.Parent().Type() == gotemplate.NodeTypeVariable { + logger.Println("Parent type", relevantChildNode.Parent().Type()) + parentType := relevantChildNode.Parent().Type() + if parentType == gotemplate.NodeTypeVariable { return h.getDefinitionForVariable(relevantChildNode, doc) } + + if parentType == gotemplate.NodeTypeSelectorExpression || parentType == gotemplate.NodeTypeField { + return h.getDefinitionForValue(chart, relevantChildNode, doc) + } return h.getDefinitionForFixedIdentifier(chart, relevantChildNode, doc) case gotemplate.NodeTypeDot, gotemplate.NodeTypeDotSymbol, gotemplate.NodeTypeFieldIdentifier: return h.getDefinitionForValue(chart, relevantChildNode, doc) @@ -130,9 +136,10 @@ func getYamlPath(node *sitter.Node, doc *lsplocal.Document) string { switch node.Type() { case gotemplate.NodeTypeDot: return lsplocal.TraverseIdentifierPathUp(node, doc) - case gotemplate.NodeTypeDotSymbol, gotemplate.NodeTypeFieldIdentifier: + case gotemplate.NodeTypeDotSymbol, gotemplate.NodeTypeFieldIdentifier, gotemplate.NodeTypeIdentifier: return lsplocal.GetFieldIdentifierPath(node, doc) default: + logger.Error("Could not get yaml path for node type ", node.Type()) return "" } } diff --git a/internal/handler/generic_usecase.go b/internal/handler/generic_usecase.go deleted file mode 100644 index abeebd16..00000000 --- a/internal/handler/generic_usecase.go +++ /dev/null @@ -1 +0,0 @@ -package handler diff --git a/internal/handler/hover.go b/internal/handler/hover.go index a7323f75..344b1bef 100644 --- a/internal/handler/hover.go +++ b/internal/handler/hover.go @@ -44,7 +44,7 @@ func (h *langHandler) Hover(ctx context.Context, params *lsp.HoverParams) (resul pt := parent.Type() ct := currentNode.Type() - if ct == "text" { + if ct == gotemplate.NodeTypeText { word := doc.WordAt(params.Position) if len(word) > 2 && string(word[len(word)-1]) == ":" { word = word[0 : len(word)-1] @@ -52,10 +52,11 @@ func (h *langHandler) Hover(ctx context.Context, params *lsp.HoverParams) (resul response, err := h.yamllsConnector.CallHover(ctx, *params, word) return response, err } - if pt == "function_call" && ct == "identifier" { + if pt == gotemplate.NodeTypeFunctionCall && ct == gotemplate.NodeTypeIdentifier { word = currentNode.Content([]byte(doc.Content)) } - if (pt == "selector_expression" || pt == "field") && (ct == "identifier" || ct == "field_identifier") { + if (pt == gotemplate.NodeTypeSelectorExpression || pt == gotemplate.NodeTypeField) && + (ct == gotemplate.NodeTypeIdentifier || ct == gotemplate.NodeTypeFieldIdentifier) { word = lspinternal.GetFieldIdentifierPath(currentNode, doc) } if ct == gotemplate.NodeTypeDot { @@ -150,7 +151,7 @@ func (h *langHandler) getValueHover(chart *charts.Chart, splittedVar []string) ( for _, valuesFiles := range valuesFiles { for _, valuesFile := range valuesFiles.ValuesFiles.AllValuesFiles() { - result, err := getTableOrValueForSelector(valuesFile.Values, strings.Join(valuesFiles.Selector, ".")) + result, err := h.getTableOrValueForSelector(valuesFile.Values, strings.Join(valuesFiles.Selector, ".")) if err == nil { results[valuesFile.URI] = result } @@ -179,13 +180,13 @@ func (h *langHandler) getValueHover(chart *charts.Chart, splittedVar []string) ( return result, nil } -func getTableOrValueForSelector(values chartutil.Values, selector string) (string, error) { +func (h *langHandler) getTableOrValueForSelector(values chartutil.Values, selector string) (string, error) { if len(selector) > 0 { localValues, err := values.Table(selector) if err != nil { logger.Debug("values.PathValue(tableName) because of error", err) value, err := values.PathValue(selector) - return fmt.Sprint(value), err + return h.formatToYAML(reflect.Indirect(reflect.ValueOf(value)), selector), err } logger.Debug("converting to YAML", localValues) return localValues.YAML() @@ -206,11 +207,17 @@ func (h *langHandler) getBuiltInObjectsHover(items []HelmDocumentation, key stri func (h *langHandler) getMetadataField(v *chart.Metadata, fieldName string) string { r := reflect.ValueOf(v) field := reflect.Indirect(r).FieldByName(fieldName) + return h.formatToYAML(field, fieldName) +} + +func (h *langHandler) formatToYAML(field reflect.Value, fieldName string) string { switch field.Kind() { case reflect.String: return field.String() - case reflect.Slice, reflect.Map: + case reflect.Map: return h.toYAML(field.Interface()) + case reflect.Slice: + return h.toYAML(map[string]interface{}{fieldName: field.Interface()}) case reflect.Bool: return fmt.Sprint(h.getBoolType(field)) default: diff --git a/internal/handler/hover_main_test.go b/internal/handler/hover_main_test.go index 632d9ab2..345db985 100644 --- a/internal/handler/hover_main_test.go +++ b/internal/handler/hover_main_test.go @@ -47,7 +47,7 @@ func TestHoverMain(t *testing.T) { Line: 25, Character: 28, }, - expected: fmt.Sprintf("### %s\n%s\n\n", filepath.Join("..", "..", "testdata", "example", "values.yaml"), "[]"), + expected: fmt.Sprintf("### %s\n%s\n\n", filepath.Join("..", "..", "testdata", "example", "values.yaml"), "imagePullSecrets: []\n"), expectedError: nil, }, { diff --git a/internal/tree-sitter/gotemplate/node-types.go b/internal/tree-sitter/gotemplate/node-types.go index b96e645d..0f535c40 100644 --- a/internal/tree-sitter/gotemplate/node-types.go +++ b/internal/tree-sitter/gotemplate/node-types.go @@ -17,6 +17,7 @@ const ( NodeTypeElseIf = "else if" NodeTypeEnd = "end" NodeTypeError = "ERROR" + NodeTypeField = "field" NodeTypeFieldIdentifier = "field_identifier" NodeTypeFunctionCall = "function_call" NodeTypeIdentifier = "identifier" diff --git a/internal/util/strings.go b/internal/util/strings.go index 68f8b6f0..f2ff86b8 100644 --- a/internal/util/strings.go +++ b/internal/util/strings.go @@ -4,7 +4,6 @@ import ( "regexp" "strings" - "github.com/mrjosh/helm-ls/internal/log" "go.lsp.dev/protocol" ) From 0565edffaa9545432a87597772ca37a492aec2d1 Mon Sep 17 00:00:00 2001 From: qvalentin Date: Mon, 1 Apr 2024 17:37:55 +0200 Subject: [PATCH 18/35] fix(hover): format numbers correctly --- internal/handler/hover.go | 3 +++ internal/handler/hover_main_test.go | 16 +++++++--------- internal/handler/hover_test.go | 22 ++++++++++++++++++++++ 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/internal/handler/hover.go b/internal/handler/hover.go index 344b1bef..99133a0e 100644 --- a/internal/handler/hover.go +++ b/internal/handler/hover.go @@ -220,7 +220,10 @@ func (h *langHandler) formatToYAML(field reflect.Value, fieldName string) string return h.toYAML(map[string]interface{}{fieldName: field.Interface()}) case reflect.Bool: return fmt.Sprint(h.getBoolType(field)) + case reflect.Float32, reflect.Float64: + return fmt.Sprint(field.Float()) default: + logger.Error("Unknown kind for hover type: ", field.Kind()) return "" } } diff --git a/internal/handler/hover_main_test.go b/internal/handler/hover_main_test.go index 345db985..494b03b0 100644 --- a/internal/handler/hover_main_test.go +++ b/internal/handler/hover_main_test.go @@ -47,7 +47,7 @@ func TestHoverMain(t *testing.T) { Line: 25, Character: 28, }, - expected: fmt.Sprintf("### %s\n%s\n\n", filepath.Join("..", "..", "testdata", "example", "values.yaml"), "imagePullSecrets: []\n"), + expected: fmt.Sprintf("### %s\n%s\n\n\n", filepath.Join("..", "..", "testdata", "example", "values.yaml"), "imagePullSecrets: []"), expectedError: nil, }, { @@ -96,13 +96,13 @@ func TestHoverMain(t *testing.T) { expectedError: nil, }, { - desc: "Test not existing values list", + desc: "Test hover values number", position: lsp.Position{ - Line: 101, - Character: 35, + Line: 8, + Character: 28, }, - expected: "", - expectedError: fmt.Errorf("Could not parse ast correctly"), + expected: fmt.Sprintf("### %s\n%s\n\n", filepath.Join("..", "..", "testdata", "example", "values.yaml"), "1"), + expectedError: nil, }, } for _, tt := range testCases { @@ -139,9 +139,7 @@ func TestHoverMain(t *testing.T) { }, }) assert.Equal(t, tt.expectedError, err) - if err == nil { - assert.Equal(t, tt.expected, result.Contents.Value) - } + assert.Equal(t, tt.expected, result.Contents.Value) }) } } diff --git a/internal/handler/hover_test.go b/internal/handler/hover_test.go index 301c0e0d..38a2499f 100644 --- a/internal/handler/hover_test.go +++ b/internal/handler/hover_test.go @@ -212,6 +212,28 @@ middleValue ### ` + filepath.Join("charts", "subchart", "charts", "subsubchart", "values.yaml") + ` value +`, + wantErr: false, + }, + { + name: "Formatting of number", + args: args{ + chart: &charts.Chart{ + ChartMetadata: &charts.ChartMetadata{}, + ValuesFiles: &charts.ValuesFiles{ + MainValuesFile: &charts.ValuesFile{ + Values: map[string]interface{}{ + "key": float64(1.2345), + }, + URI: "file://tmp/values.yaml", + }, + }, + }, + splittedVar: []string{"key"}, + }, + want: `### values.yaml +1.2345 + `, wantErr: false, }, From 4bcbf29b22dca28c3eac1b0c757444429a4986b9 Mon Sep 17 00:00:00 2001 From: qvalentin Date: Wed, 10 Apr 2024 19:48:38 +0200 Subject: [PATCH 19/35] fix(handler): remove all unimplemented panics (#73) fixes https://github.com/mrjosh/helm-ls/issues/73 --- internal/handler/handler.go | 134 ++++++++++++++++++++---------- internal/handler/text_document.go | 9 +- 2 files changed, 95 insertions(+), 48 deletions(-) diff --git a/internal/handler/handler.go b/internal/handler/handler.go index 4898aa97..0bf8ece8 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -60,182 +60,217 @@ func newHandler(connPool jsonrpc2.Conn, client protocol.Client) *langHandler { // CodeAction implements protocol.Server. func (h *langHandler) CodeAction(ctx context.Context, params *lsp.CodeActionParams) (result []lsp.CodeAction, err error) { - panic("unimplemented") + logger.Error("Code action unimplemented") + return nil, nil } // CodeLens implements protocol.Server. func (h *langHandler) CodeLens(ctx context.Context, params *lsp.CodeLensParams) (result []lsp.CodeLens, err error) { - panic("unimplemented") + logger.Error("Code lens unimplemented") + return nil, nil } // CodeLensRefresh implements protocol.Server. func (h *langHandler) CodeLensRefresh(ctx context.Context) (err error) { - panic("unimplemented") + logger.Error("Code lens refresh unimplemented") + return nil } // CodeLensResolve implements protocol.Server. func (h *langHandler) CodeLensResolve(ctx context.Context, params *lsp.CodeLens) (result *lsp.CodeLens, err error) { - panic("unimplemented") + logger.Error("Code lens resolve unimplemented") + return nil, nil } // ColorPresentation implements protocol.Server. func (h *langHandler) ColorPresentation(ctx context.Context, params *lsp.ColorPresentationParams) (result []lsp.ColorPresentation, err error) { - panic("unimplemented") + logger.Error("Color presentation unimplemented") + return nil, nil } // CompletionResolve implements protocol.Server. func (h *langHandler) CompletionResolve(ctx context.Context, params *lsp.CompletionItem) (result *lsp.CompletionItem, err error) { - panic("unimplemented") + logger.Error("Completion resolve unimplemented") + return nil, nil } // Declaration implements protocol.Server. func (h *langHandler) Declaration(ctx context.Context, params *lsp.DeclarationParams) (result []lsp.Location, err error) { - panic("unimplemented") + logger.Error("Declaration unimplemented") + return nil, nil } // DidChangeWorkspaceFolders implements protocol.Server. func (h *langHandler) DidChangeWorkspaceFolders(ctx context.Context, params *lsp.DidChangeWorkspaceFoldersParams) (err error) { - panic("unimplemented") + logger.Error("DidChangeWorkspaceFolders unimplemented") + return nil } // DocumentColor implements protocol.Server. func (h *langHandler) DocumentColor(ctx context.Context, params *lsp.DocumentColorParams) (result []lsp.ColorInformation, err error) { - panic("unimplemented") + logger.Error("Document color unimplemented") + return nil, nil } // DocumentHighlight implements protocol.Server. func (h *langHandler) DocumentHighlight(ctx context.Context, params *lsp.DocumentHighlightParams) (result []lsp.DocumentHighlight, err error) { - panic("unimplemented") + logger.Error("Document highlight unimplemented") + return nil, nil } // DocumentLink implements protocol.Server. func (h *langHandler) DocumentLink(ctx context.Context, params *lsp.DocumentLinkParams) (result []lsp.DocumentLink, err error) { - panic("unimplemented") + logger.Error("Document link unimplemented") + return nil, nil } // DocumentLinkResolve implements protocol.Server. func (h *langHandler) DocumentLinkResolve(ctx context.Context, params *lsp.DocumentLink) (result *lsp.DocumentLink, err error) { - panic("unimplemented") + logger.Error("Document link resolve unimplemented") + return nil, nil } // DocumentSymbol implements protocol.Server. func (h *langHandler) DocumentSymbol(ctx context.Context, params *lsp.DocumentSymbolParams) (result []interface{}, err error) { - panic("unimplemented") + logger.Error("Document symbol unimplemented") + return nil, nil } // ExecuteCommand implements protocol.Server. func (h *langHandler) ExecuteCommand(ctx context.Context, params *lsp.ExecuteCommandParams) (result interface{}, err error) { - panic("unimplemented") + logger.Error("Execute command unimplemented") + return nil, nil } // Exit implements protocol.Server. func (h *langHandler) Exit(ctx context.Context) (err error) { - panic("unimplemented") + return nil } // FoldingRanges implements protocol.Server. func (h *langHandler) FoldingRanges(ctx context.Context, params *lsp.FoldingRangeParams) (result []lsp.FoldingRange, err error) { - panic("unimplemented") + logger.Error("Folding ranges unimplemented") + return nil, nil } // Formatting implements protocol.Server. func (h *langHandler) Formatting(ctx context.Context, params *lsp.DocumentFormattingParams) (result []lsp.TextEdit, err error) { - panic("unimplemented") + logger.Error("Formatting unimplemented") + return nil, nil } // Implementation implements protocol.Server. func (h *langHandler) Implementation(ctx context.Context, params *lsp.ImplementationParams) (result []lsp.Location, err error) { - panic("unimplemented") + logger.Error("Implementation unimplemented") + return nil, nil } // IncomingCalls implements protocol.Server. func (h *langHandler) IncomingCalls(ctx context.Context, params *lsp.CallHierarchyIncomingCallsParams) (result []lsp.CallHierarchyIncomingCall, err error) { - panic("unimplemented") + logger.Error("Incoming calls unimplemented") + return nil, nil } // LinkedEditingRange implements protocol.Server. func (h *langHandler) LinkedEditingRange(ctx context.Context, params *lsp.LinkedEditingRangeParams) (result *lsp.LinkedEditingRanges, err error) { - panic("unimplemented") + logger.Error("Linked editing range unimplemented") + return nil, nil } // LogTrace implements protocol.Server. func (h *langHandler) LogTrace(ctx context.Context, params *lsp.LogTraceParams) (err error) { - panic("unimplemented") + logger.Error("Log trace unimplemented") + return nil } // Moniker implements protocol.Server. func (h *langHandler) Moniker(ctx context.Context, params *lsp.MonikerParams) (result []lsp.Moniker, err error) { - panic("unimplemented") + logger.Error("Moniker unimplemented") + return nil, nil } // OnTypeFormatting implements protocol.Server. func (h *langHandler) OnTypeFormatting(ctx context.Context, params *lsp.DocumentOnTypeFormattingParams) (result []lsp.TextEdit, err error) { - panic("unimplemented") + logger.Error("On type formatting unimplemented") + return nil, nil } // OutgoingCalls implements protocol.Server. func (h *langHandler) OutgoingCalls(ctx context.Context, params *lsp.CallHierarchyOutgoingCallsParams) (result []lsp.CallHierarchyOutgoingCall, err error) { - panic("unimplemented") + logger.Error("Outgoing calls unimplemented") + return nil, nil } // PrepareCallHierarchy implements protocol.Server. func (h *langHandler) PrepareCallHierarchy(ctx context.Context, params *lsp.CallHierarchyPrepareParams) (result []lsp.CallHierarchyItem, err error) { - panic("unimplemented") + logger.Error("Prepare call hierarchy unimplemented") + return nil, nil } // PrepareRename implements protocol.Server. func (h *langHandler) PrepareRename(ctx context.Context, params *lsp.PrepareRenameParams) (result *lsp.Range, err error) { - panic("unimplemented") + logger.Error("Prepare rename unimplemented") + return nil, nil } // RangeFormatting implements protocol.Server. func (h *langHandler) RangeFormatting(ctx context.Context, params *lsp.DocumentRangeFormattingParams) (result []lsp.TextEdit, err error) { - panic("unimplemented") + logger.Error("Range formatting unimplemented") + return nil, nil } // References implements protocol.Server. func (h *langHandler) References(ctx context.Context, params *lsp.ReferenceParams) (result []lsp.Location, err error) { - panic("unimplemented") + logger.Error("References unimplemented") + return nil, nil } // Rename implements protocol.Server. func (h *langHandler) Rename(ctx context.Context, params *lsp.RenameParams) (result *lsp.WorkspaceEdit, err error) { - panic("unimplemented") + logger.Error("Rename unimplemented") + return nil, nil } // Request implements protocol.Server. func (h *langHandler) Request(ctx context.Context, method string, params interface{}) (result interface{}, err error) { - panic("unimplemented") + logger.Error("Request unimplemented") + return nil, nil } // SemanticTokensFull implements protocol.Server. func (h *langHandler) SemanticTokensFull(ctx context.Context, params *lsp.SemanticTokensParams) (result *lsp.SemanticTokens, err error) { - panic("unimplemented") + logger.Error("Semantic tokens full unimplemented") + return nil, nil } // SemanticTokensFullDelta implements protocol.Server. func (h *langHandler) SemanticTokensFullDelta(ctx context.Context, params *lsp.SemanticTokensDeltaParams) (result interface{}, err error) { - panic("unimplemented") + logger.Error("Semantic tokens full delta unimplemented") + return nil, nil } // SemanticTokensRange implements protocol.Server. func (h *langHandler) SemanticTokensRange(ctx context.Context, params *lsp.SemanticTokensRangeParams) (result *lsp.SemanticTokens, err error) { - panic("unimplemented") + logger.Error("Semantic tokens range unimplemented") + return nil, nil } // SemanticTokensRefresh implements protocol.Server. func (h *langHandler) SemanticTokensRefresh(ctx context.Context) (err error) { - panic("unimplemented") + logger.Error("Semantic tokens refresh unimplemented") + return nil } // SetTrace implements protocol.Server. func (h *langHandler) SetTrace(ctx context.Context, params *lsp.SetTraceParams) (err error) { - panic("unimplemented") + logger.Error("Set trace unimplemented") + return nil } // ShowDocument implements protocol.Server. func (h *langHandler) ShowDocument(ctx context.Context, params *lsp.ShowDocumentParams) (result *lsp.ShowDocumentResult, err error) { - panic("unimplemented") + logger.Error("Show document unimplemented") + return nil, nil } // Shutdown implements protocol.Server. @@ -245,45 +280,54 @@ func (h *langHandler) Shutdown(ctx context.Context) (err error) { // SignatureHelp implements protocol.Server. func (h *langHandler) SignatureHelp(ctx context.Context, params *lsp.SignatureHelpParams) (result *lsp.SignatureHelp, err error) { - panic("unimplemented") + logger.Error("Signature help unimplemented") + return nil, nil } // Symbols implements protocol.Server. func (h *langHandler) Symbols(ctx context.Context, params *lsp.WorkspaceSymbolParams) (result []lsp.SymbolInformation, err error) { - panic("unimplemented") + logger.Error("Symbols unimplemented") + return nil, nil } // TypeDefinition implements protocol.Server. func (h *langHandler) TypeDefinition(ctx context.Context, params *lsp.TypeDefinitionParams) (result []lsp.Location, err error) { - panic("unimplemented") + logger.Error("Type definition unimplemented") + return nil, nil } // WillCreateFiles implements protocol.Server. func (h *langHandler) WillCreateFiles(ctx context.Context, params *lsp.CreateFilesParams) (result *lsp.WorkspaceEdit, err error) { - panic("unimplemented") + logger.Error("Will create files unimplemented") + return nil, nil } // WillDeleteFiles implements protocol.Server. func (h *langHandler) WillDeleteFiles(ctx context.Context, params *lsp.DeleteFilesParams) (result *lsp.WorkspaceEdit, err error) { - panic("unimplemented") + logger.Error("Will delete files unimplemented") + return nil, nil } // WillRenameFiles implements protocol.Server. func (h *langHandler) WillRenameFiles(ctx context.Context, params *lsp.RenameFilesParams) (result *lsp.WorkspaceEdit, err error) { - panic("unimplemented") + logger.Error("Will rename files unimplemented") + return nil, nil } // WillSave implements protocol.Server. func (h *langHandler) WillSave(ctx context.Context, params *lsp.WillSaveTextDocumentParams) (err error) { - panic("unimplemented") + logger.Error("Will save unimplemented") + return nil } // WillSaveWaitUntil implements protocol.Server. func (h *langHandler) WillSaveWaitUntil(ctx context.Context, params *lsp.WillSaveTextDocumentParams) (result []lsp.TextEdit, err error) { - panic("unimplemented") + logger.Error("Will save wait until unimplemented") + return nil, nil } // WorkDoneProgressCancel implements protocol.Server. func (h *langHandler) WorkDoneProgressCancel(ctx context.Context, params *lsp.WorkDoneProgressCancelParams) (err error) { - panic("unimplemented") + logger.Error("Work done progress cancel unimplemented") + return nil } diff --git a/internal/handler/text_document.go b/internal/handler/text_document.go index d9752110..2209e0c6 100644 --- a/internal/handler/text_document.go +++ b/internal/handler/text_document.go @@ -80,15 +80,18 @@ func (h *langHandler) DidChange(ctx context.Context, params *lsp.DidChangeTextDo } func (h *langHandler) DidCreateFiles(ctx context.Context, params *lsp.CreateFilesParams) (err error) { - panic("unimplemented") + logger.Error("DidCreateFiles unimplemented") + return nil } // DidDeleteFiles implements protocol.Server. func (h *langHandler) DidDeleteFiles(ctx context.Context, params *lsp.DeleteFilesParams) (err error) { - panic("unimplemented") + logger.Error("DidDeleteFiles unimplemented") + return nil } // DidRenameFiles implements protocol.Server. func (h *langHandler) DidRenameFiles(ctx context.Context, params *lsp.RenameFilesParams) (err error) { - panic("unimplemented") + logger.Error("DidRenameFiles unimplemented") + return nil } From eb85c8a677ad00b9bf52d1c208475b983d2a2fab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 27 Apr 2024 11:04:42 +0000 Subject: [PATCH 20/35] build(deps): bump golang.org/x/net from 0.19.0 to 0.23.0 Bumps [golang.org/x/net](https://github.com/golang/net) from 0.19.0 to 0.23.0. - [Commits](https://github.com/golang/net/compare/v0.19.0...v0.23.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: indirect ... Signed-off-by: dependabot[bot] --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 9b3ad0ea..9c3b5017 100644 --- a/go.mod +++ b/go.mod @@ -67,11 +67,11 @@ require ( go.uber.org/atomic v1.10.0 // indirect go.uber.org/goleak v1.2.1 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.17.0 // indirect - golang.org/x/net v0.19.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/net v0.23.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/term v0.15.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/term v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index ca232526..4a1ddbd2 100644 --- a/go.sum +++ b/go.sum @@ -164,8 +164,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -177,8 +177,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -195,13 +195,13 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= From bddefb190ff5c3ef17d4c2ebce6b1f4655015cd5 Mon Sep 17 00:00:00 2001 From: qvalentin Date: Wed, 1 May 2024 14:41:28 +0200 Subject: [PATCH 21/35] feat(docs): add coc config and reword readme --- README.md | 117 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 93 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 43a71f60..395c23cc 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ \/ /_/ \___|_|_| |_| |_\____/___/ -## Helm Language Server Protocol +## Helm Language Server Helm-ls is a [helm](https://github.com/helm/helm) language server protocol [LSP](https://microsoft.github.io/language-server-protocol/) implementation. @@ -18,21 +18,27 @@ Helm-ls is a [helm](https://github.com/helm/helm) language server protocol [LSP] * [Demo](#demo) * [Getting Started](#getting-started) - * [Installation with a package manager](#installation-with-a-package-manager) - * [Download](#download) + * [Installation with a package manager](#installation-with-a-package-manager) + * [Homebrew](#homebrew) + * [Nix](#nix) + * [Arch Linux](#arch-linux) + * [Windows](#windows) + * [mason (neovim)](#mason-neovim) + * [Manual download](#manual-download) * [Make it executable](#make-it-executable) - * [Integration with yaml-language-server](#integration-with-yaml-language-server) + * [Integration with yaml-language-server](#integration-with-yaml-language-server) * [Configuration options](#configuration-options) - * [General](#general) - * [Values Files](#values-files) - * [yaml-language-server config](#yaml-language-server-config) - * [Default Configuration](#default-configuration) + * [General](#general) + * [Values Files](#values-files) + * [yaml-language-server config](#yaml-language-server-config) + * [Default Configuration](#default-configuration) * [Editor Config examples](#editor-config-examples) - * [Neovim (using nvim-lspconfig)](#neovim-using-nvim-lspconfig) - * [Vim Helm Plugin](#vim-helm-plugin) - * [Setup laguage server](#setup-laguage-server) - * [VSCode](#vscode) - * [Emacs eglot setup](#emacs-eglot-setup) + * [Neovim (using nvim-lspconfig)](#neovim-using-nvim-lspconfig) + * [Vim Helm Plugin](#vim-helm-plugin) + * [nvim-lspconfig setup](#nvim-lspconfig-setup) + * [coc.nvim setup](#cocnvim-setup) + * [VSCode](#vscode) + * [Emacs eglot setup](#emacs-eglot-setup) * [Contributing](#contributing) * [License](#license) @@ -50,7 +56,48 @@ Helm-ls is currently available as a package for some package managers. [![Packaging status](https://repology.org/badge/vertical-allrepos/helm-ls.svg)](https://repology.org/project/helm-ls/versions) -### Download +These are some of the supported package managers. Thanks to everyone who packaged it! + +#### Homebrew + +If you are using MacOS or Linux with [Homebrew](https://brew.sh/) you can install it with brew. + +```bash +brew install helm-ls +``` + +#### Nix + +```bash +nix-shell -p helm-ls +``` + +#### Arch Linux + +You can install it from the [aur](https://aur.archlinux.org/packages/helm-ls/) using your preferred aur helper, e.g. yay: + +```bash +yay -S helm-ls +``` + +#### Windows + +You can use [scoop](https://scoop.sh/) to install it: + +```powershell +scoop bucket add extras +scoop install extras/helm-ls +``` + +#### mason (neovim) + +If you are using neovim with [mason](https://github.com/williamboman/mason.nvim) you can also install it with mason. + +```vim +:MasonInstall helm-ls +``` + +### Manual download - Download the latest helm_ls executable file from [here](https://github.com/mrjosh/helm-ls/releases/latest) and move it to your binaries directory @@ -60,9 +107,7 @@ Helm-ls is currently available as a package for some package managers. curl -L https://github.com/mrjosh/helm-ls/releases/download/master/helm_ls_{os}_{arch} --output /usr/local/bin/helm_ls ``` -If you are using neovim with [mason](https://github.com/williamboman/mason.nvim) you can also install it with mason. - -### Make it executable +#### Make it executable ```bash chmod +x /usr/local/bin/helm_ls @@ -71,8 +116,11 @@ chmod +x /usr/local/bin/helm_ls ### Integration with [yaml-language-server](https://github.com/redhat-developer/yaml-language-server) Helm-ls will use yaml-language-server to provide additional capabilities, if it is installed. -This feature is expermiental, you can disable it in the config ([see](#configuration-options)). -Having a broken template syntax (e.g. while your are stil typing) will cause diagnostics from yaml-language-server to be shown as errors. + +> [!WARNING] +> +> This feature is experimental, you can disable it in the config ([see](#configuration-options)) if you are getting a lot of errors beginning with `Yamlls:`. +> Having a broken template syntax (e.g. while your are still typing) will also cause diagnostics from yaml-language-server to be shown as errors. To install it using npm run (or use your preferred package manager): @@ -110,7 +158,7 @@ You can configure helm-ls with lsp workspace configurations. - **Path to yaml-language-server**: Specify the executable location. - **Diagnostics Settings**: - - **Limit**: Number of displayed diagnostics per file. + - **Limit**: Number of displayed diagnostics per file. Set this to 0 to disable all diagnostics from yaml-language-server but keep other features such as hover. - **Show Directly**: Show diagnostics while typing. - **Additional Settings** (see [yaml-language-server](https://github.com/redhat-developer/yaml-language-server#language-server-settings)): @@ -153,13 +201,15 @@ settings = { #### Vim Helm Plugin -You'll need [vim-helm](https://github.com/towolf/vim-helm) plugin installed before using helm_ls, to install it using vim-plug (or use your preferred plugin manager): +To get filetype detection working, you'll need the [vim-helm](https://github.com/towolf/vim-helm) plugin installed before using helm_ls, to install it using vim-plug (or use your preferred plugin manager): ```lua Plug 'towolf/vim-helm' ``` -#### Setup laguage server +#### nvim-lspconfig setup + +Add the following to your neovim lua config: ```lua local lspconfig = require('lspconfig') @@ -178,6 +228,25 @@ settings = { See [examples/nvim/init.lua](https://github.com/mrjosh/helm-ls/blob/master/examples/nvim/init.lua) for an complete example, which also includes yaml-language-server. +#### coc.nvim setup + +You can also use [coc.nvim](https://github.com/neoclide/coc.nvim) to set up the language server. +You will need to configure the use of `helm_ls` in the `langageserver` section of your `coc-settings.json` file. + +Open Neovim and type the command `:CocConfig` to access the configuration file. Find the `langageserver` section and add this configuration: + +```json +"languageserver": { + "helm": { + "command": "helm_ls", + "args": ["serve"], + "filetypes": ["helm", "helmfile"], + "rootPatterns": ["Chart.yaml"] + } +} +``` + +Save the configuration file and then either restart Neovim or type `:CocRestart` to restart the language server. ### VSCode @@ -219,10 +288,10 @@ Alternatively, you can include a comment such as the following at the top of Hel ## Contributing -Thank you for considering contributing to HelmLs project! +Thank you for considering contributing to Helm-ls project! ## License -The HelmLs is open-source software licensed under the MIT license. +The Helm-ls is open-source software licensed under the MIT license. Part of the documentation that is included in helm-ls is copied from the Go standard library. The original license is included in the files containing the documentation. From 6600fed8f5e3e59d96914986f7fb675e35296ac6 Mon Sep 17 00:00:00 2001 From: qvalentin Date: Mon, 1 Apr 2024 13:51:13 +0200 Subject: [PATCH 22/35] feat(symboltable): add symboltable --- internal/handler/text.go | 1 - internal/lsp/document.go | 9 +- internal/lsp/symbol_table.go | 164 ++++++++++++++++++++++++++++++ internal/lsp/symbol_table_test.go | 80 +++++++++++++++ internal/lsp/visitor.go | 86 ++++++++++++++++ 5 files changed, 337 insertions(+), 3 deletions(-) delete mode 100644 internal/handler/text.go create mode 100644 internal/lsp/symbol_table.go create mode 100644 internal/lsp/symbol_table_test.go create mode 100644 internal/lsp/visitor.go diff --git a/internal/handler/text.go b/internal/handler/text.go deleted file mode 100644 index abeebd16..00000000 --- a/internal/handler/text.go +++ /dev/null @@ -1 +0,0 @@ -package handler diff --git a/internal/lsp/document.go b/internal/lsp/document.go index 8c263e93..1e4e196f 100644 --- a/internal/lsp/document.go +++ b/internal/lsp/document.go @@ -37,14 +37,17 @@ func (s *DocumentStore) DidOpen(params *lsp.DidOpenTextDocumentParams, helmlsCon uri := params.TextDocument.URI path := uri.Filename() + ast := ParseAst(nil, params.TextDocument.Text) doc := &Document{ URI: uri, Path: path, Content: params.TextDocument.Text, - Ast: ParseAst(nil, params.TextDocument.Text), + Ast: ast, DiagnosticsCache: NewDiagnosticsCache(helmlsConfig), + IsOpen: true, + SymbolTable: NewSymbolTable(ast), } - // logger.Println("Storing doc ", path, s.documents) + logger.Debug("Storing doc ", path) s.documents.Store(path, doc) return doc, nil } @@ -67,6 +70,8 @@ type Document struct { lines []string Ast *sitter.Tree DiagnosticsCache DiagnosticsCache + IsOpen bool + SymbolTable *SymbolTable } // ApplyChanges updates the content of the document from LSP textDocument/didChange events. diff --git a/internal/lsp/symbol_table.go b/internal/lsp/symbol_table.go new file mode 100644 index 00000000..e04aa647 --- /dev/null +++ b/internal/lsp/symbol_table.go @@ -0,0 +1,164 @@ +package lsp + +import ( + "strings" + + "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" + sitter "github.com/smacker/go-tree-sitter" +) + +type SymbolTable struct { + values map[string][]sitter.Range + includeDefinitions map[string]sitter.Range +} + +func NewSymbolTable(ast *sitter.Tree) *SymbolTable { + return &SymbolTable{ + values: make(map[string][]sitter.Range), + includeDefinitions: make(map[string]sitter.Range), + } +} + +func (s *SymbolTable) AddValue(symbol string, pointRange sitter.Range) { + s.values[symbol] = append(s.values[symbol], pointRange) +} + +func (s *SymbolTable) AddIncludeDefinition(symbol string, pointRange sitter.Range) { + s.includeDefinitions[symbol] = pointRange +} + +func (s *SymbolTable) parseTree(ast *sitter.Tree, content []byte) { + rootNode := ast.RootNode() + + v := Visitors{ + symbolTable: s, + visitors: []Visitor{ + &ValuesVisitor{ + currentContext: []string{}, + stashedContext: [][]string{}, + symbolTable: s, + content: content, + }, + &IncludeDefinitionsVisitor{ + symbolTable: s, + content: content, + }, + }, + } + + v.visitNodesRecursiveWithScopeShift(rootNode) +} + +type IncludeDefinitionsVisitor struct { + symbolTable *SymbolTable + content []byte +} + +func (v *IncludeDefinitionsVisitor) Enter(node *sitter.Node) { + if node.Type() != gotemplate.NodeTypeDefineAction { + return + } + v.symbolTable.AddIncludeDefinition(node.ChildByFieldName("name").Content(v.content), getRangeForNode(node)) +} + +func (v *IncludeDefinitionsVisitor) Exit(node *sitter.Node) {} +func (v *IncludeDefinitionsVisitor) EnterScopeShift(node *sitter.Node, suffix string) {} +func (v *IncludeDefinitionsVisitor) ExitScopeShift(node *sitter.Node) {} + +type ValuesVisitor struct { + currentContext []string + stashedContext [][]string + symbolTable *SymbolTable + content []byte +} + +func (v *ValuesVisitor) Enter(node *sitter.Node) { + switch node.Type() { + case gotemplate.NodeTypeDot: + v.symbolTable.AddValue(strings.Join(v.currentContext, "."), getRangeForNode(node)) + case gotemplate.NodeTypeFieldIdentifier: + content := node.Content(v.content) + v.symbolTable.AddValue(strings.Join(append(v.currentContext, content), "."), getRangeForNode(node)) + case gotemplate.NodeTypeField: + content := node.ChildByFieldName("name").Content(v.content) + value := strings.Join(append(v.currentContext, content), ".") + v.symbolTable.AddValue(value, getRangeForNode(node)) + case gotemplate.NodeTypeSelectorExpression: + operandNode := node.ChildByFieldName("operand") + if operandNode.Type() == gotemplate.NodeTypeVariable && operandNode.Content(v.content) == "$" { + v.stashedContext = append(v.stashedContext, v.currentContext) + v.currentContext = []string{} + } + } +} + +func (v *ValuesVisitor) Exit(node *sitter.Node) { + switch node.Type() { + case gotemplate.NodeTypeSelectorExpression: + operandNode := node.ChildByFieldName("operand") + if operandNode.Type() == gotemplate.NodeTypeVariable && operandNode.Content(v.content) == "$" { + v.currentContext = v.stashedContext[len(v.stashedContext)-1] + v.stashedContext = v.stashedContext[:len(v.stashedContext)-1] + } + } +} + +func (v *ValuesVisitor) EnterScopeShift(node *sitter.Node, suffix string) { + switch node.Type() { + case gotemplate.NodeTypeFieldIdentifier: + content := node.Content(v.content) + suffix + v.currentContext = append(v.currentContext, content) + case gotemplate.NodeTypeField: + content := node.ChildByFieldName("name").Content(v.content) + suffix + v.currentContext = append(v.currentContext, content) + case gotemplate.NodeTypeSelectorExpression: + s := getScopeForSelectorExpression(node, v.content) + if len(s) > 0 { + s[len(s)-1] = s[len(s)-1] + suffix + if s[0] == "$" { + v.stashedContext = append(v.stashedContext, v.currentContext) + v.currentContext = []string{} + s = s[1:] + } + } + v.currentContext = append(v.currentContext, s...) + } +} + +func (v *ValuesVisitor) ExitScopeShift(node *sitter.Node) { + switch node.Type() { + case gotemplate.NodeTypeField, gotemplate.NodeTypeFieldIdentifier: + v.currentContext = v.currentContext[:len(v.currentContext)-1] + case gotemplate.NodeTypeSelectorExpression: + s := getScopeForSelectorExpression(node, v.content) + if len(s) > 0 && s[0] == "$" { + v.currentContext = v.stashedContext[len(v.stashedContext)-1] + v.stashedContext = v.stashedContext[:len(v.stashedContext)-1] + s = s[1:] + } else { + v.currentContext = v.currentContext[:len(v.currentContext)-len(s)] + } + } +} + +func getScopeForSelectorExpression(node *sitter.Node, content []byte) []string { + if node == nil { + return []string{} + } + if node.Type() == gotemplate.NodeTypeField { + return []string{node.ChildByFieldName("name").Content(content)} + } + if node.Type() == gotemplate.NodeTypeVariable { + return []string{node.Content(content)} + } + + operand := node.ChildByFieldName("operand") + operandScope := getScopeForSelectorExpression(operand, content) + field := node.ChildByFieldName("field") + if field == nil { + return operandScope + } + fieldScope := field.Content(content) + + return append(operandScope, fieldScope) +} diff --git a/internal/lsp/symbol_table_test.go b/internal/lsp/symbol_table_test.go new file mode 100644 index 00000000..e899357e --- /dev/null +++ b/internal/lsp/symbol_table_test.go @@ -0,0 +1,80 @@ +package lsp + +import ( + "testing" + + sitter "github.com/smacker/go-tree-sitter" + "github.com/stretchr/testify/assert" +) + +func TestSymbolTableForIncludeDefinitions(t *testing.T) { + content := ` + {{ define "foo" }} + {{ .Values.global. }} + {{ end }} + + {{ define "bar" }} + {{ .Values.global. }} + {{ end }} + ` + + ast := ParseAst(nil, content) + + symbolTable := NewSymbolTable(ast) + + symbolTable.parseTree(ast, []byte(content)) + + assert.Len(t, symbolTable.includeDefinitions, 2) + + // TODO: remove the double quotes + assert.Equal(t, symbolTable.includeDefinitions["\"bar\""], sitter.Range{ + StartPoint: sitter.Point{ + Row: 5, + Column: 0, + }, + EndPoint: sitter.Point{ + Row: 7, + Column: 10, + }, + StartByte: 56, + EndByte: 110, + }) +} + +func TestSymbolTableForValues(t *testing.T) { + content := ` +{{ with .Values.with.something }} +{{ .test2 }} +{{ end }} + +{{ .Test }} +{{ .Values.with.something }} + + +{{ range .list }} + {{ . }} + {{ .listinner }} + {{ $.dollar }} + {{ range .nested }} + {{ .nestedinList }} + {{ end }} + {{ range $.Values.dollar }} + {{ .nestedinList }} + {{ end }} +{{ end }} + +{{ .Test }} +` + + ast := ParseAst(nil, content) + + symbolTable := NewSymbolTable(ast) + + symbolTable.parseTree(ast, []byte(content)) + + for k, v := range symbolTable.values { + logger.Println(k, v) + } + + assert.False(t, true) +} diff --git a/internal/lsp/visitor.go b/internal/lsp/visitor.go new file mode 100644 index 00000000..c0207e14 --- /dev/null +++ b/internal/lsp/visitor.go @@ -0,0 +1,86 @@ +package lsp + +import ( + "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" + sitter "github.com/smacker/go-tree-sitter" +) + +func (v *Visitors) parseNodesRecursive(node *sitter.Node) { + for _, visitor := range v.visitors { + visitor.Enter(node) + } + for i := uint32(0); i < node.ChildCount(); i++ { + v.parseNodesRecursive(node.Child(int(i))) + } + for _, visitor := range v.visitors { + visitor.Exit(node) + } +} + +type Visitors struct { + visitors []Visitor + symbolTable *SymbolTable +} + +type Visitor interface { + Enter(node *sitter.Node) + Exit(node *sitter.Node) + EnterScopeShift(node *sitter.Node, suffix string) + ExitScopeShift(node *sitter.Node) +} + +func (v *Visitors) visitNodesRecursiveWithScopeShift(node *sitter.Node) { + for _, visitor := range v.visitors { + visitor.Enter(node) + } + + nodeType := node.Type() + switch nodeType { + case gotemplate.NodeTypeWithAction: + condition := node.ChildByFieldName("condition") + v.visitNodesRecursiveWithScopeShift(condition) + for _, visitor := range v.visitors { + visitor.EnterScopeShift(condition, "") + } + for i := uint32(1); i < node.NamedChildCount(); i++ { + consequence := node.NamedChild(int(i)) + v.visitNodesRecursiveWithScopeShift(consequence) + } + for _, visitor := range v.visitors { + visitor.ExitScopeShift(condition) + } + case gotemplate.NodeTypeRangeAction: + rangeNode := node.ChildByFieldName("range") + v.visitNodesRecursiveWithScopeShift(rangeNode) + for _, visitor := range v.visitors { + visitor.EnterScopeShift(rangeNode, "[]") + } + for i := uint32(1); i < node.NamedChildCount(); i++ { + body := node.NamedChild(int(i)) + v.visitNodesRecursiveWithScopeShift(body) + } + for _, visitor := range v.visitors { + visitor.ExitScopeShift(rangeNode) + } + case gotemplate.NodeTypeSelectorExpression: + operand := node.ChildByFieldName("operand") + v.visitNodesRecursiveWithScopeShift(operand) + for _, visitor := range v.visitors { + visitor.EnterScopeShift(operand, "") + } + field := node.ChildByFieldName("field") + v.visitNodesRecursiveWithScopeShift(field) + for _, visitor := range v.visitors { + visitor.ExitScopeShift(operand) + } + + default: + for i := uint32(0); i < node.ChildCount(); i++ { + v.visitNodesRecursiveWithScopeShift(node.Child(int(i))) + } + } + + for _, visitor := range v.visitors { + visitor.Exit(node) + } +} From f097d8ae0a80eff7e934aaeab2702b50821ba54b Mon Sep 17 00:00:00 2001 From: qvalentin Date: Mon, 1 Apr 2024 21:05:03 +0200 Subject: [PATCH 23/35] feat(reference): symbol-tables provides definition references --- internal/handler/definition.go | 4 +- internal/handler/generic_document_usecase.go | 37 +++++ internal/handler/handler.go | 6 - internal/handler/initialization.go | 1 + internal/handler/references.go | 41 +++++ internal/lsp/document.go | 9 +- internal/lsp/symbol_table.go | 157 +++++-------------- internal/lsp/symbol_table_includes.go | 38 +++++ internal/lsp/symbol_table_test.go | 109 +++++++++++-- internal/lsp/symbol_table_values.go | 103 ++++++++++++ internal/lsp/visitor.go | 28 +--- internal/util/strings.go | 7 + 12 files changed, 376 insertions(+), 164 deletions(-) create mode 100644 internal/handler/generic_document_usecase.go create mode 100644 internal/handler/references.go create mode 100644 internal/lsp/symbol_table_includes.go create mode 100644 internal/lsp/symbol_table_values.go diff --git a/internal/handler/definition.go b/internal/handler/definition.go index 4116ceb5..08800d46 100644 --- a/internal/handler/definition.go +++ b/internal/handler/definition.go @@ -39,11 +39,11 @@ func (h *langHandler) Definition(ctx context.Context, params *lsp.DefinitionPara func (h *langHandler) definitionAstParsing(chart *charts.Chart, doc *lsplocal.Document, position lsp.Position) ([]lsp.Location, error) { var ( currentNode = lsplocal.NodeAtPosition(doc.Ast, position) - pointToLoopUp = sitter.Point{ + pointToLookUp = sitter.Point{ Row: position.Line, Column: position.Character, } - relevantChildNode = lsplocal.FindRelevantChildNode(currentNode, pointToLoopUp) + relevantChildNode = lsplocal.FindRelevantChildNode(currentNode, pointToLookUp) ) nodeType := relevantChildNode.Type() diff --git a/internal/handler/generic_document_usecase.go b/internal/handler/generic_document_usecase.go new file mode 100644 index 00000000..fbd3bbb7 --- /dev/null +++ b/internal/handler/generic_document_usecase.go @@ -0,0 +1,37 @@ +package handler + +import ( + "errors" + + "github.com/mrjosh/helm-ls/internal/charts" + lsplocal "github.com/mrjosh/helm-ls/internal/lsp" + sitter "github.com/smacker/go-tree-sitter" + lsp "go.lsp.dev/protocol" +) + +func (h *langHandler) genericDocumentUseCase(params lsp.TextDocumentPositionParams) (*lsplocal.Document, *charts.Chart, *sitter.Node, error) { + doc, ok := h.documents.Get(params.TextDocument.URI) + if !ok { + return nil, nil, nil, errors.New("Could not get document: " + params.TextDocument.URI.Filename()) + } + chart, err := h.chartStore.GetChartForDoc(params.TextDocument.URI) + if err != nil { + logger.Error("Error getting chart info for file", params.TextDocument.URI, err) + } + node := h.getNode(doc, params.Position) + if node == nil { + return doc, chart, nil, errors.New("Could not get node for: " + params.TextDocument.URI.Filename()) + } + return doc, chart, node, nil +} + +func (h *langHandler) getNode(doc *lsplocal.Document, position lsp.Position) *sitter.Node { + var ( + currentNode = lsplocal.NodeAtPosition(doc.Ast, position) + pointToLookUp = sitter.Point{ + Row: position.Line, + Column: position.Character, + } + ) + return lsplocal.FindRelevantChildNode(currentNode, pointToLookUp) +} diff --git a/internal/handler/handler.go b/internal/handler/handler.go index 0bf8ece8..ac86b4f6 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -219,12 +219,6 @@ func (h *langHandler) RangeFormatting(ctx context.Context, params *lsp.DocumentR return nil, nil } -// References implements protocol.Server. -func (h *langHandler) References(ctx context.Context, params *lsp.ReferenceParams) (result []lsp.Location, err error) { - logger.Error("References unimplemented") - return nil, nil -} - // Rename implements protocol.Server. func (h *langHandler) Rename(ctx context.Context, params *lsp.RenameParams) (result *lsp.WorkspaceEdit, err error) { logger.Error("Rename unimplemented") diff --git a/internal/handler/initialization.go b/internal/handler/initialization.go index d1b7bd26..15ed6b32 100644 --- a/internal/handler/initialization.go +++ b/internal/handler/initialization.go @@ -45,6 +45,7 @@ func (h *langHandler) Initialize(ctx context.Context, params *lsp.InitializePara }, HoverProvider: true, DefinitionProvider: true, + ReferencesProvider: true, }, }, nil } diff --git a/internal/handler/references.go b/internal/handler/references.go new file mode 100644 index 00000000..06cd6cb2 --- /dev/null +++ b/internal/handler/references.go @@ -0,0 +1,41 @@ +package handler + +import ( + "context" + + "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" + "github.com/mrjosh/helm-ls/internal/util" + lsp "go.lsp.dev/protocol" +) + +// References implements protocol.Server. +func (h *langHandler) References(ctx context.Context, params *lsp.ReferenceParams) (result []lsp.Location, err error) { + doc, _, node, err := h.genericDocumentUseCase(params.TextDocumentPositionParams) + if err != nil { + return nil, err + } + + parentNode := node.Parent() + pt := parentNode.Type() + ct := node.Type() + + if pt == gotemplate.NodeTypeDefineAction && ct == gotemplate.NodeTypeInterpretedStringLiteral { + referenceRanges, ok := doc.SymbolTable.GetIncludeReference(util.RemoveQuotes(node.Content([]byte(doc.Content)))) + + locations := []lsp.Location{} + for _, referenceRange := range referenceRanges { + locations = append(locations, lsp.Location{ + URI: params.TextDocumentPositionParams.TextDocument.URI, + Range: lsp.Range{ + Start: util.PointToPosition(referenceRange.StartPoint), + End: util.PointToPosition(referenceRange.EndPoint), + }, + }) + } + + if ok { + return locations, nil + } + } + return nil, nil +} diff --git a/internal/lsp/document.go b/internal/lsp/document.go index 1e4e196f..5a5f2395 100644 --- a/internal/lsp/document.go +++ b/internal/lsp/document.go @@ -45,7 +45,7 @@ func (s *DocumentStore) DidOpen(params *lsp.DidOpenTextDocumentParams, helmlsCon Ast: ast, DiagnosticsCache: NewDiagnosticsCache(helmlsConfig), IsOpen: true, - SymbolTable: NewSymbolTable(ast), + SymbolTable: NewSymbolTable(ast, []byte(params.TextDocument.Text)), } logger.Debug("Storing doc ", path) s.documents.Store(path, doc) @@ -89,6 +89,7 @@ func (d *Document) ApplyChanges(changes []lsp.TextDocumentContentChangeEvent) { d.Content = string(content) d.ApplyChangesToAst(d.Content) + d.SymbolTable = NewSymbolTable(d.Ast, []byte(d.Content)) d.lines = nil } @@ -97,15 +98,15 @@ func (d *Document) ApplyChanges(changes []lsp.TextDocumentContentChangeEvent) { func (d *Document) WordAt(pos lsp.Position) string { logger.Debug(pos) - line, ok := d.GetLine(int(pos.Line)) + line, ok := d.getLine(int(pos.Line)) if !ok { return "" } return util.WordAt(line, int(pos.Character)) } -// GetLine returns the line at the given index. -func (d *Document) GetLine(index int) (string, bool) { +// getLine returns the line at the given index. +func (d *Document) getLine(index int) (string, bool) { lines := d.getLines() if index < 0 || index > len(lines) { return "", false diff --git a/internal/lsp/symbol_table.go b/internal/lsp/symbol_table.go index e04aa647..ad78d563 100644 --- a/internal/lsp/symbol_table.go +++ b/internal/lsp/symbol_table.go @@ -3,28 +3,55 @@ package lsp import ( "strings" - "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" sitter "github.com/smacker/go-tree-sitter" ) type SymbolTable struct { values map[string][]sitter.Range - includeDefinitions map[string]sitter.Range + includeDefinitions map[string][]sitter.Range + includeReferences map[string][]sitter.Range } -func NewSymbolTable(ast *sitter.Tree) *SymbolTable { - return &SymbolTable{ +func NewSymbolTable(ast *sitter.Tree, content []byte) *SymbolTable { + s := &SymbolTable{ values: make(map[string][]sitter.Range), - includeDefinitions: make(map[string]sitter.Range), + includeDefinitions: make(map[string][]sitter.Range), + includeReferences: make(map[string][]sitter.Range), } + s.parseTree(ast, content) + return s } -func (s *SymbolTable) AddValue(symbol string, pointRange sitter.Range) { - s.values[symbol] = append(s.values[symbol], pointRange) +func (s *SymbolTable) AddValue(path []string, pointRange sitter.Range) { + s.values[strings.Join(path, ".")] = append(s.values[strings.Join(path, ".")], pointRange) +} + +func (s *SymbolTable) GetValues(path []string) []sitter.Range { + return s.values[strings.Join(path, ".")] } func (s *SymbolTable) AddIncludeDefinition(symbol string, pointRange sitter.Range) { - s.includeDefinitions[symbol] = pointRange + s.includeDefinitions[symbol] = append(s.includeDefinitions[symbol], pointRange) +} + +func (s *SymbolTable) AddIncludeReference(symbol string, pointRange sitter.Range) { + s.includeReferences[symbol] = append(s.includeReferences[symbol], pointRange) +} + +func (s *SymbolTable) GetIncludeDefinitions(symbol string) ([]sitter.Range, bool) { + result, ok := s.includeDefinitions[symbol] + if !ok { + return []sitter.Range{}, false + } + return result, true +} + +func (s *SymbolTable) GetIncludeReference(symbol string) ([]sitter.Range, bool) { + result, ok := s.includeReferences[symbol] + if !ok { + return []sitter.Range{}, false + } + return result, true } func (s *SymbolTable) parseTree(ast *sitter.Tree, content []byte) { @@ -48,117 +75,3 @@ func (s *SymbolTable) parseTree(ast *sitter.Tree, content []byte) { v.visitNodesRecursiveWithScopeShift(rootNode) } - -type IncludeDefinitionsVisitor struct { - symbolTable *SymbolTable - content []byte -} - -func (v *IncludeDefinitionsVisitor) Enter(node *sitter.Node) { - if node.Type() != gotemplate.NodeTypeDefineAction { - return - } - v.symbolTable.AddIncludeDefinition(node.ChildByFieldName("name").Content(v.content), getRangeForNode(node)) -} - -func (v *IncludeDefinitionsVisitor) Exit(node *sitter.Node) {} -func (v *IncludeDefinitionsVisitor) EnterScopeShift(node *sitter.Node, suffix string) {} -func (v *IncludeDefinitionsVisitor) ExitScopeShift(node *sitter.Node) {} - -type ValuesVisitor struct { - currentContext []string - stashedContext [][]string - symbolTable *SymbolTable - content []byte -} - -func (v *ValuesVisitor) Enter(node *sitter.Node) { - switch node.Type() { - case gotemplate.NodeTypeDot: - v.symbolTable.AddValue(strings.Join(v.currentContext, "."), getRangeForNode(node)) - case gotemplate.NodeTypeFieldIdentifier: - content := node.Content(v.content) - v.symbolTable.AddValue(strings.Join(append(v.currentContext, content), "."), getRangeForNode(node)) - case gotemplate.NodeTypeField: - content := node.ChildByFieldName("name").Content(v.content) - value := strings.Join(append(v.currentContext, content), ".") - v.symbolTable.AddValue(value, getRangeForNode(node)) - case gotemplate.NodeTypeSelectorExpression: - operandNode := node.ChildByFieldName("operand") - if operandNode.Type() == gotemplate.NodeTypeVariable && operandNode.Content(v.content) == "$" { - v.stashedContext = append(v.stashedContext, v.currentContext) - v.currentContext = []string{} - } - } -} - -func (v *ValuesVisitor) Exit(node *sitter.Node) { - switch node.Type() { - case gotemplate.NodeTypeSelectorExpression: - operandNode := node.ChildByFieldName("operand") - if operandNode.Type() == gotemplate.NodeTypeVariable && operandNode.Content(v.content) == "$" { - v.currentContext = v.stashedContext[len(v.stashedContext)-1] - v.stashedContext = v.stashedContext[:len(v.stashedContext)-1] - } - } -} - -func (v *ValuesVisitor) EnterScopeShift(node *sitter.Node, suffix string) { - switch node.Type() { - case gotemplate.NodeTypeFieldIdentifier: - content := node.Content(v.content) + suffix - v.currentContext = append(v.currentContext, content) - case gotemplate.NodeTypeField: - content := node.ChildByFieldName("name").Content(v.content) + suffix - v.currentContext = append(v.currentContext, content) - case gotemplate.NodeTypeSelectorExpression: - s := getScopeForSelectorExpression(node, v.content) - if len(s) > 0 { - s[len(s)-1] = s[len(s)-1] + suffix - if s[0] == "$" { - v.stashedContext = append(v.stashedContext, v.currentContext) - v.currentContext = []string{} - s = s[1:] - } - } - v.currentContext = append(v.currentContext, s...) - } -} - -func (v *ValuesVisitor) ExitScopeShift(node *sitter.Node) { - switch node.Type() { - case gotemplate.NodeTypeField, gotemplate.NodeTypeFieldIdentifier: - v.currentContext = v.currentContext[:len(v.currentContext)-1] - case gotemplate.NodeTypeSelectorExpression: - s := getScopeForSelectorExpression(node, v.content) - if len(s) > 0 && s[0] == "$" { - v.currentContext = v.stashedContext[len(v.stashedContext)-1] - v.stashedContext = v.stashedContext[:len(v.stashedContext)-1] - s = s[1:] - } else { - v.currentContext = v.currentContext[:len(v.currentContext)-len(s)] - } - } -} - -func getScopeForSelectorExpression(node *sitter.Node, content []byte) []string { - if node == nil { - return []string{} - } - if node.Type() == gotemplate.NodeTypeField { - return []string{node.ChildByFieldName("name").Content(content)} - } - if node.Type() == gotemplate.NodeTypeVariable { - return []string{node.Content(content)} - } - - operand := node.ChildByFieldName("operand") - operandScope := getScopeForSelectorExpression(operand, content) - field := node.ChildByFieldName("field") - if field == nil { - return operandScope - } - fieldScope := field.Content(content) - - return append(operandScope, fieldScope) -} diff --git a/internal/lsp/symbol_table_includes.go b/internal/lsp/symbol_table_includes.go new file mode 100644 index 00000000..2f4009cb --- /dev/null +++ b/internal/lsp/symbol_table_includes.go @@ -0,0 +1,38 @@ +package lsp + +import ( + "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" + "github.com/mrjosh/helm-ls/internal/util" + sitter "github.com/smacker/go-tree-sitter" +) + +type IncludeDefinitionsVisitor struct { + symbolTable *SymbolTable + content []byte +} + +func (v *IncludeDefinitionsVisitor) Enter(node *sitter.Node) { + if node.Type() == gotemplate.NodeTypeDefineAction { + content := node.ChildByFieldName("name").Content(v.content) + v.symbolTable.AddIncludeDefinition(util.RemoveQuotes(content), getRangeForNode(node)) + } + + // TODO: move this to separate function and use early returns + if node.Type() == gotemplate.NodeTypeFunctionCall { + functionName := node.ChildByFieldName("function").Content(v.content) + if functionName == "include" { + arguments := node.ChildByFieldName("arguments") + if arguments.ChildCount() > 0 { + firstArgument := arguments.Child(0) + if firstArgument.Type() == gotemplate.NodeTypeInterpretedStringLiteral { + content := firstArgument.Content(v.content) + v.symbolTable.AddIncludeReference(util.RemoveQuotes(content), getRangeForNode(node)) + } + } + } + } +} + +func (v *IncludeDefinitionsVisitor) Exit(node *sitter.Node) {} +func (v *IncludeDefinitionsVisitor) EnterContextShift(node *sitter.Node, suffix string) {} +func (v *IncludeDefinitionsVisitor) ExitContextShift(node *sitter.Node) {} diff --git a/internal/lsp/symbol_table_test.go b/internal/lsp/symbol_table_test.go index e899357e..6ad071ec 100644 --- a/internal/lsp/symbol_table_test.go +++ b/internal/lsp/symbol_table_test.go @@ -20,9 +20,7 @@ func TestSymbolTableForIncludeDefinitions(t *testing.T) { ast := ParseAst(nil, content) - symbolTable := NewSymbolTable(ast) - - symbolTable.parseTree(ast, []byte(content)) + symbolTable := NewSymbolTable(ast, []byte(content)) assert.Len(t, symbolTable.includeDefinitions, 2) @@ -50,7 +48,6 @@ func TestSymbolTableForValues(t *testing.T) { {{ .Test }} {{ .Values.with.something }} - {{ range .list }} {{ . }} {{ .listinner }} @@ -68,13 +65,105 @@ func TestSymbolTableForValues(t *testing.T) { ast := ParseAst(nil, content) - symbolTable := NewSymbolTable(ast) - - symbolTable.parseTree(ast, []byte(content)) + symbolTable := NewSymbolTable(ast, []byte(content)) + type expectedValue struct { + path []string + startPoint sitter.Point + } - for k, v := range symbolTable.values { - logger.Println(k, v) + expected := []expectedValue{ + { + path: []string{"Test"}, + startPoint: sitter.Point{ + Row: 5, + Column: 3, + }, + }, + { + path: []string{"Test"}, + startPoint: sitter.Point{ + Row: 20, + Column: 3, + }, + }, + { + path: []string{"Values", "with", "something"}, + startPoint: sitter.Point{ + Row: 1, + Column: 21, + }, + }, + { + path: []string{"Values", "with", "something"}, + startPoint: sitter.Point{ + Row: 6, + Column: 16, + }, + }, + { + path: []string{"list"}, + startPoint: sitter.Point{ + Row: 8, + Column: 9, + }, + }, + { + path: []string{"list[]"}, + startPoint: sitter.Point{ + Row: 9, + Column: 4, + }, + }, + { + path: []string{"list[]", "listinner"}, + startPoint: sitter.Point{ + Row: 10, + Column: 4, + }, + }, + { + path: []string{"dollar"}, + startPoint: sitter.Point{ + Row: 11, + Column: 6, + }, + }, + { + path: []string{"list[]", "nested"}, + startPoint: sitter.Point{ + Row: 12, + Column: 10, + }, + }, + { + path: []string{"list[]", "nested[]", "nestedinList"}, + startPoint: sitter.Point{ + Row: 13, + Column: 5, + }, + }, + { + path: []string{"Values", "dollar"}, + startPoint: sitter.Point{ + Row: 15, + Column: 19, + }, + }, + { + path: []string{"Values", "dollar[]", "nestedinList"}, + startPoint: sitter.Point{ + Row: 16, + Column: 5, + }, + }, } - assert.False(t, true) + for _, v := range expected { + values := symbolTable.GetValues(v.path) + points := []sitter.Point{} + for _, v := range values { + points = append(points, v.StartPoint) + } + assert.Contains(t, points, v.startPoint) + } } diff --git a/internal/lsp/symbol_table_values.go b/internal/lsp/symbol_table_values.go new file mode 100644 index 00000000..65ed207e --- /dev/null +++ b/internal/lsp/symbol_table_values.go @@ -0,0 +1,103 @@ +package lsp + +import ( + "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" + sitter "github.com/smacker/go-tree-sitter" +) + +type ValuesVisitor struct { + currentContext []string + stashedContext [][]string + symbolTable *SymbolTable + content []byte +} + +func (v *ValuesVisitor) Enter(node *sitter.Node) { + switch node.Type() { + case gotemplate.NodeTypeDot: + v.symbolTable.AddValue(v.currentContext, getRangeForNode(node)) + case gotemplate.NodeTypeFieldIdentifier: + content := node.Content(v.content) + v.symbolTable.AddValue(append(v.currentContext, content), getRangeForNode(node)) + case gotemplate.NodeTypeField: + content := node.ChildByFieldName("name").Content(v.content) + v.symbolTable.AddValue(append(v.currentContext, content), getRangeForNode(node)) + case gotemplate.NodeTypeSelectorExpression: + operandNode := node.ChildByFieldName("operand") + if operandNode.Type() == gotemplate.NodeTypeVariable && operandNode.Content(v.content) == "$" { + v.stashedContext = append(v.stashedContext, v.currentContext) + v.currentContext = []string{} + } + } +} + +func (v *ValuesVisitor) Exit(node *sitter.Node) { + switch node.Type() { + case gotemplate.NodeTypeSelectorExpression: + operandNode := node.ChildByFieldName("operand") + if operandNode.Type() == gotemplate.NodeTypeVariable && operandNode.Content(v.content) == "$" { + v.currentContext = v.stashedContext[len(v.stashedContext)-1] + v.stashedContext = v.stashedContext[:len(v.stashedContext)-1] + } + } +} + +func (v *ValuesVisitor) EnterContextShift(node *sitter.Node, suffix string) { + switch node.Type() { + case gotemplate.NodeTypeFieldIdentifier: + content := node.Content(v.content) + suffix + v.currentContext = append(v.currentContext, content) + case gotemplate.NodeTypeField: + content := node.ChildByFieldName("name").Content(v.content) + suffix + v.currentContext = append(v.currentContext, content) + case gotemplate.NodeTypeSelectorExpression: + s := getContextForSelectorExpression(node, v.content) + if len(s) > 0 { + s[len(s)-1] = s[len(s)-1] + suffix + if s[0] == "$" { + v.stashedContext = append(v.stashedContext, v.currentContext) + v.currentContext = []string{} + s = s[1:] + } + } + v.currentContext = append(v.currentContext, s...) + } +} + +func (v *ValuesVisitor) ExitContextShift(node *sitter.Node) { + switch node.Type() { + case gotemplate.NodeTypeField, gotemplate.NodeTypeFieldIdentifier: + v.currentContext = v.currentContext[:len(v.currentContext)-1] + case gotemplate.NodeTypeSelectorExpression: + s := getContextForSelectorExpression(node, v.content) + if len(s) > 0 && s[0] == "$" { + v.currentContext = v.stashedContext[len(v.stashedContext)-1] + v.stashedContext = v.stashedContext[:len(v.stashedContext)-1] + s = s[1:] + } else { + v.currentContext = v.currentContext[:len(v.currentContext)-len(s)] + } + } +} + +func getContextForSelectorExpression(node *sitter.Node, content []byte) []string { + if node == nil { + return []string{} + } + if node.Type() == gotemplate.NodeTypeField { + return []string{node.ChildByFieldName("name").Content(content)} + } + if node.Type() == gotemplate.NodeTypeVariable { + return []string{node.Content(content)} + } + + operand := node.ChildByFieldName("operand") + operandScope := getContextForSelectorExpression(operand, content) + field := node.ChildByFieldName("field") + if field == nil { + return operandScope + } + fieldScope := field.Content(content) + + return append(operandScope, fieldScope) +} diff --git a/internal/lsp/visitor.go b/internal/lsp/visitor.go index c0207e14..45b19a48 100644 --- a/internal/lsp/visitor.go +++ b/internal/lsp/visitor.go @@ -5,18 +5,6 @@ import ( sitter "github.com/smacker/go-tree-sitter" ) -func (v *Visitors) parseNodesRecursive(node *sitter.Node) { - for _, visitor := range v.visitors { - visitor.Enter(node) - } - for i := uint32(0); i < node.ChildCount(); i++ { - v.parseNodesRecursive(node.Child(int(i))) - } - for _, visitor := range v.visitors { - visitor.Exit(node) - } -} - type Visitors struct { visitors []Visitor symbolTable *SymbolTable @@ -25,8 +13,8 @@ type Visitors struct { type Visitor interface { Enter(node *sitter.Node) Exit(node *sitter.Node) - EnterScopeShift(node *sitter.Node, suffix string) - ExitScopeShift(node *sitter.Node) + EnterContextShift(node *sitter.Node, suffix string) + ExitContextShift(node *sitter.Node) } func (v *Visitors) visitNodesRecursiveWithScopeShift(node *sitter.Node) { @@ -40,38 +28,38 @@ func (v *Visitors) visitNodesRecursiveWithScopeShift(node *sitter.Node) { condition := node.ChildByFieldName("condition") v.visitNodesRecursiveWithScopeShift(condition) for _, visitor := range v.visitors { - visitor.EnterScopeShift(condition, "") + visitor.EnterContextShift(condition, "") } for i := uint32(1); i < node.NamedChildCount(); i++ { consequence := node.NamedChild(int(i)) v.visitNodesRecursiveWithScopeShift(consequence) } for _, visitor := range v.visitors { - visitor.ExitScopeShift(condition) + visitor.ExitContextShift(condition) } case gotemplate.NodeTypeRangeAction: rangeNode := node.ChildByFieldName("range") v.visitNodesRecursiveWithScopeShift(rangeNode) for _, visitor := range v.visitors { - visitor.EnterScopeShift(rangeNode, "[]") + visitor.EnterContextShift(rangeNode, "[]") } for i := uint32(1); i < node.NamedChildCount(); i++ { body := node.NamedChild(int(i)) v.visitNodesRecursiveWithScopeShift(body) } for _, visitor := range v.visitors { - visitor.ExitScopeShift(rangeNode) + visitor.ExitContextShift(rangeNode) } case gotemplate.NodeTypeSelectorExpression: operand := node.ChildByFieldName("operand") v.visitNodesRecursiveWithScopeShift(operand) for _, visitor := range v.visitors { - visitor.EnterScopeShift(operand, "") + visitor.EnterContextShift(operand, "") } field := node.ChildByFieldName("field") v.visitNodesRecursiveWithScopeShift(field) for _, visitor := range v.visitors { - visitor.ExitScopeShift(operand) + visitor.ExitContextShift(operand) } default: diff --git a/internal/util/strings.go b/internal/util/strings.go index f2ff86b8..0efa9897 100644 --- a/internal/util/strings.go +++ b/internal/util/strings.go @@ -81,3 +81,10 @@ func IndexToPosition(index int, content []byte) protocol.Position { Character: uint32(char), } } + +func RemoveQuotes(s string) string { + if len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"' { + return s[1 : len(s)-1] + } + return s +} From f6a1af228747824cfb591d91e9b6e1f8c74b22ba Mon Sep 17 00:00:00 2001 From: qvalentin Date: Fri, 5 Apr 2024 19:10:18 +0200 Subject: [PATCH 24/35] feat: add references --- internal/handler/definition.go | 38 ++- internal/handler/definition_test.go | 5 +- internal/handler/generic_document_usecase.go | 25 +- internal/handler/hover.go | 46 ++-- internal/handler/references.go | 40 ++-- internal/handler/references_test.go | 221 ++++++++++++++++++ internal/handler/text_document.go | 3 +- .../generic_document_usecase.go | 18 ++ internal/language_features/includes.go | 117 ++++++++++ internal/language_features/values.go | 50 ++++ internal/lsp/symbol_table.go | 56 ++--- internal/lsp/symbol_table_includes.go | 52 +++-- internal/lsp/symbol_table_test.go | 4 +- internal/lsp/symbol_table_values.go | 62 +++-- internal/lsp/visitor.go | 6 + internal/lsp/yaml_ast.go | 4 +- internal/tree-sitter/gotemplate/node-types.go | 1 + internal/util/lsp.go | 39 +++- internal/util/points.go | 11 + 19 files changed, 643 insertions(+), 155 deletions(-) create mode 100644 internal/handler/references_test.go create mode 100644 internal/language_features/generic_document_usecase.go create mode 100644 internal/language_features/includes.go create mode 100644 internal/language_features/values.go diff --git a/internal/handler/definition.go b/internal/handler/definition.go index 08800d46..6c8b2cd6 100644 --- a/internal/handler/definition.go +++ b/internal/handler/definition.go @@ -2,11 +2,11 @@ package handler import ( "context" - "errors" "fmt" "strings" "github.com/mrjosh/helm-ls/internal/charts" + 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" "github.com/mrjosh/helm-ls/internal/util" @@ -16,16 +16,14 @@ import ( ) func (h *langHandler) Definition(ctx context.Context, params *lsp.DefinitionParams) (result []lsp.Location, err error) { - doc, ok := h.documents.Get(params.TextDocument.URI) - if !ok { - return nil, errors.New("Could not get document: " + params.TextDocument.URI.Filename()) - } - chart, err := h.chartStore.GetChartForDoc(params.TextDocument.URI) + genericDocumentUseCase, err := h.NewGenericDocumentUseCase(params.TextDocumentPositionParams) if err != nil { - logger.Error("Error getting chart info for file", params.TextDocument.URI, err) + return nil, err } + doc := genericDocumentUseCase.Document + chart := genericDocumentUseCase.Chart - result, err = h.definitionAstParsing(chart, doc, params.Position) + result, err = h.definitionAstParsing(genericDocumentUseCase, chart, doc, params.Position) if err != nil { // suppress errors for clients // otherwise using go-to-definition on words that have no definition @@ -36,21 +34,16 @@ func (h *langHandler) Definition(ctx context.Context, params *lsp.DefinitionPara return result, nil } -func (h *langHandler) definitionAstParsing(chart *charts.Chart, doc *lsplocal.Document, position lsp.Position) ([]lsp.Location, error) { +func (h *langHandler) definitionAstParsing(genericDocumentUseCase languagefeatures.GenericDocumentUseCase, chart *charts.Chart, doc *lsplocal.Document, position lsp.Position) ([]lsp.Location, error) { var ( - currentNode = lsplocal.NodeAtPosition(doc.Ast, position) - pointToLookUp = sitter.Point{ - Row: position.Line, - Column: position.Character, - } - relevantChildNode = lsplocal.FindRelevantChildNode(currentNode, pointToLookUp) + relevantChildNode = genericDocumentUseCase.Node + parentType = relevantChildNode.Parent().Type() ) nodeType := relevantChildNode.Type() switch nodeType { case gotemplate.NodeTypeIdentifier: logger.Println("Parent type", relevantChildNode.Parent().Type()) - parentType := relevantChildNode.Parent().Type() if parentType == gotemplate.NodeTypeVariable { return h.getDefinitionForVariable(relevantChildNode, doc) } @@ -63,16 +56,21 @@ func (h *langHandler) definitionAstParsing(chart *charts.Chart, doc *lsplocal.Do return h.getDefinitionForValue(chart, relevantChildNode, doc) } + if parentType == gotemplate.NodeTypeArgumentList { + includesCallFeature := languagefeatures.NewIncludesCallFeature(genericDocumentUseCase) + return includesCallFeature.Definition() + } + return []lsp.Location{}, fmt.Errorf("Definition not implemented for node type %s", relevantChildNode.Type()) } func (h *langHandler) getDefinitionForVariable(node *sitter.Node, doc *lsplocal.Document) ([]lsp.Location, error) { variableName := node.Content([]byte(doc.Content)) - defintionNode := lsplocal.GetVariableDefinition(variableName, node.Parent(), doc.Content) - if defintionNode == nil { + definitionNode := lsplocal.GetVariableDefinition(variableName, node.Parent(), doc.Content) + if definitionNode == nil { return []lsp.Location{}, fmt.Errorf("Could not find definition for %s. Variable definition not found", variableName) } - return []lsp.Location{{URI: doc.URI, Range: lsp.Range{Start: util.PointToPosition(defintionNode.StartPoint())}}}, nil + return []lsp.Location{{URI: doc.URI, Range: lsp.Range{Start: util.PointToPosition(definitionNode.StartPoint())}}}, nil } // getDefinitionForFixedIdentifier checks if the current identifier has a constant definition and returns it @@ -119,7 +117,7 @@ func (h *langHandler) getDefinitionForValue(chart *charts.Chart, node *sitter.No } } - if err == nil && definitionFileURI != "" { + if definitionFileURI != "" { locations := []lsp.Location{} for _, position := range positions { locations = append(locations, lsp.Location{ diff --git a/internal/handler/definition_test.go b/internal/handler/definition_test.go index 2bb7616a..a26cdb63 100644 --- a/internal/handler/definition_test.go +++ b/internal/handler/definition_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/mrjosh/helm-ls/internal/charts" + 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" @@ -65,7 +66,7 @@ func genericDefinitionTest(t *testing.T, position lsp.Position, expectedLocation Ast: tree, } - location, err := handler.definitionAstParsing(&charts.Chart{ + location, err := handler.definitionAstParsing(languagefeatures.GenericDocumentUseCase{}, &charts.Chart{ ChartMetadata: &charts.ChartMetadata{}, ValuesFiles: &charts.ValuesFiles{ MainValuesFile: &charts.ValuesFile{ @@ -234,7 +235,7 @@ func genericDefinitionTestMultipleValuesFiles(t *testing.T, position lsp.Positio Ast: tree, } - location, err := handler.definitionAstParsing(&charts.Chart{ + location, err := handler.definitionAstParsing(languagefeatures.GenericDocumentUseCase{}, &charts.Chart{ ValuesFiles: &charts.ValuesFiles{ MainValuesFile: &charts.ValuesFile{ Values: make(map[string]interface{}), diff --git a/internal/handler/generic_document_usecase.go b/internal/handler/generic_document_usecase.go index fbd3bbb7..d30681e1 100644 --- a/internal/handler/generic_document_usecase.go +++ b/internal/handler/generic_document_usecase.go @@ -3,16 +3,16 @@ package handler import ( "errors" - "github.com/mrjosh/helm-ls/internal/charts" + 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) genericDocumentUseCase(params lsp.TextDocumentPositionParams) (*lsplocal.Document, *charts.Chart, *sitter.Node, error) { +func (h *langHandler) NewGenericDocumentUseCase(params lsp.TextDocumentPositionParams) (languagefeatures.GenericDocumentUseCase, error) { doc, ok := h.documents.Get(params.TextDocument.URI) if !ok { - return nil, nil, nil, errors.New("Could not get document: " + params.TextDocument.URI.Filename()) + return languagefeatures.GenericDocumentUseCase{}, errors.New("Could not get document: " + params.TextDocument.URI.Filename()) } chart, err := h.chartStore.GetChartForDoc(params.TextDocument.URI) if err != nil { @@ -20,18 +20,17 @@ func (h *langHandler) genericDocumentUseCase(params lsp.TextDocumentPositionPara } node := h.getNode(doc, params.Position) if node == nil { - return doc, chart, nil, errors.New("Could not get node for: " + params.TextDocument.URI.Filename()) + return languagefeatures.GenericDocumentUseCase{}, errors.New("Could not get node for: " + params.TextDocument.URI.Filename()) } - return doc, chart, node, nil + return languagefeatures.GenericDocumentUseCase{ + Document: doc, + DocumentStore: h.documents, + Chart: chart, + Node: node, + }, nil } func (h *langHandler) getNode(doc *lsplocal.Document, position lsp.Position) *sitter.Node { - var ( - currentNode = lsplocal.NodeAtPosition(doc.Ast, position) - pointToLookUp = sitter.Point{ - Row: position.Line, - Column: position.Character, - } - ) - return lsplocal.FindRelevantChildNode(currentNode, pointToLookUp) + currentNode := lsplocal.NodeAtPosition(doc.Ast, position) + return currentNode } diff --git a/internal/handler/hover.go b/internal/handler/hover.go index 99133a0e..6c9d2f6b 100644 --- a/internal/handler/hover.go +++ b/internal/handler/hover.go @@ -4,12 +4,11 @@ import ( "context" "errors" "fmt" - "path/filepath" "reflect" - "sort" "strings" "github.com/mrjosh/helm-ls/internal/charts" + languagefeatures "github.com/mrjosh/helm-ls/internal/language_features" lspinternal "github.com/mrjosh/helm-ls/internal/lsp" "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" @@ -17,18 +16,15 @@ import ( "github.com/mrjosh/helm-ls/pkg/chart" "github.com/mrjosh/helm-ls/pkg/chartutil" lsp "go.lsp.dev/protocol" - "go.lsp.dev/uri" ) func (h *langHandler) Hover(ctx context.Context, params *lsp.HoverParams) (result *lsp.Hover, err error) { - doc, ok := h.documents.Get(params.TextDocument.URI) - if !ok { - return nil, errors.New("Could not get document: " + params.TextDocument.URI.Filename()) - } - chart, err := h.chartStore.GetChartForDoc(params.TextDocument.URI) + genericDocumentUseCase, err := h.NewGenericDocumentUseCase(params.TextDocumentPositionParams) if err != nil { - logger.Error("Error getting chart info for file", params.TextDocument.URI, err) + return nil, err } + doc := genericDocumentUseCase.Document + chart := genericDocumentUseCase.Chart var ( currentNode = lspinternal.NodeAtPosition(doc.Ast, params.Position) @@ -62,6 +58,11 @@ func (h *langHandler) Hover(ctx context.Context, params *lsp.HoverParams) (resul if ct == gotemplate.NodeTypeDot { word = lspinternal.TraverseIdentifierPathUp(currentNode, doc) } + if pt == gotemplate.NodeTypeArgumentList { + includesCallFeature := languagefeatures.NewIncludesCallFeature(genericDocumentUseCase) + response, err := includesCallFeature.Hover() + return util.BuildHoverResponse(response, wordRange), err + } var ( splitted = strings.Split(word, ".") @@ -145,39 +146,20 @@ func (h *langHandler) getChartMetadataHover(metadata *chart.Metadata, key string func (h *langHandler) getValueHover(chart *charts.Chart, splittedVar []string) (result string, err error) { var ( - valuesFiles = chart.ResolveValueFiles(splittedVar, h.chartStore) - results = map[uri.URI]string{} + valuesFiles = chart.ResolveValueFiles(splittedVar, h.chartStore) + hoverResults = util.HoverResultsWithFiles{} ) for _, valuesFiles := range valuesFiles { for _, valuesFile := range valuesFiles.ValuesFiles.AllValuesFiles() { result, err := h.getTableOrValueForSelector(valuesFile.Values, strings.Join(valuesFiles.Selector, ".")) if err == nil { - results[valuesFile.URI] = result + hoverResults = append(hoverResults, util.HoverResultWithFile{URI: valuesFile.URI, Value: result}) } } } - keys := make([]string, 0, len(results)) - for u := range results { - keys = append(keys, string(u)) - } - - sort.Sort(sort.Reverse(sort.StringSlice(keys))) - - for _, key := range keys { - uriKey := uri.New(key) - value := results[uriKey] - if value == "" { - value = "\"\"" - } - filepath, err := filepath.Rel(h.chartStore.RootURI.Filename(), uriKey.Filename()) - if err != nil { - filepath = uriKey.Filename() - } - result += fmt.Sprintf("### %s\n%s\n\n", filepath, value) - } - return result, nil + return hoverResults.Format(h.chartStore.RootURI), nil } func (h *langHandler) getTableOrValueForSelector(values chartutil.Values, selector string) (string, error) { diff --git a/internal/handler/references.go b/internal/handler/references.go index 06cd6cb2..a7cc0843 100644 --- a/internal/handler/references.go +++ b/internal/handler/references.go @@ -3,39 +3,37 @@ package handler import ( "context" + languagefeatures "github.com/mrjosh/helm-ls/internal/language_features" "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" - "github.com/mrjosh/helm-ls/internal/util" lsp "go.lsp.dev/protocol" ) -// References implements protocol.Server. func (h *langHandler) References(ctx context.Context, params *lsp.ReferenceParams) (result []lsp.Location, err error) { - doc, _, node, err := h.genericDocumentUseCase(params.TextDocumentPositionParams) + genericDocumentUseCase, err := h.NewGenericDocumentUseCase(params.TextDocumentPositionParams) if err != nil { return nil, err } - parentNode := node.Parent() + parentNode := genericDocumentUseCase.Node.Parent() pt := parentNode.Type() - ct := node.Type() + ct := genericDocumentUseCase.Node.Type() + + logger.Println("pt", pt, "ct", ct) + logger.Println(genericDocumentUseCase.NodeContent()) if pt == gotemplate.NodeTypeDefineAction && ct == gotemplate.NodeTypeInterpretedStringLiteral { - referenceRanges, ok := doc.SymbolTable.GetIncludeReference(util.RemoveQuotes(node.Content([]byte(doc.Content)))) - - locations := []lsp.Location{} - for _, referenceRange := range referenceRanges { - locations = append(locations, lsp.Location{ - URI: params.TextDocumentPositionParams.TextDocument.URI, - Range: lsp.Range{ - Start: util.PointToPosition(referenceRange.StartPoint), - End: util.PointToPosition(referenceRange.EndPoint), - }, - }) - } - - if ok { - return locations, nil - } + includesDefinitionFeature := languagefeatures.NewIncludesDefinitionFeature(genericDocumentUseCase) + return includesDefinitionFeature.References() + } + + if pt == gotemplate.NodeTypeArgumentList { + includesCallFeature := languagefeatures.NewIncludesCallFeature(genericDocumentUseCase) + return includesCallFeature.References() + } + + if (pt == gotemplate.NodeTypeField && ct == gotemplate.NodeTypeIdentifier) || ct == gotemplate.NodeTypeFieldIdentifier || ct == gotemplate.NodeTypeField { + valuesFeature := languagefeatures.NewValuesFeature(genericDocumentUseCase) + return valuesFeature.References() } return nil, nil } diff --git a/internal/handler/references_test.go b/internal/handler/references_test.go new file mode 100644 index 00000000..db5dc094 --- /dev/null +++ b/internal/handler/references_test.go @@ -0,0 +1,221 @@ +package handler + +import ( + "context" + "testing" + + "github.com/mrjosh/helm-ls/internal/adapter/yamlls" + "github.com/mrjosh/helm-ls/internal/charts" + 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" +) + +func TestRefercesIncludes(t *testing.T) { + content := `{{define "name"}} T1 {{end}} + {{include "name" .}} + {{include "name" .}} +` + + expected := []lsp.Location{ + { + URI: uri.File("/tmp/testfile.yaml"), + Range: protocol.Range{ + Start: protocol.Position{ + Line: 0x1, Character: 0x3, + }, + End: protocol.Position{ + Line: 0x1, + Character: 0x13, + }, + }, + }, + protocol.Location{ + URI: uri.File("/tmp/testfile.yaml"), + Range: protocol.Range{ + Start: protocol.Position{ + Line: 0x2, + Character: 0x3, + }, + End: protocol.Position{ + Line: 0x2, + Character: 0x13, + }, + }, + }, + protocol.Location{ + URI: uri.File("/tmp/testfile.yaml"), + Range: protocol.Range{ + Start: protocol.Position{ + Line: 0x0, + Character: 0x0, + }, + End: protocol.Position{ + Line: 0x0, + Character: 0x1c, + }, + }, + }, + } + testCases := []struct { + desc string + position lsp.Position + expected []lsp.Location + expectedError error + }{ + { + desc: "Test references on define", + position: lsp.Position{ + Line: 0, + Character: 11, + }, + expected: expected, + }, + { + desc: "Test references on include", + position: lsp.Position{ + Line: 2, + Character: 14, + }, + expected: expected, + }, + } + + for _, tt := range testCases { + t.Run(tt.desc, func(t *testing.T) { + documents := lsplocal.NewDocumentStore() + + path := "/tmp/testfile.yaml" + fileURI := uri.File(path) + + d := lsp.DidOpenTextDocumentParams{ + TextDocument: lsp.TextDocumentItem{ + URI: fileURI, + LanguageID: "", + Version: 0, + Text: string(content), + }, + } + documents.DidOpen(&d, util.DefaultConfig) + h := &langHandler{ + chartStore: charts.NewChartStore(uri.File("."), charts.NewChart), + documents: documents, + yamllsConnector: &yamlls.Connector{}, + } + result, err := h.References(context.Background(), &lsp.ReferenceParams{ + TextDocumentPositionParams: lsp.TextDocumentPositionParams{ + TextDocument: lsp.TextDocumentIdentifier{ + URI: fileURI, + }, + Position: tt.position, + }, + }) + assert.Equal(t, tt.expectedError, err) + if err == nil { + assert.Equal(t, tt.expected, result) + } + }) + } +} + +func TestRefercesTemplateContext(t *testing.T) { + content := ` +{{ .Values.test }} +{{ .Values.test.nested }} +{{ .Values.test }} +` + expected := []lsp.Location{ + { + URI: uri.File("/tmp/testfile.yaml"), + Range: protocol.Range{ + Start: protocol.Position{ + Line: 0x1, Character: 0x3, + }, + End: protocol.Position{ + Line: 0x1, + Character: 0x13, + }, + }, + }, + protocol.Location{ + URI: uri.File("/tmp/testfile.yaml"), + Range: protocol.Range{ + Start: protocol.Position{ + Line: 0x2, + Character: 0x3, + }, + End: protocol.Position{ + Line: 0x2, + Character: 0x13, + }, + }, + }, + protocol.Location{ + URI: uri.File("/tmp/testfile.yaml"), + Range: protocol.Range{ + Start: protocol.Position{ + Line: 0x0, + Character: 0x0, + }, + End: protocol.Position{ + Line: 0x0, + Character: 0x1c, + }, + }, + }, + } + testCases := []struct { + desc string + position lsp.Position + expected []lsp.Location + expectedError error + }{ + { + desc: "Test references on .Values", + position: lsp.Position{ + Line: 1, + Character: 8, + }, + expected: expected, + }, + } + + for _, tt := range testCases { + t.Run(tt.desc, func(t *testing.T) { + documents := lsplocal.NewDocumentStore() + + path := "/tmp/testfile.yaml" + fileURI := uri.File(path) + + d := lsp.DidOpenTextDocumentParams{ + TextDocument: lsp.TextDocumentItem{ + URI: fileURI, + LanguageID: "", + Version: 0, + Text: string(content), + }, + } + documents.DidOpen(&d, util.DefaultConfig) + h := &langHandler{ + chartStore: charts.NewChartStore(uri.File("."), charts.NewChart), + documents: documents, + yamllsConnector: &yamlls.Connector{}, + } + result, err := h.References(context.Background(), &lsp.ReferenceParams{ + TextDocumentPositionParams: lsp.TextDocumentPositionParams{ + TextDocument: lsp.TextDocumentIdentifier{ + URI: fileURI, + }, + Position: tt.position, + }, + }) + assert.Equal(t, tt.expectedError, err) + if err == nil { + assert.Equal(t, tt.expected, result) + } + }) + } +} diff --git a/internal/handler/text_document.go b/internal/handler/text_document.go index 2209e0c6..8f7724c7 100644 --- a/internal/handler/text_document.go +++ b/internal/handler/text_document.go @@ -4,7 +4,6 @@ import ( "context" "errors" - lspinternal "github.com/mrjosh/helm-ls/internal/lsp" lsplocal "github.com/mrjosh/helm-ls/internal/lsp" lsp "go.lsp.dev/protocol" ) @@ -63,7 +62,7 @@ func (h *langHandler) DidChange(ctx context.Context, params *lsp.DidChangeTextDo doc.ApplyChanges(params.ContentChanges) for _, change := range params.ContentChanges { - node := lspinternal.NodeAtPosition(doc.Ast, change.Range.Start) + node := lsplocal.NodeAtPosition(doc.Ast, change.Range.Start) if node.Type() != "text" { shouldSendFullUpdateToYamlls = true break diff --git a/internal/language_features/generic_document_usecase.go b/internal/language_features/generic_document_usecase.go new file mode 100644 index 00000000..355e3a96 --- /dev/null +++ b/internal/language_features/generic_document_usecase.go @@ -0,0 +1,18 @@ +package languagefeatures + +import ( + "github.com/mrjosh/helm-ls/internal/charts" + lsplocal "github.com/mrjosh/helm-ls/internal/lsp" + sitter "github.com/smacker/go-tree-sitter" +) + +type GenericDocumentUseCase struct { + Document *lsplocal.Document + DocumentStore *lsplocal.DocumentStore + Chart *charts.Chart + Node *sitter.Node +} + +func (u *GenericDocumentUseCase) NodeContent() string { + return u.Node.Content([]byte(u.Document.Content)) +} diff --git a/internal/language_features/includes.go b/internal/language_features/includes.go new file mode 100644 index 00000000..f1640950 --- /dev/null +++ b/internal/language_features/includes.go @@ -0,0 +1,117 @@ +package languagefeatures + +import ( + lsp "go.lsp.dev/protocol" + + lsplocal "github.com/mrjosh/helm-ls/internal/lsp" + "github.com/mrjosh/helm-ls/internal/util" +) + +type IncludesFeature struct { + GenericDocumentUseCase +} + +// should be called on {{ include "name" . }} +type IncludesCallFeature struct { + IncludesFeature +} + +// should be called on {{ define "name" }} +type IncludesDefinitionFeature struct { + IncludesFeature +} + +func NewIncludesCallFeature(genericDocumentUseCase GenericDocumentUseCase) *IncludesCallFeature { + return &IncludesCallFeature{ + IncludesFeature: IncludesFeature{genericDocumentUseCase}, + } +} + +func NewIncludesDefinitionFeature(genericDocumentUseCase GenericDocumentUseCase) *IncludesDefinitionFeature { + return &IncludesDefinitionFeature{ + IncludesFeature: IncludesFeature{genericDocumentUseCase}, + } +} + +func (f *IncludesCallFeature) References() (result []lsp.Location, err error) { + includeName, err := f.getIncludeName() + if err != nil { + return []lsp.Location{}, err + } + + locations := f.getReferenceLocations(includeName) + return locations, nil +} + +func (f *IncludesCallFeature) getIncludeName() (string, error) { + functionCallNode := f.Node.Parent().Parent() + includeName, err := lsplocal.ParseIncludeFunctionCall(functionCallNode, []byte(f.GenericDocumentUseCase.Document.Content)) + return includeName, err +} + +func (f *IncludesDefinitionFeature) References() (result []lsp.Location, err error) { + includeName := util.RemoveQuotes(f.GenericDocumentUseCase.NodeContent()) + + locations := f.getReferenceLocations(includeName) + return locations, nil +} + +func (f *IncludesFeature) getReferenceLocations(includeName string) []lsp.Location { + locations := []lsp.Location{} + for _, doc := range f.GenericDocumentUseCase.DocumentStore.GetAllDocs() { + referenceRanges := doc.SymbolTable.GetIncludeReference(includeName) + for _, referenceRange := range referenceRanges { + locations = append(locations, util.RangeToLocation(doc.URI, referenceRange)) + } + } + + return locations +} + +func (f *IncludesFeature) getDefinitionLocations(includeName string) []lsp.Location { + locations := []lsp.Location{} + for _, doc := range f.GenericDocumentUseCase.DocumentStore.GetAllDocs() { + referenceRanges := doc.SymbolTable.GetIncludeDefinitions(includeName) + for _, referenceRange := range referenceRanges { + locations = append(locations, util.RangeToLocation(doc.URI, referenceRange)) + } + } + + return locations +} + +func (f *IncludesFeature) getDefinitionsHover(includeName string) util.HoverResultsWithFiles { + result := util.HoverResultsWithFiles{} + for _, doc := range f.GenericDocumentUseCase.DocumentStore.GetAllDocs() { + referenceRanges := doc.SymbolTable.GetIncludeDefinitions(includeName) + for _, referenceRange := range referenceRanges { + node := doc.Ast.RootNode().NamedDescendantForPointRange(referenceRange.StartPoint, referenceRange.EndPoint) + if node != nil { + result = append(result, util.HoverResultWithFile{ + Value: node.Content([]byte(doc.Content)), + URI: doc.URI, + }) + } + } + } + + return result +} + +func (f *IncludesCallFeature) Hover() (string, error) { + includeName, err := f.getIncludeName() + if err != nil { + return "", err + } + + result := f.getDefinitionsHover(includeName) + return result.Format(f.GenericDocumentUseCase.Document.URI), nil +} + +func (f *IncludesCallFeature) Definition() (result []lsp.Location, err error) { + includeName, err := f.getIncludeName() + if err != nil { + return []lsp.Location{}, err + } + return f.getDefinitionLocations(includeName), nil +} diff --git a/internal/language_features/values.go b/internal/language_features/values.go new file mode 100644 index 00000000..b3ef6138 --- /dev/null +++ b/internal/language_features/values.go @@ -0,0 +1,50 @@ +package languagefeatures + +import ( + "fmt" + + lsp "go.lsp.dev/protocol" + + lsplocal "github.com/mrjosh/helm-ls/internal/lsp" + "github.com/mrjosh/helm-ls/internal/util" +) + +type ValuesFeature struct { + GenericDocumentUseCase +} + +func NewValuesFeature(genericDocumentUseCase GenericDocumentUseCase) *ValuesFeature { + return &ValuesFeature{ + GenericDocumentUseCase: genericDocumentUseCase, + } +} + +func (f *ValuesFeature) References() (result []lsp.Location, err error) { + includeName, err := f.getTemplateContext() + if err != nil { + return []lsp.Location{}, err + } + + locations := f.getReferenceLocations(includeName) + return locations, nil +} + +func (f *ValuesFeature) getTemplateContext() (lsplocal.TemplateContext, error) { + templateContext := f.GenericDocumentUseCase.Document.SymbolTable.GetTemplateContext(lsplocal.GetRangeForNode(f.Node)) + if len(templateContext) == 0 || templateContext == nil { + return lsplocal.TemplateContext{}, fmt.Errorf("no template context found") + } + return templateContext, nil +} + +func (f *ValuesFeature) getReferenceLocations(templateContext lsplocal.TemplateContext) []lsp.Location { + locations := []lsp.Location{} + for _, doc := range f.GenericDocumentUseCase.DocumentStore.GetAllDocs() { + referenceRanges := doc.SymbolTable.GetValues(templateContext) + for _, referenceRange := range referenceRanges { + locations = append(locations, util.RangeToLocation(doc.URI, referenceRange)) + } + } + + return locations +} diff --git a/internal/lsp/symbol_table.go b/internal/lsp/symbol_table.go index ad78d563..5fc3286f 100644 --- a/internal/lsp/symbol_table.go +++ b/internal/lsp/symbol_table.go @@ -6,28 +6,37 @@ import ( sitter "github.com/smacker/go-tree-sitter" ) +type TemplateContext []string + type SymbolTable struct { - values map[string][]sitter.Range + contexts map[string][]sitter.Range + contextsReversed map[sitter.Range]TemplateContext includeDefinitions map[string][]sitter.Range - includeReferences map[string][]sitter.Range + includeUseages map[string][]sitter.Range } func NewSymbolTable(ast *sitter.Tree, content []byte) *SymbolTable { s := &SymbolTable{ - values: make(map[string][]sitter.Range), + contexts: make(map[string][]sitter.Range), + contextsReversed: make(map[sitter.Range]TemplateContext), includeDefinitions: make(map[string][]sitter.Range), - includeReferences: make(map[string][]sitter.Range), + includeUseages: make(map[string][]sitter.Range), } s.parseTree(ast, content) return s } -func (s *SymbolTable) AddValue(path []string, pointRange sitter.Range) { - s.values[strings.Join(path, ".")] = append(s.values[strings.Join(path, ".")], pointRange) +func (s *SymbolTable) AddValue(templateContext []string, pointRange sitter.Range) { + s.contexts[strings.Join(templateContext, ".")] = append(s.contexts[strings.Join(templateContext, ".")], pointRange) + s.contextsReversed[pointRange] = templateContext } func (s *SymbolTable) GetValues(path []string) []sitter.Range { - return s.values[strings.Join(path, ".")] + return s.contexts[strings.Join(path, ".")] +} + +func (s *SymbolTable) GetTemplateContext(pointRange sitter.Range) TemplateContext { + return s.contextsReversed[pointRange] } func (s *SymbolTable) AddIncludeDefinition(symbol string, pointRange sitter.Range) { @@ -35,41 +44,26 @@ func (s *SymbolTable) AddIncludeDefinition(symbol string, pointRange sitter.Rang } func (s *SymbolTable) AddIncludeReference(symbol string, pointRange sitter.Range) { - s.includeReferences[symbol] = append(s.includeReferences[symbol], pointRange) + s.includeUseages[symbol] = append(s.includeUseages[symbol], pointRange) } -func (s *SymbolTable) GetIncludeDefinitions(symbol string) ([]sitter.Range, bool) { - result, ok := s.includeDefinitions[symbol] - if !ok { - return []sitter.Range{}, false - } - return result, true +func (s *SymbolTable) GetIncludeDefinitions(symbol string) []sitter.Range { + return s.includeDefinitions[symbol] } -func (s *SymbolTable) GetIncludeReference(symbol string) ([]sitter.Range, bool) { - result, ok := s.includeReferences[symbol] - if !ok { - return []sitter.Range{}, false - } - return result, true +func (s *SymbolTable) GetIncludeReference(symbol string) []sitter.Range { + result := s.includeUseages[symbol] + definitions := s.includeDefinitions[symbol] + return append(result, definitions...) } func (s *SymbolTable) parseTree(ast *sitter.Tree, content []byte) { rootNode := ast.RootNode() - v := Visitors{ symbolTable: s, visitors: []Visitor{ - &ValuesVisitor{ - currentContext: []string{}, - stashedContext: [][]string{}, - symbolTable: s, - content: content, - }, - &IncludeDefinitionsVisitor{ - symbolTable: s, - content: content, - }, + NewValuesVisitor(s, content), + NewIncludeDefinitionsVisitor(s, content), }, } diff --git a/internal/lsp/symbol_table_includes.go b/internal/lsp/symbol_table_includes.go index 2f4009cb..2d6b1650 100644 --- a/internal/lsp/symbol_table_includes.go +++ b/internal/lsp/symbol_table_includes.go @@ -1,6 +1,8 @@ package lsp import ( + "fmt" + "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" "github.com/mrjosh/helm-ls/internal/util" sitter "github.com/smacker/go-tree-sitter" @@ -11,26 +13,50 @@ type IncludeDefinitionsVisitor struct { content []byte } +func NewIncludeDefinitionsVisitor(symbolTable *SymbolTable, content []byte) *IncludeDefinitionsVisitor { + return &IncludeDefinitionsVisitor{ + symbolTable: symbolTable, + content: content, + } +} + func (v *IncludeDefinitionsVisitor) Enter(node *sitter.Node) { if node.Type() == gotemplate.NodeTypeDefineAction { content := node.ChildByFieldName("name").Content(v.content) - v.symbolTable.AddIncludeDefinition(util.RemoveQuotes(content), getRangeForNode(node)) + v.symbolTable.AddIncludeDefinition(util.RemoveQuotes(content), GetRangeForNode(node)) } - // TODO: move this to separate function and use early returns if node.Type() == gotemplate.NodeTypeFunctionCall { - functionName := node.ChildByFieldName("function").Content(v.content) - if functionName == "include" { - arguments := node.ChildByFieldName("arguments") - if arguments.ChildCount() > 0 { - firstArgument := arguments.Child(0) - if firstArgument.Type() == gotemplate.NodeTypeInterpretedStringLiteral { - content := firstArgument.Content(v.content) - v.symbolTable.AddIncludeReference(util.RemoveQuotes(content), getRangeForNode(node)) - } - } - } + v.EnterFunctionCall(node) + } +} + +func (v *IncludeDefinitionsVisitor) EnterFunctionCall(node *sitter.Node) { + includeName, err := ParseIncludeFunctionCall(node, v.content) + if err != nil { + return + } + + v.symbolTable.AddIncludeReference(includeName, GetRangeForNode(node)) +} + +func ParseIncludeFunctionCall(node *sitter.Node, content []byte) (string, error) { + if node.Type() != gotemplate.NodeTypeFunctionCall { + return "", fmt.Errorf("node is not a function call") + } + functionName := node.ChildByFieldName("function").Content(content) + if functionName != "include" { + return "", fmt.Errorf("function name is not include") + } + arguments := node.ChildByFieldName("arguments") + if arguments == nil || arguments.ChildCount() == 0 { + return "", fmt.Errorf("no arguments") + } + firstArgument := arguments.Child(0) + if firstArgument.Type() != gotemplate.NodeTypeInterpretedStringLiteral { + return "", fmt.Errorf("first argument is not an interpreted string literal") } + return util.RemoveQuotes(firstArgument.Content(content)), nil } func (v *IncludeDefinitionsVisitor) Exit(node *sitter.Node) {} diff --git a/internal/lsp/symbol_table_test.go b/internal/lsp/symbol_table_test.go index 6ad071ec..1f7b77ee 100644 --- a/internal/lsp/symbol_table_test.go +++ b/internal/lsp/symbol_table_test.go @@ -25,7 +25,7 @@ func TestSymbolTableForIncludeDefinitions(t *testing.T) { assert.Len(t, symbolTable.includeDefinitions, 2) // TODO: remove the double quotes - assert.Equal(t, symbolTable.includeDefinitions["\"bar\""], sitter.Range{ + assert.Equal(t, symbolTable.includeDefinitions["bar"], []sitter.Range{{ StartPoint: sitter.Point{ Row: 5, Column: 0, @@ -36,7 +36,7 @@ func TestSymbolTableForIncludeDefinitions(t *testing.T) { }, StartByte: 56, EndByte: 110, - }) + }}) } func TestSymbolTableForValues(t *testing.T) { diff --git a/internal/lsp/symbol_table_values.go b/internal/lsp/symbol_table_values.go index 65ed207e..2a1900dd 100644 --- a/internal/lsp/symbol_table_values.go +++ b/internal/lsp/symbol_table_values.go @@ -12,21 +12,55 @@ type ValuesVisitor struct { content []byte } +func NewValuesVisitor(symbolTable *SymbolTable, content []byte) *ValuesVisitor { + return &ValuesVisitor{ + currentContext: []string{}, + stashedContext: [][]string{}, + symbolTable: symbolTable, + content: content, + } +} + +func (v *ValuesVisitor) PushContext(context string) { + v.currentContext = append(v.currentContext, context) +} + +func (v *ValuesVisitor) PushContextMany(context []string) { + v.currentContext = append(v.currentContext, context...) +} + +func (v *ValuesVisitor) PopContext() { + v.currentContext = v.currentContext[:len(v.currentContext)-1] +} + +func (v *ValuesVisitor) PopContextN(n int) { + v.currentContext = v.currentContext[:len(v.currentContext)-n] +} + +func (v *ValuesVisitor) StashContext() { + v.stashedContext = append(v.stashedContext, v.currentContext) + v.currentContext = []string{} +} + +func (v *ValuesVisitor) RestoreStashedContext() { + v.currentContext = v.stashedContext[len(v.stashedContext)-1] + v.stashedContext = v.stashedContext[:len(v.stashedContext)-1] +} + func (v *ValuesVisitor) Enter(node *sitter.Node) { switch node.Type() { case gotemplate.NodeTypeDot: - v.symbolTable.AddValue(v.currentContext, getRangeForNode(node)) + v.symbolTable.AddValue(v.currentContext, GetRangeForNode(node)) case gotemplate.NodeTypeFieldIdentifier: content := node.Content(v.content) - v.symbolTable.AddValue(append(v.currentContext, content), getRangeForNode(node)) + v.symbolTable.AddValue(append(v.currentContext, content), GetRangeForNode(node)) case gotemplate.NodeTypeField: content := node.ChildByFieldName("name").Content(v.content) - v.symbolTable.AddValue(append(v.currentContext, content), getRangeForNode(node)) + v.symbolTable.AddValue(append(v.currentContext, content), GetRangeForNode(node)) case gotemplate.NodeTypeSelectorExpression: operandNode := node.ChildByFieldName("operand") if operandNode.Type() == gotemplate.NodeTypeVariable && operandNode.Content(v.content) == "$" { - v.stashedContext = append(v.stashedContext, v.currentContext) - v.currentContext = []string{} + v.StashContext() } } } @@ -36,8 +70,7 @@ func (v *ValuesVisitor) Exit(node *sitter.Node) { case gotemplate.NodeTypeSelectorExpression: operandNode := node.ChildByFieldName("operand") if operandNode.Type() == gotemplate.NodeTypeVariable && operandNode.Content(v.content) == "$" { - v.currentContext = v.stashedContext[len(v.stashedContext)-1] - v.stashedContext = v.stashedContext[:len(v.stashedContext)-1] + v.RestoreStashedContext() } } } @@ -49,33 +82,30 @@ func (v *ValuesVisitor) EnterContextShift(node *sitter.Node, suffix string) { v.currentContext = append(v.currentContext, content) case gotemplate.NodeTypeField: content := node.ChildByFieldName("name").Content(v.content) + suffix - v.currentContext = append(v.currentContext, content) + v.PushContext(content) case gotemplate.NodeTypeSelectorExpression: s := getContextForSelectorExpression(node, v.content) if len(s) > 0 { s[len(s)-1] = s[len(s)-1] + suffix if s[0] == "$" { - v.stashedContext = append(v.stashedContext, v.currentContext) - v.currentContext = []string{} + v.StashContext() s = s[1:] } } - v.currentContext = append(v.currentContext, s...) + v.PushContextMany(s) } } func (v *ValuesVisitor) ExitContextShift(node *sitter.Node) { switch node.Type() { case gotemplate.NodeTypeField, gotemplate.NodeTypeFieldIdentifier: - v.currentContext = v.currentContext[:len(v.currentContext)-1] + v.PopContext() case gotemplate.NodeTypeSelectorExpression: s := getContextForSelectorExpression(node, v.content) if len(s) > 0 && s[0] == "$" { - v.currentContext = v.stashedContext[len(v.stashedContext)-1] - v.stashedContext = v.stashedContext[:len(v.stashedContext)-1] - s = s[1:] + v.RestoreStashedContext() } else { - v.currentContext = v.currentContext[:len(v.currentContext)-len(s)] + v.PopContextN(len(s)) } } } diff --git a/internal/lsp/visitor.go b/internal/lsp/visitor.go index 45b19a48..4d652498 100644 --- a/internal/lsp/visitor.go +++ b/internal/lsp/visitor.go @@ -18,6 +18,9 @@ type Visitor interface { } func (v *Visitors) visitNodesRecursiveWithScopeShift(node *sitter.Node) { + if node == nil { + return + } for _, visitor := range v.visitors { visitor.Enter(node) } @@ -39,6 +42,9 @@ func (v *Visitors) visitNodesRecursiveWithScopeShift(node *sitter.Node) { } case gotemplate.NodeTypeRangeAction: rangeNode := node.ChildByFieldName("range") + if rangeNode == nil { + break // range is optional (e.g. {{ range $index, $element := pipeline }}) + } v.visitNodesRecursiveWithScopeShift(rangeNode) for _, visitor := range v.visitors { visitor.EnterContextShift(rangeNode, "[]") diff --git a/internal/lsp/yaml_ast.go b/internal/lsp/yaml_ast.go index a9c6a689..8d3f03d1 100644 --- a/internal/lsp/yaml_ast.go +++ b/internal/lsp/yaml_ast.go @@ -5,7 +5,7 @@ import ( sitter "github.com/smacker/go-tree-sitter" ) -func getRangeForNode(node *sitter.Node) sitter.Range { +func GetRangeForNode(node *sitter.Node) sitter.Range { return sitter.Range{ StartPoint: node.StartPoint(), EndPoint: node.EndPoint(), @@ -20,7 +20,7 @@ func getTextNodeRanges(gotemplateNode *sitter.Node) []sitter.Range { for i := 0; i < int(gotemplateNode.ChildCount()); i++ { child := gotemplateNode.Child(i) if child.Type() == gotemplate.NodeTypeText { - textNodes = append(textNodes, getRangeForNode(child)) + textNodes = append(textNodes, GetRangeForNode(child)) } else { textNodes = append(textNodes, getTextNodeRanges(child)...) } diff --git a/internal/tree-sitter/gotemplate/node-types.go b/internal/tree-sitter/gotemplate/node-types.go index 0f535c40..085a8413 100644 --- a/internal/tree-sitter/gotemplate/node-types.go +++ b/internal/tree-sitter/gotemplate/node-types.go @@ -2,6 +2,7 @@ package gotemplate const ( NodeTypeAssignment = "assignment" + NodeTypeArgumentList = "argument_list" NodeTypeBlock = "block" NodeTypeBlockAction = "block_action" NodeTypeChainedPipeline = "chained_pipeline" diff --git a/internal/util/lsp.go b/internal/util/lsp.go index e1fd5932..6cea72cc 100644 --- a/internal/util/lsp.go +++ b/internal/util/lsp.go @@ -1,6 +1,43 @@ package util -import lsp "go.lsp.dev/protocol" +import ( + "fmt" + "path/filepath" + "sort" + + lsp "go.lsp.dev/protocol" + "go.lsp.dev/uri" +) + +type HoverResultWithFile struct { + Value string + URI uri.URI +} + +type HoverResultsWithFiles []HoverResultWithFile + +func (h HoverResultsWithFiles) Format(rootURI uri.URI) string { + var formatted string + sort.Slice(h, func(i, j int) bool { + return h[i].URI < h[j].URI + }) + for _, result := range h { + value := result.Value + if value == "" { + value = "\"\"" + } + filepath, err := filepath.Rel(rootURI.Filename(), result.URI.Filename()) + if err != nil { + filepath = result.URI.Filename() + } + formatted += fmt.Sprintf("### %s\n%s\n\n", filepath, value) + } + return formatted +} + +func (h *HoverResultsWithFiles) Add(hoverResult HoverResultWithFile) { + *h = append(*h, hoverResult) +} func BuildHoverResponse(value string, wordRange lsp.Range) *lsp.Hover { content := lsp.MarkupContent{ diff --git a/internal/util/points.go b/internal/util/points.go index 1e789c1d..cdec6cc6 100644 --- a/internal/util/points.go +++ b/internal/util/points.go @@ -3,6 +3,7 @@ package util import ( sitter "github.com/smacker/go-tree-sitter" lsp "go.lsp.dev/protocol" + "go.lsp.dev/uri" ) func PointToPosition(point sitter.Point) lsp.Position { @@ -12,3 +13,13 @@ func PointToPosition(point sitter.Point) lsp.Position { func PositionToPoint(position lsp.Position) sitter.Point { return sitter.Point{Row: position.Line, Column: position.Character} } + +func RangeToLocation(URI uri.URI, range_ sitter.Range) lsp.Location { + return lsp.Location{ + URI: URI, + Range: lsp.Range{ + Start: PointToPosition(range_.StartPoint), + End: PointToPosition(range_.EndPoint), + }, + } +} From 011a1b8035e41b32f758ddf1e7be21bc7d454c4d Mon Sep 17 00:00:00 2001 From: qvalentin Date: Sun, 7 Apr 2024 13:23:53 +0200 Subject: [PATCH 25/35] feat: references for templateContext --- internal/handler/definition.go | 9 +- internal/handler/definition_test.go | 107 +++++++++++------- internal/handler/generic_document_usecase.go | 25 ++-- internal/handler/hover.go | 7 +- internal/handler/project_files.go | 41 ------- internal/handler/references.go | 26 ++--- internal/handler/references_test.go | 92 +++++++++++++-- .../generic_document_usecase.go | 12 +- internal/language_features/includes.go | 43 ++++--- .../language_features/template_context.go | 76 +++++++++++++ internal/language_features/usecases.go | 16 +++ internal/language_features/values.go | 50 -------- internal/lsp/symbol_table.go | 22 +++- internal/lsp/symbol_table_test.go | 53 +++++++-- internal/lsp/symbol_table_values.go | 48 ++++---- internal/util/lsp.go | 3 +- 16 files changed, 396 insertions(+), 234 deletions(-) delete mode 100644 internal/handler/project_files.go create mode 100644 internal/language_features/template_context.go create mode 100644 internal/language_features/usecases.go delete mode 100644 internal/language_features/values.go diff --git a/internal/handler/definition.go b/internal/handler/definition.go index 6c8b2cd6..2e2cac0f 100644 --- a/internal/handler/definition.go +++ b/internal/handler/definition.go @@ -34,12 +34,17 @@ func (h *langHandler) Definition(ctx context.Context, params *lsp.DefinitionPara return result, nil } -func (h *langHandler) definitionAstParsing(genericDocumentUseCase languagefeatures.GenericDocumentUseCase, chart *charts.Chart, doc *lsplocal.Document, position lsp.Position) ([]lsp.Location, error) { +func (h *langHandler) definitionAstParsing(genericDocumentUseCase *languagefeatures.GenericDocumentUseCase, chart *charts.Chart, doc *lsplocal.Document, position lsp.Position) ([]lsp.Location, error) { var ( relevantChildNode = genericDocumentUseCase.Node - parentType = relevantChildNode.Parent().Type() + parentNode = relevantChildNode.Parent() + parentType string ) + if parentNode != nil { + parentType = parentNode.Type() + } + nodeType := relevantChildNode.Type() switch nodeType { case gotemplate.NodeTypeIdentifier: diff --git a/internal/handler/definition_test.go b/internal/handler/definition_test.go index a26cdb63..6170fe80 100644 --- a/internal/handler/definition_test.go +++ b/internal/handler/definition_test.go @@ -2,15 +2,14 @@ package handler import ( "context" - "fmt" "reflect" "testing" + "github.com/mrjosh/helm-ls/internal/adapter/yamlls" "github.com/mrjosh/helm-ls/internal/charts" - 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" + "github.com/mrjosh/helm-ls/internal/util" + "github.com/stretchr/testify/assert" lsp "go.lsp.dev/protocol" "go.lsp.dev/uri" yamlv3 "gopkg.in/yaml.v3" @@ -33,7 +32,7 @@ var testFileContent = ` ` var ( - testDocumentTemplateURI = uri.URI("file:///test.yaml") + testDocumentTemplateURI = uri.URI("file:///templates/test.yaml") testValuesURI = uri.URI("file:///values.yaml") testOtherValuesURI = uri.URI("file:///values.other.yaml") valuesContent = ` @@ -51,22 +50,12 @@ func genericDefinitionTest(t *testing.T, position lsp.Position, expectedLocation if err != nil { t.Fatal(err) } - handler := &langHandler{ - linterName: "helm-lint", - connPool: nil, - documents: nil, - } - parser := sitter.NewParser() - parser.SetLanguage(gotemplate.GetLanguage()) - tree, _ := parser.ParseCtx(context.Background(), nil, []byte(testFileContent)) - doc := &lsplocal.Document{ - Content: testFileContent, - URI: testDocumentTemplateURI, - Ast: tree, - } + documents := lsplocal.NewDocumentStore() + fileURI := testDocumentTemplateURI + rootUri := uri.File("/") - location, err := handler.definitionAstParsing(languagefeatures.GenericDocumentUseCase{}, &charts.Chart{ + chart := &charts.Chart{ ChartMetadata: &charts.ChartMetadata{}, ValuesFiles: &charts.ValuesFiles{ MainValuesFile: &charts.ValuesFile{ @@ -77,15 +66,33 @@ func genericDefinitionTest(t *testing.T, position lsp.Position, expectedLocation AdditionalValuesFiles: []*charts.ValuesFile{}, }, RootURI: "", - }, doc, position) - - if err != nil && err.Error() != expectedError.Error() { - t.Errorf("expected %v, got %v", expectedError, err) } - - if reflect.DeepEqual(location, expectedLocations) == false { - t.Errorf("expected %v, got %v", expectedLocations, location) + d := lsp.DidOpenTextDocumentParams{ + TextDocument: lsp.TextDocumentItem{ + URI: fileURI, + LanguageID: "", + Version: 0, + Text: string(testFileContent), + }, } + documents.DidOpen(&d, util.DefaultConfig) + chartStore := charts.NewChartStore(rootUri, charts.NewChart) + chartStore.Charts = map[uri.URI]*charts.Chart{rootUri: chart} + h := &langHandler{ + chartStore: chartStore, + documents: documents, + yamllsConnector: &yamlls.Connector{}, + } + + location, err := h.Definition(context.TODO(), &lsp.DefinitionParams{ + TextDocumentPositionParams: lsp.TextDocumentPositionParams{ + TextDocument: lsp.TextDocumentIdentifier{URI: fileURI}, + Position: position, + }, + }) + + assert.Equal(t, expectedError, err) + assert.Equal(t, expectedLocations, location) } // Input: @@ -106,8 +113,7 @@ func TestDefinitionVariable(t *testing.T) { } func TestDefinitionNotImplemented(t *testing.T) { - genericDefinitionTest(t, lsp.Position{Line: 1, Character: 1}, []lsp.Location{}, - fmt.Errorf("Definition not implemented for node type %s", "{{")) + genericDefinitionTest(t, lsp.Position{Line: 1, Character: 1}, nil, nil) } // Input: @@ -220,22 +226,12 @@ func genericDefinitionTestMultipleValuesFiles(t *testing.T, position lsp.Positio if err != nil { t.Fatal(err) } - handler := &langHandler{ - linterName: "helm-lint", - connPool: nil, - documents: nil, - } - - parser := sitter.NewParser() - parser.SetLanguage(gotemplate.GetLanguage()) - tree, _ := parser.ParseCtx(context.Background(), nil, []byte(testFileContent)) - doc := &lsplocal.Document{ - Content: testFileContent, - URI: testDocumentTemplateURI, - Ast: tree, - } + documents := lsplocal.NewDocumentStore() + fileURI := testDocumentTemplateURI + rootUri := uri.File("/") - location, err := handler.definitionAstParsing(languagefeatures.GenericDocumentUseCase{}, &charts.Chart{ + chart := &charts.Chart{ + ChartMetadata: &charts.ChartMetadata{}, ValuesFiles: &charts.ValuesFiles{ MainValuesFile: &charts.ValuesFile{ Values: make(map[string]interface{}), @@ -251,7 +247,30 @@ func genericDefinitionTestMultipleValuesFiles(t *testing.T, position lsp.Positio }, }, RootURI: "", - }, doc, position) + } + d := lsp.DidOpenTextDocumentParams{ + TextDocument: lsp.TextDocumentItem{ + URI: fileURI, + LanguageID: "", + Version: 0, + Text: string(testFileContent), + }, + } + documents.DidOpen(&d, util.DefaultConfig) + chartStore := charts.NewChartStore(rootUri, charts.NewChart) + chartStore.Charts = map[uri.URI]*charts.Chart{rootUri: chart} + h := &langHandler{ + chartStore: chartStore, + documents: documents, + yamllsConnector: &yamlls.Connector{}, + } + + location, err := h.Definition(context.TODO(), &lsp.DefinitionParams{ + TextDocumentPositionParams: lsp.TextDocumentPositionParams{ + TextDocument: lsp.TextDocumentIdentifier{URI: fileURI}, + Position: position, + }, + }) if err != nil && err.Error() != expectedError.Error() { t.Errorf("expected %v, got %v", expectedError, err) diff --git a/internal/handler/generic_document_usecase.go b/internal/handler/generic_document_usecase.go index d30681e1..b4e7bf20 100644 --- a/internal/handler/generic_document_usecase.go +++ b/internal/handler/generic_document_usecase.go @@ -9,10 +9,10 @@ import ( lsp "go.lsp.dev/protocol" ) -func (h *langHandler) NewGenericDocumentUseCase(params lsp.TextDocumentPositionParams) (languagefeatures.GenericDocumentUseCase, error) { +func (h *langHandler) NewGenericDocumentUseCase(params lsp.TextDocumentPositionParams) (*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()) + return &languagefeatures.GenericDocumentUseCase{}, errors.New("Could not get document: " + params.TextDocument.URI.Filename()) } chart, err := h.chartStore.GetChartForDoc(params.TextDocument.URI) if err != nil { @@ -20,13 +20,22 @@ func (h *langHandler) NewGenericDocumentUseCase(params lsp.TextDocumentPositionP } node := h.getNode(doc, params.Position) if node == nil { - return languagefeatures.GenericDocumentUseCase{}, errors.New("Could not get node for: " + params.TextDocument.URI.Filename()) + return &languagefeatures.GenericDocumentUseCase{}, errors.New("Could not get node for: " + params.TextDocument.URI.Filename()) } - return languagefeatures.GenericDocumentUseCase{ - Document: doc, - DocumentStore: h.documents, - Chart: chart, - Node: node, + parentNode := node.Parent() + var parentNodeType string + if parentNode != nil { + parentNodeType = parentNode.Type() + } + return &languagefeatures.GenericDocumentUseCase{ + Document: doc, + DocumentStore: h.documents, + Chart: chart, + ChartStore: h.chartStore, + Node: node, + ParentNode: parentNode, + ParentNodeType: parentNodeType, + NodeType: node.Type(), }, nil } diff --git a/internal/handler/hover.go b/internal/handler/hover.go index 6c9d2f6b..0a453b0f 100644 --- a/internal/handler/hover.go +++ b/internal/handler/hover.go @@ -48,6 +48,11 @@ func (h *langHandler) Hover(ctx context.Context, params *lsp.HoverParams) (resul response, err := h.yamllsConnector.CallHover(ctx, *params, word) return response, err } + // if (pt == gotemplate.NodeTypeField && ct == gotemplate.NodeTypeIdentifier) || ct == gotemplate.NodeTypeFieldIdentifier || ct == gotemplate.NodeTypeField { + // valuesFeature := languagefeatures.NewValuesFeature(genericDocumentUseCase) + // response, err := valuesFeature.Hover() + // return util.BuildHoverResponse(response, wordRange), err + // } if pt == gotemplate.NodeTypeFunctionCall && ct == gotemplate.NodeTypeIdentifier { word = currentNode.Content([]byte(doc.Content)) } @@ -58,7 +63,7 @@ func (h *langHandler) Hover(ctx context.Context, params *lsp.HoverParams) (resul if ct == gotemplate.NodeTypeDot { word = lspinternal.TraverseIdentifierPathUp(currentNode, doc) } - if pt == gotemplate.NodeTypeArgumentList { + if pt == gotemplate.NodeTypeArgumentList && ct == gotemplate.NodeTypeInterpretedStringLiteral { includesCallFeature := languagefeatures.NewIncludesCallFeature(genericDocumentUseCase) response, err := includesCallFeature.Hover() return util.BuildHoverResponse(response, wordRange), err diff --git a/internal/handler/project_files.go b/internal/handler/project_files.go deleted file mode 100644 index 3816c178..00000000 --- a/internal/handler/project_files.go +++ /dev/null @@ -1,41 +0,0 @@ -package handler - -import ( - "errors" - "os" - "path/filepath" - - "github.com/mrjosh/helm-ls/pkg/chartutil" - lsp "go.lsp.dev/protocol" - "go.lsp.dev/uri" -) - -type ProjectFiles struct { - ValuesFile string - ChartFile string - RootURI uri.URI -} - -func (p ProjectFiles) GetValuesFileURI() lsp.DocumentURI { - return "file://" + lsp.DocumentURI(p.ValuesFile) -} -func (p ProjectFiles) GetChartFileURI() lsp.DocumentURI { - return "file://" + lsp.DocumentURI(p.ChartFile) -} - -func NewProjectFiles(rootURI uri.URI, valuesFileName string) ProjectFiles { - - if valuesFileName == "" { - valuesFileName = chartutil.ValuesfileName - } - valuesFileName = filepath.Join(rootURI.Filename(), valuesFileName) - if _, err := os.Stat(valuesFileName); errors.Is(err, os.ErrNotExist) { - valuesFileName = filepath.Join(rootURI.Filename(), "values.yml") - } - - return ProjectFiles{ - ValuesFile: valuesFileName, - ChartFile: filepath.Join(rootURI.Filename(), chartutil.ChartfileName), - RootURI: rootURI, - } -} diff --git a/internal/handler/references.go b/internal/handler/references.go index a7cc0843..80477444 100644 --- a/internal/handler/references.go +++ b/internal/handler/references.go @@ -4,7 +4,6 @@ import ( "context" languagefeatures "github.com/mrjosh/helm-ls/internal/language_features" - "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" lsp "go.lsp.dev/protocol" ) @@ -14,26 +13,17 @@ func (h *langHandler) References(ctx context.Context, params *lsp.ReferenceParam return nil, err } - parentNode := genericDocumentUseCase.Node.Parent() - pt := parentNode.Type() - ct := genericDocumentUseCase.Node.Type() - - logger.Println("pt", pt, "ct", ct) - logger.Println(genericDocumentUseCase.NodeContent()) - - if pt == gotemplate.NodeTypeDefineAction && ct == gotemplate.NodeTypeInterpretedStringLiteral { - includesDefinitionFeature := languagefeatures.NewIncludesDefinitionFeature(genericDocumentUseCase) - return includesDefinitionFeature.References() + usecases := []languagefeatures.ReferencesUseCase{ + languagefeatures.NewIncludesDefinitionFeature(genericDocumentUseCase), + languagefeatures.NewIncludesCallFeature(genericDocumentUseCase), + languagefeatures.NewValuesFeature(genericDocumentUseCase), } - if pt == gotemplate.NodeTypeArgumentList { - includesCallFeature := languagefeatures.NewIncludesCallFeature(genericDocumentUseCase) - return includesCallFeature.References() + for _, usecase := range usecases { + if usecase.AppropriateForNode(genericDocumentUseCase.NodeType, genericDocumentUseCase.ParentNodeType, genericDocumentUseCase.Node) { + return usecase.References() + } } - if (pt == gotemplate.NodeTypeField && ct == gotemplate.NodeTypeIdentifier) || ct == gotemplate.NodeTypeFieldIdentifier || ct == gotemplate.NodeTypeField { - valuesFeature := languagefeatures.NewValuesFeature(genericDocumentUseCase) - return valuesFeature.References() - } return nil, nil } diff --git a/internal/handler/references_test.go b/internal/handler/references_test.go index db5dc094..0db3d0ab 100644 --- a/internal/handler/references_test.go +++ b/internal/handler/references_test.go @@ -2,6 +2,7 @@ package handler import ( "context" + "os" "testing" "github.com/mrjosh/helm-ls/internal/adapter/yamlls" @@ -127,16 +128,16 @@ func TestRefercesTemplateContext(t *testing.T) { {{ .Values.test.nested }} {{ .Values.test }} ` - expected := []lsp.Location{ + expectedValues := []lsp.Location{ { URI: uri.File("/tmp/testfile.yaml"), Range: protocol.Range{ Start: protocol.Position{ - Line: 0x1, Character: 0x3, + Line: 0x1, Character: 4, }, End: protocol.Position{ Line: 0x1, - Character: 0x13, + Character: 0xa, }, }, }, @@ -145,11 +146,11 @@ func TestRefercesTemplateContext(t *testing.T) { Range: protocol.Range{ Start: protocol.Position{ Line: 0x2, - Character: 0x3, + Character: 0x4, }, End: protocol.Position{ Line: 0x2, - Character: 0x13, + Character: 0xa, }, }, }, @@ -157,12 +158,12 @@ func TestRefercesTemplateContext(t *testing.T) { URI: uri.File("/tmp/testfile.yaml"), Range: protocol.Range{ Start: protocol.Position{ - Line: 0x0, - Character: 0x0, + Line: 0x3, + Character: 0x4, }, End: protocol.Position{ - Line: 0x0, - Character: 0x1c, + Line: 0x3, + Character: 0xa, }, }, }, @@ -179,7 +180,7 @@ func TestRefercesTemplateContext(t *testing.T) { Line: 1, Character: 8, }, - expected: expected, + expected: expectedValues, }, } @@ -219,3 +220,74 @@ func TestRefercesTemplateContext(t *testing.T) { }) } } + +func TestRefercesTemplateContextWithTestFile(t *testing.T) { + testCases := []struct { + desc string + position lsp.Position + expectedStartPoints []lsp.Position + expectedError error + }{ + { + desc: "Test references on .Values", + position: lsp.Position{ + Line: 25, + Character: 18, + }, + expectedStartPoints: []lsp.Position{{Line: 25, Character: 16}, {Line: 31, Character: 20}}, + }, + { + desc: "Test references on .Values.imagePullSecrets", + position: lsp.Position{ + Line: 25, + Character: 31, + }, + expectedStartPoints: []lsp.Position{{Line: 25, Character: 23}}, + }, + } + + for _, tt := range testCases { + t.Run(tt.desc, func(t *testing.T) { + documents := lsplocal.NewDocumentStore() + + path := "../../testdata/example/templates/deployment.yaml" + fileURI := uri.File(path) + + content, err := os.ReadFile(path) + if err != nil { + t.Fatal("Could not read test file", err) + } + + d := lsp.DidOpenTextDocumentParams{ + TextDocument: lsp.TextDocumentItem{ + URI: fileURI, + LanguageID: "", + Version: 0, + Text: string(content), + }, + } + documents.DidOpen(&d, util.DefaultConfig) + h := &langHandler{ + chartStore: charts.NewChartStore(uri.File("."), charts.NewChart), + documents: documents, + yamllsConnector: &yamlls.Connector{}, + } + result, err := h.References(context.Background(), &lsp.ReferenceParams{ + TextDocumentPositionParams: lsp.TextDocumentPositionParams{ + TextDocument: lsp.TextDocumentIdentifier{ + URI: fileURI, + }, + Position: tt.position, + }, + }) + assert.Equal(t, tt.expectedError, err) + startPoints := []lsp.Position{} + for _, location := range result { + startPoints = append(startPoints, location.Range.Start) + } + for _, expected := range tt.expectedStartPoints { + assert.Contains(t, startPoints, expected) + } + }) + } +} diff --git a/internal/language_features/generic_document_usecase.go b/internal/language_features/generic_document_usecase.go index 355e3a96..23afbbf9 100644 --- a/internal/language_features/generic_document_usecase.go +++ b/internal/language_features/generic_document_usecase.go @@ -7,10 +7,14 @@ import ( ) type GenericDocumentUseCase struct { - Document *lsplocal.Document - DocumentStore *lsplocal.DocumentStore - Chart *charts.Chart - Node *sitter.Node + Document *lsplocal.Document + DocumentStore *lsplocal.DocumentStore + Chart *charts.Chart + Node *sitter.Node + ChartStore *charts.ChartStore + NodeType string + ParentNode *sitter.Node + ParentNodeType string } func (u *GenericDocumentUseCase) NodeContent() string { diff --git a/internal/language_features/includes.go b/internal/language_features/includes.go index f1640950..0168392a 100644 --- a/internal/language_features/includes.go +++ b/internal/language_features/includes.go @@ -4,32 +4,47 @@ import ( lsp "go.lsp.dev/protocol" lsplocal "github.com/mrjosh/helm-ls/internal/lsp" + "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" "github.com/mrjosh/helm-ls/internal/util" + sitter "github.com/smacker/go-tree-sitter" ) type IncludesFeature struct { - GenericDocumentUseCase + *GenericDocumentUseCase } -// should be called on {{ include "name" . }} type IncludesCallFeature struct { - IncludesFeature + *IncludesFeature +} + +// should be called on {{ include "name" . }} +func (f *IncludesCallFeature) AppropriateForNode(currentNodeType string, parentNodeType string, node *sitter.Node) bool { + if parentNodeType != gotemplate.NodeTypeArgumentList { + return false + } + functionCallNode := node.Parent().Parent() + _, err := lsplocal.ParseIncludeFunctionCall(functionCallNode, []byte(f.GenericDocumentUseCase.Document.Content)) + return err == nil } -// should be called on {{ define "name" }} type IncludesDefinitionFeature struct { - IncludesFeature + *IncludesFeature } -func NewIncludesCallFeature(genericDocumentUseCase GenericDocumentUseCase) *IncludesCallFeature { +// should be called on {{ define "name" }} +func (f *IncludesDefinitionFeature) AppropriateForNode(currentNodeType string, parentNodeType string, node *sitter.Node) bool { + return parentNodeType == gotemplate.NodeTypeDefineAction && currentNodeType == gotemplate.NodeTypeInterpretedStringLiteral +} + +func NewIncludesCallFeature(genericDocumentUseCase *GenericDocumentUseCase) *IncludesCallFeature { return &IncludesCallFeature{ - IncludesFeature: IncludesFeature{genericDocumentUseCase}, + IncludesFeature: &IncludesFeature{genericDocumentUseCase}, } } -func NewIncludesDefinitionFeature(genericDocumentUseCase GenericDocumentUseCase) *IncludesDefinitionFeature { +func NewIncludesDefinitionFeature(genericDocumentUseCase *GenericDocumentUseCase) *IncludesDefinitionFeature { return &IncludesDefinitionFeature{ - IncludesFeature: IncludesFeature{genericDocumentUseCase}, + IncludesFeature: &IncludesFeature{genericDocumentUseCase}, } } @@ -39,21 +54,17 @@ func (f *IncludesCallFeature) References() (result []lsp.Location, err error) { return []lsp.Location{}, err } - locations := f.getReferenceLocations(includeName) - return locations, nil + return f.getReferenceLocations(includeName), nil } func (f *IncludesCallFeature) getIncludeName() (string, error) { functionCallNode := f.Node.Parent().Parent() - includeName, err := lsplocal.ParseIncludeFunctionCall(functionCallNode, []byte(f.GenericDocumentUseCase.Document.Content)) - return includeName, err + return lsplocal.ParseIncludeFunctionCall(functionCallNode, []byte(f.GenericDocumentUseCase.Document.Content)) } func (f *IncludesDefinitionFeature) References() (result []lsp.Location, err error) { includeName := util.RemoveQuotes(f.GenericDocumentUseCase.NodeContent()) - - locations := f.getReferenceLocations(includeName) - return locations, nil + return f.getReferenceLocations(includeName), nil } func (f *IncludesFeature) getReferenceLocations(includeName string) []lsp.Location { diff --git a/internal/language_features/template_context.go b/internal/language_features/template_context.go new file mode 100644 index 00000000..0eaeb736 --- /dev/null +++ b/internal/language_features/template_context.go @@ -0,0 +1,76 @@ +package languagefeatures + +import ( + "fmt" + + lsp "go.lsp.dev/protocol" + + lsplocal "github.com/mrjosh/helm-ls/internal/lsp" + "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" + "github.com/mrjosh/helm-ls/internal/util" + sitter "github.com/smacker/go-tree-sitter" +) + +type TemplateContextFeature struct { + *GenericDocumentUseCase +} + +func NewValuesFeature(genericDocumentUseCase *GenericDocumentUseCase) *TemplateContextFeature { + return &TemplateContextFeature{ + GenericDocumentUseCase: genericDocumentUseCase, + } +} + +func (f *TemplateContextFeature) AppropriateForNode(currentNodeType string, parentNodeType string, node *sitter.Node) bool { + return (parentNodeType == gotemplate.NodeTypeField && currentNodeType == gotemplate.NodeTypeIdentifier) || currentNodeType == gotemplate.NodeTypeFieldIdentifier || currentNodeType == gotemplate.NodeTypeField +} + +func (f *TemplateContextFeature) References() (result []lsp.Location, err error) { + includeName, err := f.getTemplateContext() + if err != nil { + return []lsp.Location{}, err + } + + locations := f.getReferenceLocations(includeName) + return locations, nil +} + +func (f *TemplateContextFeature) getTemplateContext() (lsplocal.TemplateContext, error) { + templateContext := f.GenericDocumentUseCase.Document.SymbolTable.GetTemplateContext(lsplocal.GetRangeForNode(f.Node)) + if len(templateContext) == 0 || templateContext == nil { + return lsplocal.TemplateContext{}, fmt.Errorf("no template context found") + } + return templateContext, nil +} + +func (f *TemplateContextFeature) getReferenceLocations(templateContext lsplocal.TemplateContext) []lsp.Location { + locations := []lsp.Location{} + for _, doc := range f.GenericDocumentUseCase.DocumentStore.GetAllDocs() { + referenceRanges := doc.SymbolTable.GetTemplateContextRanges(templateContext) + for _, referenceRange := range referenceRanges { + locations = append(locations, util.RangeToLocation(doc.URI, referenceRange)) + } + } + + return append(locations, f.getDefinitionLocations(templateContext)...) +} + +func (f *TemplateContextFeature) getDefinitionLocations(templateContext lsplocal.TemplateContext) []lsp.Location { + locations := []lsp.Location{} + if len(templateContext) == 0 || templateContext == nil { + return []lsp.Location{} + } + + switch templateContext[0] { + case "Values": + for _, value := range f.Chart.ResolveValueFiles(templateContext.Tail(), f.ChartStore) { + locations = append(locations, value.ValuesFiles.GetPositionsForValue(value.Selector)...) + } + } + return locations +} + +func (f *TemplateContextFeature) Hover() (string, error) { + templateContext, err := f.getTemplateContext() + return templateContext.Format(), err +} diff --git a/internal/language_features/usecases.go b/internal/language_features/usecases.go new file mode 100644 index 00000000..d5b2c801 --- /dev/null +++ b/internal/language_features/usecases.go @@ -0,0 +1,16 @@ +package languagefeatures + +import ( + sitter "github.com/smacker/go-tree-sitter" + lsp "go.lsp.dev/protocol" +) + +// interface for use cases +type UseCase interface { + AppropriateForNode(currentNodeType string, parentNodeType string, node *sitter.Node) bool +} + +type ReferencesUseCase interface { + UseCase + References() (result []lsp.Location, err error) +} diff --git a/internal/language_features/values.go b/internal/language_features/values.go deleted file mode 100644 index b3ef6138..00000000 --- a/internal/language_features/values.go +++ /dev/null @@ -1,50 +0,0 @@ -package languagefeatures - -import ( - "fmt" - - lsp "go.lsp.dev/protocol" - - lsplocal "github.com/mrjosh/helm-ls/internal/lsp" - "github.com/mrjosh/helm-ls/internal/util" -) - -type ValuesFeature struct { - GenericDocumentUseCase -} - -func NewValuesFeature(genericDocumentUseCase GenericDocumentUseCase) *ValuesFeature { - return &ValuesFeature{ - GenericDocumentUseCase: genericDocumentUseCase, - } -} - -func (f *ValuesFeature) References() (result []lsp.Location, err error) { - includeName, err := f.getTemplateContext() - if err != nil { - return []lsp.Location{}, err - } - - locations := f.getReferenceLocations(includeName) - return locations, nil -} - -func (f *ValuesFeature) getTemplateContext() (lsplocal.TemplateContext, error) { - templateContext := f.GenericDocumentUseCase.Document.SymbolTable.GetTemplateContext(lsplocal.GetRangeForNode(f.Node)) - if len(templateContext) == 0 || templateContext == nil { - return lsplocal.TemplateContext{}, fmt.Errorf("no template context found") - } - return templateContext, nil -} - -func (f *ValuesFeature) getReferenceLocations(templateContext lsplocal.TemplateContext) []lsp.Location { - locations := []lsp.Location{} - for _, doc := range f.GenericDocumentUseCase.DocumentStore.GetAllDocs() { - referenceRanges := doc.SymbolTable.GetValues(templateContext) - for _, referenceRange := range referenceRanges { - locations = append(locations, util.RangeToLocation(doc.URI, referenceRange)) - } - } - - return locations -} diff --git a/internal/lsp/symbol_table.go b/internal/lsp/symbol_table.go index 5fc3286f..c9b5e5c3 100644 --- a/internal/lsp/symbol_table.go +++ b/internal/lsp/symbol_table.go @@ -8,6 +8,14 @@ import ( type TemplateContext []string +func (t TemplateContext) Format() string { + return strings.Join(t, ".") +} + +func (t TemplateContext) Tail() TemplateContext { + return t[1:] +} + type SymbolTable struct { contexts map[string][]sitter.Range contextsReversed map[sitter.Range]TemplateContext @@ -26,13 +34,15 @@ func NewSymbolTable(ast *sitter.Tree, content []byte) *SymbolTable { return s } -func (s *SymbolTable) AddValue(templateContext []string, pointRange sitter.Range) { - s.contexts[strings.Join(templateContext, ".")] = append(s.contexts[strings.Join(templateContext, ".")], pointRange) - s.contextsReversed[pointRange] = templateContext +func (s *SymbolTable) AddTemplateContext(templateContext TemplateContext, pointRange sitter.Range) { + s.contexts[templateContext.Format()] = append(s.contexts[strings.Join(templateContext, ".")], pointRange) + sliceCopy := make(TemplateContext, len(templateContext)) + copy(sliceCopy, templateContext) + s.contextsReversed[pointRange] = sliceCopy } -func (s *SymbolTable) GetValues(path []string) []sitter.Range { - return s.contexts[strings.Join(path, ".")] +func (s *SymbolTable) GetTemplateContextRanges(templateContext TemplateContext) []sitter.Range { + return s.contexts[templateContext.Format()] } func (s *SymbolTable) GetTemplateContext(pointRange sitter.Range) TemplateContext { @@ -62,7 +72,7 @@ func (s *SymbolTable) parseTree(ast *sitter.Tree, content []byte) { v := Visitors{ symbolTable: s, visitors: []Visitor{ - NewValuesVisitor(s, content), + NewTemplateContextVisitor(s, content), NewIncludeDefinitionsVisitor(s, content), }, } diff --git a/internal/lsp/symbol_table_test.go b/internal/lsp/symbol_table_test.go index 1f7b77ee..de977495 100644 --- a/internal/lsp/symbol_table_test.go +++ b/internal/lsp/symbol_table_test.go @@ -1,6 +1,7 @@ package lsp import ( + "os" "testing" sitter "github.com/smacker/go-tree-sitter" @@ -24,7 +25,6 @@ func TestSymbolTableForIncludeDefinitions(t *testing.T) { assert.Len(t, symbolTable.includeDefinitions, 2) - // TODO: remove the double quotes assert.Equal(t, symbolTable.includeDefinitions["bar"], []sitter.Range{{ StartPoint: sitter.Point{ Row: 5, @@ -76,14 +76,14 @@ func TestSymbolTableForValues(t *testing.T) { path: []string{"Test"}, startPoint: sitter.Point{ Row: 5, - Column: 3, + Column: 4, }, }, { path: []string{"Test"}, startPoint: sitter.Point{ Row: 20, - Column: 3, + Column: 4, }, }, { @@ -104,7 +104,7 @@ func TestSymbolTableForValues(t *testing.T) { path: []string{"list"}, startPoint: sitter.Point{ Row: 8, - Column: 9, + Column: 10, }, }, { @@ -118,7 +118,7 @@ func TestSymbolTableForValues(t *testing.T) { path: []string{"list[]", "listinner"}, startPoint: sitter.Point{ Row: 10, - Column: 4, + Column: 5, }, }, { @@ -132,14 +132,14 @@ func TestSymbolTableForValues(t *testing.T) { path: []string{"list[]", "nested"}, startPoint: sitter.Point{ Row: 12, - Column: 10, + Column: 11, }, }, { path: []string{"list[]", "nested[]", "nestedinList"}, startPoint: sitter.Point{ Row: 13, - Column: 5, + Column: 6, }, }, { @@ -153,13 +153,48 @@ func TestSymbolTableForValues(t *testing.T) { path: []string{"Values", "dollar[]", "nestedinList"}, startPoint: sitter.Point{ Row: 16, - Column: 5, + Column: 6, + }, + }, + } + + for _, v := range expected { + values := symbolTable.GetTemplateContextRanges(v.path) + points := []sitter.Point{} + for _, v := range values { + points = append(points, v.StartPoint) + } + assert.Contains(t, points, v.startPoint) + } +} + +func TestSymbolTableForValuesTestFile(t *testing.T) { + path := "../../testdata/example/templates/deployment.yaml" + + content, err := os.ReadFile(path) + if err != nil { + t.Fatal("Could not read test file", err) + } + ast := ParseAst(nil, string(content)) + + symbolTable := NewSymbolTable(ast, []byte(content)) + type expectedValue struct { + path []string + startPoint sitter.Point + } + + expected := []expectedValue{ + { + path: []string{"Values", "ingress"}, + startPoint: sitter.Point{ + Row: 0x47, + Column: 0x18, }, }, } for _, v := range expected { - values := symbolTable.GetValues(v.path) + values := symbolTable.GetTemplateContextRanges(v.path) points := []sitter.Point{} for _, v := range values { points = append(points, v.StartPoint) diff --git a/internal/lsp/symbol_table_values.go b/internal/lsp/symbol_table_values.go index 2a1900dd..4b81e7dd 100644 --- a/internal/lsp/symbol_table_values.go +++ b/internal/lsp/symbol_table_values.go @@ -5,58 +5,58 @@ import ( sitter "github.com/smacker/go-tree-sitter" ) -type ValuesVisitor struct { - currentContext []string - stashedContext [][]string +type TemplateContextVisitor struct { + currentContext TemplateContext + stashedContext []TemplateContext symbolTable *SymbolTable content []byte } -func NewValuesVisitor(symbolTable *SymbolTable, content []byte) *ValuesVisitor { - return &ValuesVisitor{ - currentContext: []string{}, - stashedContext: [][]string{}, +func NewTemplateContextVisitor(symbolTable *SymbolTable, content []byte) *TemplateContextVisitor { + return &TemplateContextVisitor{ + currentContext: TemplateContext{}, + stashedContext: []TemplateContext{}, symbolTable: symbolTable, content: content, } } -func (v *ValuesVisitor) PushContext(context string) { +func (v *TemplateContextVisitor) PushContext(context string) { v.currentContext = append(v.currentContext, context) } -func (v *ValuesVisitor) PushContextMany(context []string) { +func (v *TemplateContextVisitor) PushContextMany(context []string) { v.currentContext = append(v.currentContext, context...) } -func (v *ValuesVisitor) PopContext() { +func (v *TemplateContextVisitor) PopContext() { v.currentContext = v.currentContext[:len(v.currentContext)-1] } -func (v *ValuesVisitor) PopContextN(n int) { +func (v *TemplateContextVisitor) PopContextN(n int) { v.currentContext = v.currentContext[:len(v.currentContext)-n] } -func (v *ValuesVisitor) StashContext() { +func (v *TemplateContextVisitor) StashContext() { v.stashedContext = append(v.stashedContext, v.currentContext) v.currentContext = []string{} } -func (v *ValuesVisitor) RestoreStashedContext() { +func (v *TemplateContextVisitor) RestoreStashedContext() { v.currentContext = v.stashedContext[len(v.stashedContext)-1] v.stashedContext = v.stashedContext[:len(v.stashedContext)-1] } -func (v *ValuesVisitor) Enter(node *sitter.Node) { +func (v *TemplateContextVisitor) Enter(node *sitter.Node) { switch node.Type() { case gotemplate.NodeTypeDot: - v.symbolTable.AddValue(v.currentContext, GetRangeForNode(node)) + v.symbolTable.AddTemplateContext(v.currentContext, GetRangeForNode(node)) case gotemplate.NodeTypeFieldIdentifier: content := node.Content(v.content) - v.symbolTable.AddValue(append(v.currentContext, content), GetRangeForNode(node)) + v.symbolTable.AddTemplateContext(append(v.currentContext, content), GetRangeForNode(node)) case gotemplate.NodeTypeField: content := node.ChildByFieldName("name").Content(v.content) - v.symbolTable.AddValue(append(v.currentContext, content), GetRangeForNode(node)) + v.symbolTable.AddTemplateContext(append(v.currentContext, content), GetRangeForNode(node.ChildByFieldName("name"))) case gotemplate.NodeTypeSelectorExpression: operandNode := node.ChildByFieldName("operand") if operandNode.Type() == gotemplate.NodeTypeVariable && operandNode.Content(v.content) == "$" { @@ -65,7 +65,7 @@ func (v *ValuesVisitor) Enter(node *sitter.Node) { } } -func (v *ValuesVisitor) Exit(node *sitter.Node) { +func (v *TemplateContextVisitor) Exit(node *sitter.Node) { switch node.Type() { case gotemplate.NodeTypeSelectorExpression: operandNode := node.ChildByFieldName("operand") @@ -75,7 +75,7 @@ func (v *ValuesVisitor) Exit(node *sitter.Node) { } } -func (v *ValuesVisitor) EnterContextShift(node *sitter.Node, suffix string) { +func (v *TemplateContextVisitor) EnterContextShift(node *sitter.Node, suffix string) { switch node.Type() { case gotemplate.NodeTypeFieldIdentifier: content := node.Content(v.content) + suffix @@ -96,7 +96,7 @@ func (v *ValuesVisitor) EnterContextShift(node *sitter.Node, suffix string) { } } -func (v *ValuesVisitor) ExitContextShift(node *sitter.Node) { +func (v *TemplateContextVisitor) ExitContextShift(node *sitter.Node) { switch node.Type() { case gotemplate.NodeTypeField, gotemplate.NodeTypeFieldIdentifier: v.PopContext() @@ -110,15 +110,15 @@ func (v *ValuesVisitor) ExitContextShift(node *sitter.Node) { } } -func getContextForSelectorExpression(node *sitter.Node, content []byte) []string { +func getContextForSelectorExpression(node *sitter.Node, content []byte) TemplateContext { if node == nil { - return []string{} + return TemplateContext{} } if node.Type() == gotemplate.NodeTypeField { - return []string{node.ChildByFieldName("name").Content(content)} + return TemplateContext{node.ChildByFieldName("name").Content(content)} } if node.Type() == gotemplate.NodeTypeVariable { - return []string{node.Content(content)} + return TemplateContext{node.Content(content)} } operand := node.ChildByFieldName("operand") diff --git a/internal/util/lsp.go b/internal/util/lsp.go index 6cea72cc..c77636fa 100644 --- a/internal/util/lsp.go +++ b/internal/util/lsp.go @@ -19,8 +19,9 @@ type HoverResultsWithFiles []HoverResultWithFile func (h HoverResultsWithFiles) Format(rootURI uri.URI) string { var formatted string sort.Slice(h, func(i, j int) bool { - return h[i].URI < h[j].URI + return h[i].URI > h[j].URI }) + for _, result := range h { value := result.Value if value == "" { From 26fdef5c3fd16669c2257a057664b9aef91f7716 Mon Sep 17 00:00:00 2001 From: qvalentin Date: Sat, 13 Apr 2024 21:28:00 +0200 Subject: [PATCH 26/35] feat(hover): template context hover usecase --- internal/charts/chart.go | 19 ++ .../helm}/helm-documentation.go | 70 +++++- internal/handler/completion.go | 37 ++-- internal/handler/definition.go | 162 +------------- internal/handler/definition_test.go | 12 +- internal/handler/hover.go | 200 ++---------------- internal/handler/hover_main_test.go | 13 +- internal/handler/references.go | 4 +- .../language_features/built_in_objects.go | 81 +++++++ internal/language_features/function_call.go | 30 +++ .../generic_template_context.go | 43 ++++ internal/language_features/includes.go | 11 +- .../language_features/template_context.go | 91 ++++++-- .../template_context_hover_test.go} | 11 +- internal/language_features/usecases.go | 13 +- internal/language_features/variables.go | 33 +++ internal/lsp/symbol_table_includes.go | 4 +- internal/util/values.go | 50 +++++ internal/util/yaml.go | 2 +- internal/util/yaml_test.go | 10 +- pkg/chartutil/values.go | 2 +- 21 files changed, 482 insertions(+), 416 deletions(-) rename internal/{handler => documentation/helm}/helm-documentation.go (80%) create mode 100644 internal/language_features/built_in_objects.go create mode 100644 internal/language_features/function_call.go create mode 100644 internal/language_features/generic_template_context.go rename internal/{handler/hover_test.go => language_features/template_context_hover_test.go} (95%) create mode 100644 internal/language_features/variables.go create mode 100644 internal/util/values.go diff --git a/internal/charts/chart.go b/internal/charts/chart.go index 66177e3e..4e4f16b4 100644 --- a/internal/charts/chart.go +++ b/internal/charts/chart.go @@ -1,8 +1,11 @@ package charts import ( + "strings" + "github.com/mrjosh/helm-ls/internal/log" "github.com/mrjosh/helm-ls/internal/util" + lsp "go.lsp.dev/protocol" "go.lsp.dev/uri" ) @@ -55,3 +58,19 @@ func (c *Chart) ResolveValueFiles(query []string, chartStore *ChartStore) []*Que return append(ownResult, parentChart.ResolveValueFiles(extendedQuery, chartStore)...) } + +func (c *Chart) GetValueLocation(templateContext []string) (lsp.Location, error) { + modifyedVar := make([]string, 0) + // for Charts, we make the first letter lowercase + for _, value := range templateContext { + restOfString := "" + if (len(value)) > 1 { + restOfString = value[1:] + } + firstLetterLowercase := strings.ToLower(string(value[0])) + restOfString + modifyedVar = append(modifyedVar, firstLetterLowercase) + } + position, err := util.GetPositionOfNode(&c.ChartMetadata.YamlNode, modifyedVar) + + return lsp.Location{URI: c.ChartMetadata.URI, Range: lsp.Range{Start: position}}, err +} diff --git a/internal/handler/helm-documentation.go b/internal/documentation/helm/helm-documentation.go similarity index 80% rename from internal/handler/helm-documentation.go rename to internal/documentation/helm/helm-documentation.go index 9b81877a..16253da8 100644 --- a/internal/handler/helm-documentation.go +++ b/internal/documentation/helm/helm-documentation.go @@ -1,4 +1,6 @@ -package handler +package helmdocs + +import "github.com/mrjosh/helm-ls/internal/util" type HelmDocumentation struct { Name string @@ -7,9 +9,10 @@ type HelmDocumentation struct { } var ( - basicItems = []HelmDocumentation{ + BuiltInObjects = []HelmDocumentation{ {"Values", ".Values", `The values made available through values.yaml, --set and -f.`}, {"Chart", ".Chart", "Chart metadata"}, + {"Subcharts", ".Subcharts", "This provides access to the scope (.Values, .Charts, .Releases etc.) of subcharts to the parent. For example .Subcharts.mySubChart.myValue to access the myValue in the mySubChart chart."}, {"Files", ".Files.Get $str", "access non-template files within the chart"}, {"Capabilities", ".Capabilities.KubeVersion ", "access capabilities of Kubernetes"}, {"Release", ".Release", `Built-in release values. Attributes include: @@ -21,8 +24,9 @@ var ( - .Release.IsInstall: True if this is an install - .Release.Revision: The revision number `}, + {"Template", ".Template", "Contains information about the current template that is being executed"}, } - builtinFuncs = []HelmDocumentation{ + BuiltinFuncs = []HelmDocumentation{ {"template", "template $str $ctx", "render the template at location $str"}, {"define", "define $str", "define a template with the name $str"}, {"and", "and $a $b ...", "if $a then $b else $a"}, @@ -44,7 +48,7 @@ var ( {"le", "le $a $b", "returns true if $a <= $b"}, {"ge", "ge $a $b", "returns true if $a >= $b"}, } - sprigFuncs = []HelmDocumentation{ + SprigFuncs = []HelmDocumentation{ // 2.12.0 {"snakecase", "snakecase $str", "Convert $str to snake_case"}, {"camelcase", "camelcase $str", "convert string to camelCase"}, @@ -173,7 +177,7 @@ var ( {"derivePassword", "derivePassword $counter $long $pass $user $domain", "generate a password from [Master Password](http://masterpasswordapp.com/algorithm.html) spec"}, {"generatePrivateKey", "generatePrivateKey 'ecdsa'", "generate private PEM key (takes dsa, rsa, or ecdsa)"}, } - helmFuncs = []HelmDocumentation{ + HelmFuncs = []HelmDocumentation{ {"include", "include $str $ctx", "(chainable) include the named template with the given context."}, {"toYaml", "toYaml $var", "convert $var to YAML"}, {"toJson", "toJson $var", "convert $var to JSON"}, @@ -183,13 +187,22 @@ var ( {"required", "required $str $val", "fail template with message $str if $val is not provided or is empty"}, } - capabilitiesVals = []HelmDocumentation{ - {"KubeVersion", ".Capabilities.KubeVersion", "Kubernetes version"}, + CapabilitiesVals = []HelmDocumentation{ {"TillerVersion", ".Capabilities.TillerVersion", "Tiller version"}, - {"ApiVersions.Has", `.Capabilities.ApiVersions.Has "batch/v1"`, "Returns true if the given Kubernetes API/version is present on the cluster"}, + {"APIVersions", "Capabilities.APIVersions", "A set of versions."}, + {"APIVersions.Has", "Capabilities.APIVersions.Has $version", "Indicates whether a version (e.g., batch/v1) or resource (e.g., apps/v1/Deployment) is available on the cluster."}, + {"KubeVersion", "Capabilities.KubeVersion", "The Kubernetes version."}, + {"KubeVersion.Version", "Capabilities.KubeVersion.Version", "The Kubernetes version in semver format."}, + {"KubeVersion.Major", "Capabilities.KubeVersion.Major", "The Kubernetes major version."}, + {"KubeVersion.Minor", "Capabilities.KubeVersion.Minor", "The Kubernetes minor version."}, + {"KubeVersion.GitCommit", "Capabilities.HelmVersion", "The object containing the Helm Version details, it is the same output of helm version."}, + {"KubeVersion.GitTreeState", "Capabilities.HelmVersion.Version", "The current Helm version in semver format."}, + {"HelmVersion.GitCommit", "Capabilities.HelmVersion.GitCommit", "The Helm git sha1."}, + {"HelmVersion.GitTreeState", "Capabilities.HelmVersion.GitTreeState", "The state of the Helm git tree."}, + {"HelmVersion.GoVersion", "Capabilities.HelmVersion.GoVersion", "The version of the Go compiler used."}, } - chartVals = []HelmDocumentation{ + ChartVals = []HelmDocumentation{ {"Name", ".Chart.Name", "Name of the chart"}, {"Version", ".Chart.Version", "Version of the chart"}, {"Description", ".Chart.Description", "Chart description"}, @@ -201,9 +214,21 @@ var ( {"AppVersion", ".Chart.AppVersion", "The version of the main app contained in this chart"}, {"Deprecated", ".Chart.Deprecated", "If true, this chart is no longer maintained"}, {"TillerVersion", ".Chart.TillerVersion", "The version (range) if Tiller that this chart can run on."}, + {"APIVersion", ".Chart.APIVersion", "The API Version of this chart"}, + {"Condition", ".Chart.Condition", "The condition to check to enable chart"}, + {"Tags", ".Chart.Tags", "The tags to check to enable chart"}, + {"Annotations", ".Chart.Annotations", "Additional annotations (key-value pairs)"}, + {"KubeVersion", ".Chart.KubeVersion", "Kubernetes version required"}, + {"Dependencies", ".Chart.Dependencies", "List of chart dependencies"}, + {"Type", ".Chart.Type", "Chart type (application or library)"}, + } + + TemplateVals = []HelmDocumentation{ + {"Name", ".Template.Name", "A namespaced file path to the current template (e.g. mychart/templates/mytemplate.yaml)"}, + {"BasePath", ".Template.BasePath", "The namespaced path to the templates directory of the current chart (e.g. mychart/templates)."}, } - releaseVals = []HelmDocumentation{ + ReleaseVals = []HelmDocumentation{ {"Name", ".Release.Name", "Name of the release"}, {"Time", ".Release.Time", "Time of the release"}, {"Namespace", ".Release.Namespace", "Default namespace of the release"}, @@ -213,7 +238,7 @@ var ( {"Revision", ".Release.Revision", "Release revision number (starts at 1)"}, } - filesVals = []HelmDocumentation{ + FilesVals = []HelmDocumentation{ {"Get", ".Files.Get $path", "Get file contents. Path is relative to chart."}, {"GetBytes", ".Files.GetBytes $path", "Get file contents as a byte array. Path is relative to chart."}, {"Glob", ".Files.Glob $glob", "Returns a list of files whose names match the given shell glob pattern."}, @@ -221,4 +246,27 @@ var ( {"AsSecrets", ".Files.AsSecrets $path", "Returns the file bodies as Base 64 encoded strings."}, {"AsConfig", ".Files.AsConfig $path", "Returns file bodies as a YAML map."}, } + + BuiltInOjectVals = map[string][]HelmDocumentation{ + "Chart": ChartVals, + "Release": ReleaseVals, + "Files": FilesVals, + "Capabilities": CapabilitiesVals, + "Template": TemplateVals, + } ) + +func GetFunctionByName(name string) (HelmDocumentation, bool) { + completionItems := [][]HelmDocumentation{ + BuiltinFuncs, + SprigFuncs, + HelmFuncs, + } + toSearch := util.ConcatMultipleSlices(completionItems) + for _, completionItem := range toSearch { + if name == completionItem.Name { + return completionItem, true + } + } + return HelmDocumentation{}, false +} diff --git a/internal/handler/completion.go b/internal/handler/completion.go index f6a9feaa..c5c32cb0 100644 --- a/internal/handler/completion.go +++ b/internal/handler/completion.go @@ -10,6 +10,7 @@ import ( "github.com/mrjosh/helm-ls/internal/charts" lsplocal "github.com/mrjosh/helm-ls/internal/lsp" gotemplate "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" + "github.com/mrjosh/helm-ls/internal/util" "github.com/mrjosh/helm-ls/pkg/chartutil" sitter "github.com/smacker/go-tree-sitter" "go.lsp.dev/protocol" @@ -17,6 +18,7 @@ import ( yaml "gopkg.in/yaml.v2" "github.com/mrjosh/helm-ls/internal/documentation/godocs" + helmdocs "github.com/mrjosh/helm-ls/internal/documentation/helm" ) var ( @@ -26,9 +28,9 @@ var ( ) func init() { - functionsCompletionItems = append(functionsCompletionItems, getFunctionCompletionItems(helmFuncs)...) - functionsCompletionItems = append(functionsCompletionItems, getFunctionCompletionItems(builtinFuncs)...) - functionsCompletionItems = append(functionsCompletionItems, getFunctionCompletionItems(sprigFuncs)...) + functionsCompletionItems = append(functionsCompletionItems, getFunctionCompletionItems(helmdocs.HelmFuncs)...) + functionsCompletionItems = append(functionsCompletionItems, getFunctionCompletionItems(helmdocs.BuiltinFuncs)...) + functionsCompletionItems = append(functionsCompletionItems, getFunctionCompletionItems(helmdocs.SprigFuncs)...) textCompletionsItems = append(textCompletionsItems, getTextCompletionItems(godocs.TextSnippets)...) } @@ -69,7 +71,7 @@ func (h *langHandler) Completion(ctx context.Context, params *lsp.CompletionPara logger.Println(fmt.Sprintf("Word found for completions is < %s >", word)) items = make([]lsp.CompletionItem, 0) - for _, v := range basicItems { + for _, v := range helmdocs.BuiltInObjects { items = append(items, lsp.CompletionItem{ Label: v.Name, InsertText: v.Name, @@ -89,17 +91,17 @@ func (h *langHandler) Completion(ctx context.Context, params *lsp.CompletionPara switch variableSplitted[0] { case "Chart": - items = getVariableCompletionItems(chartVals) + items = getVariableCompletionItems(helmdocs.ChartVals) case "Values": items = h.getValuesCompletions(chart, variableSplitted[1:]) case "Release": - items = getVariableCompletionItems(releaseVals) + items = getVariableCompletionItems(helmdocs.ReleaseVals) case "Files": - items = getVariableCompletionItems(filesVals) + items = getVariableCompletionItems(helmdocs.FilesVals) case "Capabilities": - items = getVariableCompletionItems(capabilitiesVals) + items = getVariableCompletionItems(helmdocs.CapabilitiesVals) default: - items = getVariableCompletionItems(basicItems) + items = getVariableCompletionItems(helmdocs.BuiltInObjects) items = append(items, functionsCompletionItems...) } @@ -214,7 +216,7 @@ func (h *langHandler) setItem(items []lsp.CompletionItem, value interface{}, var documentation = h.toYAML(value) case reflect.Bool: itemKind = lsp.CompletionItemKindVariable - documentation = h.getBoolType(value) + documentation = util.GetBoolType(value) case reflect.Float32, reflect.Float64: documentation = fmt.Sprintf("%.2f", valueOf.Float()) itemKind = lsp.CompletionItemKindVariable @@ -238,21 +240,14 @@ func (h *langHandler) toYAML(value interface{}) string { return string(valBytes) } -func (h *langHandler) getBoolType(value interface{}) string { - if val, ok := value.(bool); ok && val { - return "True" - } - return "False" -} - -func getVariableCompletionItems(helmDocs []HelmDocumentation) (result []lsp.CompletionItem) { +func getVariableCompletionItems(helmDocs []helmdocs.HelmDocumentation) (result []lsp.CompletionItem) { for _, item := range helmDocs { result = append(result, variableCompletionItem(item)) } return result } -func variableCompletionItem(helmDocumentation HelmDocumentation) lsp.CompletionItem { +func variableCompletionItem(helmDocumentation helmdocs.HelmDocumentation) lsp.CompletionItem { return lsp.CompletionItem{ Label: helmDocumentation.Name, InsertText: helmDocumentation.Name, @@ -262,14 +257,14 @@ func variableCompletionItem(helmDocumentation HelmDocumentation) lsp.CompletionI } } -func getFunctionCompletionItems(helmDocs []HelmDocumentation) (result []lsp.CompletionItem) { +func getFunctionCompletionItems(helmDocs []helmdocs.HelmDocumentation) (result []lsp.CompletionItem) { for _, item := range helmDocs { result = append(result, functionCompletionItem(item)) } return result } -func functionCompletionItem(helmDocumentation HelmDocumentation) lsp.CompletionItem { +func functionCompletionItem(helmDocumentation helmdocs.HelmDocumentation) lsp.CompletionItem { return lsp.CompletionItem{ Label: helmDocumentation.Name, InsertText: helmDocumentation.Name, diff --git a/internal/handler/definition.go b/internal/handler/definition.go index 2e2cac0f..a3d5f5e6 100644 --- a/internal/handler/definition.go +++ b/internal/handler/definition.go @@ -2,169 +2,29 @@ package handler import ( "context" - "fmt" - "strings" - "github.com/mrjosh/helm-ls/internal/charts" 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" - "github.com/mrjosh/helm-ls/internal/util" - sitter "github.com/smacker/go-tree-sitter" lsp "go.lsp.dev/protocol" - "gopkg.in/yaml.v3" ) -func (h *langHandler) Definition(ctx context.Context, params *lsp.DefinitionParams) (result []lsp.Location, err error) { +func (h *langHandler) Definition(_ context.Context, params *lsp.DefinitionParams) (result []lsp.Location, err error) { genericDocumentUseCase, err := h.NewGenericDocumentUseCase(params.TextDocumentPositionParams) if err != nil { return nil, err } - doc := genericDocumentUseCase.Document - chart := genericDocumentUseCase.Chart - - result, err = h.definitionAstParsing(genericDocumentUseCase, chart, doc, params.Position) - if err != nil { - // suppress errors for clients - // otherwise using go-to-definition on words that have no definition - // will result in an error - logger.Println("Error getting definitions", err) - return nil, nil - } - return result, nil -} - -func (h *langHandler) definitionAstParsing(genericDocumentUseCase *languagefeatures.GenericDocumentUseCase, chart *charts.Chart, doc *lsplocal.Document, position lsp.Position) ([]lsp.Location, error) { - var ( - relevantChildNode = genericDocumentUseCase.Node - parentNode = relevantChildNode.Parent() - parentType string - ) - - if parentNode != nil { - parentType = parentNode.Type() + usecases := []languagefeatures.DefinitionUseCase{ + languagefeatures.NewBuiltInObjectsFeature(genericDocumentUseCase), // has to be before template context + languagefeatures.NewTemplateContextFeature(genericDocumentUseCase), + languagefeatures.NewIncludesCallFeature(genericDocumentUseCase), + languagefeatures.NewVariablesFeature(genericDocumentUseCase), } - nodeType := relevantChildNode.Type() - switch nodeType { - case gotemplate.NodeTypeIdentifier: - logger.Println("Parent type", relevantChildNode.Parent().Type()) - if parentType == gotemplate.NodeTypeVariable { - return h.getDefinitionForVariable(relevantChildNode, doc) + for _, usecase := range usecases { + if usecase.AppropriateForNode() { + result, err := usecase.Definition() + return result, err } - - if parentType == gotemplate.NodeTypeSelectorExpression || parentType == gotemplate.NodeTypeField { - return h.getDefinitionForValue(chart, relevantChildNode, doc) - } - return h.getDefinitionForFixedIdentifier(chart, relevantChildNode, doc) - case gotemplate.NodeTypeDot, gotemplate.NodeTypeDotSymbol, gotemplate.NodeTypeFieldIdentifier: - return h.getDefinitionForValue(chart, relevantChildNode, doc) } - if parentType == gotemplate.NodeTypeArgumentList { - includesCallFeature := languagefeatures.NewIncludesCallFeature(genericDocumentUseCase) - return includesCallFeature.Definition() - } - - return []lsp.Location{}, fmt.Errorf("Definition not implemented for node type %s", relevantChildNode.Type()) -} - -func (h *langHandler) getDefinitionForVariable(node *sitter.Node, doc *lsplocal.Document) ([]lsp.Location, error) { - variableName := node.Content([]byte(doc.Content)) - definitionNode := lsplocal.GetVariableDefinition(variableName, node.Parent(), doc.Content) - if definitionNode == nil { - return []lsp.Location{}, fmt.Errorf("Could not find definition for %s. Variable definition not found", variableName) - } - return []lsp.Location{{URI: doc.URI, Range: lsp.Range{Start: util.PointToPosition(definitionNode.StartPoint())}}}, nil -} - -// getDefinitionForFixedIdentifier checks if the current identifier has a constant definition and returns it -func (h *langHandler) getDefinitionForFixedIdentifier(chart *charts.Chart, node *sitter.Node, doc *lsplocal.Document) ([]lsp.Location, error) { - name := node.Content([]byte(doc.Content)) - switch name { - case "Values": - result := []lsp.Location{} - - for _, valueFile := range chart.ValuesFiles.AllValuesFiles() { - result = append(result, lsp.Location{URI: valueFile.URI}) - } - return result, nil - - case "Chart": - return []lsp.Location{ - {URI: chart.ChartMetadata.URI}, - }, - nil - } - - return []lsp.Location{}, fmt.Errorf("Could not find definition for %s. Fixed identifier not found", name) -} - -func (h *langHandler) getDefinitionForValue(chart *charts.Chart, node *sitter.Node, doc *lsplocal.Document) ([]lsp.Location, error) { - var ( - yamlPathString = getYamlPath(node, doc) - yamlPath, err = util.NewYamlPath(yamlPathString) - definitionFileURI lsp.DocumentURI - positions []lsp.Position - ) - if err != nil { - return []lsp.Location{}, err - } - - if yamlPath.IsValuesPath() { - return h.getValueDefinition(chart, yamlPath.GetTail()), nil - } - if yamlPath.IsChartPath() { - definitionFileURI = chart.ChartMetadata.URI - position, err := h.getChartDefinition(&chart.ChartMetadata.YamlNode, yamlPath.GetTail()) - if err == nil { - positions = append(positions, position) - } - } - - if definitionFileURI != "" { - locations := []lsp.Location{} - for _, position := range positions { - locations = append(locations, lsp.Location{ - URI: definitionFileURI, - Range: lsp.Range{Start: position}, - }) - } - return locations, nil - } - return []lsp.Location{}, fmt.Errorf("Could not find definition for %s. No definition found", yamlPath) -} - -func getYamlPath(node *sitter.Node, doc *lsplocal.Document) string { - switch node.Type() { - case gotemplate.NodeTypeDot: - return lsplocal.TraverseIdentifierPathUp(node, doc) - case gotemplate.NodeTypeDotSymbol, gotemplate.NodeTypeFieldIdentifier, gotemplate.NodeTypeIdentifier: - return lsplocal.GetFieldIdentifierPath(node, doc) - default: - logger.Error("Could not get yaml path for node type ", node.Type()) - return "" - } -} - -func (h *langHandler) getValueDefinition(chart *charts.Chart, splittedVar []string) []lsp.Location { - locations := []lsp.Location{} - for _, value := range chart.ResolveValueFiles(splittedVar, h.chartStore) { - locations = append(locations, value.ValuesFiles.GetPositionsForValue(value.Selector)...) - } - return locations -} - -func (h *langHandler) getChartDefinition(chartNode *yaml.Node, splittedVar []string) (lsp.Position, error) { - modifyedVar := make([]string, 0) - // for Charts, we make the first letter lowercase - for _, value := range splittedVar { - restOfString := "" - if (len(value)) > 1 { - restOfString = value[1:] - } - firstLetterLowercase := strings.ToLower(string(value[0])) + restOfString - modifyedVar = append(modifyedVar, firstLetterLowercase) - } - return util.GetPositionOfNode(chartNode, modifyedVar) + return nil, nil } diff --git a/internal/handler/definition_test.go b/internal/handler/definition_test.go index 6170fe80..f9cfe690 100644 --- a/internal/handler/definition_test.go +++ b/internal/handler/definition_test.go @@ -208,11 +208,11 @@ func TestDefinitionValueFile(t *testing.T) { URI: testValuesURI, Range: lsp.Range{ Start: lsp.Position{ - Line: 1, + Line: 0, Character: 0, }, End: lsp.Position{ - Line: 1, + Line: 0, Character: 0, }, }, @@ -289,11 +289,11 @@ func TestDefinitionValueFileMulitpleValues(t *testing.T) { URI: testValuesURI, Range: lsp.Range{ Start: lsp.Position{ - Line: 1, + Line: 0, Character: 0, }, End: lsp.Position{ - Line: 1, + Line: 0, Character: 0, }, }, @@ -301,11 +301,11 @@ func TestDefinitionValueFileMulitpleValues(t *testing.T) { URI: testOtherValuesURI, Range: lsp.Range{ Start: lsp.Position{ - Line: 1, + Line: 0, Character: 0, }, End: lsp.Position{ - Line: 1, + Line: 0, Character: 0, }, }, diff --git a/internal/handler/hover.go b/internal/handler/hover.go index 0a453b0f..7df10878 100644 --- a/internal/handler/hover.go +++ b/internal/handler/hover.go @@ -2,19 +2,12 @@ package handler import ( "context" - "errors" - "fmt" - "reflect" - "strings" - "github.com/mrjosh/helm-ls/internal/charts" languagefeatures "github.com/mrjosh/helm-ls/internal/language_features" lspinternal "github.com/mrjosh/helm-ls/internal/lsp" "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" "github.com/mrjosh/helm-ls/internal/util" - "github.com/mrjosh/helm-ls/pkg/chart" - "github.com/mrjosh/helm-ls/pkg/chartutil" lsp "go.lsp.dev/protocol" ) @@ -23,194 +16,31 @@ func (h *langHandler) Hover(ctx context.Context, params *lsp.HoverParams) (resul if err != nil { return nil, err } - doc := genericDocumentUseCase.Document - chart := genericDocumentUseCase.Chart - var ( - currentNode = lspinternal.NodeAtPosition(doc.Ast, params.Position) - parent = currentNode.Parent() - wordRange = lspinternal.GetLspRangeForNode(currentNode) - word string - ) + wordRange := lspinternal.GetLspRangeForNode(genericDocumentUseCase.Node) - if parent == nil { - err = errors.New("Could not parse ast correctly") - return nil, err + usecases := []languagefeatures.HoverUseCase{ + languagefeatures.NewBuiltInObjectsFeature(genericDocumentUseCase), // has to be before template context + languagefeatures.NewTemplateContextFeature(genericDocumentUseCase), + languagefeatures.NewIncludesCallFeature(genericDocumentUseCase), + languagefeatures.NewFunctionCallFeature(genericDocumentUseCase), + } + + for _, usecase := range usecases { + if usecase.AppropriateForNode() { + result, err := usecase.Hover() + return util.BuildHoverResponse(result, wordRange), err + } } - pt := parent.Type() - ct := currentNode.Type() - if ct == gotemplate.NodeTypeText { - word := doc.WordAt(params.Position) + 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 } - // if (pt == gotemplate.NodeTypeField && ct == gotemplate.NodeTypeIdentifier) || ct == gotemplate.NodeTypeFieldIdentifier || ct == gotemplate.NodeTypeField { - // valuesFeature := languagefeatures.NewValuesFeature(genericDocumentUseCase) - // response, err := valuesFeature.Hover() - // return util.BuildHoverResponse(response, wordRange), err - // } - if pt == gotemplate.NodeTypeFunctionCall && ct == gotemplate.NodeTypeIdentifier { - word = currentNode.Content([]byte(doc.Content)) - } - if (pt == gotemplate.NodeTypeSelectorExpression || pt == gotemplate.NodeTypeField) && - (ct == gotemplate.NodeTypeIdentifier || ct == gotemplate.NodeTypeFieldIdentifier) { - word = lspinternal.GetFieldIdentifierPath(currentNode, doc) - } - if ct == gotemplate.NodeTypeDot { - word = lspinternal.TraverseIdentifierPathUp(currentNode, doc) - } - if pt == gotemplate.NodeTypeArgumentList && ct == gotemplate.NodeTypeInterpretedStringLiteral { - includesCallFeature := languagefeatures.NewIncludesCallFeature(genericDocumentUseCase) - response, err := includesCallFeature.Hover() - return util.BuildHoverResponse(response, wordRange), err - } - - var ( - splitted = strings.Split(word, ".") - variableSplitted = []string{} - value string - ) - if word == "" { - return nil, err - } - - for _, s := range splitted { - if s != "" { - variableSplitted = append(variableSplitted, s) - } - } - - // // $ always points to the root context so we must remove it before looking up tables - if variableSplitted[0] == "$" && len(variableSplitted) > 1 { - variableSplitted = variableSplitted[1:] - } - - logger.Println(fmt.Sprintf("Hover checking for word < %s >", word)) - - if len(variableSplitted) > 1 { - switch variableSplitted[0] { - case "Values": - value, err = h.getValueHover(chart, variableSplitted[1:]) - case "Chart": - value, err = h.getChartMetadataHover(&chart.ChartMetadata.Metadata, variableSplitted[1]) - case "Release": - value, err = h.getBuiltInObjectsHover(releaseVals, variableSplitted[1]) - case "Files": - value, err = h.getBuiltInObjectsHover(filesVals, variableSplitted[1]) - case "Capabilities": - value, err = h.getBuiltInObjectsHover(capabilitiesVals, variableSplitted[1]) - } - - if err == nil { - if value == "" { - value = "\"\"" - } - result := util.BuildHoverResponse(value, wordRange) - return result, err - } - return nil, err - } - - searchWord := variableSplitted[0] - completionItems := [][]HelmDocumentation{ - basicItems, - builtinFuncs, - sprigFuncs, - helmFuncs, - } - toSearch := util.ConcatMultipleSlices(completionItems) - - logger.Println("Start search with word " + searchWord) - for _, completionItem := range toSearch { - if searchWord == completionItem.Name { - result := util.BuildHoverResponse(fmt.Sprint(completionItem.Doc), wordRange) - return result, err - } - } return nil, err } - -func (h *langHandler) getChartMetadataHover(metadata *chart.Metadata, key string) (string, error) { - for _, completionItem := range chartVals { - if key == completionItem.Name { - logger.Println("Getting metadatafield of " + key) - - documentation := completionItem.Doc - value := h.getMetadataField(metadata, key) - - return fmt.Sprintf("%s\n\n%s\n", documentation, value), nil - } - } - return "", fmt.Errorf("%s was no known Chart Metadata property", key) -} - -func (h *langHandler) getValueHover(chart *charts.Chart, splittedVar []string) (result string, err error) { - var ( - valuesFiles = chart.ResolveValueFiles(splittedVar, h.chartStore) - hoverResults = util.HoverResultsWithFiles{} - ) - - for _, valuesFiles := range valuesFiles { - for _, valuesFile := range valuesFiles.ValuesFiles.AllValuesFiles() { - result, err := h.getTableOrValueForSelector(valuesFile.Values, strings.Join(valuesFiles.Selector, ".")) - if err == nil { - hoverResults = append(hoverResults, util.HoverResultWithFile{URI: valuesFile.URI, Value: result}) - } - } - } - - return hoverResults.Format(h.chartStore.RootURI), nil -} - -func (h *langHandler) getTableOrValueForSelector(values chartutil.Values, selector string) (string, error) { - if len(selector) > 0 { - localValues, err := values.Table(selector) - if err != nil { - logger.Debug("values.PathValue(tableName) because of error", err) - value, err := values.PathValue(selector) - return h.formatToYAML(reflect.Indirect(reflect.ValueOf(value)), selector), err - } - logger.Debug("converting to YAML", localValues) - return localValues.YAML() - } - return values.YAML() -} - -func (h *langHandler) getBuiltInObjectsHover(items []HelmDocumentation, key string) (string, error) { - for _, completionItem := range items { - if key == completionItem.Name { - documentation := completionItem.Doc - return fmt.Sprintf("%s\n", documentation), nil - } - } - return "", fmt.Errorf("%s was no known built-in object", key) -} - -func (h *langHandler) getMetadataField(v *chart.Metadata, fieldName string) string { - r := reflect.ValueOf(v) - field := reflect.Indirect(r).FieldByName(fieldName) - return h.formatToYAML(field, fieldName) -} - -func (h *langHandler) formatToYAML(field reflect.Value, fieldName string) string { - switch field.Kind() { - case reflect.String: - return field.String() - case reflect.Map: - return h.toYAML(field.Interface()) - case reflect.Slice: - return h.toYAML(map[string]interface{}{fieldName: field.Interface()}) - case reflect.Bool: - return fmt.Sprint(h.getBoolType(field)) - case reflect.Float32, reflect.Float64: - return fmt.Sprint(field.Float()) - default: - logger.Error("Unknown kind for hover type: ", field.Kind()) - return "" - } -} diff --git a/internal/handler/hover_main_test.go b/internal/handler/hover_main_test.go index 494b03b0..f0dde49e 100644 --- a/internal/handler/hover_main_test.go +++ b/internal/handler/hover_main_test.go @@ -29,7 +29,7 @@ func TestHoverMain(t *testing.T) { Line: 7, Character: 10, }, - expected: "negate the boolean value of $x", + expected: "not $x\n\nnegate the boolean value of $x", expectedError: nil, }, { @@ -74,7 +74,16 @@ func TestHoverMain(t *testing.T) { Line: 68, Character: 24, }, - expected: "Returns a list of files whose names match the given shell glob pattern.\n", + expected: "Returns a list of files whose names match the given shell glob pattern.", + expectedError: nil, + }, + { + desc: "Test hover on .Files", + position: lsp.Position{ + Line: 68, + Character: 20, + }, + expected: "access non-template files within the chart", expectedError: nil, }, { diff --git a/internal/handler/references.go b/internal/handler/references.go index 80477444..d7be4e9d 100644 --- a/internal/handler/references.go +++ b/internal/handler/references.go @@ -16,11 +16,11 @@ func (h *langHandler) References(ctx context.Context, params *lsp.ReferenceParam usecases := []languagefeatures.ReferencesUseCase{ languagefeatures.NewIncludesDefinitionFeature(genericDocumentUseCase), languagefeatures.NewIncludesCallFeature(genericDocumentUseCase), - languagefeatures.NewValuesFeature(genericDocumentUseCase), + languagefeatures.NewTemplateContextFeature(genericDocumentUseCase), } for _, usecase := range usecases { - if usecase.AppropriateForNode(genericDocumentUseCase.NodeType, genericDocumentUseCase.ParentNodeType, genericDocumentUseCase.Node) { + if usecase.AppropriateForNode() { return usecase.References() } } diff --git a/internal/language_features/built_in_objects.go b/internal/language_features/built_in_objects.go new file mode 100644 index 00000000..a80ebfca --- /dev/null +++ b/internal/language_features/built_in_objects.go @@ -0,0 +1,81 @@ +package languagefeatures + +import ( + helmdocs "github.com/mrjosh/helm-ls/internal/documentation/helm" + lsplocal "github.com/mrjosh/helm-ls/internal/lsp" + "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" + lsp "go.lsp.dev/protocol" +) + +type BuiltInObjectsFeature struct { + *GenericTemplateContextFeature +} + +func NewBuiltInObjectsFeature(genericDocumentUseCase *GenericDocumentUseCase) *BuiltInObjectsFeature { + return &BuiltInObjectsFeature{ + GenericTemplateContextFeature: &GenericTemplateContextFeature{genericDocumentUseCase}, + } +} + +func (f *BuiltInObjectsFeature) AppropriateForNode() bool { + if !(f.ParentNodeType == gotemplate.NodeTypeField && f.NodeType == gotemplate.NodeTypeIdentifier) && + f.NodeType != gotemplate.NodeTypeIdentifier && + f.NodeType != gotemplate.NodeTypeFieldIdentifier && + f.NodeType != gotemplate.NodeTypeDot && + f.NodeType != gotemplate.NodeTypeDotSymbol { + return false + } + + allowedBuiltIns := []string{"Chart", "Values", "Files", "Template", "Release"} + + templateContext, _ := f.getTemplateContext() + if len(templateContext) != 1 { + return false + } + for _, allowedBuiltIn := range allowedBuiltIns { + if templateContext[0] == allowedBuiltIn { + return true + } + } + return false +} + +func (f *BuiltInObjectsFeature) References() (result []lsp.Location, err error) { + templateContext, err := f.getTemplateContext() + if err != nil { + return []lsp.Location{}, err + } + + locations := f.getReferencesFromSymbolTable(templateContext) + return append(locations, f.getDefinitionLocations(templateContext)...), err +} + +func (f *BuiltInObjectsFeature) getDefinitionLocations(templateContext lsplocal.TemplateContext) []lsp.Location { + locations := []lsp.Location{} + + switch templateContext[0] { + case "Values": + for _, valueFile := range f.Chart.ValuesFiles.AllValuesFiles() { + locations = append(locations, lsp.Location{URI: valueFile.URI}) + } + return locations + case "Chart": + return []lsp.Location{{URI: f.Chart.ChartMetadata.URI}} + } + + return []lsp.Location{} +} + +func (f *BuiltInObjectsFeature) Hover() (string, error) { + templateContext, _ := f.getTemplateContext() + docs, err := f.builtInOjectDocsLookup(templateContext[0], helmdocs.BuiltInObjects) + return docs.Doc, err +} + +func (f *BuiltInObjectsFeature) Definition() (result []lsp.Location, err error) { + templateContext, err := f.getTemplateContext() + if err != nil { + return []lsp.Location{}, err + } + return f.getDefinitionLocations(templateContext), nil +} diff --git a/internal/language_features/function_call.go b/internal/language_features/function_call.go new file mode 100644 index 00000000..e35cd6cf --- /dev/null +++ b/internal/language_features/function_call.go @@ -0,0 +1,30 @@ +package languagefeatures + +import ( + "fmt" + + helmdocs "github.com/mrjosh/helm-ls/internal/documentation/helm" + "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" +) + +type FunctionCallFeature struct { + *GenericDocumentUseCase +} + +func NewFunctionCallFeature(genericDocumentUseCase *GenericDocumentUseCase) *FunctionCallFeature { + return &FunctionCallFeature{ + GenericDocumentUseCase: genericDocumentUseCase, + } +} + +func (f *FunctionCallFeature) AppropriateForNode() bool { + return f.NodeType == gotemplate.NodeTypeIdentifier && f.ParentNodeType == gotemplate.NodeTypeFunctionCall +} + +func (f *FunctionCallFeature) Hover() (string, error) { + documentation, ok := helmdocs.GetFunctionByName(f.NodeContent()) + if !ok { + return "", fmt.Errorf("could not find documentation for function %s", f.NodeContent()) + } + return fmt.Sprintf("%s\n\n%s", documentation.Detail, documentation.Doc), nil +} diff --git a/internal/language_features/generic_template_context.go b/internal/language_features/generic_template_context.go new file mode 100644 index 00000000..fcb4c225 --- /dev/null +++ b/internal/language_features/generic_template_context.go @@ -0,0 +1,43 @@ +package languagefeatures + +import ( + "fmt" + + helmdocs "github.com/mrjosh/helm-ls/internal/documentation/helm" + lsplocal "github.com/mrjosh/helm-ls/internal/lsp" + "github.com/mrjosh/helm-ls/internal/util" + lsp "go.lsp.dev/protocol" +) + +type GenericTemplateContextFeature struct { + *GenericDocumentUseCase +} + +func (f *GenericTemplateContextFeature) getTemplateContext() (lsplocal.TemplateContext, error) { + templateContext := f.GenericDocumentUseCase.Document.SymbolTable.GetTemplateContext(lsplocal.GetRangeForNode(f.Node)) + if len(templateContext) == 0 || templateContext == nil { + return lsplocal.TemplateContext{}, fmt.Errorf("no template context found") + } + return templateContext, nil +} + +func (f *GenericTemplateContextFeature) getReferencesFromSymbolTable(templateContext lsplocal.TemplateContext) []lsp.Location { + locations := []lsp.Location{} + for _, doc := range f.GenericDocumentUseCase.DocumentStore.GetAllDocs() { + referenceRanges := doc.SymbolTable.GetTemplateContextRanges(templateContext) + for _, referenceRange := range referenceRanges { + locations = append(locations, util.RangeToLocation(doc.URI, referenceRange)) + } + } + + return locations +} + +func (f *GenericTemplateContextFeature) builtInOjectDocsLookup(key string, docs []helmdocs.HelmDocumentation) (helmdocs.HelmDocumentation, error) { + for _, item := range docs { + if key == item.Name { + return item, nil + } + } + return helmdocs.HelmDocumentation{}, fmt.Errorf("key %s not found on built-in object", key) +} diff --git a/internal/language_features/includes.go b/internal/language_features/includes.go index 0168392a..84b0fbd9 100644 --- a/internal/language_features/includes.go +++ b/internal/language_features/includes.go @@ -6,7 +6,6 @@ import ( lsplocal "github.com/mrjosh/helm-ls/internal/lsp" "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" "github.com/mrjosh/helm-ls/internal/util" - sitter "github.com/smacker/go-tree-sitter" ) type IncludesFeature struct { @@ -18,11 +17,11 @@ type IncludesCallFeature struct { } // should be called on {{ include "name" . }} -func (f *IncludesCallFeature) AppropriateForNode(currentNodeType string, parentNodeType string, node *sitter.Node) bool { - if parentNodeType != gotemplate.NodeTypeArgumentList { +func (f *IncludesCallFeature) AppropriateForNode() bool { + if f.ParentNodeType != gotemplate.NodeTypeArgumentList { return false } - functionCallNode := node.Parent().Parent() + functionCallNode := f.Node.Parent().Parent() _, err := lsplocal.ParseIncludeFunctionCall(functionCallNode, []byte(f.GenericDocumentUseCase.Document.Content)) return err == nil } @@ -32,8 +31,8 @@ type IncludesDefinitionFeature struct { } // should be called on {{ define "name" }} -func (f *IncludesDefinitionFeature) AppropriateForNode(currentNodeType string, parentNodeType string, node *sitter.Node) bool { - return parentNodeType == gotemplate.NodeTypeDefineAction && currentNodeType == gotemplate.NodeTypeInterpretedStringLiteral +func (f *IncludesDefinitionFeature) AppropriateForNode() bool { + return f.ParentNodeType == gotemplate.NodeTypeDefineAction && f.NodeType == gotemplate.NodeTypeInterpretedStringLiteral } func NewIncludesCallFeature(genericDocumentUseCase *GenericDocumentUseCase) *IncludesCallFeature { diff --git a/internal/language_features/template_context.go b/internal/language_features/template_context.go index 0eaeb736..78f5f7d9 100644 --- a/internal/language_features/template_context.go +++ b/internal/language_features/template_context.go @@ -2,45 +2,54 @@ package languagefeatures import ( "fmt" + "reflect" + "strings" lsp "go.lsp.dev/protocol" + helmdocs "github.com/mrjosh/helm-ls/internal/documentation/helm" lsplocal "github.com/mrjosh/helm-ls/internal/lsp" "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" "github.com/mrjosh/helm-ls/internal/util" - sitter "github.com/smacker/go-tree-sitter" + "github.com/mrjosh/helm-ls/pkg/chart" + "github.com/mrjosh/helm-ls/pkg/chartutil" ) type TemplateContextFeature struct { - *GenericDocumentUseCase + *GenericTemplateContextFeature } -func NewValuesFeature(genericDocumentUseCase *GenericDocumentUseCase) *TemplateContextFeature { +func NewTemplateContextFeature(genericDocumentUseCase *GenericDocumentUseCase) *TemplateContextFeature { return &TemplateContextFeature{ - GenericDocumentUseCase: genericDocumentUseCase, + GenericTemplateContextFeature: &GenericTemplateContextFeature{genericDocumentUseCase}, } } -func (f *TemplateContextFeature) AppropriateForNode(currentNodeType string, parentNodeType string, node *sitter.Node) bool { - return (parentNodeType == gotemplate.NodeTypeField && currentNodeType == gotemplate.NodeTypeIdentifier) || currentNodeType == gotemplate.NodeTypeFieldIdentifier || currentNodeType == gotemplate.NodeTypeField +func (f *TemplateContextFeature) AppropriateForNode() bool { + if f.NodeType == gotemplate.NodeTypeDot { + return true + } + return (f.ParentNodeType == gotemplate.NodeTypeField && f.NodeType == gotemplate.NodeTypeIdentifier) || + f.NodeType == gotemplate.NodeTypeFieldIdentifier || + f.NodeType == gotemplate.NodeTypeField } func (f *TemplateContextFeature) References() (result []lsp.Location, err error) { - includeName, err := f.getTemplateContext() + templateContext, err := f.getTemplateContext() if err != nil { return []lsp.Location{}, err } - locations := f.getReferenceLocations(includeName) - return locations, nil + locations := f.getReferenceLocations(templateContext) + return append(locations, f.getDefinitionLocations(templateContext)...), nil } -func (f *TemplateContextFeature) getTemplateContext() (lsplocal.TemplateContext, error) { - templateContext := f.GenericDocumentUseCase.Document.SymbolTable.GetTemplateContext(lsplocal.GetRangeForNode(f.Node)) - if len(templateContext) == 0 || templateContext == nil { - return lsplocal.TemplateContext{}, fmt.Errorf("no template context found") +func (f *TemplateContextFeature) Definition() (result []lsp.Location, err error) { + templateContext, err := f.getTemplateContext() + if err != nil { + return []lsp.Location{}, err } - return templateContext, nil + return f.getDefinitionLocations(templateContext), nil } func (f *TemplateContextFeature) getReferenceLocations(templateContext lsplocal.TemplateContext) []lsp.Location { @@ -57,20 +66,68 @@ func (f *TemplateContextFeature) getReferenceLocations(templateContext lsplocal. func (f *TemplateContextFeature) getDefinitionLocations(templateContext lsplocal.TemplateContext) []lsp.Location { locations := []lsp.Location{} - if len(templateContext) == 0 || templateContext == nil { - return []lsp.Location{} - } switch templateContext[0] { case "Values": for _, value := range f.Chart.ResolveValueFiles(templateContext.Tail(), f.ChartStore) { locations = append(locations, value.ValuesFiles.GetPositionsForValue(value.Selector)...) } + return locations + case "Chart": + location, _ := f.Chart.GetValueLocation(templateContext.Tail()) + return []lsp.Location{location} } return locations } func (f *TemplateContextFeature) Hover() (string, error) { templateContext, err := f.getTemplateContext() + + switch templateContext[0] { + case "Values": + return f.valuesHover(templateContext.Tail()) + case "Chart": + docs, error := f.builtInOjectDocsLookup(templateContext.Tail().Format(), helmdocs.BuiltInOjectVals[templateContext[0]]) + value := f.getMetadataField(&f.Chart.ChartMetadata.Metadata, docs.Name) + return fmt.Sprintf("%s\n\n%s\n", docs.Doc, value), error + case "Release", "Files", "Capabilities", "Template": + docs, error := f.builtInOjectDocsLookup(templateContext.Tail().Format(), helmdocs.BuiltInOjectVals[templateContext[0]]) + return docs.Doc, error + } + return templateContext.Format(), err } + +func (f *TemplateContextFeature) valuesHover(templateContext lsplocal.TemplateContext) (string, error) { + var ( + valuesFiles = f.Chart.ResolveValueFiles(templateContext, f.ChartStore) + hoverResults = util.HoverResultsWithFiles{} + ) + for _, valuesFiles := range valuesFiles { + for _, valuesFile := range valuesFiles.ValuesFiles.AllValuesFiles() { + result, err := f.getTableOrValueForSelector(valuesFile.Values, strings.Join(valuesFiles.Selector, ".")) + if err == nil { + hoverResults = append(hoverResults, util.HoverResultWithFile{URI: valuesFile.URI, Value: result}) + } + } + } + return hoverResults.Format(f.ChartStore.RootURI), nil +} + +func (f *TemplateContextFeature) getMetadataField(v *chart.Metadata, fieldName string) string { + r := reflect.ValueOf(v) + field := reflect.Indirect(r).FieldByName(fieldName) + return util.FormatToYAML(field, fieldName) +} + +func (f *TemplateContextFeature) getTableOrValueForSelector(values chartutil.Values, selector string) (string, error) { + if len(selector) > 0 { + localValues, err := values.Table(selector) + if err != nil { + value, err := values.PathValue(selector) + return util.FormatToYAML(reflect.Indirect(reflect.ValueOf(value)), selector), err + } + return localValues.YAML() + } + return values.YAML() +} diff --git a/internal/handler/hover_test.go b/internal/language_features/template_context_hover_test.go similarity index 95% rename from internal/handler/hover_test.go rename to internal/language_features/template_context_hover_test.go index 38a2499f..47af0098 100644 --- a/internal/handler/hover_test.go +++ b/internal/language_features/template_context_hover_test.go @@ -1,4 +1,4 @@ -package handler +package languagefeatures import ( "path/filepath" @@ -240,13 +240,16 @@ value } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - h := &langHandler{ - chartStore: &charts.ChartStore{ + genericDocumentUseCase := &GenericDocumentUseCase{ + Chart: tt.args.chart, + ChartStore: &charts.ChartStore{ RootURI: uri.New("file://tmp/"), Charts: tt.args.parentCharts, }, + // Node: tt.args.chart.ValuesFiles.MainValuesFile.Node, } - got, err := h.getValueHover(tt.args.chart, tt.args.splittedVar) + valuesFeature := NewTemplateContextFeature(genericDocumentUseCase) + got, err := valuesFeature.valuesHover(tt.args.splittedVar) if (err != nil) != tt.wantErr { t.Errorf("langHandler.getValueHover() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/internal/language_features/usecases.go b/internal/language_features/usecases.go index d5b2c801..01377a0b 100644 --- a/internal/language_features/usecases.go +++ b/internal/language_features/usecases.go @@ -1,16 +1,25 @@ package languagefeatures import ( - sitter "github.com/smacker/go-tree-sitter" lsp "go.lsp.dev/protocol" ) // interface for use cases type UseCase interface { - AppropriateForNode(currentNodeType string, parentNodeType string, node *sitter.Node) bool + AppropriateForNode() bool } type ReferencesUseCase interface { UseCase References() (result []lsp.Location, err error) } + +type HoverUseCase interface { + UseCase + Hover() (result string, err error) +} + +type DefinitionUseCase interface { + UseCase + Definition() (result []lsp.Location, err error) +} diff --git a/internal/language_features/variables.go b/internal/language_features/variables.go new file mode 100644 index 00000000..6a04b2ab --- /dev/null +++ b/internal/language_features/variables.go @@ -0,0 +1,33 @@ +package languagefeatures + +import ( + "fmt" + + lsplocal "github.com/mrjosh/helm-ls/internal/lsp" + "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" + "github.com/mrjosh/helm-ls/internal/util" + lsp "go.lsp.dev/protocol" +) + +type VariablesFeature struct { + *GenericDocumentUseCase +} + +func NewVariablesFeature(genericDocumentUseCase *GenericDocumentUseCase) *VariablesFeature { + return &VariablesFeature{ + GenericDocumentUseCase: genericDocumentUseCase, + } +} + +func (f *VariablesFeature) AppropriateForNode() bool { + return f.NodeType == gotemplate.NodeTypeIdentifier && f.ParentNodeType == gotemplate.NodeTypeVariable +} + +func (f *VariablesFeature) Definition() (result []lsp.Location, err error) { + variableName := f.GenericDocumentUseCase.NodeContent() + definitionNode := lsplocal.GetVariableDefinition(variableName, f.GenericDocumentUseCase.ParentNode, f.Document.Content) + if definitionNode == nil { + return []lsp.Location{}, fmt.Errorf("Could not find definition for %s. Variable definition not found", variableName) + } + return []lsp.Location{{URI: f.Document.URI, Range: lsp.Range{Start: util.PointToPosition(definitionNode.StartPoint())}}}, nil +} diff --git a/internal/lsp/symbol_table_includes.go b/internal/lsp/symbol_table_includes.go index 2d6b1650..9e35c76e 100644 --- a/internal/lsp/symbol_table_includes.go +++ b/internal/lsp/symbol_table_includes.go @@ -45,8 +45,8 @@ func ParseIncludeFunctionCall(node *sitter.Node, content []byte) (string, error) return "", fmt.Errorf("node is not a function call") } functionName := node.ChildByFieldName("function").Content(content) - if functionName != "include" { - return "", fmt.Errorf("function name is not include") + if functionName != "include" && functionName != "template" { // template is not documented but was seen in the wild + return "", fmt.Errorf("function name is not include or template") } arguments := node.ChildByFieldName("arguments") if arguments == nil || arguments.ChildCount() == 0 { diff --git a/internal/util/values.go b/internal/util/values.go new file mode 100644 index 00000000..e04c20df --- /dev/null +++ b/internal/util/values.go @@ -0,0 +1,50 @@ +package util + +import ( + "fmt" + "reflect" + + "github.com/mrjosh/helm-ls/pkg/chartutil" + "gopkg.in/yaml.v2" +) + +func GetTableOrValueForSelector(values chartutil.Values, selector string) (string, error) { + if len(selector) > 0 { + localValues, err := values.Table(selector) + if err != nil { + value, err := values.PathValue(selector) + return FormatToYAML(reflect.Indirect(reflect.ValueOf(value)), selector), err + } + return localValues.YAML() + } + return values.YAML() +} + +func FormatToYAML(field reflect.Value, fieldName string) string { + switch field.Kind() { + case reflect.String: + return field.String() + case reflect.Map: + return toYAML(field.Interface()) + case reflect.Slice: + return toYAML(map[string]interface{}{fieldName: field.Interface()}) + case reflect.Bool: + return fmt.Sprint(GetBoolType(field)) + case reflect.Float32, reflect.Float64: + return fmt.Sprint(field.Float()) + default: + return "" + } +} + +func toYAML(value interface{}) string { + valBytes, _ := yaml.Marshal(value) + return string(valBytes) +} + +func GetBoolType(value interface{}) string { + if val, ok := value.(bool); ok && val { + return "True" + } + return "False" +} diff --git a/internal/util/yaml.go b/internal/util/yaml.go index 66f758ef..6bef26f2 100644 --- a/internal/util/yaml.go +++ b/internal/util/yaml.go @@ -17,7 +17,7 @@ func GetPositionOfNode(node *yamlv3.Node, query []string) (lsp.Position, error) return lsp.Position{Line: uint32(node.Line) - 1, Character: uint32(node.Column) - 1}, nil } - query[0] = strings.TrimSuffix(query[0], "[0]") + query[0] = strings.TrimSuffix(query[0], "[]") switch node.Kind { case yamlv3.DocumentNode: diff --git a/internal/util/yaml_test.go b/internal/util/yaml_test.go index 56b5edb3..e54c4373 100644 --- a/internal/util/yaml_test.go +++ b/internal/util/yaml_test.go @@ -60,19 +60,19 @@ func TestGetPositionOfNodeWithList(t *testing.T) { t.Errorf("error yml parsing") } - result, err := GetPositionOfNode(&node, []string{"list[0]"}) + result, err := GetPositionOfNode(&node, []string{"list[]"}) expected := lsp.Position{Line: 32, Character: 0} assert.NoError(t, err) assert.Equal(t, expected, result) - result, err = GetPositionOfNode(&node, []string{"list[0]", "first"}) + result, err = GetPositionOfNode(&node, []string{"list[]", "first"}) expected = lsp.Position{Line: 33, Character: 4} assert.NoError(t, err) assert.Equal(t, expected, result) - result, err = GetPositionOfNode(&node, []string{"notExistingList[0]", "first"}) + result, err = GetPositionOfNode(&node, []string{"notExistingList[]", "first"}) expected = lsp.Position{} assert.Error(t, err) @@ -87,7 +87,7 @@ func TestGetPositionOfNodeInEmptyDocument(t *testing.T) { t.Errorf("error yml parsing") } - result, err := GetPositionOfNode(&node, []string{"list[0]"}) + result, err := GetPositionOfNode(&node, []string{"list[]"}) expected := lsp.Position{} assert.Error(t, err) @@ -99,7 +99,7 @@ func TestGetPositionOfNodeInEmptyDocument(t *testing.T) { t.Errorf("error yml parsing") } - result, err = GetPositionOfNode(&node, []string{"list[0]"}) + result, err = GetPositionOfNode(&node, []string{"list[]"}) expected = lsp.Position{} assert.Error(t, err) diff --git a/pkg/chartutil/values.go b/pkg/chartutil/values.go index 192339b5..1f6dd3b8 100644 --- a/pkg/chartutil/values.go +++ b/pkg/chartutil/values.go @@ -92,7 +92,7 @@ func (v Values) Encode(w io.Writer) error { } func tableLookup(v Values, simple string) (Values, error) { - if strings.HasSuffix(simple, "[0]") { + if strings.HasSuffix(simple, "[]") { return arryLookup(v, simple) } From bfa9b5208f5d2e59d93f427ab15f480006d8b14f Mon Sep 17 00:00:00 2001 From: qvalentin Date: Mon, 15 Apr 2024 19:12:13 +0200 Subject: [PATCH 27/35] feat(completion): tests and refactoring --- internal/documentation/godocs/gotemplate.go | 127 +++++---- .../documentation/helm/helm-documentation.go | 8 +- internal/handler/completion.go | 245 +++--------------- internal/handler/completion_main_test.go | 175 +++++++++++++ internal/handler/completion_values_test.go | 171 ------------ internal/handler/generic_document_usecase.go | 21 +- internal/language_features/function_call.go | 6 + .../generic_document_usecase.go | 10 + .../generic_template_context.go | 6 +- .../language_features/template_context.go | 75 ++++-- .../template_context_completion_test.go | 85 ++++++ internal/language_features/usecases.go | 5 + internal/language_features/variables.go | 3 +- internal/lsp/ast.go | 79 ++---- internal/lsp/ast_field_identifier_test.go | 127 --------- internal/lsp/ast_test.go | 37 +++ internal/lsp/symbol_table.go | 9 +- internal/lsp/symbol_table_test.go | 8 + internal/protocol/completion.go | 42 +++ internal/util/values.go | 69 +++++ internal/util/values_test.go | 47 ++++ .../example/templates/completion-test.yaml | 12 + 22 files changed, 702 insertions(+), 665 deletions(-) create mode 100644 internal/handler/completion_main_test.go delete mode 100644 internal/handler/completion_values_test.go create mode 100644 internal/language_features/template_context_completion_test.go delete mode 100644 internal/lsp/ast_field_identifier_test.go create mode 100644 internal/lsp/ast_test.go create mode 100644 internal/protocol/completion.go create mode 100644 internal/util/values_test.go create mode 100644 testdata/example/templates/completion-test.yaml diff --git a/internal/documentation/godocs/gotemplate.go b/internal/documentation/godocs/gotemplate.go index 79121e0c..13f3d442 100644 --- a/internal/documentation/godocs/gotemplate.go +++ b/internal/documentation/godocs/gotemplate.go @@ -37,70 +37,67 @@ type GoTemplateSnippet struct { Detail string Doc string Snippet string - Filter string } -var ( - TextSnippets = []GoTemplateSnippet{ - { - Name: "comment", - Detail: "{{- /* a comment with white space trimmed from preceding and following text */ -}}", - Doc: "A comment; discarded. May contain newlines. Comments do not nest and must start and end at the delimiters, as shown here.", - Snippet: "{{- /* $1 */ -}}", - }, - { - Name: "{{ }}", - Detail: "template", - Doc: "", - Snippet: "{{- $0 }}", - }, - { - Name: "if", - Detail: "{{if pipeline}} T1 {{end}}", - Doc: "If the value of the pipeline is empty, no output is generated; otherwise, T1 is executed. The empty values are false, 0, any nil pointer or interface value, and any array, slice, map, or string of length zero. Dot is unaffected.", - Snippet: "{{- if $1 }}\n $0 \n{{- end }}", - }, - { - Name: "if else", - Detail: "{{if pipeline}} T1 {{else}} T0 {{end}}", - Doc: "If the value of the pipeline is empty, T0 is executed; otherwise, T1 is executed. Dot is unaffected.", - Snippet: "{{- if $1 }}\n $2 \n{{- else }}\n $0 \n{{- end }}", - }, - { - Name: "if else if", - Detail: "{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}", - Doc: "To simplify the appearance of if-else chains, the else action of an if may include another if directly; the effect is exactly the same as writing {{if pipeline}} T1 {{else}}{{if pipeline}} T0 {{end}}{{end}}", - Snippet: "{{- if $1 }}\n $2 \n{{- else if $4 }}\n $0 \n{{- end }}", - }, - { - Name: "range", - Detail: "{{range pipeline}} T1 {{end}}", - Doc: "The value of the pipeline must be an array, slice, map, or channel. If the value of the pipeline has length zero, nothing is output; otherwise, dot is set to the successive elements of the array, slice, or map and T1 is executed. If the value is a map and the keys are of basic type with a defined order, the elements will be visited in sorted key order.", - Snippet: "{{- range $1 }}\n $0 \n{{- end }}", - }, - { - Name: "range else", - Detail: "{{range pipeline}} T1 {{else}} T0 {{end}}", - Doc: "The value of the pipeline must be an array, slice, map, or channel. If the value of the pipeline has length zero, dot is unaffected and T0 is executed; otherwise, dot is set to the successive elements of the array, slice, or map and T1 is executed.", - Snippet: "{{- range $1 }}\n $2 {{- else }}\n $0 \n{{- end }}", - }, - { - Name: "block", - Detail: "{{block \"name\" pipeline}} T1 {{end}}", - Doc: "A block is shorthand for defining a template {{define \"name\"}} T1 {{end}} and then executing it in place {{template \"name\" pipeline}} The typical use is to define a set of root templates that are then customized by redefining the block templates within.", - Snippet: "{{- block $1 }}\n $0 \n{{- end }}", - }, - { - Name: "with", - Detail: "{{with pipeline}} T1 {{end}}", - Doc: "If the value of the pipeline is empty, no output is generated; otherwise, dot is set to the value of the pipeline and T1 is executed.", - Snippet: "{{- with $1 }}\n $0 \n{{- end }}", - }, - { - Name: "with else", - Detail: "{{with pipeline}} T1 {{else}} T0 {{end}}", - Doc: "If the value of the pipeline is empty, dot is unaffected and T0 is executed; otherwise, dot is set to the value of the pipeline and T1 is executed", - Snippet: "{{- with $1 }}\n $2 {{- else }}\n $0 \n{{- end }}", - }, - } -) +var TextSnippets = []GoTemplateSnippet{ + { + Name: "comment", + Detail: "{{- /* a comment with white space trimmed from preceding and following text */ -}}", + Doc: "A comment; discarded. May contain newlines. Comments do not nest and must start and end at the delimiters, as shown here.", + Snippet: "{{- /* $1 */ -}}", + }, + { + Name: "{{ }}", + Detail: "template", + Doc: "", + Snippet: "{{- $0 }}", + }, + { + Name: "if", + Detail: "{{if pipeline}} T1 {{end}}", + Doc: "If the value of the pipeline is empty, no output is generated; otherwise, T1 is executed. The empty values are false, 0, any nil pointer or interface value, and any array, slice, map, or string of length zero. Dot is unaffected.", + Snippet: "{{- if $1 }}\n $0 \n{{- end }}", + }, + { + Name: "if else", + Detail: "{{if pipeline}} T1 {{else}} T0 {{end}}", + Doc: "If the value of the pipeline is empty, T0 is executed; otherwise, T1 is executed. Dot is unaffected.", + Snippet: "{{- if $1 }}\n $2 \n{{- else }}\n $0 \n{{- end }}", + }, + { + Name: "if else if", + Detail: "{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}", + Doc: "To simplify the appearance of if-else chains, the else action of an if may include another if directly; the effect is exactly the same as writing {{if pipeline}} T1 {{else}}{{if pipeline}} T0 {{end}}{{end}}", + Snippet: "{{- if $1 }}\n $2 \n{{- else if $4 }}\n $0 \n{{- end }}", + }, + { + Name: "range", + Detail: "{{range pipeline}} T1 {{end}}", + Doc: "The value of the pipeline must be an array, slice, map, or channel. If the value of the pipeline has length zero, nothing is output; otherwise, dot is set to the successive elements of the array, slice, or map and T1 is executed. If the value is a map and the keys are of basic type with a defined order, the elements will be visited in sorted key order.", + Snippet: "{{- range $1 }}\n $0 \n{{- end }}", + }, + { + Name: "range else", + Detail: "{{range pipeline}} T1 {{else}} T0 {{end}}", + Doc: "The value of the pipeline must be an array, slice, map, or channel. If the value of the pipeline has length zero, dot is unaffected and T0 is executed; otherwise, dot is set to the successive elements of the array, slice, or map and T1 is executed.", + Snippet: "{{- range $1 }}\n $2 {{- else }}\n $0 \n{{- end }}", + }, + { + Name: "block", + Detail: "{{block \"name\" pipeline}} T1 {{end}}", + Doc: "A block is shorthand for defining a template {{define \"name\"}} T1 {{end}} and then executing it in place {{template \"name\" pipeline}} The typical use is to define a set of root templates that are then customized by redefining the block templates within.", + Snippet: "{{- block $1 }}\n $0 \n{{- end }}", + }, + { + Name: "with", + Detail: "{{with pipeline}} T1 {{end}}", + Doc: "If the value of the pipeline is empty, no output is generated; otherwise, dot is set to the value of the pipeline and T1 is executed.", + Snippet: "{{- with $1 }}\n $0 \n{{- end }}", + }, + { + Name: "with else", + Detail: "{{with pipeline}} T1 {{else}} T0 {{end}}", + Doc: "If the value of the pipeline is empty, dot is unaffected and T0 is executed; otherwise, dot is set to the value of the pipeline and T1 is executed", + Snippet: "{{- with $1 }}\n $2 {{- else }}\n $0 \n{{- end }}", + }, +} diff --git a/internal/documentation/helm/helm-documentation.go b/internal/documentation/helm/helm-documentation.go index 16253da8..86d8c1b5 100644 --- a/internal/documentation/helm/helm-documentation.go +++ b/internal/documentation/helm/helm-documentation.go @@ -1,6 +1,10 @@ package helmdocs -import "github.com/mrjosh/helm-ls/internal/util" +import ( + "slices" + + "github.com/mrjosh/helm-ls/internal/util" +) type HelmDocumentation struct { Name string @@ -187,6 +191,8 @@ var ( {"required", "required $str $val", "fail template with message $str if $val is not provided or is empty"}, } + AllFuncs = slices.Concat(HelmFuncs, SprigFuncs, BuiltinFuncs) + CapabilitiesVals = []HelmDocumentation{ {"TillerVersion", ".Capabilities.TillerVersion", "Tiller version"}, {"APIVersions", "Capabilities.APIVersions", "A set of versions."}, diff --git a/internal/handler/completion.go b/internal/handler/completion.go index c5c32cb0..fb6a0c9a 100644 --- a/internal/handler/completion.go +++ b/internal/handler/completion.go @@ -2,109 +2,76 @@ package handler import ( "context" - "errors" "fmt" - "reflect" - "strings" - "github.com/mrjosh/helm-ls/internal/charts" + 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" - "github.com/mrjosh/helm-ls/internal/util" - "github.com/mrjosh/helm-ls/pkg/chartutil" sitter "github.com/smacker/go-tree-sitter" "go.lsp.dev/protocol" lsp "go.lsp.dev/protocol" - yaml "gopkg.in/yaml.v2" "github.com/mrjosh/helm-ls/internal/documentation/godocs" helmdocs "github.com/mrjosh/helm-ls/internal/documentation/helm" ) var ( - emptyItems = make([]lsp.CompletionItem, 0) - functionsCompletionItems = make([]lsp.CompletionItem, 0) - textCompletionsItems = make([]lsp.CompletionItem, 0) + emptyItems = make([]lsp.CompletionItem, 0) + textCompletionsItems = make([]lsp.CompletionItem, 0) ) func init() { - functionsCompletionItems = append(functionsCompletionItems, getFunctionCompletionItems(helmdocs.HelmFuncs)...) - functionsCompletionItems = append(functionsCompletionItems, getFunctionCompletionItems(helmdocs.BuiltinFuncs)...) - functionsCompletionItems = append(functionsCompletionItems, getFunctionCompletionItems(helmdocs.SprigFuncs)...) textCompletionsItems = append(textCompletionsItems, getTextCompletionItems(godocs.TextSnippets)...) } func (h *langHandler) Completion(ctx context.Context, params *lsp.CompletionParams) (result *lsp.CompletionList, err error) { - doc, ok := h.documents.Get(params.TextDocument.URI) - if !ok { - return nil, errors.New("Could not get document: " + params.TextDocument.URI.Filename()) - } - chart, err := h.chartStore.GetChartForDoc(params.TextDocument.URI) + logger.Debug("Running completion with params", params) + genericDocumentUseCase, err := h.NewGenericDocumentUseCase(params.TextDocumentPositionParams) if err != nil { - logger.Error("Error getting chart info for file", params.TextDocument.URI, err) + 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), + } + + for _, usecase := range usecases { + if usecase.AppropriateForNode() { + return usecase.Completion() + } } - word, isTextNode := completionAstParsing(doc, params.Position) + 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 } - var ( - splitted = strings.Split(word, ".") - items []lsp.CompletionItem - variableSplitted = []string{} - ) - - for n, s := range splitted { - // we want to keep the last empty string to be able - // distinguish between 'global.' and 'global' - if s == "" && n != len(splitted)-1 { - continue - } - variableSplitted = append(variableSplitted, s) - } - logger.Println(fmt.Sprintf("Word found for completions is < %s >", word)) - - items = make([]lsp.CompletionItem, 0) + items := []lsp.CompletionItem{} for _, v := range helmdocs.BuiltInObjects { items = append(items, lsp.CompletionItem{ Label: v.Name, - InsertText: v.Name, + InsertText: "." + v.Name, Detail: v.Detail, Documentation: v.Doc, }) } - if len(variableSplitted) == 0 { - return &lsp.CompletionList{IsIncomplete: false, Items: items}, err - } - - // $ always points to the root context so we can safely remove it - // as long the LSP does not know about ranges - if variableSplitted[0] == "$" && len(variableSplitted) > 1 { - variableSplitted = variableSplitted[1:] - } - - switch variableSplitted[0] { - case "Chart": - items = getVariableCompletionItems(helmdocs.ChartVals) - case "Values": - items = h.getValuesCompletions(chart, variableSplitted[1:]) - case "Release": - items = getVariableCompletionItems(helmdocs.ReleaseVals) - case "Files": - items = getVariableCompletionItems(helmdocs.FilesVals) - case "Capabilities": - items = getVariableCompletionItems(helmdocs.CapabilitiesVals) - default: - items = getVariableCompletionItems(helmdocs.BuiltInObjects) - items = append(items, functionsCompletionItems...) - } - return &lsp.CompletionList{IsIncomplete: false, Items: items}, err } @@ -129,151 +96,17 @@ func completionAstParsing(doc *lsplocal.Document, position lsp.Position) (string word string ) - logger.Debug("currentNode", currentNode) - logger.Debug("relevantChildNode", relevantChildNode) - - switch relevantChildNode.Type() { + nodeType := relevantChildNode.Type() + switch nodeType { case gotemplate.NodeTypeIdentifier: word = relevantChildNode.Content([]byte(doc.Content)) - case gotemplate.NodeTypeDot: - logger.Debug("TraverseIdentifierPathUp for dot node") - word = lsplocal.TraverseIdentifierPathUp(relevantChildNode, doc) - case gotemplate.NodeTypeDotSymbol: - logger.Debug("GetFieldIdentifierPath") - word = lsplocal.GetFieldIdentifierPath(relevantChildNode, doc) case gotemplate.NodeTypeText, gotemplate.NodeTypeTemplate: return word, true } + logger.Debug("word", word) return word, false } -func (h *langHandler) getValuesCompletions(chart *charts.Chart, splittedVar []string) (result []lsp.CompletionItem) { - m := make(map[string]lsp.CompletionItem) - for _, queriedValuesFiles := range chart.ResolveValueFiles(splittedVar, h.chartStore) { - for _, valuesFile := range queriedValuesFiles.ValuesFiles.AllValuesFiles() { - for _, item := range h.getValue(valuesFile.Values, queriedValuesFiles.Selector) { - m[item.InsertText] = item - } - } - } - - for _, item := range m { - result = append(result, item) - } - - return result -} - -func (h *langHandler) getValue(values chartutil.Values, splittedVar []string) []lsp.CompletionItem { - var ( - err error - tableName = strings.Join(splittedVar, ".") - localValues chartutil.Values - items = make([]lsp.CompletionItem, 0) - ) - - if len(splittedVar) > 0 { - - localValues, err = values.Table(tableName) - if err != nil { - logger.Println(err) - if len(splittedVar) > 1 { - // the current tableName was not found, maybe because it is incomplete, we can use the previous one - // e.g. gobal.im -> im was not found - // but global contains the key image, so we return all keys of global - localValues, err = values.Table(strings.Join(splittedVar[:len(splittedVar)-1], ".")) - if err != nil { - logger.Println(err) - return emptyItems - } - values = localValues - } - } else { - values = localValues - } - - } - - for variable, value := range values { - items = h.setItem(items, value, variable) - } - - return items -} - -func (h *langHandler) setItem(items []lsp.CompletionItem, value interface{}, variable string) []lsp.CompletionItem { - var ( - itemKind = lsp.CompletionItemKindVariable - valueOf = reflect.ValueOf(value) - documentation = valueOf.String() - ) - - logger.Debug("ValueKind: ", valueOf) - - switch valueOf.Kind() { - case reflect.Slice, reflect.Map: - itemKind = lsp.CompletionItemKindStruct - documentation = h.toYAML(value) - case reflect.Bool: - itemKind = lsp.CompletionItemKindVariable - documentation = util.GetBoolType(value) - case reflect.Float32, reflect.Float64: - documentation = fmt.Sprintf("%.2f", valueOf.Float()) - itemKind = lsp.CompletionItemKindVariable - case reflect.Invalid: - documentation = "" - default: - itemKind = lsp.CompletionItemKindField - } - - return append(items, lsp.CompletionItem{ - Label: variable, - InsertText: variable, - Documentation: documentation, - Detail: valueOf.Kind().String(), - Kind: itemKind, - }) -} - -func (h *langHandler) toYAML(value interface{}) string { - valBytes, _ := yaml.Marshal(value) - return string(valBytes) -} - -func getVariableCompletionItems(helmDocs []helmdocs.HelmDocumentation) (result []lsp.CompletionItem) { - for _, item := range helmDocs { - result = append(result, variableCompletionItem(item)) - } - return result -} - -func variableCompletionItem(helmDocumentation helmdocs.HelmDocumentation) lsp.CompletionItem { - return lsp.CompletionItem{ - Label: helmDocumentation.Name, - InsertText: helmDocumentation.Name, - Detail: helmDocumentation.Detail, - Documentation: helmDocumentation.Doc, - Kind: lsp.CompletionItemKindVariable, - } -} - -func getFunctionCompletionItems(helmDocs []helmdocs.HelmDocumentation) (result []lsp.CompletionItem) { - for _, item := range helmDocs { - result = append(result, functionCompletionItem(item)) - } - return result -} - -func functionCompletionItem(helmDocumentation helmdocs.HelmDocumentation) lsp.CompletionItem { - return lsp.CompletionItem{ - Label: helmDocumentation.Name, - InsertText: helmDocumentation.Name, - Detail: helmDocumentation.Detail, - Documentation: helmDocumentation.Doc, - Kind: lsp.CompletionItemKindFunction, - } -} - func getTextCompletionItems(gotemplateSnippet []godocs.GoTemplateSnippet) (result []lsp.CompletionItem) { for _, item := range gotemplateSnippet { result = append(result, textCompletionItem(item)) @@ -284,14 +117,14 @@ func getTextCompletionItems(gotemplateSnippet []godocs.GoTemplateSnippet) (resul func textCompletionItem(gotemplateSnippet godocs.GoTemplateSnippet) lsp.CompletionItem { return lsp.CompletionItem{ Label: gotemplateSnippet.Name, - TextEdit: &lsp.TextEdit{ - Range: lsp.Range{}, - NewText: gotemplateSnippet.Snippet, - }, + // 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, - FilterText: gotemplateSnippet.Filter, } } diff --git a/internal/handler/completion_main_test.go b/internal/handler/completion_main_test.go new file mode 100644 index 00000000..d3b3fd5e --- /dev/null +++ b/internal/handler/completion_main_test.go @@ -0,0 +1,175 @@ +package handler + +import ( + "context" + "os" + "testing" + + "github.com/mrjosh/helm-ls/internal/adapter/yamlls" + "github.com/mrjosh/helm-ls/internal/charts" + helmdocs "github.com/mrjosh/helm-ls/internal/documentation/helm" + lsplocal "github.com/mrjosh/helm-ls/internal/lsp" + "github.com/mrjosh/helm-ls/internal/util" + "github.com/stretchr/testify/assert" + lsp "go.lsp.dev/protocol" + "go.lsp.dev/uri" +) + +func TestCompletionMain(t *testing.T) { + testCases := []struct { + desc string + position lsp.Position + expectedInsertText string + notExpectedInsertTexts []string + expectedError error + }{ + { + desc: "Test completion on {{ if (and .Values. ) }}", + position: lsp.Position{ + Line: 8, + Character: 19, + }, + expectedInsertText: "replicaCount", + notExpectedInsertTexts: []string{ + helmdocs.HelmFuncs[0].Name, + }, + expectedError: nil, + }, + { + desc: "Test completion on .Chart.N", + position: lsp.Position{ + Line: 5, + Character: 11, + }, + expectedInsertText: "Name", + notExpectedInsertTexts: []string{ + helmdocs.HelmFuncs[0].Name, + "replicaCount", + "toYaml", + }, + expectedError: nil, + }, + { + desc: "Test completion on .Values.", + position: lsp.Position{ + Line: 0, + Character: 11, + }, + expectedInsertText: "replicaCount", + notExpectedInsertTexts: []string{ + helmdocs.HelmFuncs[0].Name, + }, + expectedError: nil, + }, + { + desc: "Test completion on {{ . }}", + position: lsp.Position{ + Line: 6, + Character: 4, + }, + expectedInsertText: "Chart", + notExpectedInsertTexts: []string{ + helmdocs.HelmFuncs[0].Name, + "replicaCount", + "toYaml", + }, + expectedError: nil, + }, + { + desc: "Test completion on .Values.re", + position: lsp.Position{ + Line: 1, + Character: 13, + }, + expectedInsertText: "replicaCount", + notExpectedInsertTexts: []string{ + helmdocs.HelmFuncs[0].Name, + }, + expectedError: nil, + }, + { + desc: "Test completion on {{ toY }}", + position: lsp.Position{ + Line: 3, + Character: 6, + }, + expectedInsertText: "toYaml", + notExpectedInsertTexts: []string{ + "replicaCount", + }, + 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{ + // Line: 4, + // Character: 3, + // }, + // expectedInsertText: "toYaml", + // notExpectedInsertTexts: []string{ + // "replicaCount", + // }, + // expectedError: nil, + // }, + } + for _, tt := range testCases { + t.Run(tt.desc, func(t *testing.T) { + documents := lsplocal.NewDocumentStore() + + path := "../../testdata/example/templates/completion-test.yaml" + fileURI := uri.File(path) + + content, err := os.ReadFile(path) + if err != nil { + t.Fatal("Could not read test file", err) + } + d := lsp.DidOpenTextDocumentParams{ + TextDocument: lsp.TextDocumentItem{ + URI: fileURI, + LanguageID: "", + Version: 0, + Text: string(content), + }, + } + 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: tt.position, + }, + }) + assert.Equal(t, tt.expectedError, err) + assert.NotNil(t, result) + + insertTexts := []string{} + for _, item := range result.Items { + insertTexts = append(insertTexts, item.InsertText) + } + assert.Contains(t, insertTexts, tt.expectedInsertText) + + for _, notExpectedInsertText := range tt.notExpectedInsertTexts { + assert.NotContains(t, insertTexts, notExpectedInsertText) + } + }) + } +} diff --git a/internal/handler/completion_values_test.go b/internal/handler/completion_values_test.go deleted file mode 100644 index 9c454cdf..00000000 --- a/internal/handler/completion_values_test.go +++ /dev/null @@ -1,171 +0,0 @@ -package handler - -import ( - "testing" - - "github.com/mrjosh/helm-ls/internal/charts" - lsplocal "github.com/mrjosh/helm-ls/internal/lsp" - "github.com/mrjosh/helm-ls/pkg/chart" - "github.com/stretchr/testify/assert" - "go.lsp.dev/protocol" - "gopkg.in/yaml.v3" -) - -func TestEmptyValues(t *testing.T) { - handler := &langHandler{ - linterName: "helm-lint", - connPool: nil, - documents: nil, - } - - result := handler.getValue(make(map[string]interface{}), []string{"global"}) - - if len(result) != 0 { - t.Errorf("Length of result was not zero.") - } - result = handler.getValue(make(map[string]interface{}), []string{""}) - - if len(result) != 0 { - t.Errorf("Length of result was not zero.") - } -} - -func TestValues(t *testing.T) { - handler := &langHandler{ - linterName: "helm-lint", - connPool: nil, - documents: nil, - } - nested := map[string]interface{}{"nested": "value"} - values := map[string]interface{}{"global": nested} - - result := handler.getValue(values, []string{"g"}) - - if len(result) != 1 || result[0].InsertText != "global" { - t.Errorf("Completion for g was not global but was %s.", result[0].InsertText) - } - - result = handler.getValue(values, []string{""}) - - if len(result) != 1 || result[0].InsertText != "global" { - t.Errorf("Completion for \"\" was not global but was %s.", result[0].InsertText) - } - - result = handler.getValue(values, []string{"global", "nes"}) - if len(result) != 1 || result[0].InsertText != "nested" { - t.Errorf("Completion for global.nes was not nested but was %s.", result[0].InsertText) - } -} - -func TestWrongValues(t *testing.T) { - handler := &langHandler{ - linterName: "helm-lint", - connPool: nil, - documents: nil, - } - nested := map[string]interface{}{"nested": 1} - values := map[string]interface{}{"global": nested} - - result := handler.getValue(values, []string{"some", "wrong", "values"}) - if len(result) != 0 { - t.Errorf("Length of result was not zero.") - } - - result = handler.getValue(values, []string{"some", "wrong"}) - if len(result) != 0 { - t.Errorf("Length of result was not zero.") - } - - result = handler.getValue(values, []string{"some", ""}) - if len(result) != 0 { - t.Errorf("Length of result was not zero.") - } - - result = handler.getValue(values, []string{"global", "nested", ""}) - if len(result) != 0 { - t.Errorf("Length of result was not zero.") - } -} - -func TestCompletionAstParsing(t *testing.T) { - documentText := `{{ .Values.global. }}` - expectedWord := ".Values.global." - doc := &lsplocal.Document{ - Content: documentText, - Ast: lsplocal.ParseAst(nil, documentText), - } - position := protocol.Position{ - Line: 0, - Character: 18, - } - word, _ := completionAstParsing(doc, position) - if expectedWord != word { - t.Errorf("Expected word '%s', but got '%s'", expectedWord, word) - } -} - -func TestGetValuesCompletions(t *testing.T) { - handler := &langHandler{ - linterName: "helm-lint", - connPool: nil, - documents: nil, - } - nested := map[string]interface{}{"nested": "value"} - valuesMain := map[string]interface{}{"global": nested} - valuesAdditional := map[string]interface{}{"glob": nested} - chart := &charts.Chart{ - ChartMetadata: &charts.ChartMetadata{Metadata: chart.Metadata{Name: "test"}}, - ValuesFiles: &charts.ValuesFiles{ - MainValuesFile: &charts.ValuesFile{ - Values: valuesMain, - ValueNode: yaml.Node{}, - URI: "", - }, - AdditionalValuesFiles: []*charts.ValuesFile{ - { - Values: valuesAdditional, - ValueNode: yaml.Node{}, - URI: "", - }, - }, - }, - RootURI: "", - } - - result := handler.getValuesCompletions(chart, []string{"g"}) - assert.Equal(t, 2, len(result)) - - result = handler.getValuesCompletions(chart, []string{"something", "different"}) - assert.Empty(t, result) -} - -func TestGetValuesCompletionsContainsNoDupliactes(t *testing.T) { - handler := &langHandler{ - linterName: "helm-lint", - connPool: nil, - documents: nil, - } - nested := map[string]interface{}{"nested": "value"} - valuesMain := map[string]interface{}{"global": nested} - valuesAdditional := map[string]interface{}{"global": nested} - testChart := &charts.Chart{ - ChartMetadata: &charts.ChartMetadata{Metadata: chart.Metadata{Name: "test"}}, - ValuesFiles: &charts.ValuesFiles{ - MainValuesFile: &charts.ValuesFile{ - Values: valuesMain, - ValueNode: yaml.Node{}, - URI: "", - }, - AdditionalValuesFiles: []*charts.ValuesFile{ - { - Values: valuesAdditional, - URI: "", - }, - }, - }, - RootURI: "", - } - - result := handler.getValuesCompletions(testChart, []string{"g"}) - assert.Equal(t, 1, len(result)) -} diff --git a/internal/handler/generic_document_usecase.go b/internal/handler/generic_document_usecase.go index b4e7bf20..841e521e 100644 --- a/internal/handler/generic_document_usecase.go +++ b/internal/handler/generic_document_usecase.go @@ -22,21 +22,12 @@ func (h *langHandler) NewGenericDocumentUseCase(params lsp.TextDocumentPositionP if node == nil { return &languagefeatures.GenericDocumentUseCase{}, errors.New("Could not get node for: " + params.TextDocument.URI.Filename()) } - parentNode := node.Parent() - var parentNodeType string - if parentNode != nil { - parentNodeType = parentNode.Type() - } - return &languagefeatures.GenericDocumentUseCase{ - Document: doc, - DocumentStore: h.documents, - Chart: chart, - ChartStore: h.chartStore, - Node: node, - ParentNode: parentNode, - ParentNodeType: parentNodeType, - NodeType: node.Type(), - }, nil + 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 { diff --git a/internal/language_features/function_call.go b/internal/language_features/function_call.go index e35cd6cf..a47cf650 100644 --- a/internal/language_features/function_call.go +++ b/internal/language_features/function_call.go @@ -4,7 +4,9 @@ import ( "fmt" helmdocs "github.com/mrjosh/helm-ls/internal/documentation/helm" + "github.com/mrjosh/helm-ls/internal/protocol" "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" + lsp "go.lsp.dev/protocol" ) type FunctionCallFeature struct { @@ -28,3 +30,7 @@ func (f *FunctionCallFeature) Hover() (string, error) { } return fmt.Sprintf("%s\n\n%s", documentation.Detail, documentation.Doc), nil } + +func (f *FunctionCallFeature) Completion() (result *lsp.CompletionList, err error) { + return protocol.NewCompletionResults(helmdocs.AllFuncs).ToLSP(), nil +} diff --git a/internal/language_features/generic_document_usecase.go b/internal/language_features/generic_document_usecase.go index 23afbbf9..90b369c2 100644 --- a/internal/language_features/generic_document_usecase.go +++ b/internal/language_features/generic_document_usecase.go @@ -20,3 +20,13 @@ type GenericDocumentUseCase struct { func (u *GenericDocumentUseCase) NodeContent() string { return u.Node.Content([]byte(u.Document.Content)) } + +func (u GenericDocumentUseCase) WithNode(node *sitter.Node) *GenericDocumentUseCase { + u.Node = node + u.NodeType = node.Type() + u.ParentNode = node.Parent() + if u.ParentNode != nil { + u.ParentNodeType = u.ParentNode.Type() + } + return &u +} diff --git a/internal/language_features/generic_template_context.go b/internal/language_features/generic_template_context.go index fcb4c225..13bf565b 100644 --- a/internal/language_features/generic_template_context.go +++ b/internal/language_features/generic_template_context.go @@ -14,11 +14,7 @@ type GenericTemplateContextFeature struct { } func (f *GenericTemplateContextFeature) getTemplateContext() (lsplocal.TemplateContext, error) { - templateContext := f.GenericDocumentUseCase.Document.SymbolTable.GetTemplateContext(lsplocal.GetRangeForNode(f.Node)) - if len(templateContext) == 0 || templateContext == nil { - return lsplocal.TemplateContext{}, fmt.Errorf("no template context found") - } - return templateContext, nil + return f.GenericDocumentUseCase.Document.SymbolTable.GetTemplateContext(lsplocal.GetRangeForNode(f.Node)) } func (f *GenericTemplateContextFeature) getReferencesFromSymbolTable(templateContext lsplocal.TemplateContext) []lsp.Location { diff --git a/internal/language_features/template_context.go b/internal/language_features/template_context.go index 78f5f7d9..26cfb80c 100644 --- a/internal/language_features/template_context.go +++ b/internal/language_features/template_context.go @@ -9,10 +9,10 @@ import ( helmdocs "github.com/mrjosh/helm-ls/internal/documentation/helm" 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" "github.com/mrjosh/helm-ls/pkg/chart" - "github.com/mrjosh/helm-ls/pkg/chartutil" ) type TemplateContextFeature struct { @@ -26,7 +26,10 @@ func NewTemplateContextFeature(genericDocumentUseCase *GenericDocumentUseCase) * } func (f *TemplateContextFeature) AppropriateForNode() bool { - if f.NodeType == gotemplate.NodeTypeDot { + nodeContent := f.NodeContent() + println(nodeContent) + + if f.NodeType == gotemplate.NodeTypeDot || f.NodeType == gotemplate.NodeTypeDotSymbol { return true } return (f.ParentNodeType == gotemplate.NodeTypeField && f.NodeType == gotemplate.NodeTypeIdentifier) || @@ -87,12 +90,12 @@ func (f *TemplateContextFeature) Hover() (string, error) { case "Values": return f.valuesHover(templateContext.Tail()) case "Chart": - docs, error := f.builtInOjectDocsLookup(templateContext.Tail().Format(), helmdocs.BuiltInOjectVals[templateContext[0]]) + docs, err := f.builtInOjectDocsLookup(templateContext.Tail().Format(), helmdocs.BuiltInOjectVals[templateContext[0]]) value := f.getMetadataField(&f.Chart.ChartMetadata.Metadata, docs.Name) - return fmt.Sprintf("%s\n\n%s\n", docs.Doc, value), error + return fmt.Sprintf("%s\n\n%s\n", docs.Doc, value), err case "Release", "Files", "Capabilities", "Template": - docs, error := f.builtInOjectDocsLookup(templateContext.Tail().Format(), helmdocs.BuiltInOjectVals[templateContext[0]]) - return docs.Doc, error + docs, err := f.builtInOjectDocsLookup(templateContext.Tail().Format(), helmdocs.BuiltInOjectVals[templateContext[0]]) + return docs.Doc, err } return templateContext.Format(), err @@ -105,7 +108,7 @@ func (f *TemplateContextFeature) valuesHover(templateContext lsplocal.TemplateCo ) for _, valuesFiles := range valuesFiles { for _, valuesFile := range valuesFiles.ValuesFiles.AllValuesFiles() { - result, err := f.getTableOrValueForSelector(valuesFile.Values, strings.Join(valuesFiles.Selector, ".")) + result, err := util.GetTableOrValueForSelector(valuesFile.Values, strings.Join(valuesFiles.Selector, ".")) if err == nil { hoverResults = append(hoverResults, util.HoverResultWithFile{URI: valuesFile.URI, Value: result}) } @@ -120,14 +123,56 @@ func (f *TemplateContextFeature) getMetadataField(v *chart.Metadata, fieldName s return util.FormatToYAML(field, fieldName) } -func (f *TemplateContextFeature) getTableOrValueForSelector(values chartutil.Values, selector string) (string, error) { - if len(selector) > 0 { - localValues, err := values.Table(selector) - if err != nil { - value, err := values.PathValue(selector) - return util.FormatToYAML(reflect.Indirect(reflect.ValueOf(value)), selector), err +func (f *TemplateContextFeature) Completion() (result *lsp.CompletionList, err error) { + templateContext, err := f.getTemplateContext() + if err != nil { + return nil, err + } + + if len(templateContext) == 0 { + result := helmdocs.BuiltInObjects + return protocol.NewCompletionResults(result).ToLSP(), nil + } + + if len(templateContext) == 1 { + result, ok := helmdocs.BuiltInOjectVals[templateContext[0]] + if !ok { + result := helmdocs.BuiltInObjects + return protocol.NewCompletionResults(result).ToLSP(), nil } - return localValues.YAML() + return protocol.NewCompletionResults(result).ToLSP(), nil } - return values.YAML() + + switch templateContext[0] { + case "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 { + result := helmdocs.BuiltInObjects + return protocol.NewCompletionResults(result).ToLSP(), nil + } + return protocol.NewCompletionResults(result).ToLSP(), nil + + } + + return nil, nil +} + +func (f *TemplateContextFeature) valuesCompletion(templateContext lsplocal.TemplateContext) (*lsp.CompletionList, error) { + m := make(map[string]lsp.CompletionItem) + for _, queriedValuesFiles := range f.Chart.ResolveValueFiles(templateContext.Tail(), f.ChartStore) { + for _, valuesFile := range queriedValuesFiles.ValuesFiles.AllValuesFiles() { + for _, item := range util.GetValueCompletion(valuesFile.Values, queriedValuesFiles.Selector) { + m[item.InsertText] = item + } + } + } + completions := []lsp.CompletionItem{} + for _, item := range m { + completions = append(completions, item) + } + + return &lsp.CompletionList{Items: completions, IsIncomplete: false}, nil } diff --git a/internal/language_features/template_context_completion_test.go b/internal/language_features/template_context_completion_test.go new file mode 100644 index 00000000..86cb17f9 --- /dev/null +++ b/internal/language_features/template_context_completion_test.go @@ -0,0 +1,85 @@ +package languagefeatures + +import ( + "testing" + + "github.com/mrjosh/helm-ls/internal/charts" + "github.com/mrjosh/helm-ls/pkg/chart" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" +) + +func TestGetValuesCompletions(t *testing.T) { + nested := map[string]interface{}{"nested": "value"} + valuesMain := map[string]interface{}{"global": nested} + valuesAdditional := map[string]interface{}{"glob": nested} + chart := &charts.Chart{ + ChartMetadata: &charts.ChartMetadata{Metadata: chart.Metadata{Name: "test"}}, + ValuesFiles: &charts.ValuesFiles{ + MainValuesFile: &charts.ValuesFile{ + Values: valuesMain, + ValueNode: yaml.Node{}, + URI: "", + }, + AdditionalValuesFiles: []*charts.ValuesFile{ + { + Values: valuesAdditional, + ValueNode: yaml.Node{}, + URI: "", + }, + }, + }, + RootURI: "", + } + + templateConextFeature := TemplateContextFeature{ + GenericTemplateContextFeature: &GenericTemplateContextFeature{ + GenericDocumentUseCase: &GenericDocumentUseCase{ + Chart: chart, + }, + }, + } + + result, err := templateConextFeature.valuesCompletion([]string{"Values", "g"}) + assert.NoError(t, err) + assert.Len(t, result.Items, 2) + + result, err = templateConextFeature.valuesCompletion([]string{"Values", "something", "different"}) + assert.NoError(t, err) + assert.Len(t, result.Items, 0) +} + +func TestGetValuesCompletionsContainsNoDupliactes(t *testing.T) { + nested := map[string]interface{}{"nested": "value"} + valuesMain := map[string]interface{}{"global": nested} + valuesAdditional := map[string]interface{}{"global": nested} + chart := &charts.Chart{ + ChartMetadata: &charts.ChartMetadata{Metadata: chart.Metadata{Name: "test"}}, + ValuesFiles: &charts.ValuesFiles{ + MainValuesFile: &charts.ValuesFile{ + Values: valuesMain, + ValueNode: yaml.Node{}, + URI: "", + }, + AdditionalValuesFiles: []*charts.ValuesFile{ + { + Values: valuesAdditional, + URI: "", + }, + }, + }, + RootURI: "", + } + + templateConextFeature := TemplateContextFeature{ + GenericTemplateContextFeature: &GenericTemplateContextFeature{ + GenericDocumentUseCase: &GenericDocumentUseCase{ + Chart: chart, + }, + }, + } + + result, err := templateConextFeature.valuesCompletion([]string{"Values", "g"}) + assert.NoError(t, err) + assert.Len(t, result.Items, 1) +} diff --git a/internal/language_features/usecases.go b/internal/language_features/usecases.go index 01377a0b..0fa4fa16 100644 --- a/internal/language_features/usecases.go +++ b/internal/language_features/usecases.go @@ -23,3 +23,8 @@ type DefinitionUseCase interface { UseCase Definition() (result []lsp.Location, err error) } + +type CompletionUseCase interface { + UseCase + Completion() (result *lsp.CompletionList, err error) +} diff --git a/internal/language_features/variables.go b/internal/language_features/variables.go index 6a04b2ab..e6ebca61 100644 --- a/internal/language_features/variables.go +++ b/internal/language_features/variables.go @@ -20,7 +20,8 @@ func NewVariablesFeature(genericDocumentUseCase *GenericDocumentUseCase) *Variab } func (f *VariablesFeature) AppropriateForNode() bool { - return f.NodeType == gotemplate.NodeTypeIdentifier && f.ParentNodeType == gotemplate.NodeTypeVariable + return f.NodeType == gotemplate.NodeTypeIdentifier && + f.ParentNodeType == gotemplate.NodeTypeVariable } func (f *VariablesFeature) Definition() (result []lsp.Location, err error) { diff --git a/internal/lsp/ast.go b/internal/lsp/ast.go index 44330c38..00a134b1 100644 --- a/internal/lsp/ast.go +++ b/internal/lsp/ast.go @@ -2,7 +2,6 @@ package lsp import ( "context" - "fmt" "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" sitter "github.com/smacker/go-tree-sitter" @@ -34,6 +33,9 @@ func FindDirectChildNodeByStart(currentNode *sitter.Node, pointToLookUp sitter.P func FindRelevantChildNode(currentNode *sitter.Node, pointToLookUp sitter.Point) *sitter.Node { for i := 0; i < int(currentNode.ChildCount()); i++ { child := currentNode.Child(i) + if child == nil { + continue + } if isPointLargerOrEq(pointToLookUp, child.StartPoint()) && isPointLargerOrEq(child.EndPoint(), pointToLookUp) { return FindRelevantChildNode(child, pointToLookUp) } @@ -41,68 +43,31 @@ func FindRelevantChildNode(currentNode *sitter.Node, pointToLookUp sitter.Point) return currentNode } -func isPointLargerOrEq(a sitter.Point, b sitter.Point) bool { - if a.Row == b.Row { - return a.Column >= b.Column - } - return a.Row > b.Row -} - -func GetFieldIdentifierPath(node *sitter.Node, doc *Document) (path string) { - path = buildFieldIdentifierPath(node, doc) - logger.Debug(fmt.Sprintf("buildFieldIdentifierPath: %s for node %s with parent %s", path, node, node.Parent())) - - return path -} - -func buildFieldIdentifierPath(node *sitter.Node, doc *Document) string { - prepend := node.PrevNamedSibling() - - currentPath := node.Content([]byte(doc.Content)) - if prepend != nil { - nodeContent := node.Content([]byte(doc.Content)) - if nodeContent == "." { - nodeContent = "" +func FindRelevantChildNodeCompletion(currentNode *sitter.Node, pointToLookUp sitter.Point) *sitter.Node { + childCount := int(currentNode.ChildCount()) + for i := childCount - 1; i >= 0; i-- { + child := currentNode.Child(i) + if child == nil { + continue + } + if isPointLargerOrEq(pointToLookUp, child.StartPoint()) && isPointLargerOrEq(child.EndPoint(), pointToLookUp) { + return FindRelevantChildNodeCompletion(child, pointToLookUp) } - currentPath = prepend.Content([]byte(doc.Content)) + "." + nodeContent - logger.Println("Adding currentpath", currentPath) - } else if node.Parent() != nil && node.Parent().Type() == gotemplate.NodeTypeError { - return buildFieldIdentifierPath(node.Parent(), doc) - } - - if currentPath[0:1] == "$" { - return currentPath } - - if currentPath[0:1] != "." { - currentPath = "." + currentPath + if currentNode.Type() == " " { + return FindRelevantChildNodeCompletion(currentNode.Parent(), sitter.Point{ + Row: pointToLookUp.Row, + Column: pointToLookUp.Column - 1, + }) } - - return TraverseIdentifierPathUp(node, doc) + currentPath + return currentNode } -func TraverseIdentifierPathUp(node *sitter.Node, doc *Document) string { - parent := node.Parent() - - if parent == nil { - return "" - } - - switch parent.Type() { - case "range_action": - if node.PrevNamedSibling() == nil { - return TraverseIdentifierPathUp(parent, doc) - } - logger.Debug("Range action found") - return TraverseIdentifierPathUp(parent, doc) + parent.NamedChild(0).Content([]byte(doc.Content)) + "[0]" - case "with_action": - if node.PrevNamedSibling() == nil { - return TraverseIdentifierPathUp(parent, doc) - } - logger.Debug("With action found") - return TraverseIdentifierPathUp(parent, doc) + parent.NamedChild(0).Content([]byte(doc.Content)) +func isPointLargerOrEq(a sitter.Point, b sitter.Point) bool { + if a.Row == b.Row { + return a.Column >= b.Column } - return TraverseIdentifierPathUp(parent, doc) + return a.Row > b.Row } func (d *Document) ApplyChangesToAst(newContent string) { diff --git a/internal/lsp/ast_field_identifier_test.go b/internal/lsp/ast_field_identifier_test.go deleted file mode 100644 index bb06b23d..00000000 --- a/internal/lsp/ast_field_identifier_test.go +++ /dev/null @@ -1,127 +0,0 @@ -package lsp - -import ( - "testing" - - sitter "github.com/smacker/go-tree-sitter" - "github.com/stretchr/testify/assert" - lsp "go.lsp.dev/protocol" -) - -func TestGetFieldIdentifierPathSimple(t *testing.T) { - template := `{{ .Values.test }}` - - ast := ParseAst(nil, template) - // (template [0, 0] - [1, 0] - // (selector_expression [0, 3] - [0, 15] - // operand: (field [0, 3] - [0, 10] - // name: (identifier [0, 4] - [0, 10])) - // field: (field_identifier [0, 11] - [0, 15])) - - test_start := sitter.Point{Row: 0, Column: 12} - testNode := ast.RootNode().NamedDescendantForPointRange(test_start, test_start) - - if testNode.Content([]byte(template)) != "test" { - t.Errorf("Nodes were not correctly selected") - } - - doc := Document{ - Content: template, - Ast: ast, - } - - result := GetFieldIdentifierPath(testNode, &doc) - assert.Equal(t, ".Values.test", result) -} - -func TestGetFieldIdentifierPathWith(t *testing.T) { - template := `{{ with .Values }}{{ .test }} {{ end }}` - - ast := ParseAst(nil, template) - // (template [0, 0] - [1, 0] - // (with_action [0, 0] - [0, 39] - // condition: (field [0, 8] - [0, 15] - // name: (identifier [0, 9] - [0, 15])) - // consequence: (field [0, 21] - [0, 26] - // name: (identifier [0, 22] - [0, 26])))) - - test_start := sitter.Point{Row: 0, Column: 22} - testNode := ast.RootNode().NamedDescendantForPointRange(test_start, test_start) - - if testNode.Content([]byte(template)) != "test" { - t.Errorf("Nodes were not correctly selected") - } - - doc := Document{ - Content: template, - Ast: ast, - } - - result := GetFieldIdentifierPath(testNode, &doc) - assert.Equal(t, ".Values.test", result) -} - -func TestGetFieldIdentifierPathFunction(t *testing.T) { - template := `{{ and .Values.test1 .Values.test2 }}` - - ast := ParseAst(nil, template) - // (template [0, 0] - [1, 0] - // (function_call [0, 3] - [0, 35] - // function: (identifier [0, 3] - [0, 6]) - // arguments: (argument_list [0, 7] - [0, 35] - // (selector_expression [0, 7] - [0, 20] - // operand: (field [0, 7] - [0, 14] - // name: (identifier [0, 8] - [0, 14])) - // field: (field_identifier [0, 15] - [0, 20])) - // (selector_expression [0, 21] - [0, 34] - // operand: (field [0, 21] - [0, 28] - // name: (identifier [0, 22] - [0, 28])) - // field: (field_identifier [0, 29] - [0, 34]))))) - // - test1_start := sitter.Point{Row: 0, Column: 16} - test2_start := sitter.Point{Row: 0, Column: 33} - test1Node := ast.RootNode().NamedDescendantForPointRange(test1_start, test1_start) - test2Node := ast.RootNode().NamedDescendantForPointRange(test2_start, test2_start) - - test1NodeContent := test1Node.Content([]byte(template)) - test2NodeContent := test2Node.Content([]byte(template)) - - assert.Equal(t, "test1", test1NodeContent, "Nodes were not correctly selected") - assert.Equal(t, "test2", test2NodeContent, "Nodes were not correctly selected") - - doc := Document{ - Content: template, - Ast: ast, - } - - assert.Equal(t, ".Values.test1", GetFieldIdentifierPath(test1Node, &doc)) - assert.Equal(t, ".Values.test2", GetFieldIdentifierPath(test2Node, &doc)) -} - -func TestGetFieldIdentifierPathFunctionForCompletion(t *testing.T) { - template := `{{ and .Values.image .Values. }}` - // | -> complete at dot - - ast := ParseAst(nil, template) - - var ( - position = lsp.Position{Line: 0, Character: 29} - currentNode = NodeAtPosition(ast, position) - pointToLoopUp = sitter.Point{ - Row: position.Line, - Column: position.Character, - } - relevantChildNode = FindRelevantChildNode(currentNode, pointToLoopUp) - ) - - childNodeContent := relevantChildNode.Content([]byte(template)) - - assert.Equal(t, ".", childNodeContent, "Nodes were not correctly selected ") - - doc := Document{ - Content: template, - Ast: ast, - } - - assert.Equal(t, ".Values.", GetFieldIdentifierPath(relevantChildNode, &doc)) -} diff --git a/internal/lsp/ast_test.go b/internal/lsp/ast_test.go new file mode 100644 index 00000000..775220f6 --- /dev/null +++ b/internal/lsp/ast_test.go @@ -0,0 +1,37 @@ +package lsp + +import ( + "testing" + + sitter "github.com/smacker/go-tree-sitter" + "github.com/stretchr/testify/assert" +) + +func TestFindRelevantChildNodeCompletio(t *testing.T) { + + template := `{{ .Values. }} +{{ .Values.re }} + +{{ toY }} + +{{ .Chart.N }} +{{ . }} +` + ast := ParseAst(nil, template) + + logger.Println("RootNode:", ast.RootNode().String()) + + node := FindRelevantChildNodeCompletion(ast.RootNode(), sitter.Point{ + Row: 0, + Column: 11, + }) + + assert.Equal(t, node.StartPoint(), sitter.Point{ + Row: 0, + Column: 11, + }) + assert.Equal(t, node.EndPoint(), sitter.Point{ + Row: 0, + Column: 11, + }) +} diff --git a/internal/lsp/symbol_table.go b/internal/lsp/symbol_table.go index c9b5e5c3..2f3d6719 100644 --- a/internal/lsp/symbol_table.go +++ b/internal/lsp/symbol_table.go @@ -1,6 +1,7 @@ package lsp import ( + "fmt" "strings" sitter "github.com/smacker/go-tree-sitter" @@ -45,8 +46,12 @@ func (s *SymbolTable) GetTemplateContextRanges(templateContext TemplateContext) return s.contexts[templateContext.Format()] } -func (s *SymbolTable) GetTemplateContext(pointRange sitter.Range) TemplateContext { - return s.contextsReversed[pointRange] +func (s *SymbolTable) GetTemplateContext(pointRange sitter.Range) (TemplateContext, error) { + result, ok := s.contextsReversed[pointRange] + if !ok { + return result, fmt.Errorf("no template context found") + } + return result, nil } func (s *SymbolTable) AddIncludeDefinition(symbol string, pointRange sitter.Range) { diff --git a/internal/lsp/symbol_table_test.go b/internal/lsp/symbol_table_test.go index de977495..62ab6fe4 100644 --- a/internal/lsp/symbol_table_test.go +++ b/internal/lsp/symbol_table_test.go @@ -61,6 +61,7 @@ func TestSymbolTableForValues(t *testing.T) { {{ end }} {{ .Test }} +{{ . }} ` ast := ParseAst(nil, content) @@ -156,6 +157,13 @@ func TestSymbolTableForValues(t *testing.T) { Column: 6, }, }, + { + path: []string{}, + startPoint: sitter.Point{ + Row: 21, + Column: 3, + }, + }, } for _, v := range expected { diff --git a/internal/protocol/completion.go b/internal/protocol/completion.go new file mode 100644 index 00000000..3e1efe5f --- /dev/null +++ b/internal/protocol/completion.go @@ -0,0 +1,42 @@ +package protocol + +import ( + helmdocs "github.com/mrjosh/helm-ls/internal/documentation/helm" + lsp "go.lsp.dev/protocol" +) + +type CompletionResults []CompletionResult + +func NewCompletionResults(docs []helmdocs.HelmDocumentation) *CompletionResults { + result := CompletionResults{} + + for _, doc := range docs { + result = append(result, CompletionResult{doc}) + } + + return &result +} + +type CompletionResult struct { + Documentation helmdocs.HelmDocumentation +} + +func (c *CompletionResult) ToLSP() (result lsp.CompletionItem) { + return lsp.CompletionItem{ + Label: c.Documentation.Name, + Detail: c.Documentation.Detail, + InsertText: c.Documentation.Name, + InsertTextFormat: lsp.InsertTextFormatSnippet, + Kind: lsp.CompletionItemKindConstant, // TODO: make this more variable + } +} + +func (c *CompletionResults) ToLSP() (result *lsp.CompletionList) { + items := []lsp.CompletionItem{} + + for _, completion := range *c { + items = append(items, completion.ToLSP()) + } + + return &lsp.CompletionList{Items: items} +} diff --git a/internal/util/values.go b/internal/util/values.go index e04c20df..3171f741 100644 --- a/internal/util/values.go +++ b/internal/util/values.go @@ -3,8 +3,10 @@ package util import ( "fmt" "reflect" + "strings" "github.com/mrjosh/helm-ls/pkg/chartutil" + lsp "go.lsp.dev/protocol" "gopkg.in/yaml.v2" ) @@ -20,6 +22,73 @@ func GetTableOrValueForSelector(values chartutil.Values, selector string) (strin return values.YAML() } +func GetValueCompletion(values chartutil.Values, splittedVar []string) []lsp.CompletionItem { + var ( + err error + tableName = strings.Join(splittedVar, ".") + localValues chartutil.Values + items = make([]lsp.CompletionItem, 0) + ) + + if len(splittedVar) > 0 { + + localValues, err = values.Table(tableName) + if err != nil { + if len(splittedVar) > 1 { + // the current tableName was not found, maybe because it is incomplete, we can use the previous one + // e.g. gobal.im -> im was not found + // but global contains the key image, so we return all keys of global + localValues, err = values.Table(strings.Join(splittedVar[:len(splittedVar)-1], ".")) + if err != nil { + return []lsp.CompletionItem{} + } + values = localValues + } + } else { + values = localValues + } + + } + + for variable, value := range values { + items = setItem(items, value, variable) + } + + return items +} + +func setItem(items []lsp.CompletionItem, value interface{}, variable string) []lsp.CompletionItem { + var ( + itemKind = lsp.CompletionItemKindVariable + valueOf = reflect.ValueOf(value) + documentation = valueOf.String() + ) + + switch valueOf.Kind() { + case reflect.Slice, reflect.Map: + itemKind = lsp.CompletionItemKindStruct + documentation = toYAML(value) + case reflect.Bool: + itemKind = lsp.CompletionItemKindVariable + documentation = GetBoolType(value) + case reflect.Float32, reflect.Float64: + documentation = fmt.Sprintf("%.2f", valueOf.Float()) + itemKind = lsp.CompletionItemKindVariable + case reflect.Invalid: + documentation = "" + default: + itemKind = lsp.CompletionItemKindField + } + + return append(items, lsp.CompletionItem{ + Label: variable, + InsertText: variable, + Documentation: documentation, + Detail: valueOf.Kind().String(), + Kind: itemKind, + }) +} + func FormatToYAML(field reflect.Value, fieldName string) string { switch field.Kind() { case reflect.String: diff --git a/internal/util/values_test.go b/internal/util/values_test.go new file mode 100644 index 00000000..90116abd --- /dev/null +++ b/internal/util/values_test.go @@ -0,0 +1,47 @@ +package util + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestEmptyValues(t *testing.T) { + _, err := GetTableOrValueForSelector(make(map[string]interface{}), "global") + assert.Error(t, err) + + result, err := GetTableOrValueForSelector(make(map[string]interface{}), "") + assert.NoError(t, err) + assert.Equal(t, "{}\n", result) +} + +func TestValues(t *testing.T) { + nested := map[string]interface{}{"nested": "value"} + values := map[string]interface{}{"global": nested} + + result := GetValueCompletion(values, []string{"g"}) + assert.Len(t, result, 1) + assert.Equal(t, "global", result[0].InsertText) + + result = GetValueCompletion(values, []string{""}) + assert.Len(t, result, 1) + assert.Equal(t, "global", result[0].InsertText) + + result = GetValueCompletion(values, []string{"global", "nes"}) + assert.Len(t, result, 1) + assert.Equal(t, "nested", result[0].InsertText) +} + +func TestWrongValues(t *testing.T) { + nested := map[string]interface{}{"nested": 1} + values := map[string]interface{}{"global": nested} + + _, err := GetTableOrValueForSelector(values, "some.wrong.values") + assert.Error(t, err) + + _, err = GetTableOrValueForSelector(values, "some.wrong") + assert.Error(t, err) + + _, err = GetTableOrValueForSelector(values, "some") + assert.Error(t, err) +} diff --git a/testdata/example/templates/completion-test.yaml b/testdata/example/templates/completion-test.yaml new file mode 100644 index 00000000..cea82b73 --- /dev/null +++ b/testdata/example/templates/completion-test.yaml @@ -0,0 +1,12 @@ +{{ .Values. }} +{{ .Values.re }} + +{{ toY }} + +{{ .Chart.N }} +{{ . }} + +{{ toYaml .Release. }} +{{ toYaml (.Release. ) }} +{{ .Release. }} + From d8e5cd42613b084d7452e6830e2f287cae99e956 Mon Sep 17 00:00:00 2001 From: qvalentin Date: Fri, 3 May 2024 20:56:18 +0200 Subject: [PATCH 28/35] fix: update ts grammar --- .../yamlls/diagnostics_integration_test.go | 1 + .../documentation/helm/helm-documentation.go | 4 +- internal/lsp/ast_test.go | 11 +- internal/tree-sitter/gotemplate/parser.c | 8251 +++++++++-------- internal/util/slices.go | 2 + 5 files changed, 4366 insertions(+), 3903 deletions(-) diff --git a/internal/adapter/yamlls/diagnostics_integration_test.go b/internal/adapter/yamlls/diagnostics_integration_test.go index 15446b45..345053e9 100644 --- a/internal/adapter/yamlls/diagnostics_integration_test.go +++ b/internal/adapter/yamlls/diagnostics_integration_test.go @@ -166,4 +166,5 @@ func TestYamllsDiagnosticsIntegrationWithSchema(t *testing.T) { } assert.Contains(t, diagnostic, expected) + assert.Len(t, diagnostic, 1) } diff --git a/internal/documentation/helm/helm-documentation.go b/internal/documentation/helm/helm-documentation.go index 86d8c1b5..d1e0773d 100644 --- a/internal/documentation/helm/helm-documentation.go +++ b/internal/documentation/helm/helm-documentation.go @@ -1,8 +1,6 @@ package helmdocs import ( - "slices" - "github.com/mrjosh/helm-ls/internal/util" ) @@ -191,7 +189,7 @@ var ( {"required", "required $str $val", "fail template with message $str if $val is not provided or is empty"}, } - AllFuncs = slices.Concat(HelmFuncs, SprigFuncs, BuiltinFuncs) + AllFuncs = util.ConcatMultipleSlices([][]HelmDocumentation{HelmFuncs, SprigFuncs, BuiltinFuncs}) CapabilitiesVals = []HelmDocumentation{ {"TillerVersion", ".Capabilities.TillerVersion", "Tiller version"}, diff --git a/internal/lsp/ast_test.go b/internal/lsp/ast_test.go index 775220f6..de22a9fa 100644 --- a/internal/lsp/ast_test.go +++ b/internal/lsp/ast_test.go @@ -8,7 +8,6 @@ import ( ) func TestFindRelevantChildNodeCompletio(t *testing.T) { - template := `{{ .Values. }} {{ .Values.re }} @@ -26,12 +25,12 @@ func TestFindRelevantChildNodeCompletio(t *testing.T) { Column: 11, }) - assert.Equal(t, node.StartPoint(), sitter.Point{ + assert.Equal(t, sitter.Point{ Row: 0, - Column: 11, - }) - assert.Equal(t, node.EndPoint(), sitter.Point{ + Column: 10, + }, node.StartPoint()) + assert.Equal(t, sitter.Point{ Row: 0, Column: 11, - }) + }, node.EndPoint()) } diff --git a/internal/tree-sitter/gotemplate/parser.c b/internal/tree-sitter/gotemplate/parser.c index 09bae993..ed4c264d 100644 --- a/internal/tree-sitter/gotemplate/parser.c +++ b/internal/tree-sitter/gotemplate/parser.c @@ -6,15 +6,15 @@ #endif #define LANGUAGE_VERSION 14 -#define STATE_COUNT 375 +#define STATE_COUNT 379 #define LARGE_STATE_COUNT 2 -#define SYMBOL_COUNT 82 +#define SYMBOL_COUNT 84 #define ALIAS_COUNT 1 -#define TOKEN_COUNT 42 +#define TOKEN_COUNT 43 #define EXTERNAL_TOKEN_COUNT 0 #define FIELD_COUNT 17 #define MAX_ALIAS_SEQUENCE_LENGTH 12 -#define PRODUCTION_ID_COUNT 34 +#define PRODUCTION_ID_COUNT 35 enum { aux_sym_text_token1 = 1, @@ -40,65 +40,67 @@ enum { anon_sym_ = 21, sym_pipeline_stub = 22, anon_sym_DOT = 23, - anon_sym_DOLLAR = 24, - sym_identifier = 25, - sym_int_literal = 26, - sym_float_literal = 27, - sym_imaginary_literal = 28, - sym_rune_literal = 29, - sym_true = 30, - sym_false = 31, - sym_nil = 32, - sym_raw_string_literal = 33, - anon_sym_DQUOTE = 34, - aux_sym_interpreted_string_literal_token1 = 35, - sym_escape_sequence = 36, - sym_comment = 37, - anon_sym_LBRACE_LBRACE = 38, - anon_sym_LBRACE_LBRACE_DASH = 39, - anon_sym_RBRACE_RBRACE = 40, - anon_sym_DASH_RBRACE_RBRACE = 41, - sym_template = 42, - sym__block = 43, - sym_text = 44, - sym__action = 45, - sym__comment_action = 46, - sym__pipeline_action = 47, - sym_if_action = 48, - sym__else_if_clause = 49, - sym__else_clause = 50, - sym__if_actions_end = 51, - sym_range_variable_definition = 52, - sym_range_action = 53, - sym_template_action = 54, - sym_define_action = 55, - sym_block_action = 56, - sym_with_action = 57, - sym__pipeline = 58, - sym_variable_definition = 59, - sym_assignment = 60, - sym_chained_pipeline = 61, - sym_parenthesized_pipeline = 62, - sym_method_call = 63, - sym_function_call = 64, - sym_argument_list = 65, - sym__expression = 66, - sym_selector_expression = 67, - sym__field_identifier = 68, - sym_field = 69, - sym_variable = 70, - sym__literal = 71, - sym__boolean_literal = 72, - sym_dot = 73, - sym__string_literal = 74, - sym_interpreted_string_literal = 75, - sym__left_delimiter = 76, - sym__right_delimiter = 77, - aux_sym_template_repeat1 = 78, - aux_sym_if_action_repeat1 = 79, - aux_sym_argument_list_repeat1 = 80, - aux_sym_interpreted_string_literal_repeat1 = 81, - alias_sym_field_identifier = 82, + anon_sym_DOT2 = 24, + anon_sym_DOLLAR = 25, + sym_identifier = 26, + sym_int_literal = 27, + sym_float_literal = 28, + sym_imaginary_literal = 29, + sym_rune_literal = 30, + sym_true = 31, + sym_false = 32, + sym_nil = 33, + sym_raw_string_literal = 34, + anon_sym_DQUOTE = 35, + aux_sym_interpreted_string_literal_token1 = 36, + sym_escape_sequence = 37, + sym_comment = 38, + anon_sym_LBRACE_LBRACE = 39, + anon_sym_LBRACE_LBRACE_DASH = 40, + anon_sym_RBRACE_RBRACE = 41, + anon_sym_DASH_RBRACE_RBRACE = 42, + sym_template = 43, + sym__block = 44, + sym_text = 45, + sym__action = 46, + sym__comment_action = 47, + sym__pipeline_action = 48, + sym_if_action = 49, + sym__else_if_clause = 50, + sym__else_clause = 51, + sym__if_actions_end = 52, + sym_range_variable_definition = 53, + sym_range_action = 54, + sym_template_action = 55, + sym_define_action = 56, + sym_block_action = 57, + sym_with_action = 58, + sym__pipeline = 59, + sym_variable_definition = 60, + sym_assignment = 61, + sym_chained_pipeline = 62, + sym_parenthesized_pipeline = 63, + sym_method_call = 64, + sym_function_call = 65, + sym_argument_list = 66, + sym__expression = 67, + sym_selector_expression = 68, + sym_unfished_selector_expression = 69, + sym__field_identifier = 70, + sym_field = 71, + sym_variable = 72, + sym__literal = 73, + sym__boolean_literal = 74, + sym_dot = 75, + sym__string_literal = 76, + sym_interpreted_string_literal = 77, + sym__left_delimiter = 78, + sym__right_delimiter = 79, + aux_sym_template_repeat1 = 80, + aux_sym_if_action_repeat1 = 81, + aux_sym_argument_list_repeat1 = 82, + aux_sym_interpreted_string_literal_repeat1 = 83, + alias_sym_field_identifier = 84, }; static const char * const ts_symbol_names[] = { @@ -126,6 +128,7 @@ static const char * const ts_symbol_names[] = { [anon_sym_] = " ", [sym_pipeline_stub] = "pipeline_stub", [anon_sym_DOT] = ".", + [anon_sym_DOT2] = ".", [anon_sym_DOLLAR] = "$", [sym_identifier] = "identifier", [sym_int_literal] = "int_literal", @@ -170,6 +173,7 @@ static const char * const ts_symbol_names[] = { [sym_argument_list] = "argument_list", [sym__expression] = "_expression", [sym_selector_expression] = "selector_expression", + [sym_unfished_selector_expression] = "unfished_selector_expression", [sym__field_identifier] = "_field_identifier", [sym_field] = "field", [sym_variable] = "variable", @@ -212,6 +216,7 @@ static const TSSymbol ts_symbol_map[] = { [anon_sym_] = anon_sym_, [sym_pipeline_stub] = sym_pipeline_stub, [anon_sym_DOT] = anon_sym_DOT, + [anon_sym_DOT2] = anon_sym_DOT, [anon_sym_DOLLAR] = anon_sym_DOLLAR, [sym_identifier] = sym_identifier, [sym_int_literal] = sym_int_literal, @@ -256,6 +261,7 @@ static const TSSymbol ts_symbol_map[] = { [sym_argument_list] = sym_argument_list, [sym__expression] = sym__expression, [sym_selector_expression] = sym_selector_expression, + [sym_unfished_selector_expression] = sym_unfished_selector_expression, [sym__field_identifier] = sym__field_identifier, [sym_field] = sym_field, [sym_variable] = sym_variable, @@ -370,6 +376,10 @@ static const TSSymbolMetadata ts_symbol_metadata[] = { .visible = true, .named = false, }, + [anon_sym_DOT2] = { + .visible = true, + .named = false, + }, [anon_sym_DOLLAR] = { .visible = true, .named = false, @@ -546,6 +556,10 @@ static const TSSymbolMetadata ts_symbol_metadata[] = { .visible = true, .named = true, }, + [sym_unfished_selector_expression] = { + .visible = true, + .named = true, + }, [sym__field_identifier] = { .visible = false, .named = true, @@ -653,35 +667,36 @@ static const TSFieldMapSlice ts_field_map_slices[PRODUCTION_ID_COUNT] = { [1] = {.index = 0, .length = 1}, [2] = {.index = 1, .length = 1}, [3] = {.index = 2, .length = 2}, - [4] = {.index = 4, .length = 2}, - [5] = {.index = 6, .length = 1}, - [7] = {.index = 7, .length = 2}, - [8] = {.index = 9, .length = 2}, - [9] = {.index = 11, .length = 2}, - [10] = {.index = 13, .length = 1}, - [11] = {.index = 14, .length = 2}, - [12] = {.index = 16, .length = 2}, - [13] = {.index = 18, .length = 2}, - [14] = {.index = 20, .length = 3}, - [15] = {.index = 23, .length = 4}, - [16] = {.index = 27, .length = 3}, - [17] = {.index = 30, .length = 4}, - [18] = {.index = 34, .length = 4}, - [19] = {.index = 38, .length = 1}, - [20] = {.index = 39, .length = 3}, - [21] = {.index = 42, .length = 1}, - [22] = {.index = 43, .length = 5}, - [23] = {.index = 48, .length = 1}, - [24] = {.index = 49, .length = 2}, - [25] = {.index = 51, .length = 2}, - [26] = {.index = 53, .length = 2}, - [27] = {.index = 55, .length = 3}, - [28] = {.index = 58, .length = 1}, - [29] = {.index = 59, .length = 2}, - [30] = {.index = 61, .length = 2}, - [31] = {.index = 63, .length = 2}, - [32] = {.index = 65, .length = 3}, - [33] = {.index = 68, .length = 3}, + [4] = {.index = 4, .length = 1}, + [5] = {.index = 5, .length = 2}, + [6] = {.index = 7, .length = 1}, + [8] = {.index = 8, .length = 2}, + [9] = {.index = 10, .length = 2}, + [10] = {.index = 12, .length = 2}, + [11] = {.index = 14, .length = 1}, + [12] = {.index = 15, .length = 2}, + [13] = {.index = 17, .length = 2}, + [14] = {.index = 19, .length = 2}, + [15] = {.index = 21, .length = 3}, + [16] = {.index = 24, .length = 4}, + [17] = {.index = 28, .length = 3}, + [18] = {.index = 31, .length = 4}, + [19] = {.index = 35, .length = 4}, + [20] = {.index = 39, .length = 1}, + [21] = {.index = 40, .length = 3}, + [22] = {.index = 43, .length = 1}, + [23] = {.index = 44, .length = 5}, + [24] = {.index = 49, .length = 1}, + [25] = {.index = 50, .length = 2}, + [26] = {.index = 52, .length = 2}, + [27] = {.index = 54, .length = 2}, + [28] = {.index = 56, .length = 3}, + [29] = {.index = 59, .length = 1}, + [30] = {.index = 60, .length = 2}, + [31] = {.index = 62, .length = 2}, + [32] = {.index = 64, .length = 2}, + [33] = {.index = 66, .length = 3}, + [34] = {.index = 69, .length = 3}, }; static const TSFieldMapEntry ts_field_map_entries[] = { @@ -693,98 +708,100 @@ static const TSFieldMapEntry ts_field_map_entries[] = { {field_arguments, 1}, {field_function, 0}, [4] = + {field_operand, 0}, + [5] = {field_arguments, 1}, {field_method, 0}, - [6] = - {field_name, 2}, [7] = + {field_name, 2}, + [8] = {field_field, 2}, {field_operand, 0}, - [9] = + [10] = {field_value, 2}, {field_variable, 0}, - [11] = + [12] = {field_condition, 0, .inherited = true}, {field_option, 0, .inherited = true}, - [13] = - {field_condition, 2}, [14] = + {field_condition, 2}, + [15] = {field_argument, 3}, {field_name, 2}, - [16] = + [17] = {field_alternative, 4, .inherited = true}, {field_condition, 2}, - [18] = + [19] = {field_condition, 2}, {field_consequence, 4}, - [20] = + [21] = {field_condition, 2}, {field_condition, 4, .inherited = true}, {field_option, 4, .inherited = true}, - [23] = + [24] = {field_condition, 0, .inherited = true}, {field_condition, 1, .inherited = true}, {field_option, 0, .inherited = true}, {field_option, 1, .inherited = true}, - [27] = + [28] = {field_alternative, 5, .inherited = true}, {field_condition, 2}, {field_consequence, 4}, - [30] = + [31] = {field_condition, 2}, {field_condition, 5, .inherited = true}, {field_consequence, 4}, {field_option, 5, .inherited = true}, - [34] = + [35] = {field_alternative, 5, .inherited = true}, {field_condition, 2}, {field_condition, 4, .inherited = true}, {field_option, 4, .inherited = true}, - [38] = - {field_range, 2}, [39] = + {field_range, 2}, + [40] = {field_element, 2}, {field_index, 0}, {field_range, 4}, - [42] = - {field_alternative, 3}, [43] = + {field_alternative, 3}, + [44] = {field_alternative, 6, .inherited = true}, {field_condition, 2}, {field_condition, 5, .inherited = true}, {field_consequence, 4}, {field_option, 5, .inherited = true}, - [48] = - {field_body, 4}, [49] = {field_body, 4}, + [50] = + {field_body, 4}, {field_range, 2}, - [51] = + [52] = {field_body, 4}, {field_name, 2}, - [53] = + [54] = {field_condition, 2}, {field_option, 4}, - [55] = + [56] = {field_argument, 3}, {field_body, 5}, {field_name, 2}, - [58] = - {field_alternative, 7}, [59] = {field_alternative, 7}, + [60] = + {field_alternative, 7}, {field_range, 2}, - [61] = + [62] = {field_alternative, 7}, {field_condition, 2}, - [63] = + [64] = {field_alternative, 8}, {field_body, 4}, - [65] = + [66] = {field_alternative, 8}, {field_body, 4}, {field_range, 2}, - [68] = + [69] = {field_alternative, 8}, {field_condition, 2}, {field_consequence, 4}, @@ -792,7 +809,7 @@ static const TSFieldMapEntry ts_field_map_entries[] = { static const TSSymbol ts_alias_sequences[PRODUCTION_ID_COUNT][MAX_ALIAS_SEQUENCE_LENGTH] = { [0] = {0}, - [6] = { + [7] = { [0] = alias_sym_field_identifier, }, }; @@ -808,156 +825,156 @@ static const TSStateId ts_primary_state_ids[STATE_COUNT] = { [3] = 2, [4] = 4, [5] = 5, - [6] = 6, + [6] = 5, [7] = 7, [8] = 8, [9] = 9, - [10] = 9, - [11] = 8, - [12] = 4, - [13] = 5, - [14] = 6, - [15] = 7, + [10] = 8, + [11] = 4, + [12] = 12, + [13] = 7, + [14] = 12, + [15] = 9, [16] = 16, [17] = 17, [18] = 18, [19] = 19, - [20] = 16, - [21] = 21, + [20] = 17, + [21] = 18, [22] = 22, - [23] = 22, + [23] = 16, [24] = 24, - [25] = 24, - [26] = 26, - [27] = 21, + [25] = 25, + [26] = 22, + [27] = 27, [28] = 28, - [29] = 28, + [29] = 25, [30] = 30, [31] = 31, - [32] = 32, - [33] = 26, - [34] = 34, - [35] = 18, + [32] = 27, + [33] = 33, + [34] = 24, + [35] = 35, [36] = 36, - [37] = 31, - [38] = 32, + [37] = 37, + [38] = 37, [39] = 39, - [40] = 36, + [40] = 40, [41] = 19, - [42] = 42, - [43] = 17, - [44] = 44, - [45] = 45, - [46] = 45, - [47] = 39, - [48] = 30, - [49] = 42, - [50] = 34, - [51] = 44, + [42] = 36, + [43] = 39, + [44] = 30, + [45] = 28, + [46] = 31, + [47] = 47, + [48] = 40, + [49] = 33, + [50] = 47, + [51] = 35, [52] = 52, - [53] = 52, - [54] = 54, + [53] = 53, + [54] = 52, [55] = 55, [56] = 56, - [57] = 55, - [58] = 58, - [59] = 54, - [60] = 58, - [61] = 56, - [62] = 62, - [63] = 63, - [64] = 62, - [65] = 63, - [66] = 66, - [67] = 66, + [57] = 53, + [58] = 56, + [59] = 59, + [60] = 60, + [61] = 55, + [62] = 60, + [63] = 59, + [64] = 64, + [65] = 65, + [66] = 64, + [67] = 65, [68] = 68, - [69] = 69, + [69] = 68, [70] = 70, - [71] = 68, + [71] = 71, [72] = 72, [73] = 73, [74] = 74, - [75] = 69, + [75] = 71, [76] = 76, - [77] = 72, + [77] = 77, [78] = 78, - [79] = 78, - [80] = 70, - [81] = 76, - [82] = 82, - [83] = 83, + [79] = 76, + [80] = 73, + [81] = 72, + [82] = 78, + [83] = 77, [84] = 84, - [85] = 83, - [86] = 84, - [87] = 87, - [88] = 88, + [85] = 85, + [86] = 86, + [87] = 85, + [88] = 86, [89] = 89, [90] = 90, [91] = 91, [92] = 92, [93] = 93, - [94] = 90, + [94] = 94, [95] = 95, [96] = 96, [97] = 97, - [98] = 92, + [98] = 98, [99] = 99, - [100] = 89, - [101] = 101, + [100] = 100, + [101] = 91, [102] = 102, - [103] = 88, - [104] = 104, + [103] = 89, + [104] = 95, [105] = 105, - [106] = 93, + [106] = 106, [107] = 107, [108] = 108, [109] = 109, [110] = 110, - [111] = 109, - [112] = 110, - [113] = 107, - [114] = 114, - [115] = 115, + [111] = 111, + [112] = 112, + [113] = 111, + [114] = 112, + [115] = 109, [116] = 116, - [117] = 117, + [117] = 102, [118] = 118, - [119] = 99, + [119] = 96, [120] = 120, [121] = 121, - [122] = 105, - [123] = 123, - [124] = 95, - [125] = 104, - [126] = 97, - [127] = 127, - [128] = 101, - [129] = 127, - [130] = 96, - [131] = 123, - [132] = 102, - [133] = 114, - [134] = 115, - [135] = 117, - [136] = 118, - [137] = 137, - [138] = 120, - [139] = 139, - [140] = 91, - [141] = 108, - [142] = 142, + [122] = 122, + [123] = 97, + [124] = 107, + [125] = 125, + [126] = 125, + [127] = 106, + [128] = 99, + [129] = 122, + [130] = 110, + [131] = 131, + [132] = 98, + [133] = 100, + [134] = 94, + [135] = 105, + [136] = 108, + [137] = 93, + [138] = 116, + [139] = 120, + [140] = 121, + [141] = 141, + [142] = 141, [143] = 143, [144] = 144, [145] = 145, - [146] = 144, - [147] = 143, - [148] = 142, - [149] = 149, - [150] = 150, - [151] = 150, + [146] = 146, + [147] = 147, + [148] = 146, + [149] = 144, + [150] = 147, + [151] = 145, [152] = 152, [153] = 153, [154] = 154, - [155] = 155, + [155] = 154, [156] = 156, [157] = 157, [158] = 158, @@ -984,8 +1001,8 @@ static const TSStateId ts_primary_state_ids[STATE_COUNT] = { [179] = 179, [180] = 180, [181] = 181, - [182] = 162, - [183] = 183, + [182] = 182, + [183] = 161, [184] = 184, [185] = 185, [186] = 186, @@ -996,112 +1013,112 @@ static const TSStateId ts_primary_state_ids[STATE_COUNT] = { [191] = 191, [192] = 192, [193] = 193, - [194] = 185, - [195] = 176, + [194] = 194, + [195] = 195, [196] = 196, [197] = 197, - [198] = 198, - [199] = 178, - [200] = 200, - [201] = 201, + [198] = 164, + [199] = 182, + [200] = 191, + [201] = 178, [202] = 202, - [203] = 193, - [204] = 201, - [205] = 198, - [206] = 197, - [207] = 184, - [208] = 167, - [209] = 192, - [210] = 155, - [211] = 191, - [212] = 157, - [213] = 190, - [214] = 189, - [215] = 188, - [216] = 187, - [217] = 168, - [218] = 183, - [219] = 179, - [220] = 175, - [221] = 170, - [222] = 160, - [223] = 156, - [224] = 154, - [225] = 173, - [226] = 153, - [227] = 174, - [228] = 169, - [229] = 186, - [230] = 172, + [203] = 181, + [204] = 169, + [205] = 172, + [206] = 206, + [207] = 187, + [208] = 189, + [209] = 185, + [210] = 197, + [211] = 206, + [212] = 170, + [213] = 159, + [214] = 175, + [215] = 174, + [216] = 162, + [217] = 184, + [218] = 157, + [219] = 165, + [220] = 160, + [221] = 166, + [222] = 176, + [223] = 163, + [224] = 167, + [225] = 196, + [226] = 173, + [227] = 168, + [228] = 156, + [229] = 171, + [230] = 179, [231] = 180, - [232] = 177, - [233] = 166, - [234] = 165, - [235] = 181, - [236] = 171, - [237] = 158, - [238] = 159, - [239] = 152, - [240] = 164, - [241] = 163, - [242] = 161, + [232] = 194, + [233] = 195, + [234] = 188, + [235] = 186, + [236] = 193, + [237] = 192, + [238] = 190, + [239] = 177, + [240] = 158, + [241] = 241, + [242] = 242, [243] = 243, [244] = 244, - [245] = 145, + [245] = 245, [246] = 246, - [247] = 247, - [248] = 248, - [249] = 149, + [247] = 241, + [248] = 245, + [249] = 249, [250] = 250, - [251] = 251, - [252] = 200, - [253] = 253, + [251] = 244, + [252] = 252, + [253] = 153, [254] = 254, - [255] = 255, - [256] = 255, + [255] = 152, + [256] = 256, [257] = 257, [258] = 258, - [259] = 254, - [260] = 260, - [261] = 244, + [259] = 242, + [260] = 243, + [261] = 261, [262] = 262, - [263] = 246, + [263] = 263, [264] = 264, - [265] = 264, - [266] = 243, - [267] = 262, - [268] = 248, - [269] = 253, - [270] = 250, - [271] = 251, - [272] = 258, + [265] = 265, + [266] = 265, + [267] = 267, + [268] = 268, + [269] = 264, + [270] = 270, + [271] = 271, + [272] = 272, [273] = 273, - [274] = 274, - [275] = 275, - [276] = 275, - [277] = 277, - [278] = 278, + [274] = 270, + [275] = 268, + [276] = 256, + [277] = 262, + [278] = 254, [279] = 279, - [280] = 280, + [280] = 267, [281] = 281, - [282] = 279, - [283] = 283, - [284] = 274, - [285] = 285, - [286] = 281, + [282] = 252, + [283] = 249, + [284] = 273, + [285] = 250, + [286] = 263, [287] = 287, - [288] = 278, - [289] = 287, - [290] = 280, - [291] = 285, - [292] = 277, - [293] = 293, - [294] = 294, + [288] = 288, + [289] = 279, + [290] = 288, + [291] = 287, + [292] = 271, + [293] = 261, + [294] = 272, [295] = 295, [296] = 296, - [297] = 297, + [297] = 295, [298] = 298, - [299] = 298, + [299] = 299, [300] = 300, [301] = 301, [302] = 302, @@ -1110,73 +1127,77 @@ static const TSStateId ts_primary_state_ids[STATE_COUNT] = { [305] = 305, [306] = 306, [307] = 307, - [308] = 306, - [309] = 302, + [308] = 307, + [309] = 309, [310] = 310, - [311] = 295, - [312] = 305, + [311] = 305, + [312] = 303, [313] = 313, [314] = 314, [315] = 315, [316] = 316, [317] = 317, - [318] = 318, + [318] = 299, [319] = 319, - [320] = 320, + [320] = 317, [321] = 321, [322] = 322, [323] = 323, - [324] = 324, + [324] = 316, [325] = 325, - [326] = 318, - [327] = 301, - [328] = 294, + [326] = 326, + [327] = 327, + [328] = 328, [329] = 329, - [330] = 304, - [331] = 331, + [330] = 330, + [331] = 302, [332] = 332, [333] = 333, - [334] = 334, - [335] = 325, - [336] = 293, - [337] = 333, - [338] = 324, - [339] = 303, - [340] = 300, - [341] = 310, - [342] = 297, - [343] = 314, - [344] = 316, - [345] = 321, - [346] = 346, - [347] = 296, - [348] = 348, + [334] = 310, + [335] = 326, + [336] = 323, + [337] = 300, + [338] = 298, + [339] = 325, + [340] = 304, + [341] = 341, + [342] = 342, + [343] = 313, + [344] = 306, + [345] = 315, + [346] = 309, + [347] = 314, + [348] = 322, [349] = 349, - [350] = 331, + [350] = 319, [351] = 329, [352] = 352, - [353] = 307, - [354] = 320, - [355] = 322, - [356] = 334, - [357] = 319, - [358] = 332, - [359] = 352, - [360] = 315, - [361] = 349, - [362] = 346, - [363] = 313, - [364] = 348, - [365] = 317, - [366] = 366, - [367] = 367, - [368] = 368, - [369] = 366, + [353] = 353, + [354] = 330, + [355] = 355, + [356] = 332, + [357] = 328, + [358] = 358, + [359] = 341, + [360] = 301, + [361] = 342, + [362] = 362, + [363] = 355, + [364] = 349, + [365] = 352, + [366] = 353, + [367] = 358, + [368] = 362, + [369] = 321, [370] = 370, [371] = 371, - [372] = 371, - [373] = 373, + [372] = 372, + [373] = 370, [374] = 374, + [375] = 375, + [376] = 375, + [377] = 377, + [378] = 378, }; static inline bool sym_identifier_character_set_1(int32_t c) { @@ -14562,459 +14583,508 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { eof = lexer->eof(lexer); switch (state) { case 0: - if (eof) ADVANCE(73); - if (lookahead == '"') ADVANCE(175); - if (lookahead == '$') ADVANCE(110); - if (lookahead == '\'') ADVANCE(21); - if (lookahead == '(') ADVANCE(104); - if (lookahead == ')') ADVANCE(105); - if (lookahead == ',') ADVANCE(95); - if (lookahead == '-') ADVANCE(32); - if (lookahead == '.') ADVANCE(109); - if (lookahead == '/') ADVANCE(14); - if (lookahead == '0') ADVANCE(160); - if (lookahead == ':') ADVANCE(19); - if (lookahead == '=') ADVANCE(102); - if (lookahead == '[') ADVANCE(28); - if (lookahead == '\\') ADVANCE(20); - if (sym_identifier_character_set_1(lookahead)) ADVANCE(159); - if (lookahead == '`') ADVANCE(25); - if (lookahead == 'b') ADVANCE(136); - if (lookahead == 'd') ADVANCE(124); - if (lookahead == 'e') ADVANCE(112); - if (lookahead == 'f') ADVANCE(157); - if (lookahead == 'i') ADVANCE(126); - if (lookahead == 'n') ADVANCE(133); - if (lookahead == 'p') ADVANCE(130); - if (lookahead == 'r') ADVANCE(156); - if (lookahead == 't') ADVANCE(111); - if (lookahead == 'w') ADVANCE(131); - if (lookahead == '{') ADVANCE(29); - if (lookahead == '|') ADVANCE(103); - if (lookahead == '}') ADVANCE(30); + if (eof) ADVANCE(77); + if (lookahead == '"') ADVANCE(180); + if (lookahead == '$') ADVANCE(115); + if (lookahead == '\'') ADVANCE(25); + if (lookahead == '(') ADVANCE(108); + if (lookahead == ')') ADVANCE(109); + if (lookahead == ',') ADVANCE(99); + if (lookahead == '-') ADVANCE(36); + if (lookahead == '.') ADVANCE(113); + if (lookahead == '/') ADVANCE(18); + if (lookahead == '0') ADVANCE(165); + if (lookahead == ':') ADVANCE(23); + if (lookahead == '=') ADVANCE(106); + if (lookahead == '[') ADVANCE(32); + if (lookahead == '\\') ADVANCE(24); + if (sym_identifier_character_set_1(lookahead)) ADVANCE(164); + if (lookahead == '`') ADVANCE(29); + if (lookahead == 'b') ADVANCE(141); + if (lookahead == 'd') ADVANCE(129); + if (lookahead == 'e') ADVANCE(117); + if (lookahead == 'f') ADVANCE(162); + if (lookahead == 'i') ADVANCE(131); + if (lookahead == 'n') ADVANCE(138); + if (lookahead == 'p') ADVANCE(135); + if (lookahead == 'r') ADVANCE(161); + if (lookahead == 't') ADVANCE(116); + if (lookahead == 'w') ADVANCE(136); + if (lookahead == '{') ADVANCE(33); + if (lookahead == '|') ADVANCE(107); + if (lookahead == '}') ADVANCE(34); if (lookahead == '\t' || lookahead == '\n' || lookahead == '\r' || - lookahead == ' ') SKIP(72) - if (('1' <= lookahead && lookahead <= '9')) ADVANCE(162); + lookahead == ' ') SKIP(76) + if (('1' <= lookahead && lookahead <= '9')) ADVANCE(167); END_STATE(); case 1: - if (lookahead == '\n') SKIP(9) - if (lookahead == '"') ADVANCE(175); - if (lookahead == '\\') ADVANCE(20); + if (lookahead == '\n') SKIP(12) + if (lookahead == '"') ADVANCE(180); + if (lookahead == '\\') ADVANCE(24); if (lookahead == '\t' || lookahead == '\r' || - lookahead == ' ') ADVANCE(176); - if (lookahead != 0) ADVANCE(177); + lookahead == ' ') ADVANCE(181); + if (lookahead != 0) ADVANCE(182); END_STATE(); case 2: - if (lookahead == ' ') ADVANCE(106); - if (lookahead == '"') ADVANCE(175); - if (lookahead == '$') ADVANCE(110); - if (lookahead == '\'') ADVANCE(21); - if (lookahead == '(') ADVANCE(104); - if (lookahead == ')') ADVANCE(105); - if (lookahead == '-') ADVANCE(32); - if (lookahead == '.') ADVANCE(109); - if (lookahead == '0') ADVANCE(160); - if (lookahead == ':') ADVANCE(19); - if (lookahead == '=') ADVANCE(102); - if (sym_identifier_character_set_2(lookahead)) ADVANCE(159); - if (lookahead == '`') ADVANCE(25); - if (lookahead == 'f') ADVANCE(157); - if (lookahead == 'n') ADVANCE(133); - if (lookahead == 't') ADVANCE(149); - if (lookahead == '|') ADVANCE(103); - if (lookahead == '}') ADVANCE(30); + if (lookahead == ' ') ADVANCE(110); + if (lookahead == '"') ADVANCE(180); + if (lookahead == '$') ADVANCE(115); + if (lookahead == '\'') ADVANCE(25); + if (lookahead == '(') ADVANCE(108); + if (lookahead == ')') ADVANCE(109); + if (lookahead == '-') ADVANCE(36); + if (lookahead == '.') ADVANCE(113); + if (lookahead == '0') ADVANCE(165); + if (sym_identifier_character_set_2(lookahead)) ADVANCE(164); + if (lookahead == '`') ADVANCE(29); + if (lookahead == 'f') ADVANCE(162); + if (lookahead == 'n') ADVANCE(138); + if (lookahead == 't') ADVANCE(154); + if (lookahead == '|') ADVANCE(107); + if (lookahead == '}') ADVANCE(34); if (lookahead == '\t' || lookahead == '\n' || - lookahead == '\r') SKIP(2) - if (('1' <= lookahead && lookahead <= '9')) ADVANCE(162); + lookahead == '\r') SKIP(3) + if (('1' <= lookahead && lookahead <= '9')) ADVANCE(167); END_STATE(); case 3: - if (lookahead == ' ') ADVANCE(106); - if (lookahead == ')') ADVANCE(105); - if (lookahead == '-') ADVANCE(32); - if (lookahead == '.') ADVANCE(108); - if (lookahead == ':') ADVANCE(19); - if (lookahead == '=') ADVANCE(102); - if (sym_identifier_character_set_3(lookahead)) ADVANCE(159); - if (lookahead == '|') ADVANCE(103); - if (lookahead == '}') ADVANCE(30); + if (lookahead == ' ') ADVANCE(110); + if (lookahead == '"') ADVANCE(180); + if (lookahead == '$') ADVANCE(115); + if (lookahead == '\'') ADVANCE(25); + if (lookahead == '(') ADVANCE(108); + if (lookahead == ')') ADVANCE(109); + if (lookahead == '-') ADVANCE(36); + if (lookahead == '.') ADVANCE(114); + if (lookahead == '0') ADVANCE(165); + if (sym_identifier_character_set_2(lookahead)) ADVANCE(164); + if (lookahead == '`') ADVANCE(29); + if (lookahead == 'f') ADVANCE(162); + if (lookahead == 'n') ADVANCE(138); + if (lookahead == 't') ADVANCE(154); + if (lookahead == '|') ADVANCE(107); + if (lookahead == '}') ADVANCE(34); if (lookahead == '\t' || lookahead == '\n' || lookahead == '\r') SKIP(3) + if (('1' <= lookahead && lookahead <= '9')) ADVANCE(167); END_STATE(); case 4: - if (lookahead == '"') ADVANCE(175); - if (lookahead == '$') ADVANCE(110); - if (lookahead == '\'') ADVANCE(21); - if (lookahead == '(') ADVANCE(104); - if (lookahead == ')') ADVANCE(105); - if (lookahead == '-') ADVANCE(32); - if (lookahead == '.') ADVANCE(109); - if (lookahead == '0') ADVANCE(160); - if (sym_identifier_character_set_2(lookahead)) ADVANCE(159); - if (lookahead == '`') ADVANCE(25); - if (lookahead == 'f') ADVANCE(157); - if (lookahead == 'n') ADVANCE(133); - if (lookahead == 't') ADVANCE(149); - if (lookahead == '|') ADVANCE(103); - if (lookahead == '}') ADVANCE(30); + if (lookahead == ' ') ADVANCE(110); + if (lookahead == ')') ADVANCE(109); + if (lookahead == '-') ADVANCE(36); + if (lookahead == '.') ADVANCE(112); + if (lookahead == ':') ADVANCE(23); + if (lookahead == '=') ADVANCE(106); + if (sym_identifier_character_set_3(lookahead)) ADVANCE(164); + if (lookahead == '|') ADVANCE(107); + if (lookahead == '}') ADVANCE(34); if (lookahead == '\t' || lookahead == '\n' || - lookahead == '\r' || - lookahead == ' ') SKIP(4) - if (('1' <= lookahead && lookahead <= '9')) ADVANCE(162); + lookahead == '\r') SKIP(5) END_STATE(); case 5: - if (lookahead == '"') ADVANCE(175); - if (lookahead == '$') ADVANCE(110); - if (lookahead == '\'') ADVANCE(21); - if (lookahead == '(') ADVANCE(104); - if (lookahead == '.') ADVANCE(109); - if (lookahead == '/') ADVANCE(14); - if (lookahead == '0') ADVANCE(160); - if (sym_identifier_character_set_4(lookahead)) ADVANCE(159); - if (lookahead == '`') ADVANCE(25); - if (lookahead == 'b') ADVANCE(136); - if (lookahead == 'd') ADVANCE(124); - if (lookahead == 'e') ADVANCE(112); - if (lookahead == 'f') ADVANCE(157); - if (lookahead == 'i') ADVANCE(126); - if (lookahead == 'n') ADVANCE(133); - if (lookahead == 'r') ADVANCE(156); - if (lookahead == 't') ADVANCE(111); - if (lookahead == 'w') ADVANCE(131); + if (lookahead == ' ') ADVANCE(110); + if (lookahead == ')') ADVANCE(109); + if (lookahead == '-') ADVANCE(36); + if (lookahead == ':') ADVANCE(23); + if (lookahead == '=') ADVANCE(106); + if (sym_identifier_character_set_3(lookahead)) ADVANCE(164); + if (lookahead == '|') ADVANCE(107); + if (lookahead == '}') ADVANCE(34); if (lookahead == '\t' || lookahead == '\n' || - lookahead == '\r' || - lookahead == ' ') SKIP(5) - if (('1' <= lookahead && lookahead <= '9')) ADVANCE(162); + lookahead == '\r') SKIP(5) END_STATE(); case 6: - if (lookahead == '"') ADVANCE(175); - if (lookahead == '$') ADVANCE(110); - if (lookahead == '\'') ADVANCE(21); - if (lookahead == '(') ADVANCE(104); - if (lookahead == '.') ADVANCE(109); - if (lookahead == '/') ADVANCE(14); - if (lookahead == '0') ADVANCE(160); - if (sym_identifier_character_set_4(lookahead)) ADVANCE(159); - if (lookahead == '`') ADVANCE(25); - if (lookahead == 'b') ADVANCE(136); - if (lookahead == 'd') ADVANCE(124); - if (lookahead == 'e') ADVANCE(142); - if (lookahead == 'f') ADVANCE(157); - if (lookahead == 'i') ADVANCE(126); - if (lookahead == 'n') ADVANCE(133); - if (lookahead == 'r') ADVANCE(156); - if (lookahead == 't') ADVANCE(111); - if (lookahead == 'w') ADVANCE(131); + if (lookahead == '"') ADVANCE(180); + if (lookahead == '$') ADVANCE(115); + if (lookahead == '\'') ADVANCE(25); + if (lookahead == '(') ADVANCE(108); + if (lookahead == ')') ADVANCE(109); + if (lookahead == '-') ADVANCE(36); + if (lookahead == '.') ADVANCE(113); + if (lookahead == '0') ADVANCE(165); + if (sym_identifier_character_set_2(lookahead)) ADVANCE(164); + if (lookahead == '`') ADVANCE(29); + if (lookahead == 'f') ADVANCE(162); + if (lookahead == 'n') ADVANCE(138); + if (lookahead == 't') ADVANCE(154); + if (lookahead == '|') ADVANCE(107); + if (lookahead == '}') ADVANCE(34); if (lookahead == '\t' || lookahead == '\n' || lookahead == '\r' || - lookahead == ' ') SKIP(6) - if (('1' <= lookahead && lookahead <= '9')) ADVANCE(162); + lookahead == ' ') SKIP(7) + if (('1' <= lookahead && lookahead <= '9')) ADVANCE(167); END_STATE(); case 7: - if (lookahead == '"') ADVANCE(175); - if (lookahead == '$') ADVANCE(110); - if (lookahead == '\'') ADVANCE(21); - if (lookahead == '(') ADVANCE(104); - if (lookahead == '.') ADVANCE(109); - if (lookahead == '/') ADVANCE(14); - if (lookahead == '0') ADVANCE(160); - if (sym_identifier_character_set_4(lookahead)) ADVANCE(159); - if (lookahead == '`') ADVANCE(25); - if (lookahead == 'b') ADVANCE(136); - if (lookahead == 'd') ADVANCE(124); - if (lookahead == 'e') ADVANCE(113); - if (lookahead == 'f') ADVANCE(157); - if (lookahead == 'i') ADVANCE(126); - if (lookahead == 'n') ADVANCE(133); - if (lookahead == 'r') ADVANCE(156); - if (lookahead == 't') ADVANCE(111); - if (lookahead == 'w') ADVANCE(131); + if (lookahead == '"') ADVANCE(180); + if (lookahead == '$') ADVANCE(115); + if (lookahead == '\'') ADVANCE(25); + if (lookahead == '(') ADVANCE(108); + if (lookahead == ')') ADVANCE(109); + if (lookahead == '-') ADVANCE(36); + if (lookahead == '.') ADVANCE(114); + if (lookahead == '0') ADVANCE(165); + if (sym_identifier_character_set_2(lookahead)) ADVANCE(164); + if (lookahead == '`') ADVANCE(29); + if (lookahead == 'f') ADVANCE(162); + if (lookahead == 'n') ADVANCE(138); + if (lookahead == 't') ADVANCE(154); + if (lookahead == '|') ADVANCE(107); + if (lookahead == '}') ADVANCE(34); if (lookahead == '\t' || lookahead == '\n' || lookahead == '\r' || lookahead == ' ') SKIP(7) - if (('1' <= lookahead && lookahead <= '9')) ADVANCE(162); + if (('1' <= lookahead && lookahead <= '9')) ADVANCE(167); END_STATE(); case 8: - if (lookahead == '"') ADVANCE(175); - if (lookahead == '$') ADVANCE(110); - if (lookahead == '\'') ADVANCE(21); - if (lookahead == '(') ADVANCE(104); - if (lookahead == '.') ADVANCE(109); - if (lookahead == '/') ADVANCE(14); - if (lookahead == '0') ADVANCE(160); - if (sym_identifier_character_set_5(lookahead)) ADVANCE(159); - if (lookahead == '`') ADVANCE(25); - if (lookahead == 'b') ADVANCE(136); - if (lookahead == 'd') ADVANCE(124); - if (lookahead == 'f') ADVANCE(157); - if (lookahead == 'i') ADVANCE(126); - if (lookahead == 'n') ADVANCE(133); - if (lookahead == 'r') ADVANCE(156); - if (lookahead == 't') ADVANCE(111); - if (lookahead == 'w') ADVANCE(131); + if (lookahead == '"') ADVANCE(180); + if (lookahead == '$') ADVANCE(115); + if (lookahead == '\'') ADVANCE(25); + if (lookahead == '(') ADVANCE(108); + if (lookahead == '.') ADVANCE(114); + if (lookahead == '/') ADVANCE(18); + if (lookahead == '0') ADVANCE(165); + if (sym_identifier_character_set_4(lookahead)) ADVANCE(164); + if (lookahead == '`') ADVANCE(29); + if (lookahead == 'b') ADVANCE(141); + if (lookahead == 'd') ADVANCE(129); + if (lookahead == 'e') ADVANCE(117); + if (lookahead == 'f') ADVANCE(162); + if (lookahead == 'i') ADVANCE(131); + if (lookahead == 'n') ADVANCE(138); + if (lookahead == 'r') ADVANCE(161); + if (lookahead == 't') ADVANCE(116); + if (lookahead == 'w') ADVANCE(136); if (lookahead == '\t' || lookahead == '\n' || lookahead == '\r' || lookahead == ' ') SKIP(8) - if (('1' <= lookahead && lookahead <= '9')) ADVANCE(162); + if (('1' <= lookahead && lookahead <= '9')) ADVANCE(167); END_STATE(); case 9: - if (lookahead == '"') ADVANCE(175); + if (lookahead == '"') ADVANCE(180); + if (lookahead == '$') ADVANCE(115); + if (lookahead == '\'') ADVANCE(25); + if (lookahead == '(') ADVANCE(108); + if (lookahead == '.') ADVANCE(114); + if (lookahead == '/') ADVANCE(18); + if (lookahead == '0') ADVANCE(165); + if (sym_identifier_character_set_4(lookahead)) ADVANCE(164); + if (lookahead == '`') ADVANCE(29); + if (lookahead == 'b') ADVANCE(141); + if (lookahead == 'd') ADVANCE(129); + if (lookahead == 'e') ADVANCE(147); + if (lookahead == 'f') ADVANCE(162); + if (lookahead == 'i') ADVANCE(131); + if (lookahead == 'n') ADVANCE(138); + if (lookahead == 'r') ADVANCE(161); + if (lookahead == 't') ADVANCE(116); + if (lookahead == 'w') ADVANCE(136); if (lookahead == '\t' || lookahead == '\n' || lookahead == '\r' || lookahead == ' ') SKIP(9) + if (('1' <= lookahead && lookahead <= '9')) ADVANCE(167); END_STATE(); case 10: - if (lookahead == '"') ADVANCE(76); - if (lookahead == '{') ADVANCE(69); - if (lookahead != 0 && - lookahead != '\n') ADVANCE(10); + if (lookahead == '"') ADVANCE(180); + if (lookahead == '$') ADVANCE(115); + if (lookahead == '\'') ADVANCE(25); + if (lookahead == '(') ADVANCE(108); + if (lookahead == '.') ADVANCE(114); + if (lookahead == '/') ADVANCE(18); + if (lookahead == '0') ADVANCE(165); + if (sym_identifier_character_set_4(lookahead)) ADVANCE(164); + if (lookahead == '`') ADVANCE(29); + if (lookahead == 'b') ADVANCE(141); + if (lookahead == 'd') ADVANCE(129); + if (lookahead == 'e') ADVANCE(118); + if (lookahead == 'f') ADVANCE(162); + if (lookahead == 'i') ADVANCE(131); + if (lookahead == 'n') ADVANCE(138); + if (lookahead == 'r') ADVANCE(161); + if (lookahead == 't') ADVANCE(116); + if (lookahead == 'w') ADVANCE(136); + if (lookahead == '\t' || + lookahead == '\n' || + lookahead == '\r' || + lookahead == ' ') SKIP(10) + if (('1' <= lookahead && lookahead <= '9')) ADVANCE(167); END_STATE(); case 11: - if (lookahead == '\'') ADVANCE(170); + if (lookahead == '"') ADVANCE(180); + if (lookahead == '$') ADVANCE(115); + if (lookahead == '\'') ADVANCE(25); + if (lookahead == '(') ADVANCE(108); + if (lookahead == '.') ADVANCE(114); + if (lookahead == '/') ADVANCE(18); + if (lookahead == '0') ADVANCE(165); + if (sym_identifier_character_set_5(lookahead)) ADVANCE(164); + if (lookahead == '`') ADVANCE(29); + if (lookahead == 'b') ADVANCE(141); + if (lookahead == 'd') ADVANCE(129); + if (lookahead == 'f') ADVANCE(162); + if (lookahead == 'i') ADVANCE(131); + if (lookahead == 'n') ADVANCE(138); + if (lookahead == 'r') ADVANCE(161); + if (lookahead == 't') ADVANCE(116); + if (lookahead == 'w') ADVANCE(136); + if (lookahead == '\t' || + lookahead == '\n' || + lookahead == '\r' || + lookahead == ' ') SKIP(11) + if (('1' <= lookahead && lookahead <= '9')) ADVANCE(167); END_STATE(); case 12: - if (lookahead == '\'') ADVANCE(78); - if (lookahead == '{') ADVANCE(70); - if (lookahead != 0 && - lookahead != '\n') ADVANCE(12); - END_STATE(); - case 13: - if (lookahead == ')') ADVANCE(105); - if (lookahead == ',') ADVANCE(95); - if (lookahead == '-') ADVANCE(32); - if (lookahead == '.') ADVANCE(108); - if (lookahead == ':') ADVANCE(19); - if (lookahead == '=') ADVANCE(102); - if (sym_identifier_character_set_3(lookahead)) ADVANCE(159); - if (lookahead == '|') ADVANCE(103); - if (lookahead == '}') ADVANCE(30); + if (lookahead == '"') ADVANCE(180); if (lookahead == '\t' || lookahead == '\n' || lookahead == '\r' || - lookahead == ' ') SKIP(13) + lookahead == ' ') SKIP(12) + END_STATE(); + case 13: + if (lookahead == '"') ADVANCE(80); + if (lookahead == '{') ADVANCE(73); + if (lookahead != 0 && + lookahead != '\n') ADVANCE(13); END_STATE(); case 14: - if (lookahead == '*') ADVANCE(16); - if (lookahead == '/') ADVANCE(182); + if (lookahead == '\'') ADVANCE(175); END_STATE(); case 15: - if (lookahead == '*') ADVANCE(15); - if (lookahead == '/') ADVANCE(181); - if (lookahead != 0) ADVANCE(16); + if (lookahead == '\'') ADVANCE(82); + if (lookahead == '{') ADVANCE(74); + if (lookahead != 0 && + lookahead != '\n') ADVANCE(15); END_STATE(); case 16: - if (lookahead == '*') ADVANCE(15); - if (lookahead != 0) ADVANCE(16); + if (lookahead == ')') ADVANCE(109); + if (lookahead == ',') ADVANCE(99); + if (lookahead == '-') ADVANCE(36); + if (lookahead == '.') ADVANCE(112); + if (lookahead == ':') ADVANCE(23); + if (lookahead == '=') ADVANCE(106); + if (sym_identifier_character_set_3(lookahead)) ADVANCE(164); + if (lookahead == '|') ADVANCE(107); + if (lookahead == '}') ADVANCE(34); + if (lookahead == '\t' || + lookahead == '\n' || + lookahead == '\r' || + lookahead == ' ') SKIP(17) END_STATE(); case 17: - if (lookahead == '.') ADVANCE(168); - if (lookahead == 'E' || - lookahead == 'e') ADVANCE(36); - if (lookahead == '_') ADVANCE(47); - if (lookahead == 'i') ADVANCE(169); - if (('0' <= lookahead && lookahead <= '9')) ADVANCE(17); + if (lookahead == ')') ADVANCE(109); + if (lookahead == ',') ADVANCE(99); + if (lookahead == '-') ADVANCE(36); + if (lookahead == ':') ADVANCE(23); + if (lookahead == '=') ADVANCE(106); + if (sym_identifier_character_set_3(lookahead)) ADVANCE(164); + if (lookahead == '|') ADVANCE(107); + if (lookahead == '}') ADVANCE(34); + if (lookahead == '\t' || + lookahead == '\n' || + lookahead == '\r' || + lookahead == ' ') SKIP(17) END_STATE(); case 18: - if (lookahead == '.') ADVANCE(52); - if (lookahead == '_') ADVANCE(51); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(163); + if (lookahead == '*') ADVANCE(20); + if (lookahead == '/') ADVANCE(187); END_STATE(); case 19: - if (lookahead == '=') ADVANCE(96); + if (lookahead == '*') ADVANCE(19); + if (lookahead == '/') ADVANCE(186); + if (lookahead != 0) ADVANCE(20); END_STATE(); case 20: - if (lookahead == 'U') ADVANCE(65); - if (lookahead == 'u') ADVANCE(57); - if (lookahead == 'x') ADVANCE(53); - if (('0' <= lookahead && lookahead <= '9')) ADVANCE(180); - if (lookahead != 0) ADVANCE(178); + if (lookahead == '*') ADVANCE(19); + if (lookahead != 0) ADVANCE(20); END_STATE(); case 21: - if (lookahead == '\\') ADVANCE(44); - if (lookahead != 0 && - lookahead != '\'') ADVANCE(11); + if (lookahead == '.') ADVANCE(173); + if (lookahead == 'E' || + lookahead == 'e') ADVANCE(40); + if (lookahead == '_') ADVANCE(51); + if (lookahead == 'i') ADVANCE(174); + if (('0' <= lookahead && lookahead <= '9')) ADVANCE(21); END_STATE(); case 22: - if (lookahead == ']') ADVANCE(74); - if (lookahead == '{') ADVANCE(68); - if (lookahead != 0 && - lookahead != '\n') ADVANCE(22); + if (lookahead == '.') ADVANCE(56); + if (lookahead == '_') ADVANCE(55); + if (('0' <= lookahead && lookahead <= '9') || + ('A' <= lookahead && lookahead <= 'F') || + ('a' <= lookahead && lookahead <= 'f')) ADVANCE(168); END_STATE(); case 23: - if (lookahead == '_') ADVANCE(39); - if (lookahead == '0' || - lookahead == '1') ADVANCE(164); + if (lookahead == '=') ADVANCE(100); END_STATE(); case 24: - if (lookahead == '_') ADVANCE(42); - if (('0' <= lookahead && lookahead <= '7')) ADVANCE(165); + if (lookahead == 'U') ADVANCE(69); + if (lookahead == 'u') ADVANCE(61); + if (lookahead == 'x') ADVANCE(57); + if (('0' <= lookahead && lookahead <= '9')) ADVANCE(185); + if (lookahead != 0) ADVANCE(183); END_STATE(); case 25: - if (lookahead == '`') ADVANCE(174); - if (lookahead != 0) ADVANCE(25); + if (lookahead == '\\') ADVANCE(48); + if (lookahead != 0 && + lookahead != '\'') ADVANCE(14); END_STATE(); case 26: - if (lookahead == 'f') ADVANCE(91); + if (lookahead == ']') ADVANCE(78); + if (lookahead == '{') ADVANCE(72); + if (lookahead != 0 && + lookahead != '\n') ADVANCE(26); END_STATE(); case 27: - if (lookahead == 'i') ADVANCE(26); + if (lookahead == '_') ADVANCE(43); + if (lookahead == '0' || + lookahead == '1') ADVANCE(169); END_STATE(); case 28: - if (lookahead == '{') ADVANCE(68); - if (lookahead != 0 && - lookahead != '\n') ADVANCE(22); + if (lookahead == '_') ADVANCE(46); + if (('0' <= lookahead && lookahead <= '7')) ADVANCE(170); END_STATE(); case 29: - if (lookahead == '{') ADVANCE(183); + if (lookahead == '`') ADVANCE(179); + if (lookahead != 0) ADVANCE(29); END_STATE(); case 30: - if (lookahead == '}') ADVANCE(185); + if (lookahead == 'f') ADVANCE(95); END_STATE(); case 31: - if (lookahead == '}') ADVANCE(186); + if (lookahead == 'i') ADVANCE(30); END_STATE(); case 32: - if (lookahead == '}') ADVANCE(31); + if (lookahead == '{') ADVANCE(72); + if (lookahead != 0 && + lookahead != '\n') ADVANCE(26); END_STATE(); case 33: + if (lookahead == '{') ADVANCE(188); + END_STATE(); + case 34: + if (lookahead == '}') ADVANCE(190); + END_STATE(); + case 35: + if (lookahead == '}') ADVANCE(191); + END_STATE(); + case 36: + if (lookahead == '}') ADVANCE(35); + END_STATE(); + case 37: if (lookahead == '\t' || - lookahead == ' ') ADVANCE(22); + lookahead == ' ') ADVANCE(26); if (lookahead != 0 && lookahead != '\n' && - lookahead != '{') ADVANCE(83); + lookahead != '{') ADVANCE(87); END_STATE(); - case 34: + case 38: if (lookahead == '\t' || - lookahead == ' ') ADVANCE(10); + lookahead == ' ') ADVANCE(13); if (lookahead != 0 && lookahead != '\n' && - lookahead != '{') ADVANCE(81); + lookahead != '{') ADVANCE(85); END_STATE(); - case 35: + case 39: if (lookahead == '\t' || - lookahead == ' ') ADVANCE(12); + lookahead == ' ') ADVANCE(15); if (lookahead != 0 && lookahead != '\n' && - lookahead != '{') ADVANCE(82); + lookahead != '{') ADVANCE(86); END_STATE(); - case 36: + case 40: if (lookahead == '+' || - lookahead == '-') ADVANCE(48); - if (('0' <= lookahead && lookahead <= '9')) ADVANCE(166); + lookahead == '-') ADVANCE(52); + if (('0' <= lookahead && lookahead <= '9')) ADVANCE(171); END_STATE(); - case 37: + case 41: if (lookahead == 'P' || - lookahead == 'p') ADVANCE(36); - if (lookahead == '_') ADVANCE(52); + lookahead == 'p') ADVANCE(40); + if (lookahead == '_') ADVANCE(56); if (('0' <= lookahead && lookahead <= '9') || ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(37); + ('a' <= lookahead && lookahead <= 'f')) ADVANCE(41); END_STATE(); - case 38: + case 42: if (lookahead == 'P' || - lookahead == 'p') ADVANCE(36); + lookahead == 'p') ADVANCE(40); if (('0' <= lookahead && lookahead <= '9') || ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(37); - END_STATE(); - case 39: - if (lookahead == '0' || - lookahead == '1') ADVANCE(164); - END_STATE(); - case 40: - if (lookahead == '8' || - lookahead == '9') ADVANCE(17); - if (('0' <= lookahead && lookahead <= '7')) ADVANCE(161); - END_STATE(); - case 41: - if (('0' <= lookahead && lookahead <= '7')) ADVANCE(11); - END_STATE(); - case 42: - if (('0' <= lookahead && lookahead <= '7')) ADVANCE(165); + ('a' <= lookahead && lookahead <= 'f')) ADVANCE(41); END_STATE(); case 43: - if (('0' <= lookahead && lookahead <= '7')) ADVANCE(41); + if (lookahead == '0' || + lookahead == '1') ADVANCE(169); END_STATE(); case 44: - if (sym_rune_literal_character_set_1(lookahead)) ADVANCE(11); - if (lookahead == 'U') ADVANCE(66); - if (lookahead == 'u') ADVANCE(58); - if (lookahead == 'x') ADVANCE(54); - if (('0' <= lookahead && lookahead <= '7')) ADVANCE(43); + if (lookahead == '8' || + lookahead == '9') ADVANCE(21); + if (('0' <= lookahead && lookahead <= '7')) ADVANCE(166); END_STATE(); case 45: - if (('0' <= lookahead && lookahead <= '9')) ADVANCE(162); + if (('0' <= lookahead && lookahead <= '7')) ADVANCE(14); END_STATE(); case 46: - if (('0' <= lookahead && lookahead <= '9')) ADVANCE(167); + if (('0' <= lookahead && lookahead <= '7')) ADVANCE(170); END_STATE(); case 47: - if (('0' <= lookahead && lookahead <= '9')) ADVANCE(17); + if (('0' <= lookahead && lookahead <= '7')) ADVANCE(45); END_STATE(); case 48: - if (('0' <= lookahead && lookahead <= '9')) ADVANCE(166); + if (sym_rune_literal_character_set_1(lookahead)) ADVANCE(14); + if (lookahead == 'U') ADVANCE(70); + if (lookahead == 'u') ADVANCE(62); + if (lookahead == 'x') ADVANCE(58); + if (('0' <= lookahead && lookahead <= '7')) ADVANCE(47); END_STATE(); case 49: - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(11); + if (('0' <= lookahead && lookahead <= '9')) ADVANCE(167); END_STATE(); case 50: - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(178); + if (('0' <= lookahead && lookahead <= '9')) ADVANCE(172); END_STATE(); case 51: - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(163); + if (('0' <= lookahead && lookahead <= '9')) ADVANCE(21); END_STATE(); case 52: - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(37); + if (('0' <= lookahead && lookahead <= '9')) ADVANCE(171); END_STATE(); case 53: if (('0' <= lookahead && lookahead <= '9') || ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(50); + ('a' <= lookahead && lookahead <= 'f')) ADVANCE(14); END_STATE(); case 54: if (('0' <= lookahead && lookahead <= '9') || ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(49); + ('a' <= lookahead && lookahead <= 'f')) ADVANCE(183); END_STATE(); case 55: if (('0' <= lookahead && lookahead <= '9') || ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(53); + ('a' <= lookahead && lookahead <= 'f')) ADVANCE(168); END_STATE(); case 56: if (('0' <= lookahead && lookahead <= '9') || ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(54); + ('a' <= lookahead && lookahead <= 'f')) ADVANCE(41); END_STATE(); case 57: if (('0' <= lookahead && lookahead <= '9') || ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(55); + ('a' <= lookahead && lookahead <= 'f')) ADVANCE(54); END_STATE(); case 58: if (('0' <= lookahead && lookahead <= '9') || ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(56); + ('a' <= lookahead && lookahead <= 'f')) ADVANCE(53); END_STATE(); case 59: if (('0' <= lookahead && lookahead <= '9') || @@ -15057,693 +15127,717 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { ('a' <= lookahead && lookahead <= 'f')) ADVANCE(64); END_STATE(); case 67: + if (('0' <= lookahead && lookahead <= '9') || + ('A' <= lookahead && lookahead <= 'F') || + ('a' <= lookahead && lookahead <= 'f')) ADVANCE(65); + END_STATE(); + case 68: + if (('0' <= lookahead && lookahead <= '9') || + ('A' <= lookahead && lookahead <= 'F') || + ('a' <= lookahead && lookahead <= 'f')) ADVANCE(66); + END_STATE(); + case 69: + if (('0' <= lookahead && lookahead <= '9') || + ('A' <= lookahead && lookahead <= 'F') || + ('a' <= lookahead && lookahead <= 'f')) ADVANCE(67); + END_STATE(); + case 70: + if (('0' <= lookahead && lookahead <= '9') || + ('A' <= lookahead && lookahead <= 'F') || + ('a' <= lookahead && lookahead <= 'f')) ADVANCE(68); + END_STATE(); + case 71: if (lookahead != 0 && lookahead != '\t' && lookahead != '\n' && lookahead != ' ' && - lookahead != '{') ADVANCE(85); + lookahead != '{') ADVANCE(89); END_STATE(); - case 68: + case 72: if (lookahead != 0 && lookahead != '\n' && - lookahead != '{') ADVANCE(22); + lookahead != '{') ADVANCE(26); END_STATE(); - case 69: + case 73: if (lookahead != 0 && lookahead != '\n' && - lookahead != '{') ADVANCE(10); + lookahead != '{') ADVANCE(13); END_STATE(); - case 70: + case 74: if (lookahead != 0 && lookahead != '\n' && - lookahead != '{') ADVANCE(12); + lookahead != '{') ADVANCE(15); END_STATE(); - case 71: - if (eof) ADVANCE(73); - if (lookahead == '\n') SKIP(71) - if (lookahead == '\r') ADVANCE(80); - if (lookahead == '"') ADVANCE(84); - if (lookahead == '\'') ADVANCE(86); - if (lookahead == '[') ADVANCE(87); - if (lookahead == '{') ADVANCE(89); + case 75: + if (eof) ADVANCE(77); + if (lookahead == '\n') SKIP(75) + if (lookahead == '\r') ADVANCE(84); + if (lookahead == '"') ADVANCE(88); + if (lookahead == '\'') ADVANCE(90); + if (lookahead == '[') ADVANCE(91); + if (lookahead == '{') ADVANCE(93); if (lookahead == '\t' || - lookahead == ' ') ADVANCE(88); - if (lookahead != 0) ADVANCE(85); + lookahead == ' ') ADVANCE(92); + if (lookahead != 0) ADVANCE(89); END_STATE(); - case 72: - if (eof) ADVANCE(73); - if (lookahead == '"') ADVANCE(175); - if (lookahead == '$') ADVANCE(110); - if (lookahead == '\'') ADVANCE(21); - if (lookahead == '(') ADVANCE(104); - if (lookahead == ')') ADVANCE(105); - if (lookahead == ',') ADVANCE(95); - if (lookahead == '-') ADVANCE(32); - if (lookahead == '.') ADVANCE(109); - if (lookahead == '/') ADVANCE(14); - if (lookahead == '0') ADVANCE(160); - if (lookahead == ':') ADVANCE(19); - if (lookahead == '=') ADVANCE(102); - if (lookahead == '[') ADVANCE(28); - if (sym_identifier_character_set_1(lookahead)) ADVANCE(159); - if (lookahead == '`') ADVANCE(25); - if (lookahead == 'b') ADVANCE(136); - if (lookahead == 'd') ADVANCE(124); - if (lookahead == 'e') ADVANCE(112); - if (lookahead == 'f') ADVANCE(157); - if (lookahead == 'i') ADVANCE(126); - if (lookahead == 'n') ADVANCE(133); - if (lookahead == 'p') ADVANCE(130); - if (lookahead == 'r') ADVANCE(156); - if (lookahead == 't') ADVANCE(111); - if (lookahead == 'w') ADVANCE(131); - if (lookahead == '{') ADVANCE(29); - if (lookahead == '|') ADVANCE(103); - if (lookahead == '}') ADVANCE(30); + case 76: + if (eof) ADVANCE(77); + if (lookahead == '"') ADVANCE(180); + if (lookahead == '$') ADVANCE(115); + if (lookahead == '\'') ADVANCE(25); + if (lookahead == '(') ADVANCE(108); + if (lookahead == ')') ADVANCE(109); + if (lookahead == ',') ADVANCE(99); + if (lookahead == '-') ADVANCE(36); + if (lookahead == '.') ADVANCE(114); + if (lookahead == '/') ADVANCE(18); + if (lookahead == '0') ADVANCE(165); + if (lookahead == ':') ADVANCE(23); + if (lookahead == '=') ADVANCE(106); + if (lookahead == '[') ADVANCE(32); + if (sym_identifier_character_set_1(lookahead)) ADVANCE(164); + if (lookahead == '`') ADVANCE(29); + if (lookahead == 'b') ADVANCE(141); + if (lookahead == 'd') ADVANCE(129); + if (lookahead == 'e') ADVANCE(117); + if (lookahead == 'f') ADVANCE(162); + if (lookahead == 'i') ADVANCE(131); + if (lookahead == 'n') ADVANCE(138); + if (lookahead == 'p') ADVANCE(135); + if (lookahead == 'r') ADVANCE(161); + if (lookahead == 't') ADVANCE(116); + if (lookahead == 'w') ADVANCE(136); + if (lookahead == '{') ADVANCE(33); + if (lookahead == '|') ADVANCE(107); + if (lookahead == '}') ADVANCE(34); if (lookahead == '\t' || lookahead == '\n' || lookahead == '\r' || - lookahead == ' ') SKIP(72) - if (('1' <= lookahead && lookahead <= '9')) ADVANCE(162); + lookahead == ' ') SKIP(76) + if (('1' <= lookahead && lookahead <= '9')) ADVANCE(167); END_STATE(); - case 73: + case 77: ACCEPT_TOKEN(ts_builtin_sym_end); END_STATE(); - case 74: + case 78: ACCEPT_TOKEN(aux_sym_text_token1); - if (lookahead == ']') ADVANCE(74); - if (lookahead == '{') ADVANCE(68); + if (lookahead == ']') ADVANCE(78); + if (lookahead == '{') ADVANCE(72); if (lookahead != 0 && - lookahead != '\n') ADVANCE(22); + lookahead != '\n') ADVANCE(26); END_STATE(); - case 75: + case 79: ACCEPT_TOKEN(aux_sym_text_token1); - if (lookahead == ']') ADVANCE(75); - if (lookahead == '{') ADVANCE(33); + if (lookahead == ']') ADVANCE(79); + if (lookahead == '{') ADVANCE(37); if (lookahead == '\t' || - lookahead == ' ') ADVANCE(22); + lookahead == ' ') ADVANCE(26); if (lookahead != 0 && - lookahead != '\n') ADVANCE(83); + lookahead != '\n') ADVANCE(87); END_STATE(); - case 76: + case 80: ACCEPT_TOKEN(aux_sym_text_token2); - if (lookahead == '"') ADVANCE(76); - if (lookahead == '{') ADVANCE(69); + if (lookahead == '"') ADVANCE(80); + if (lookahead == '{') ADVANCE(73); if (lookahead != 0 && - lookahead != '\n') ADVANCE(10); + lookahead != '\n') ADVANCE(13); END_STATE(); - case 77: + case 81: ACCEPT_TOKEN(aux_sym_text_token2); - if (lookahead == '"') ADVANCE(77); - if (lookahead == '{') ADVANCE(34); + if (lookahead == '"') ADVANCE(81); + if (lookahead == '{') ADVANCE(38); if (lookahead == '\t' || - lookahead == ' ') ADVANCE(10); + lookahead == ' ') ADVANCE(13); if (lookahead != 0 && - lookahead != '\n') ADVANCE(81); + lookahead != '\n') ADVANCE(85); END_STATE(); - case 78: + case 82: ACCEPT_TOKEN(aux_sym_text_token3); - if (lookahead == '\'') ADVANCE(78); - if (lookahead == '{') ADVANCE(70); + if (lookahead == '\'') ADVANCE(82); + if (lookahead == '{') ADVANCE(74); if (lookahead != 0 && - lookahead != '\n') ADVANCE(12); + lookahead != '\n') ADVANCE(15); END_STATE(); - case 79: + case 83: ACCEPT_TOKEN(aux_sym_text_token3); - if (lookahead == '\'') ADVANCE(79); - if (lookahead == '{') ADVANCE(35); + if (lookahead == '\'') ADVANCE(83); + if (lookahead == '{') ADVANCE(39); if (lookahead == '\t' || - lookahead == ' ') ADVANCE(12); + lookahead == ' ') ADVANCE(15); if (lookahead != 0 && - lookahead != '\n') ADVANCE(82); + lookahead != '\n') ADVANCE(86); END_STATE(); - case 80: + case 84: ACCEPT_TOKEN(aux_sym_text_token4); - if (lookahead == '\r') ADVANCE(80); - if (lookahead == '"') ADVANCE(84); - if (lookahead == '\'') ADVANCE(86); - if (lookahead == '[') ADVANCE(87); - if (lookahead == '{') ADVANCE(89); + if (lookahead == '\r') ADVANCE(84); + if (lookahead == '"') ADVANCE(88); + if (lookahead == '\'') ADVANCE(90); + if (lookahead == '[') ADVANCE(91); + if (lookahead == '{') ADVANCE(93); if (lookahead == '\t' || - lookahead == ' ') ADVANCE(88); + lookahead == ' ') ADVANCE(92); if (lookahead != 0 && - lookahead != '\n') ADVANCE(85); + lookahead != '\n') ADVANCE(89); END_STATE(); - case 81: + case 85: ACCEPT_TOKEN(aux_sym_text_token4); - if (lookahead == '"') ADVANCE(77); - if (lookahead == '{') ADVANCE(34); + if (lookahead == '"') ADVANCE(81); + if (lookahead == '{') ADVANCE(38); if (lookahead == '\t' || - lookahead == ' ') ADVANCE(10); + lookahead == ' ') ADVANCE(13); if (lookahead != 0 && - lookahead != '\n') ADVANCE(81); + lookahead != '\n') ADVANCE(85); END_STATE(); - case 82: + case 86: ACCEPT_TOKEN(aux_sym_text_token4); - if (lookahead == '\'') ADVANCE(79); - if (lookahead == '{') ADVANCE(35); + if (lookahead == '\'') ADVANCE(83); + if (lookahead == '{') ADVANCE(39); if (lookahead == '\t' || - lookahead == ' ') ADVANCE(12); + lookahead == ' ') ADVANCE(15); if (lookahead != 0 && - lookahead != '\n') ADVANCE(82); + lookahead != '\n') ADVANCE(86); END_STATE(); - case 83: + case 87: ACCEPT_TOKEN(aux_sym_text_token4); - if (lookahead == ']') ADVANCE(75); - if (lookahead == '{') ADVANCE(33); + if (lookahead == ']') ADVANCE(79); + if (lookahead == '{') ADVANCE(37); if (lookahead == '\t' || - lookahead == ' ') ADVANCE(22); + lookahead == ' ') ADVANCE(26); if (lookahead != 0 && - lookahead != '\n') ADVANCE(83); + lookahead != '\n') ADVANCE(87); END_STATE(); - case 84: + case 88: ACCEPT_TOKEN(aux_sym_text_token4); - if (lookahead == '{') ADVANCE(34); + if (lookahead == '{') ADVANCE(38); if (lookahead == '\t' || - lookahead == ' ') ADVANCE(10); + lookahead == ' ') ADVANCE(13); if (lookahead != 0 && - lookahead != '\n') ADVANCE(81); + lookahead != '\n') ADVANCE(85); END_STATE(); - case 85: + case 89: ACCEPT_TOKEN(aux_sym_text_token4); - if (lookahead == '{') ADVANCE(67); + if (lookahead == '{') ADVANCE(71); if (lookahead != 0 && lookahead != '\t' && lookahead != '\n' && - lookahead != ' ') ADVANCE(85); + lookahead != ' ') ADVANCE(89); END_STATE(); - case 86: + case 90: ACCEPT_TOKEN(aux_sym_text_token4); - if (lookahead == '{') ADVANCE(35); + if (lookahead == '{') ADVANCE(39); if (lookahead == '\t' || - lookahead == ' ') ADVANCE(12); + lookahead == ' ') ADVANCE(15); if (lookahead != 0 && - lookahead != '\n') ADVANCE(82); + lookahead != '\n') ADVANCE(86); END_STATE(); - case 87: + case 91: ACCEPT_TOKEN(aux_sym_text_token4); - if (lookahead == '{') ADVANCE(33); + if (lookahead == '{') ADVANCE(37); if (lookahead == '\t' || - lookahead == ' ') ADVANCE(22); + lookahead == ' ') ADVANCE(26); if (lookahead != 0 && - lookahead != '\n') ADVANCE(83); + lookahead != '\n') ADVANCE(87); END_STATE(); - case 88: + case 92: ACCEPT_TOKEN(aux_sym_text_token5); - if (lookahead == '\r') ADVANCE(80); - if (lookahead == '"') ADVANCE(84); - if (lookahead == '\'') ADVANCE(86); - if (lookahead == '[') ADVANCE(87); - if (lookahead == '{') ADVANCE(89); + if (lookahead == '\r') ADVANCE(84); + if (lookahead == '"') ADVANCE(88); + if (lookahead == '\'') ADVANCE(90); + if (lookahead == '[') ADVANCE(91); + if (lookahead == '{') ADVANCE(93); if (lookahead == '\t' || - lookahead == ' ') ADVANCE(88); + lookahead == ' ') ADVANCE(92); if (lookahead != 0 && - lookahead != '\n') ADVANCE(85); + lookahead != '\n') ADVANCE(89); END_STATE(); - case 89: + case 93: ACCEPT_TOKEN(aux_sym_text_token5); - if (lookahead == '{') ADVANCE(183); + if (lookahead == '{') ADVANCE(188); if (lookahead != 0 && lookahead != '\t' && lookahead != '\n' && - lookahead != ' ') ADVANCE(85); - END_STATE(); - case 90: - ACCEPT_TOKEN(anon_sym_if); - if (sym_identifier_character_set_6(lookahead)) ADVANCE(159); - END_STATE(); - case 91: - ACCEPT_TOKEN(anon_sym_elseif); - END_STATE(); - case 92: - ACCEPT_TOKEN(anon_sym_else); - if (lookahead == ' ') ADVANCE(27); - if (sym_identifier_character_set_6(lookahead)) ADVANCE(159); - END_STATE(); - case 93: - ACCEPT_TOKEN(anon_sym_else); - if (sym_identifier_character_set_6(lookahead)) ADVANCE(159); + lookahead != ' ') ADVANCE(89); END_STATE(); case 94: - ACCEPT_TOKEN(anon_sym_end); - if (sym_identifier_character_set_6(lookahead)) ADVANCE(159); + ACCEPT_TOKEN(anon_sym_if); + if (sym_identifier_character_set_6(lookahead)) ADVANCE(164); END_STATE(); case 95: - ACCEPT_TOKEN(anon_sym_COMMA); + ACCEPT_TOKEN(anon_sym_elseif); END_STATE(); case 96: - ACCEPT_TOKEN(anon_sym_COLON_EQ); + ACCEPT_TOKEN(anon_sym_else); + if (lookahead == ' ') ADVANCE(31); + if (sym_identifier_character_set_6(lookahead)) ADVANCE(164); END_STATE(); case 97: - ACCEPT_TOKEN(anon_sym_range); - if (sym_identifier_character_set_6(lookahead)) ADVANCE(159); + ACCEPT_TOKEN(anon_sym_else); + if (sym_identifier_character_set_6(lookahead)) ADVANCE(164); END_STATE(); case 98: - ACCEPT_TOKEN(anon_sym_template); - if (sym_identifier_character_set_6(lookahead)) ADVANCE(159); + ACCEPT_TOKEN(anon_sym_end); + if (sym_identifier_character_set_6(lookahead)) ADVANCE(164); END_STATE(); case 99: - ACCEPT_TOKEN(anon_sym_define); - if (sym_identifier_character_set_6(lookahead)) ADVANCE(159); + ACCEPT_TOKEN(anon_sym_COMMA); END_STATE(); case 100: - ACCEPT_TOKEN(anon_sym_block); - if (sym_identifier_character_set_6(lookahead)) ADVANCE(159); + ACCEPT_TOKEN(anon_sym_COLON_EQ); END_STATE(); case 101: - ACCEPT_TOKEN(anon_sym_with); - if (sym_identifier_character_set_6(lookahead)) ADVANCE(159); + ACCEPT_TOKEN(anon_sym_range); + if (sym_identifier_character_set_6(lookahead)) ADVANCE(164); END_STATE(); case 102: - ACCEPT_TOKEN(anon_sym_EQ); + ACCEPT_TOKEN(anon_sym_template); + if (sym_identifier_character_set_6(lookahead)) ADVANCE(164); END_STATE(); case 103: - ACCEPT_TOKEN(anon_sym_PIPE); + ACCEPT_TOKEN(anon_sym_define); + if (sym_identifier_character_set_6(lookahead)) ADVANCE(164); END_STATE(); case 104: - ACCEPT_TOKEN(anon_sym_LPAREN); + ACCEPT_TOKEN(anon_sym_block); + if (sym_identifier_character_set_6(lookahead)) ADVANCE(164); END_STATE(); case 105: - ACCEPT_TOKEN(anon_sym_RPAREN); + ACCEPT_TOKEN(anon_sym_with); + if (sym_identifier_character_set_6(lookahead)) ADVANCE(164); END_STATE(); case 106: - ACCEPT_TOKEN(anon_sym_); - if (lookahead == ' ') ADVANCE(106); + ACCEPT_TOKEN(anon_sym_EQ); END_STATE(); case 107: - ACCEPT_TOKEN(sym_pipeline_stub); - if (sym_identifier_character_set_6(lookahead)) ADVANCE(159); + ACCEPT_TOKEN(anon_sym_PIPE); END_STATE(); case 108: - ACCEPT_TOKEN(anon_sym_DOT); + ACCEPT_TOKEN(anon_sym_LPAREN); END_STATE(); case 109: - ACCEPT_TOKEN(anon_sym_DOT); - if (('0' <= lookahead && lookahead <= '9')) ADVANCE(167); + ACCEPT_TOKEN(anon_sym_RPAREN); END_STATE(); case 110: - ACCEPT_TOKEN(anon_sym_DOLLAR); + ACCEPT_TOKEN(anon_sym_); + if (lookahead == ' ') ADVANCE(110); END_STATE(); case 111: - ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_7(lookahead)) ADVANCE(159); - if (lookahead == 'e') ADVANCE(141); - if (lookahead == 'r') ADVANCE(155); + ACCEPT_TOKEN(sym_pipeline_stub); + if (sym_identifier_character_set_6(lookahead)) ADVANCE(164); END_STATE(); case 112: - ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_8(lookahead)) ADVANCE(159); - if (lookahead == 'l') ADVANCE(150); - if (lookahead == 'n') ADVANCE(115); + ACCEPT_TOKEN(anon_sym_DOT); END_STATE(); case 113: - ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_8(lookahead)) ADVANCE(159); - if (lookahead == 'l') ADVANCE(152); - if (lookahead == 'n') ADVANCE(115); + ACCEPT_TOKEN(anon_sym_DOT); + if (('0' <= lookahead && lookahead <= '9')) ADVANCE(172); END_STATE(); case 114: - ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_9(lookahead)) ADVANCE(159); - if (lookahead == 'c') ADVANCE(135); + ACCEPT_TOKEN(anon_sym_DOT2); + if (('0' <= lookahead && lookahead <= '9')) ADVANCE(172); END_STATE(); case 115: - ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_10(lookahead)) ADVANCE(159); - if (lookahead == 'd') ADVANCE(94); + ACCEPT_TOKEN(anon_sym_DOLLAR); END_STATE(); case 116: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_11(lookahead)) ADVANCE(159); - if (lookahead == 'e') ADVANCE(92); + if (sym_identifier_character_set_7(lookahead)) ADVANCE(164); + if (lookahead == 'e') ADVANCE(146); + if (lookahead == 'r') ADVANCE(160); END_STATE(); case 117: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_11(lookahead)) ADVANCE(159); - if (lookahead == 'e') ADVANCE(171); + if (sym_identifier_character_set_8(lookahead)) ADVANCE(164); + if (lookahead == 'l') ADVANCE(155); + if (lookahead == 'n') ADVANCE(120); END_STATE(); case 118: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_11(lookahead)) ADVANCE(159); - if (lookahead == 'e') ADVANCE(172); + if (sym_identifier_character_set_8(lookahead)) ADVANCE(164); + if (lookahead == 'l') ADVANCE(157); + if (lookahead == 'n') ADVANCE(120); END_STATE(); case 119: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_11(lookahead)) ADVANCE(159); - if (lookahead == 'e') ADVANCE(97); + if (sym_identifier_character_set_9(lookahead)) ADVANCE(164); + if (lookahead == 'c') ADVANCE(140); END_STATE(); case 120: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_11(lookahead)) ADVANCE(159); - if (lookahead == 'e') ADVANCE(99); + if (sym_identifier_character_set_10(lookahead)) ADVANCE(164); + if (lookahead == 'd') ADVANCE(98); END_STATE(); case 121: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_11(lookahead)) ADVANCE(159); - if (lookahead == 'e') ADVANCE(107); + if (sym_identifier_character_set_11(lookahead)) ADVANCE(164); + if (lookahead == 'e') ADVANCE(96); END_STATE(); case 122: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_11(lookahead)) ADVANCE(159); - if (lookahead == 'e') ADVANCE(98); + if (sym_identifier_character_set_11(lookahead)) ADVANCE(164); + if (lookahead == 'e') ADVANCE(176); END_STATE(); case 123: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_11(lookahead)) ADVANCE(159); - if (lookahead == 'e') ADVANCE(93); + if (sym_identifier_character_set_11(lookahead)) ADVANCE(164); + if (lookahead == 'e') ADVANCE(177); END_STATE(); case 124: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_11(lookahead)) ADVANCE(159); - if (lookahead == 'e') ADVANCE(127); + if (sym_identifier_character_set_11(lookahead)) ADVANCE(164); + if (lookahead == 'e') ADVANCE(101); END_STATE(); case 125: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_11(lookahead)) ADVANCE(159); - if (lookahead == 'e') ADVANCE(140); + if (sym_identifier_character_set_11(lookahead)) ADVANCE(164); + if (lookahead == 'e') ADVANCE(103); END_STATE(); case 126: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_12(lookahead)) ADVANCE(159); - if (lookahead == 'f') ADVANCE(90); + if (sym_identifier_character_set_11(lookahead)) ADVANCE(164); + if (lookahead == 'e') ADVANCE(111); END_STATE(); case 127: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_12(lookahead)) ADVANCE(159); - if (lookahead == 'f') ADVANCE(132); + if (sym_identifier_character_set_11(lookahead)) ADVANCE(164); + if (lookahead == 'e') ADVANCE(102); END_STATE(); case 128: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_13(lookahead)) ADVANCE(159); - if (lookahead == 'g') ADVANCE(119); + if (sym_identifier_character_set_11(lookahead)) ADVANCE(164); + if (lookahead == 'e') ADVANCE(97); END_STATE(); case 129: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_14(lookahead)) ADVANCE(159); - if (lookahead == 'h') ADVANCE(101); + if (sym_identifier_character_set_11(lookahead)) ADVANCE(164); + if (lookahead == 'e') ADVANCE(132); END_STATE(); case 130: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_15(lookahead)) ADVANCE(159); - if (lookahead == 'i') ADVANCE(147); + if (sym_identifier_character_set_11(lookahead)) ADVANCE(164); + if (lookahead == 'e') ADVANCE(145); END_STATE(); case 131: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_15(lookahead)) ADVANCE(159); - if (lookahead == 'i') ADVANCE(153); + if (sym_identifier_character_set_12(lookahead)) ADVANCE(164); + if (lookahead == 'f') ADVANCE(94); END_STATE(); case 132: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_15(lookahead)) ADVANCE(159); - if (lookahead == 'i') ADVANCE(144); + if (sym_identifier_character_set_12(lookahead)) ADVANCE(164); + if (lookahead == 'f') ADVANCE(137); END_STATE(); case 133: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_15(lookahead)) ADVANCE(159); - if (lookahead == 'i') ADVANCE(137); + if (sym_identifier_character_set_13(lookahead)) ADVANCE(164); + if (lookahead == 'g') ADVANCE(124); END_STATE(); case 134: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_15(lookahead)) ADVANCE(159); - if (lookahead == 'i') ADVANCE(145); + if (sym_identifier_character_set_14(lookahead)) ADVANCE(164); + if (lookahead == 'h') ADVANCE(105); END_STATE(); case 135: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_16(lookahead)) ADVANCE(159); - if (lookahead == 'k') ADVANCE(100); + if (sym_identifier_character_set_15(lookahead)) ADVANCE(164); + if (lookahead == 'i') ADVANCE(152); END_STATE(); case 136: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_17(lookahead)) ADVANCE(159); - if (lookahead == 'l') ADVANCE(146); + if (sym_identifier_character_set_15(lookahead)) ADVANCE(164); + if (lookahead == 'i') ADVANCE(158); END_STATE(); case 137: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_17(lookahead)) ADVANCE(159); - if (lookahead == 'l') ADVANCE(173); + if (sym_identifier_character_set_15(lookahead)) ADVANCE(164); + if (lookahead == 'i') ADVANCE(149); END_STATE(); case 138: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_17(lookahead)) ADVANCE(159); - if (lookahead == 'l') ADVANCE(158); + if (sym_identifier_character_set_15(lookahead)) ADVANCE(164); + if (lookahead == 'i') ADVANCE(142); END_STATE(); case 139: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_17(lookahead)) ADVANCE(159); - if (lookahead == 'l') ADVANCE(151); + if (sym_identifier_character_set_15(lookahead)) ADVANCE(164); + if (lookahead == 'i') ADVANCE(150); END_STATE(); case 140: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_17(lookahead)) ADVANCE(159); - if (lookahead == 'l') ADVANCE(134); + if (sym_identifier_character_set_16(lookahead)) ADVANCE(164); + if (lookahead == 'k') ADVANCE(104); END_STATE(); case 141: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_18(lookahead)) ADVANCE(159); - if (lookahead == 'm') ADVANCE(148); + if (sym_identifier_character_set_17(lookahead)) ADVANCE(164); + if (lookahead == 'l') ADVANCE(151); END_STATE(); case 142: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_19(lookahead)) ADVANCE(159); - if (lookahead == 'n') ADVANCE(115); + if (sym_identifier_character_set_17(lookahead)) ADVANCE(164); + if (lookahead == 'l') ADVANCE(178); END_STATE(); case 143: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_19(lookahead)) ADVANCE(159); - if (lookahead == 'n') ADVANCE(128); + if (sym_identifier_character_set_17(lookahead)) ADVANCE(164); + if (lookahead == 'l') ADVANCE(163); END_STATE(); case 144: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_19(lookahead)) ADVANCE(159); - if (lookahead == 'n') ADVANCE(120); + if (sym_identifier_character_set_17(lookahead)) ADVANCE(164); + if (lookahead == 'l') ADVANCE(156); END_STATE(); case 145: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_19(lookahead)) ADVANCE(159); - if (lookahead == 'n') ADVANCE(121); + if (sym_identifier_character_set_17(lookahead)) ADVANCE(164); + if (lookahead == 'l') ADVANCE(139); END_STATE(); case 146: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_20(lookahead)) ADVANCE(159); - if (lookahead == 'o') ADVANCE(114); + if (sym_identifier_character_set_18(lookahead)) ADVANCE(164); + if (lookahead == 'm') ADVANCE(153); END_STATE(); case 147: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_21(lookahead)) ADVANCE(159); - if (lookahead == 'p') ADVANCE(125); + if (sym_identifier_character_set_19(lookahead)) ADVANCE(164); + if (lookahead == 'n') ADVANCE(120); END_STATE(); case 148: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_21(lookahead)) ADVANCE(159); - if (lookahead == 'p') ADVANCE(138); + if (sym_identifier_character_set_19(lookahead)) ADVANCE(164); + if (lookahead == 'n') ADVANCE(133); END_STATE(); case 149: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_22(lookahead)) ADVANCE(159); - if (lookahead == 'r') ADVANCE(155); + if (sym_identifier_character_set_19(lookahead)) ADVANCE(164); + if (lookahead == 'n') ADVANCE(125); END_STATE(); case 150: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_23(lookahead)) ADVANCE(159); - if (lookahead == 's') ADVANCE(116); + if (sym_identifier_character_set_19(lookahead)) ADVANCE(164); + if (lookahead == 'n') ADVANCE(126); END_STATE(); case 151: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_23(lookahead)) ADVANCE(159); - if (lookahead == 's') ADVANCE(118); + if (sym_identifier_character_set_20(lookahead)) ADVANCE(164); + if (lookahead == 'o') ADVANCE(119); END_STATE(); case 152: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_23(lookahead)) ADVANCE(159); - if (lookahead == 's') ADVANCE(123); + if (sym_identifier_character_set_21(lookahead)) ADVANCE(164); + if (lookahead == 'p') ADVANCE(130); END_STATE(); case 153: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_24(lookahead)) ADVANCE(159); - if (lookahead == 't') ADVANCE(129); + if (sym_identifier_character_set_21(lookahead)) ADVANCE(164); + if (lookahead == 'p') ADVANCE(143); END_STATE(); case 154: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_24(lookahead)) ADVANCE(159); - if (lookahead == 't') ADVANCE(122); + if (sym_identifier_character_set_22(lookahead)) ADVANCE(164); + if (lookahead == 'r') ADVANCE(160); END_STATE(); case 155: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_25(lookahead)) ADVANCE(159); - if (lookahead == 'u') ADVANCE(117); + if (sym_identifier_character_set_23(lookahead)) ADVANCE(164); + if (lookahead == 's') ADVANCE(121); END_STATE(); case 156: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_26(lookahead)) ADVANCE(159); - if (lookahead == 'a') ADVANCE(143); + if (sym_identifier_character_set_23(lookahead)) ADVANCE(164); + if (lookahead == 's') ADVANCE(123); END_STATE(); case 157: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_26(lookahead)) ADVANCE(159); - if (lookahead == 'a') ADVANCE(139); + if (sym_identifier_character_set_23(lookahead)) ADVANCE(164); + if (lookahead == 's') ADVANCE(128); END_STATE(); case 158: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_26(lookahead)) ADVANCE(159); - if (lookahead == 'a') ADVANCE(154); + if (sym_identifier_character_set_24(lookahead)) ADVANCE(164); + if (lookahead == 't') ADVANCE(134); END_STATE(); case 159: ACCEPT_TOKEN(sym_identifier); - if (sym_identifier_character_set_6(lookahead)) ADVANCE(159); + if (sym_identifier_character_set_24(lookahead)) ADVANCE(164); + if (lookahead == 't') ADVANCE(127); END_STATE(); case 160: - ACCEPT_TOKEN(sym_int_literal); - if (lookahead == '.') ADVANCE(168); + ACCEPT_TOKEN(sym_identifier); + if (sym_identifier_character_set_25(lookahead)) ADVANCE(164); + if (lookahead == 'u') ADVANCE(122); + END_STATE(); + case 161: + ACCEPT_TOKEN(sym_identifier); + if (sym_identifier_character_set_26(lookahead)) ADVANCE(164); + if (lookahead == 'a') ADVANCE(148); + END_STATE(); + case 162: + ACCEPT_TOKEN(sym_identifier); + if (sym_identifier_character_set_26(lookahead)) ADVANCE(164); + if (lookahead == 'a') ADVANCE(144); + END_STATE(); + case 163: + ACCEPT_TOKEN(sym_identifier); + if (sym_identifier_character_set_26(lookahead)) ADVANCE(164); + if (lookahead == 'a') ADVANCE(159); + END_STATE(); + case 164: + ACCEPT_TOKEN(sym_identifier); + if (sym_identifier_character_set_6(lookahead)) ADVANCE(164); + END_STATE(); + case 165: + ACCEPT_TOKEN(sym_int_literal); + if (lookahead == '.') ADVANCE(173); if (lookahead == 'B' || - lookahead == 'b') ADVANCE(23); + lookahead == 'b') ADVANCE(27); if (lookahead == 'E' || - lookahead == 'e') ADVANCE(36); + lookahead == 'e') ADVANCE(40); if (lookahead == 'O' || - lookahead == 'o') ADVANCE(24); + lookahead == 'o') ADVANCE(28); if (lookahead == 'X' || - lookahead == 'x') ADVANCE(18); - if (lookahead == '_') ADVANCE(40); - if (lookahead == 'i') ADVANCE(169); + lookahead == 'x') ADVANCE(22); + if (lookahead == '_') ADVANCE(44); + if (lookahead == 'i') ADVANCE(174); if (lookahead == '8' || - lookahead == '9') ADVANCE(17); - if (('0' <= lookahead && lookahead <= '7')) ADVANCE(161); + lookahead == '9') ADVANCE(21); + if (('0' <= lookahead && lookahead <= '7')) ADVANCE(166); END_STATE(); - case 161: + case 166: ACCEPT_TOKEN(sym_int_literal); - if (lookahead == '.') ADVANCE(168); + if (lookahead == '.') ADVANCE(173); if (lookahead == 'E' || - lookahead == 'e') ADVANCE(36); - if (lookahead == '_') ADVANCE(40); - if (lookahead == 'i') ADVANCE(169); + lookahead == 'e') ADVANCE(40); + if (lookahead == '_') ADVANCE(44); + if (lookahead == 'i') ADVANCE(174); if (lookahead == '8' || - lookahead == '9') ADVANCE(17); - if (('0' <= lookahead && lookahead <= '7')) ADVANCE(161); + lookahead == '9') ADVANCE(21); + if (('0' <= lookahead && lookahead <= '7')) ADVANCE(166); END_STATE(); - case 162: + case 167: ACCEPT_TOKEN(sym_int_literal); - if (lookahead == '.') ADVANCE(168); + if (lookahead == '.') ADVANCE(173); if (lookahead == 'E' || - lookahead == 'e') ADVANCE(36); - if (lookahead == '_') ADVANCE(45); - if (lookahead == 'i') ADVANCE(169); - if (('0' <= lookahead && lookahead <= '9')) ADVANCE(162); + lookahead == 'e') ADVANCE(40); + if (lookahead == '_') ADVANCE(49); + if (lookahead == 'i') ADVANCE(174); + if (('0' <= lookahead && lookahead <= '9')) ADVANCE(167); END_STATE(); - case 163: + case 168: ACCEPT_TOKEN(sym_int_literal); - if (lookahead == '.') ADVANCE(38); + if (lookahead == '.') ADVANCE(42); if (lookahead == 'P' || - lookahead == 'p') ADVANCE(36); - if (lookahead == '_') ADVANCE(51); - if (lookahead == 'i') ADVANCE(169); + lookahead == 'p') ADVANCE(40); + if (lookahead == '_') ADVANCE(55); + if (lookahead == 'i') ADVANCE(174); if (('0' <= lookahead && lookahead <= '9') || ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(163); + ('a' <= lookahead && lookahead <= 'f')) ADVANCE(168); END_STATE(); - case 164: + case 169: ACCEPT_TOKEN(sym_int_literal); - if (lookahead == '_') ADVANCE(39); - if (lookahead == 'i') ADVANCE(169); + if (lookahead == '_') ADVANCE(43); + if (lookahead == 'i') ADVANCE(174); if (lookahead == '0' || - lookahead == '1') ADVANCE(164); + lookahead == '1') ADVANCE(169); END_STATE(); - case 165: + case 170: ACCEPT_TOKEN(sym_int_literal); - if (lookahead == '_') ADVANCE(42); - if (lookahead == 'i') ADVANCE(169); - if (('0' <= lookahead && lookahead <= '7')) ADVANCE(165); + if (lookahead == '_') ADVANCE(46); + if (lookahead == 'i') ADVANCE(174); + if (('0' <= lookahead && lookahead <= '7')) ADVANCE(170); END_STATE(); - case 166: + case 171: ACCEPT_TOKEN(sym_float_literal); - if (lookahead == '_') ADVANCE(48); - if (lookahead == 'i') ADVANCE(169); - if (('0' <= lookahead && lookahead <= '9')) ADVANCE(166); + if (lookahead == '_') ADVANCE(52); + if (lookahead == 'i') ADVANCE(174); + if (('0' <= lookahead && lookahead <= '9')) ADVANCE(171); END_STATE(); - case 167: + case 172: ACCEPT_TOKEN(sym_float_literal); if (lookahead == 'E' || - lookahead == 'e') ADVANCE(36); - if (lookahead == '_') ADVANCE(46); - if (lookahead == 'i') ADVANCE(169); - if (('0' <= lookahead && lookahead <= '9')) ADVANCE(167); + lookahead == 'e') ADVANCE(40); + if (lookahead == '_') ADVANCE(50); + if (lookahead == 'i') ADVANCE(174); + if (('0' <= lookahead && lookahead <= '9')) ADVANCE(172); END_STATE(); - case 168: + case 173: ACCEPT_TOKEN(sym_float_literal); if (lookahead == 'E' || - lookahead == 'e') ADVANCE(36); - if (lookahead == 'i') ADVANCE(169); - if (('0' <= lookahead && lookahead <= '9')) ADVANCE(167); + lookahead == 'e') ADVANCE(40); + if (lookahead == 'i') ADVANCE(174); + if (('0' <= lookahead && lookahead <= '9')) ADVANCE(172); END_STATE(); - case 169: + case 174: ACCEPT_TOKEN(sym_imaginary_literal); END_STATE(); - case 170: + case 175: ACCEPT_TOKEN(sym_rune_literal); END_STATE(); - case 171: + case 176: ACCEPT_TOKEN(sym_true); - if (sym_identifier_character_set_6(lookahead)) ADVANCE(159); + if (sym_identifier_character_set_6(lookahead)) ADVANCE(164); END_STATE(); - case 172: + case 177: ACCEPT_TOKEN(sym_false); - if (sym_identifier_character_set_6(lookahead)) ADVANCE(159); + if (sym_identifier_character_set_6(lookahead)) ADVANCE(164); END_STATE(); - case 173: + case 178: ACCEPT_TOKEN(sym_nil); - if (sym_identifier_character_set_6(lookahead)) ADVANCE(159); + if (sym_identifier_character_set_6(lookahead)) ADVANCE(164); END_STATE(); - case 174: + case 179: ACCEPT_TOKEN(sym_raw_string_literal); END_STATE(); - case 175: + case 180: ACCEPT_TOKEN(anon_sym_DQUOTE); END_STATE(); - case 176: + case 181: ACCEPT_TOKEN(aux_sym_interpreted_string_literal_token1); if (lookahead == '\t' || lookahead == '\r' || - lookahead == ' ') ADVANCE(176); + lookahead == ' ') ADVANCE(181); if (lookahead != 0 && lookahead != '\n' && lookahead != '"' && - lookahead != '\\') ADVANCE(177); + lookahead != '\\') ADVANCE(182); END_STATE(); - case 177: + case 182: ACCEPT_TOKEN(aux_sym_interpreted_string_literal_token1); if (lookahead != 0 && lookahead != '\n' && lookahead != '"' && - lookahead != '\\') ADVANCE(177); + lookahead != '\\') ADVANCE(182); END_STATE(); - case 178: + case 183: ACCEPT_TOKEN(sym_escape_sequence); END_STATE(); - case 179: + case 184: ACCEPT_TOKEN(sym_escape_sequence); - if (('0' <= lookahead && lookahead <= '9')) ADVANCE(178); + if (('0' <= lookahead && lookahead <= '9')) ADVANCE(183); END_STATE(); - case 180: + case 185: ACCEPT_TOKEN(sym_escape_sequence); - if (('0' <= lookahead && lookahead <= '9')) ADVANCE(179); + if (('0' <= lookahead && lookahead <= '9')) ADVANCE(184); END_STATE(); - case 181: + case 186: ACCEPT_TOKEN(sym_comment); END_STATE(); - case 182: + case 187: ACCEPT_TOKEN(sym_comment); if (lookahead != 0 && - lookahead != '\n') ADVANCE(182); + lookahead != '\n') ADVANCE(187); END_STATE(); - case 183: + case 188: ACCEPT_TOKEN(anon_sym_LBRACE_LBRACE); - if (lookahead == '-') ADVANCE(184); + if (lookahead == '-') ADVANCE(189); END_STATE(); - case 184: + case 189: ACCEPT_TOKEN(anon_sym_LBRACE_LBRACE_DASH); END_STATE(); - case 185: + case 190: ACCEPT_TOKEN(anon_sym_RBRACE_RBRACE); END_STATE(); - case 186: + case 191: ACCEPT_TOKEN(anon_sym_DASH_RBRACE_RBRACE); END_STATE(); default: @@ -15753,268 +15847,268 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { static const TSLexMode ts_lex_modes[STATE_COUNT] = { [0] = {.lex_state = 0}, - [1] = {.lex_state = 71}, - [2] = {.lex_state = 5}, - [3] = {.lex_state = 5}, - [4] = {.lex_state = 7}, - [5] = {.lex_state = 7}, - [6] = {.lex_state = 7}, - [7] = {.lex_state = 7}, - [8] = {.lex_state = 7}, - [9] = {.lex_state = 7}, - [10] = {.lex_state = 7}, - [11] = {.lex_state = 7}, - [12] = {.lex_state = 7}, - [13] = {.lex_state = 7}, - [14] = {.lex_state = 7}, - [15] = {.lex_state = 7}, - [16] = {.lex_state = 6}, - [17] = {.lex_state = 6}, - [18] = {.lex_state = 6}, - [19] = {.lex_state = 6}, - [20] = {.lex_state = 6}, - [21] = {.lex_state = 6}, - [22] = {.lex_state = 6}, - [23] = {.lex_state = 6}, - [24] = {.lex_state = 6}, - [25] = {.lex_state = 6}, - [26] = {.lex_state = 6}, - [27] = {.lex_state = 6}, - [28] = {.lex_state = 6}, - [29] = {.lex_state = 6}, - [30] = {.lex_state = 6}, - [31] = {.lex_state = 6}, - [32] = {.lex_state = 6}, - [33] = {.lex_state = 6}, - [34] = {.lex_state = 6}, - [35] = {.lex_state = 6}, - [36] = {.lex_state = 6}, - [37] = {.lex_state = 6}, - [38] = {.lex_state = 6}, - [39] = {.lex_state = 6}, - [40] = {.lex_state = 6}, - [41] = {.lex_state = 6}, - [42] = {.lex_state = 6}, - [43] = {.lex_state = 6}, - [44] = {.lex_state = 6}, - [45] = {.lex_state = 6}, - [46] = {.lex_state = 6}, - [47] = {.lex_state = 6}, - [48] = {.lex_state = 6}, - [49] = {.lex_state = 6}, - [50] = {.lex_state = 6}, - [51] = {.lex_state = 6}, - [52] = {.lex_state = 8}, - [53] = {.lex_state = 8}, - [54] = {.lex_state = 2}, - [55] = {.lex_state = 2}, - [56] = {.lex_state = 2}, - [57] = {.lex_state = 4}, - [58] = {.lex_state = 2}, - [59] = {.lex_state = 4}, - [60] = {.lex_state = 4}, - [61] = {.lex_state = 4}, - [62] = {.lex_state = 4}, - [63] = {.lex_state = 4}, - [64] = {.lex_state = 4}, - [65] = {.lex_state = 4}, - [66] = {.lex_state = 4}, - [67] = {.lex_state = 4}, - [68] = {.lex_state = 4}, - [69] = {.lex_state = 4}, - [70] = {.lex_state = 4}, - [71] = {.lex_state = 4}, - [72] = {.lex_state = 4}, - [73] = {.lex_state = 4}, - [74] = {.lex_state = 4}, - [75] = {.lex_state = 4}, - [76] = {.lex_state = 4}, - [77] = {.lex_state = 4}, - [78] = {.lex_state = 4}, - [79] = {.lex_state = 4}, - [80] = {.lex_state = 4}, - [81] = {.lex_state = 4}, - [82] = {.lex_state = 4}, - [83] = {.lex_state = 71}, - [84] = {.lex_state = 71}, - [85] = {.lex_state = 71}, - [86] = {.lex_state = 71}, - [87] = {.lex_state = 71}, - [88] = {.lex_state = 71}, - [89] = {.lex_state = 71}, - [90] = {.lex_state = 71}, - [91] = {.lex_state = 71}, - [92] = {.lex_state = 71}, - [93] = {.lex_state = 71}, - [94] = {.lex_state = 71}, - [95] = {.lex_state = 71}, - [96] = {.lex_state = 71}, - [97] = {.lex_state = 71}, - [98] = {.lex_state = 71}, - [99] = {.lex_state = 71}, - [100] = {.lex_state = 71}, - [101] = {.lex_state = 71}, - [102] = {.lex_state = 71}, - [103] = {.lex_state = 71}, - [104] = {.lex_state = 71}, - [105] = {.lex_state = 71}, - [106] = {.lex_state = 71}, - [107] = {.lex_state = 71}, - [108] = {.lex_state = 71}, - [109] = {.lex_state = 71}, - [110] = {.lex_state = 71}, - [111] = {.lex_state = 71}, - [112] = {.lex_state = 71}, - [113] = {.lex_state = 71}, - [114] = {.lex_state = 71}, - [115] = {.lex_state = 71}, - [116] = {.lex_state = 71}, - [117] = {.lex_state = 71}, - [118] = {.lex_state = 71}, - [119] = {.lex_state = 71}, - [120] = {.lex_state = 71}, - [121] = {.lex_state = 71}, - [122] = {.lex_state = 71}, - [123] = {.lex_state = 71}, - [124] = {.lex_state = 71}, - [125] = {.lex_state = 71}, - [126] = {.lex_state = 71}, - [127] = {.lex_state = 71}, - [128] = {.lex_state = 71}, - [129] = {.lex_state = 71}, - [130] = {.lex_state = 71}, - [131] = {.lex_state = 71}, - [132] = {.lex_state = 71}, - [133] = {.lex_state = 71}, - [134] = {.lex_state = 71}, - [135] = {.lex_state = 71}, - [136] = {.lex_state = 71}, - [137] = {.lex_state = 71}, - [138] = {.lex_state = 71}, - [139] = {.lex_state = 71}, - [140] = {.lex_state = 71}, - [141] = {.lex_state = 71}, - [142] = {.lex_state = 2}, - [143] = {.lex_state = 2}, + [1] = {.lex_state = 75}, + [2] = {.lex_state = 8}, + [3] = {.lex_state = 8}, + [4] = {.lex_state = 10}, + [5] = {.lex_state = 10}, + [6] = {.lex_state = 10}, + [7] = {.lex_state = 10}, + [8] = {.lex_state = 10}, + [9] = {.lex_state = 10}, + [10] = {.lex_state = 10}, + [11] = {.lex_state = 10}, + [12] = {.lex_state = 10}, + [13] = {.lex_state = 10}, + [14] = {.lex_state = 10}, + [15] = {.lex_state = 10}, + [16] = {.lex_state = 9}, + [17] = {.lex_state = 9}, + [18] = {.lex_state = 9}, + [19] = {.lex_state = 9}, + [20] = {.lex_state = 9}, + [21] = {.lex_state = 9}, + [22] = {.lex_state = 9}, + [23] = {.lex_state = 9}, + [24] = {.lex_state = 9}, + [25] = {.lex_state = 9}, + [26] = {.lex_state = 9}, + [27] = {.lex_state = 9}, + [28] = {.lex_state = 9}, + [29] = {.lex_state = 9}, + [30] = {.lex_state = 9}, + [31] = {.lex_state = 9}, + [32] = {.lex_state = 9}, + [33] = {.lex_state = 9}, + [34] = {.lex_state = 9}, + [35] = {.lex_state = 9}, + [36] = {.lex_state = 9}, + [37] = {.lex_state = 9}, + [38] = {.lex_state = 9}, + [39] = {.lex_state = 9}, + [40] = {.lex_state = 9}, + [41] = {.lex_state = 9}, + [42] = {.lex_state = 9}, + [43] = {.lex_state = 9}, + [44] = {.lex_state = 9}, + [45] = {.lex_state = 9}, + [46] = {.lex_state = 9}, + [47] = {.lex_state = 9}, + [48] = {.lex_state = 9}, + [49] = {.lex_state = 9}, + [50] = {.lex_state = 9}, + [51] = {.lex_state = 9}, + [52] = {.lex_state = 11}, + [53] = {.lex_state = 2}, + [54] = {.lex_state = 11}, + [55] = {.lex_state = 3}, + [56] = {.lex_state = 3}, + [57] = {.lex_state = 6}, + [58] = {.lex_state = 7}, + [59] = {.lex_state = 3}, + [60] = {.lex_state = 3}, + [61] = {.lex_state = 7}, + [62] = {.lex_state = 7}, + [63] = {.lex_state = 7}, + [64] = {.lex_state = 7}, + [65] = {.lex_state = 7}, + [66] = {.lex_state = 7}, + [67] = {.lex_state = 7}, + [68] = {.lex_state = 7}, + [69] = {.lex_state = 7}, + [70] = {.lex_state = 7}, + [71] = {.lex_state = 7}, + [72] = {.lex_state = 7}, + [73] = {.lex_state = 7}, + [74] = {.lex_state = 7}, + [75] = {.lex_state = 7}, + [76] = {.lex_state = 7}, + [77] = {.lex_state = 7}, + [78] = {.lex_state = 7}, + [79] = {.lex_state = 7}, + [80] = {.lex_state = 7}, + [81] = {.lex_state = 7}, + [82] = {.lex_state = 7}, + [83] = {.lex_state = 7}, + [84] = {.lex_state = 7}, + [85] = {.lex_state = 75}, + [86] = {.lex_state = 75}, + [87] = {.lex_state = 75}, + [88] = {.lex_state = 75}, + [89] = {.lex_state = 75}, + [90] = {.lex_state = 75}, + [91] = {.lex_state = 75}, + [92] = {.lex_state = 75}, + [93] = {.lex_state = 75}, + [94] = {.lex_state = 75}, + [95] = {.lex_state = 75}, + [96] = {.lex_state = 75}, + [97] = {.lex_state = 75}, + [98] = {.lex_state = 75}, + [99] = {.lex_state = 75}, + [100] = {.lex_state = 75}, + [101] = {.lex_state = 75}, + [102] = {.lex_state = 75}, + [103] = {.lex_state = 75}, + [104] = {.lex_state = 75}, + [105] = {.lex_state = 75}, + [106] = {.lex_state = 75}, + [107] = {.lex_state = 75}, + [108] = {.lex_state = 75}, + [109] = {.lex_state = 75}, + [110] = {.lex_state = 75}, + [111] = {.lex_state = 75}, + [112] = {.lex_state = 75}, + [113] = {.lex_state = 75}, + [114] = {.lex_state = 75}, + [115] = {.lex_state = 75}, + [116] = {.lex_state = 75}, + [117] = {.lex_state = 75}, + [118] = {.lex_state = 75}, + [119] = {.lex_state = 75}, + [120] = {.lex_state = 75}, + [121] = {.lex_state = 75}, + [122] = {.lex_state = 75}, + [123] = {.lex_state = 75}, + [124] = {.lex_state = 75}, + [125] = {.lex_state = 75}, + [126] = {.lex_state = 75}, + [127] = {.lex_state = 75}, + [128] = {.lex_state = 75}, + [129] = {.lex_state = 75}, + [130] = {.lex_state = 75}, + [131] = {.lex_state = 75}, + [132] = {.lex_state = 75}, + [133] = {.lex_state = 75}, + [134] = {.lex_state = 75}, + [135] = {.lex_state = 75}, + [136] = {.lex_state = 75}, + [137] = {.lex_state = 75}, + [138] = {.lex_state = 75}, + [139] = {.lex_state = 75}, + [140] = {.lex_state = 75}, + [141] = {.lex_state = 75}, + [142] = {.lex_state = 75}, + [143] = {.lex_state = 75}, [144] = {.lex_state = 2}, - [145] = {.lex_state = 4}, - [146] = {.lex_state = 4}, - [147] = {.lex_state = 4}, - [148] = {.lex_state = 4}, - [149] = {.lex_state = 4}, - [150] = {.lex_state = 13}, - [151] = {.lex_state = 3}, - [152] = {.lex_state = 71}, - [153] = {.lex_state = 71}, - [154] = {.lex_state = 71}, - [155] = {.lex_state = 71}, - [156] = {.lex_state = 71}, - [157] = {.lex_state = 71}, - [158] = {.lex_state = 71}, - [159] = {.lex_state = 71}, - [160] = {.lex_state = 71}, - [161] = {.lex_state = 71}, - [162] = {.lex_state = 2}, - [163] = {.lex_state = 71}, - [164] = {.lex_state = 71}, - [165] = {.lex_state = 71}, - [166] = {.lex_state = 71}, - [167] = {.lex_state = 71}, - [168] = {.lex_state = 71}, - [169] = {.lex_state = 71}, - [170] = {.lex_state = 71}, - [171] = {.lex_state = 71}, - [172] = {.lex_state = 71}, - [173] = {.lex_state = 71}, - [174] = {.lex_state = 71}, - [175] = {.lex_state = 71}, - [176] = {.lex_state = 2}, - [177] = {.lex_state = 71}, - [178] = {.lex_state = 71}, - [179] = {.lex_state = 71}, - [180] = {.lex_state = 71}, - [181] = {.lex_state = 71}, - [182] = {.lex_state = 0}, - [183] = {.lex_state = 71}, - [184] = {.lex_state = 71}, - [185] = {.lex_state = 71}, - [186] = {.lex_state = 71}, - [187] = {.lex_state = 71}, - [188] = {.lex_state = 71}, - [189] = {.lex_state = 71}, - [190] = {.lex_state = 71}, - [191] = {.lex_state = 71}, - [192] = {.lex_state = 71}, - [193] = {.lex_state = 0}, - [194] = {.lex_state = 71}, - [195] = {.lex_state = 0}, - [196] = {.lex_state = 0}, - [197] = {.lex_state = 2}, - [198] = {.lex_state = 2}, - [199] = {.lex_state = 71}, - [200] = {.lex_state = 3}, - [201] = {.lex_state = 0}, - [202] = {.lex_state = 2}, - [203] = {.lex_state = 0}, - [204] = {.lex_state = 0}, - [205] = {.lex_state = 2}, - [206] = {.lex_state = 2}, - [207] = {.lex_state = 71}, - [208] = {.lex_state = 71}, - [209] = {.lex_state = 71}, - [210] = {.lex_state = 71}, - [211] = {.lex_state = 71}, - [212] = {.lex_state = 71}, - [213] = {.lex_state = 71}, - [214] = {.lex_state = 71}, - [215] = {.lex_state = 71}, - [216] = {.lex_state = 71}, - [217] = {.lex_state = 71}, - [218] = {.lex_state = 71}, - [219] = {.lex_state = 71}, - [220] = {.lex_state = 71}, - [221] = {.lex_state = 71}, - [222] = {.lex_state = 71}, - [223] = {.lex_state = 71}, - [224] = {.lex_state = 71}, - [225] = {.lex_state = 71}, - [226] = {.lex_state = 71}, - [227] = {.lex_state = 71}, - [228] = {.lex_state = 71}, - [229] = {.lex_state = 71}, - [230] = {.lex_state = 71}, - [231] = {.lex_state = 71}, - [232] = {.lex_state = 71}, - [233] = {.lex_state = 71}, - [234] = {.lex_state = 71}, - [235] = {.lex_state = 71}, - [236] = {.lex_state = 71}, - [237] = {.lex_state = 71}, - [238] = {.lex_state = 71}, - [239] = {.lex_state = 71}, - [240] = {.lex_state = 71}, - [241] = {.lex_state = 71}, - [242] = {.lex_state = 71}, - [243] = {.lex_state = 2}, - [244] = {.lex_state = 2}, + [145] = {.lex_state = 3}, + [146] = {.lex_state = 2}, + [147] = {.lex_state = 2}, + [148] = {.lex_state = 6}, + [149] = {.lex_state = 6}, + [150] = {.lex_state = 6}, + [151] = {.lex_state = 7}, + [152] = {.lex_state = 7}, + [153] = {.lex_state = 7}, + [154] = {.lex_state = 16}, + [155] = {.lex_state = 4}, + [156] = {.lex_state = 75}, + [157] = {.lex_state = 75}, + [158] = {.lex_state = 75}, + [159] = {.lex_state = 75}, + [160] = {.lex_state = 75}, + [161] = {.lex_state = 16}, + [162] = {.lex_state = 75}, + [163] = {.lex_state = 75}, + [164] = {.lex_state = 75}, + [165] = {.lex_state = 75}, + [166] = {.lex_state = 75}, + [167] = {.lex_state = 75}, + [168] = {.lex_state = 75}, + [169] = {.lex_state = 75}, + [170] = {.lex_state = 75}, + [171] = {.lex_state = 75}, + [172] = {.lex_state = 75}, + [173] = {.lex_state = 75}, + [174] = {.lex_state = 75}, + [175] = {.lex_state = 75}, + [176] = {.lex_state = 75}, + [177] = {.lex_state = 75}, + [178] = {.lex_state = 4}, + [179] = {.lex_state = 75}, + [180] = {.lex_state = 75}, + [181] = {.lex_state = 75}, + [182] = {.lex_state = 75}, + [183] = {.lex_state = 4}, + [184] = {.lex_state = 75}, + [185] = {.lex_state = 75}, + [186] = {.lex_state = 75}, + [187] = {.lex_state = 75}, + [188] = {.lex_state = 75}, + [189] = {.lex_state = 75}, + [190] = {.lex_state = 75}, + [191] = {.lex_state = 75}, + [192] = {.lex_state = 75}, + [193] = {.lex_state = 75}, + [194] = {.lex_state = 75}, + [195] = {.lex_state = 75}, + [196] = {.lex_state = 75}, + [197] = {.lex_state = 0}, + [198] = {.lex_state = 75}, + [199] = {.lex_state = 75}, + [200] = {.lex_state = 75}, + [201] = {.lex_state = 16}, + [202] = {.lex_state = 16}, + [203] = {.lex_state = 75}, + [204] = {.lex_state = 75}, + [205] = {.lex_state = 75}, + [206] = {.lex_state = 0}, + [207] = {.lex_state = 75}, + [208] = {.lex_state = 75}, + [209] = {.lex_state = 75}, + [210] = {.lex_state = 0}, + [211] = {.lex_state = 0}, + [212] = {.lex_state = 75}, + [213] = {.lex_state = 75}, + [214] = {.lex_state = 75}, + [215] = {.lex_state = 75}, + [216] = {.lex_state = 75}, + [217] = {.lex_state = 75}, + [218] = {.lex_state = 75}, + [219] = {.lex_state = 75}, + [220] = {.lex_state = 75}, + [221] = {.lex_state = 75}, + [222] = {.lex_state = 75}, + [223] = {.lex_state = 75}, + [224] = {.lex_state = 75}, + [225] = {.lex_state = 75}, + [226] = {.lex_state = 75}, + [227] = {.lex_state = 75}, + [228] = {.lex_state = 75}, + [229] = {.lex_state = 75}, + [230] = {.lex_state = 75}, + [231] = {.lex_state = 75}, + [232] = {.lex_state = 75}, + [233] = {.lex_state = 75}, + [234] = {.lex_state = 75}, + [235] = {.lex_state = 75}, + [236] = {.lex_state = 75}, + [237] = {.lex_state = 75}, + [238] = {.lex_state = 75}, + [239] = {.lex_state = 75}, + [240] = {.lex_state = 75}, + [241] = {.lex_state = 2}, + [242] = {.lex_state = 4}, + [243] = {.lex_state = 4}, + [244] = {.lex_state = 4}, [245] = {.lex_state = 2}, [246] = {.lex_state = 2}, [247] = {.lex_state = 2}, [248] = {.lex_state = 2}, [249] = {.lex_state = 2}, [250] = {.lex_state = 2}, - [251] = {.lex_state = 2}, - [252] = {.lex_state = 13}, - [253] = {.lex_state = 0}, - [254] = {.lex_state = 0}, - [255] = {.lex_state = 0}, - [256] = {.lex_state = 0}, + [251] = {.lex_state = 16}, + [252] = {.lex_state = 2}, + [253] = {.lex_state = 2}, + [254] = {.lex_state = 2}, + [255] = {.lex_state = 2}, + [256] = {.lex_state = 2}, [257] = {.lex_state = 0}, - [258] = {.lex_state = 0}, - [259] = {.lex_state = 0}, - [260] = {.lex_state = 0}, + [258] = {.lex_state = 2}, + [259] = {.lex_state = 16}, + [260] = {.lex_state = 16}, [261] = {.lex_state = 0}, - [262] = {.lex_state = 0}, + [262] = {.lex_state = 1}, [263] = {.lex_state = 0}, [264] = {.lex_state = 0}, [265] = {.lex_state = 0}, @@ -16023,28 +16117,28 @@ static const TSLexMode ts_lex_modes[STATE_COUNT] = { [268] = {.lex_state = 0}, [269] = {.lex_state = 0}, [270] = {.lex_state = 0}, - [271] = {.lex_state = 0}, + [271] = {.lex_state = 1}, [272] = {.lex_state = 0}, - [273] = {.lex_state = 1}, + [273] = {.lex_state = 0}, [274] = {.lex_state = 0}, [275] = {.lex_state = 0}, [276] = {.lex_state = 0}, - [277] = {.lex_state = 0}, - [278] = {.lex_state = 1}, + [277] = {.lex_state = 1}, + [278] = {.lex_state = 0}, [279] = {.lex_state = 0}, [280] = {.lex_state = 0}, - [281] = {.lex_state = 0}, + [281] = {.lex_state = 1}, [282] = {.lex_state = 0}, [283] = {.lex_state = 0}, [284] = {.lex_state = 0}, [285] = {.lex_state = 0}, [286] = {.lex_state = 0}, - [287] = {.lex_state = 1}, - [288] = {.lex_state = 1}, - [289] = {.lex_state = 1}, + [287] = {.lex_state = 0}, + [288] = {.lex_state = 0}, + [289] = {.lex_state = 0}, [290] = {.lex_state = 0}, [291] = {.lex_state = 0}, - [292] = {.lex_state = 0}, + [292] = {.lex_state = 1}, [293] = {.lex_state = 0}, [294] = {.lex_state = 0}, [295] = {.lex_state = 0}, @@ -16118,15 +16212,19 @@ static const TSLexMode ts_lex_modes[STATE_COUNT] = { [363] = {.lex_state = 0}, [364] = {.lex_state = 0}, [365] = {.lex_state = 0}, - [366] = {.lex_state = 13}, + [366] = {.lex_state = 0}, [367] = {.lex_state = 0}, [368] = {.lex_state = 0}, - [369] = {.lex_state = 13}, + [369] = {.lex_state = 0}, [370] = {.lex_state = 0}, [371] = {.lex_state = 0}, [372] = {.lex_state = 0}, [373] = {.lex_state = 0}, [374] = {.lex_state = 0}, + [375] = {.lex_state = 0}, + [376] = {.lex_state = 0}, + [377] = {.lex_state = 0}, + [378] = {.lex_state = 0}, }; static const uint16_t ts_parse_table[LARGE_STATE_COUNT][SYMBOL_COUNT] = { @@ -16150,6 +16248,7 @@ static const uint16_t ts_parse_table[LARGE_STATE_COUNT][SYMBOL_COUNT] = { [anon_sym_RPAREN] = ACTIONS(1), [sym_pipeline_stub] = ACTIONS(1), [anon_sym_DOT] = ACTIONS(1), + [anon_sym_DOT2] = ACTIONS(1), [anon_sym_DOLLAR] = ACTIONS(1), [sym_identifier] = ACTIONS(1), [sym_int_literal] = ACTIONS(1), @@ -16169,20 +16268,20 @@ static const uint16_t ts_parse_table[LARGE_STATE_COUNT][SYMBOL_COUNT] = { [anon_sym_DASH_RBRACE_RBRACE] = ACTIONS(1), }, [1] = { - [sym_template] = STATE(374), - [sym__block] = STATE(87), - [sym_text] = STATE(87), - [sym__action] = STATE(87), - [sym__comment_action] = STATE(87), - [sym__pipeline_action] = STATE(87), - [sym_if_action] = STATE(87), - [sym_range_action] = STATE(87), - [sym_template_action] = STATE(87), - [sym_define_action] = STATE(87), - [sym_block_action] = STATE(87), - [sym_with_action] = STATE(87), - [sym__left_delimiter] = STATE(53), - [aux_sym_template_repeat1] = STATE(87), + [sym_template] = STATE(378), + [sym__block] = STATE(90), + [sym_text] = STATE(90), + [sym__action] = STATE(90), + [sym__comment_action] = STATE(90), + [sym__pipeline_action] = STATE(90), + [sym_if_action] = STATE(90), + [sym_range_action] = STATE(90), + [sym_template_action] = STATE(90), + [sym_define_action] = STATE(90), + [sym_block_action] = STATE(90), + [sym_with_action] = STATE(90), + [sym__left_delimiter] = STATE(54), + [aux_sym_template_repeat1] = STATE(90), [ts_builtin_sym_end] = ACTIONS(3), [aux_sym_text_token1] = ACTIONS(5), [aux_sym_text_token2] = ACTIONS(5), @@ -16195,7 +16294,7 @@ static const uint16_t ts_parse_table[LARGE_STATE_COUNT][SYMBOL_COUNT] = { }; static const uint16_t ts_small_parse_table[] = { - [0] = 20, + [0] = 22, ACTIONS(9), 1, anon_sym_if, ACTIONS(11), 1, @@ -16217,7 +16316,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -16226,8 +16325,12 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_DQUOTE, ACTIONS(41), 1, sym_comment, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -16241,12 +16344,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -16255,7 +16357,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [80] = 20, + [85] = 22, ACTIONS(9), 1, anon_sym_if, ACTIONS(11), 1, @@ -16275,7 +16377,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -16286,8 +16388,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(43), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -16301,12 +16407,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -16315,7 +16420,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [160] = 19, + [170] = 21, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -16331,7 +16436,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -16344,8 +16449,12 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_else, ACTIONS(47), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -16359,12 +16468,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -16373,7 +16481,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [237] = 19, + [252] = 21, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -16389,7 +16497,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -16402,8 +16510,12 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_else, ACTIONS(51), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -16417,12 +16529,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -16431,7 +16542,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [314] = 19, + [334] = 21, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -16447,7 +16558,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -16460,8 +16571,12 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_else, ACTIONS(55), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -16475,12 +16590,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -16489,7 +16603,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [391] = 19, + [416] = 21, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -16505,7 +16619,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -16518,8 +16632,12 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_else, ACTIONS(59), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -16533,12 +16651,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -16547,7 +16664,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [468] = 19, + [498] = 21, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -16563,7 +16680,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -16576,8 +16693,12 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_else, ACTIONS(63), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -16591,12 +16712,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -16605,7 +16725,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [545] = 19, + [580] = 21, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -16621,7 +16741,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -16634,8 +16754,12 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_else, ACTIONS(67), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -16649,12 +16773,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -16663,7 +16786,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [622] = 19, + [662] = 21, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -16679,7 +16802,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -16692,8 +16815,12 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_else, ACTIONS(71), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -16707,12 +16834,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -16721,7 +16847,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [699] = 19, + [744] = 21, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -16737,7 +16863,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -16750,8 +16876,12 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_else, ACTIONS(75), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -16765,12 +16895,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -16779,7 +16908,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [776] = 19, + [826] = 21, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -16795,7 +16924,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -16808,8 +16937,12 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_else, ACTIONS(79), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -16823,12 +16956,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -16837,7 +16969,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [853] = 19, + [908] = 21, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -16853,7 +16985,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -16866,8 +16998,12 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_else, ACTIONS(83), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -16881,12 +17017,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -16895,7 +17030,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [930] = 19, + [990] = 21, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -16911,7 +17046,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -16924,8 +17059,12 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_else, ACTIONS(87), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -16939,12 +17078,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -16953,7 +17091,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [1007] = 19, + [1072] = 21, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -16969,7 +17107,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -16982,8 +17120,12 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_else, ACTIONS(91), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -16997,12 +17139,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -17011,7 +17152,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [1084] = 18, + [1154] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -17027,7 +17168,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -17038,8 +17179,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(93), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -17053,12 +17198,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -17067,7 +17211,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [1158] = 18, + [1233] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -17083,7 +17227,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -17094,8 +17238,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(95), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -17109,12 +17257,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -17123,7 +17270,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [1232] = 18, + [1312] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -17139,7 +17286,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -17150,8 +17297,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(97), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -17165,12 +17316,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -17179,7 +17329,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [1306] = 18, + [1391] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -17195,7 +17345,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -17206,8 +17356,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(99), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -17221,12 +17375,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -17235,7 +17388,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [1380] = 18, + [1470] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -17251,7 +17404,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -17262,8 +17415,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(101), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -17277,12 +17434,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -17291,7 +17447,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [1454] = 18, + [1549] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -17307,7 +17463,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -17318,8 +17474,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(103), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -17333,12 +17493,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -17347,7 +17506,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [1528] = 18, + [1628] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -17363,7 +17522,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -17374,8 +17533,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(105), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -17389,12 +17552,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -17403,7 +17565,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [1602] = 18, + [1707] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -17419,7 +17581,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -17430,8 +17592,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(107), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -17445,12 +17611,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -17459,7 +17624,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [1676] = 18, + [1786] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -17475,7 +17640,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -17486,8 +17651,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(109), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -17501,12 +17670,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -17515,7 +17683,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [1750] = 18, + [1865] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -17531,7 +17699,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -17542,8 +17710,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(111), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -17557,12 +17729,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -17571,7 +17742,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [1824] = 18, + [1944] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -17587,7 +17758,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -17598,8 +17769,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(113), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -17613,12 +17788,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -17627,7 +17801,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [1898] = 18, + [2023] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -17643,7 +17817,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -17654,8 +17828,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(115), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -17669,12 +17847,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -17683,7 +17860,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [1972] = 18, + [2102] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -17699,7 +17876,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -17710,8 +17887,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(117), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -17725,12 +17906,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -17739,7 +17919,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [2046] = 18, + [2181] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -17755,7 +17935,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -17766,8 +17946,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(119), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -17781,12 +17965,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -17795,7 +17978,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [2120] = 18, + [2260] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -17811,7 +17994,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -17822,8 +18005,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(121), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -17837,12 +18024,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -17851,7 +18037,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [2194] = 18, + [2339] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -17867,7 +18053,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -17878,8 +18064,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(123), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -17893,12 +18083,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -17907,7 +18096,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [2268] = 18, + [2418] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -17923,7 +18112,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -17934,8 +18123,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(125), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -17949,12 +18142,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -17963,7 +18155,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [2342] = 18, + [2497] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -17979,7 +18171,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -17990,8 +18182,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(127), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -18005,12 +18201,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -18019,7 +18214,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [2416] = 18, + [2576] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -18035,7 +18230,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -18046,8 +18241,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(129), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -18061,12 +18260,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -18075,7 +18273,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [2490] = 18, + [2655] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -18091,7 +18289,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -18102,8 +18300,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(131), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -18117,12 +18319,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -18131,7 +18332,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [2564] = 18, + [2734] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -18147,7 +18348,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -18158,8 +18359,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(133), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -18173,12 +18378,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -18187,7 +18391,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [2638] = 18, + [2813] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -18203,7 +18407,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -18214,8 +18418,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(135), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -18229,12 +18437,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -18243,7 +18450,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [2712] = 18, + [2892] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -18259,7 +18466,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -18270,8 +18477,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(137), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -18285,12 +18496,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -18299,7 +18509,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [2786] = 18, + [2971] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -18315,7 +18525,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -18326,8 +18536,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(139), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -18341,12 +18555,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -18355,7 +18568,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [2860] = 18, + [3050] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -18371,7 +18584,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -18382,8 +18595,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(141), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -18397,12 +18614,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -18411,7 +18627,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [2934] = 18, + [3129] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -18427,7 +18643,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -18438,8 +18654,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(143), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -18453,12 +18673,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -18467,7 +18686,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [3008] = 18, + [3208] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -18483,7 +18702,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -18494,8 +18713,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(145), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -18509,12 +18732,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -18523,7 +18745,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [3082] = 18, + [3287] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -18539,7 +18761,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -18550,8 +18772,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(147), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -18565,12 +18791,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -18579,7 +18804,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [3156] = 18, + [3366] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -18595,7 +18820,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -18606,8 +18831,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(149), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -18621,12 +18850,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -18635,7 +18863,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [3230] = 18, + [3445] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -18651,7 +18879,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -18662,8 +18890,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(151), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -18677,12 +18909,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -18691,7 +18922,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [3304] = 18, + [3524] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -18707,7 +18938,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -18718,8 +18949,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(153), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -18733,12 +18968,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -18747,7 +18981,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [3378] = 18, + [3603] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -18763,7 +18997,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -18774,8 +19008,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(155), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -18789,12 +19027,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -18803,7 +19040,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [3452] = 18, + [3682] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -18819,7 +19056,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -18830,8 +19067,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(157), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -18845,12 +19086,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -18859,7 +19099,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [3526] = 18, + [3761] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -18875,7 +19115,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -18886,8 +19126,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(159), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -18901,12 +19145,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -18915,7 +19158,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [3600] = 18, + [3840] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -18931,7 +19174,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -18942,8 +19185,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(161), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -18957,12 +19204,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -18971,7 +19217,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [3674] = 18, + [3919] = 20, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -18987,7 +19233,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -18998,8 +19244,12 @@ static const uint16_t ts_small_parse_table[] = { sym_comment, ACTIONS(163), 1, anon_sym_end, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -19013,12 +19263,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -19027,7 +19276,7 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [3748] = 17, + [3998] = 19, ACTIONS(9), 1, anon_sym_if, ACTIONS(17), 1, @@ -19043,7 +19292,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, @@ -19052,8 +19301,12 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_DQUOTE, ACTIONS(41), 1, sym_comment, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -19067,12 +19320,64 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(255), 13, + STATE(294), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, + sym_method_call, + sym_function_call, + sym__expression, + sym__literal, + sym__boolean_literal, + sym_dot, + sym__string_literal, + sym_interpreted_string_literal, + [4074] = 15, + ACTIONS(167), 1, + anon_sym_LPAREN, + ACTIONS(169), 1, + anon_sym_, + ACTIONS(171), 1, + anon_sym_DOT, + ACTIONS(173), 1, + anon_sym_DOT2, + ACTIONS(175), 1, + anon_sym_DOLLAR, + ACTIONS(177), 1, + sym_identifier, + ACTIONS(181), 1, + anon_sym_DQUOTE, + STATE(56), 1, + sym_unfished_selector_expression, + STATE(178), 1, + sym_variable, + STATE(243), 1, sym_parenthesized_pipeline, + STATE(254), 1, + sym_argument_list, + STATE(53), 2, + sym_selector_expression, + sym_field, + ACTIONS(165), 4, + anon_sym_PIPE, + anon_sym_RPAREN, + anon_sym_RBRACE_RBRACE, + anon_sym_DASH_RBRACE_RBRACE, + ACTIONS(179), 8, + sym_int_literal, + sym_float_literal, + sym_imaginary_literal, + sym_rune_literal, + sym_true, + sym_false, + sym_nil, + sym_raw_string_literal, + STATE(245), 12, + sym__pipeline, + sym_variable_definition, + sym_assignment, + sym_chained_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -19081,52 +19386,55 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [3819] = 17, + [4142] = 19, ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, sym_identifier, ACTIONS(39), 1, anon_sym_DQUOTE, - ACTIONS(165), 1, + ACTIONS(183), 1, anon_sym_if, - ACTIONS(167), 1, + ACTIONS(185), 1, anon_sym_range, - ACTIONS(169), 1, + ACTIONS(187), 1, anon_sym_template, - ACTIONS(171), 1, + ACTIONS(189), 1, anon_sym_define, - ACTIONS(173), 1, + ACTIONS(191), 1, anon_sym_block, - ACTIONS(175), 1, + ACTIONS(193), 1, anon_sym_with, - ACTIONS(181), 1, + ACTIONS(199), 1, sym_comment, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, - ACTIONS(179), 3, + ACTIONS(197), 3, sym_imaginary_literal, sym_rune_literal, sym_raw_string_literal, - ACTIONS(177), 5, + ACTIONS(195), 5, sym_int_literal, sym_float_literal, sym_true, sym_false, sym_nil, - STATE(256), 13, + STATE(272), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -19135,32 +19443,36 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [3890] = 12, - ACTIONS(185), 1, + [4218] = 14, + ACTIONS(167), 1, anon_sym_LPAREN, - ACTIONS(187), 1, - anon_sym_, - ACTIONS(189), 1, - anon_sym_DOT, - ACTIONS(191), 1, + ACTIONS(173), 1, + anon_sym_DOT2, + ACTIONS(175), 1, anon_sym_DOLLAR, - ACTIONS(193), 1, + ACTIONS(177), 1, sym_identifier, - ACTIONS(197), 1, + ACTIONS(181), 1, anon_sym_DQUOTE, - STATE(176), 1, + ACTIONS(203), 1, + anon_sym_, + STATE(56), 1, + sym_unfished_selector_expression, + STATE(178), 1, sym_variable, - STATE(244), 1, + STATE(243), 1, + sym_parenthesized_pipeline, + STATE(256), 1, sym_argument_list, - STATE(55), 2, + STATE(53), 2, sym_selector_expression, sym_field, - ACTIONS(183), 4, + ACTIONS(201), 4, anon_sym_PIPE, anon_sym_RPAREN, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - ACTIONS(195), 8, + ACTIONS(179), 8, sym_int_literal, sym_float_literal, sym_imaginary_literal, @@ -19169,12 +19481,11 @@ static const uint16_t ts_small_parse_table[] = { sym_false, sym_nil, sym_raw_string_literal, - STATE(206), 13, + STATE(245), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -19183,31 +19494,36 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [3950] = 11, - ACTIONS(185), 1, + [4283] = 14, + ACTIONS(167), 1, anon_sym_LPAREN, - ACTIONS(191), 1, + ACTIONS(169), 1, + anon_sym_, + ACTIONS(173), 1, + anon_sym_DOT2, + ACTIONS(175), 1, anon_sym_DOLLAR, - ACTIONS(193), 1, + ACTIONS(177), 1, sym_identifier, - ACTIONS(197), 1, + ACTIONS(181), 1, anon_sym_DQUOTE, - ACTIONS(201), 1, - anon_sym_, - STATE(176), 1, + STATE(56), 1, + sym_unfished_selector_expression, + STATE(178), 1, sym_variable, - STATE(246), 1, + STATE(243), 1, + sym_parenthesized_pipeline, + STATE(254), 1, sym_argument_list, - STATE(55), 2, + STATE(53), 2, sym_selector_expression, sym_field, - ACTIONS(199), 5, + ACTIONS(165), 4, anon_sym_PIPE, anon_sym_RPAREN, - anon_sym_DOT, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - ACTIONS(195), 8, + ACTIONS(179), 8, sym_int_literal, sym_float_literal, sym_imaginary_literal, @@ -19216,12 +19532,11 @@ static const uint16_t ts_small_parse_table[] = { sym_false, sym_nil, sym_raw_string_literal, - STATE(206), 13, + STATE(245), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -19230,44 +19545,50 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [4008] = 11, - ACTIONS(185), 1, + [4348] = 15, + ACTIONS(173), 1, + anon_sym_DOT2, + ACTIONS(177), 1, + sym_identifier, + ACTIONS(205), 1, anon_sym_LPAREN, - ACTIONS(189), 1, + ACTIONS(207), 1, anon_sym_DOT, - ACTIONS(191), 1, + ACTIONS(209), 1, anon_sym_DOLLAR, - ACTIONS(193), 1, - sym_identifier, - ACTIONS(197), 1, + ACTIONS(215), 1, anon_sym_DQUOTE, - ACTIONS(205), 1, - anon_sym_, - STATE(176), 1, + STATE(56), 1, + sym_unfished_selector_expression, + STATE(178), 1, sym_variable, - STATE(55), 2, + STATE(243), 1, + sym_parenthesized_pipeline, + STATE(278), 1, + sym_argument_list, + STATE(53), 2, sym_selector_expression, sym_field, - ACTIONS(203), 4, + ACTIONS(213), 3, + sym_imaginary_literal, + sym_rune_literal, + sym_raw_string_literal, + ACTIONS(169), 4, anon_sym_PIPE, anon_sym_RPAREN, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - ACTIONS(207), 8, + ACTIONS(211), 5, sym_int_literal, sym_float_literal, - sym_imaginary_literal, - sym_rune_literal, sym_true, sym_false, sym_nil, - sym_raw_string_literal, - STATE(247), 13, + STATE(248), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -19276,45 +19597,48 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [4065] = 12, - ACTIONS(193), 1, + [4415] = 14, + ACTIONS(173), 1, + anon_sym_DOT2, + ACTIONS(177), 1, sym_identifier, - ACTIONS(199), 1, - anon_sym_DOT, - ACTIONS(209), 1, + ACTIONS(205), 1, anon_sym_LPAREN, - ACTIONS(211), 1, + ACTIONS(209), 1, anon_sym_DOLLAR, - ACTIONS(217), 1, + ACTIONS(215), 1, anon_sym_DQUOTE, - STATE(176), 1, + STATE(56), 1, + sym_unfished_selector_expression, + STATE(178), 1, sym_variable, - STATE(263), 1, + STATE(243), 1, + sym_parenthesized_pipeline, + STATE(278), 1, sym_argument_list, - STATE(55), 2, + STATE(53), 2, sym_selector_expression, sym_field, - ACTIONS(215), 3, + ACTIONS(213), 3, sym_imaginary_literal, sym_rune_literal, sym_raw_string_literal, - ACTIONS(201), 4, + ACTIONS(169), 4, anon_sym_PIPE, anon_sym_RPAREN, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - ACTIONS(213), 5, + ACTIONS(211), 5, sym_int_literal, sym_float_literal, sym_true, sym_false, sym_nil, - STATE(197), 13, + STATE(248), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -19323,30 +19647,34 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [4124] = 11, - ACTIONS(185), 1, + [4479] = 13, + ACTIONS(167), 1, anon_sym_LPAREN, - ACTIONS(189), 1, - anon_sym_DOT, - ACTIONS(191), 1, + ACTIONS(173), 1, + anon_sym_DOT2, + ACTIONS(175), 1, anon_sym_DOLLAR, - ACTIONS(193), 1, + ACTIONS(177), 1, sym_identifier, - ACTIONS(197), 1, + ACTIONS(181), 1, anon_sym_DQUOTE, - ACTIONS(221), 1, + ACTIONS(219), 1, anon_sym_, - STATE(176), 1, + STATE(56), 1, + sym_unfished_selector_expression, + STATE(178), 1, sym_variable, - STATE(55), 2, + STATE(243), 1, + sym_parenthesized_pipeline, + STATE(53), 2, sym_selector_expression, sym_field, - ACTIONS(219), 4, + ACTIONS(217), 4, anon_sym_PIPE, anon_sym_RPAREN, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - ACTIONS(207), 8, + ACTIONS(221), 8, sym_int_literal, sym_float_literal, sym_imaginary_literal, @@ -19355,12 +19683,11 @@ static const uint16_t ts_small_parse_table[] = { sym_false, sym_nil, sym_raw_string_literal, - STATE(247), 13, + STATE(258), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -19369,45 +19696,47 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [4181] = 12, - ACTIONS(189), 1, - anon_sym_DOT, - ACTIONS(193), 1, - sym_identifier, - ACTIONS(209), 1, + [4541] = 13, + ACTIONS(167), 1, anon_sym_LPAREN, - ACTIONS(211), 1, + ACTIONS(173), 1, + anon_sym_DOT2, + ACTIONS(175), 1, anon_sym_DOLLAR, - ACTIONS(217), 1, + ACTIONS(177), 1, + sym_identifier, + ACTIONS(181), 1, anon_sym_DQUOTE, - STATE(176), 1, + ACTIONS(225), 1, + anon_sym_, + STATE(56), 1, + sym_unfished_selector_expression, + STATE(178), 1, sym_variable, - STATE(261), 1, - sym_argument_list, - STATE(55), 2, + STATE(243), 1, + sym_parenthesized_pipeline, + STATE(53), 2, sym_selector_expression, sym_field, - ACTIONS(215), 3, - sym_imaginary_literal, - sym_rune_literal, - sym_raw_string_literal, - ACTIONS(187), 4, + ACTIONS(223), 4, anon_sym_PIPE, anon_sym_RPAREN, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - ACTIONS(213), 5, + ACTIONS(221), 8, sym_int_literal, sym_float_literal, + sym_imaginary_literal, + sym_rune_literal, sym_true, sym_false, sym_nil, - STATE(197), 13, + sym_raw_string_literal, + STATE(258), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -19416,43 +19745,48 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [4240] = 11, - ACTIONS(189), 1, - anon_sym_DOT, - ACTIONS(193), 1, + [4603] = 14, + ACTIONS(173), 1, + anon_sym_DOT2, + ACTIONS(177), 1, sym_identifier, - ACTIONS(209), 1, + ACTIONS(205), 1, anon_sym_LPAREN, - ACTIONS(211), 1, + ACTIONS(209), 1, anon_sym_DOLLAR, - ACTIONS(217), 1, + ACTIONS(215), 1, anon_sym_DQUOTE, - STATE(176), 1, + STATE(56), 1, + sym_unfished_selector_expression, + STATE(178), 1, sym_variable, - STATE(55), 2, + STATE(243), 1, + sym_parenthesized_pipeline, + STATE(276), 1, + sym_argument_list, + STATE(53), 2, sym_selector_expression, sym_field, - ACTIONS(223), 3, + ACTIONS(213), 3, sym_imaginary_literal, sym_rune_literal, sym_raw_string_literal, - ACTIONS(221), 4, + ACTIONS(203), 4, anon_sym_PIPE, anon_sym_RPAREN, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - ACTIONS(207), 5, + ACTIONS(211), 5, sym_int_literal, sym_float_literal, sym_true, sym_false, sym_nil, - STATE(247), 13, + STATE(248), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -19461,43 +19795,46 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [4296] = 11, - ACTIONS(189), 1, - anon_sym_DOT, - ACTIONS(193), 1, + [4667] = 13, + ACTIONS(173), 1, + anon_sym_DOT2, + ACTIONS(177), 1, sym_identifier, - ACTIONS(209), 1, + ACTIONS(205), 1, anon_sym_LPAREN, - ACTIONS(211), 1, + ACTIONS(209), 1, anon_sym_DOLLAR, - ACTIONS(217), 1, + ACTIONS(215), 1, anon_sym_DQUOTE, - STATE(176), 1, + STATE(56), 1, + sym_unfished_selector_expression, + STATE(178), 1, sym_variable, - STATE(55), 2, + STATE(243), 1, + sym_parenthesized_pipeline, + STATE(53), 2, sym_selector_expression, sym_field, - ACTIONS(223), 3, + ACTIONS(227), 3, sym_imaginary_literal, sym_rune_literal, sym_raw_string_literal, - ACTIONS(205), 4, + ACTIONS(225), 4, anon_sym_PIPE, anon_sym_RPAREN, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - ACTIONS(207), 5, + ACTIONS(221), 5, sym_int_literal, sym_float_literal, sym_true, sym_false, sym_nil, - STATE(247), 13, + STATE(258), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -19506,43 +19843,46 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [4352] = 12, - ACTIONS(27), 1, + [4728] = 13, + ACTIONS(173), 1, + anon_sym_DOT2, + ACTIONS(177), 1, + sym_identifier, + ACTIONS(205), 1, anon_sym_LPAREN, - ACTIONS(29), 1, - anon_sym_DOT, - ACTIONS(31), 1, + ACTIONS(209), 1, anon_sym_DOLLAR, - ACTIONS(33), 1, - sym_identifier, - ACTIONS(39), 1, + ACTIONS(215), 1, anon_sym_DQUOTE, - STATE(168), 1, - sym__right_delimiter, - STATE(195), 1, + STATE(56), 1, + sym_unfished_selector_expression, + STATE(178), 1, sym_variable, - ACTIONS(229), 2, - anon_sym_RBRACE_RBRACE, - anon_sym_DASH_RBRACE_RBRACE, - STATE(57), 2, + STATE(243), 1, + sym_parenthesized_pipeline, + STATE(53), 2, sym_selector_expression, sym_field, ACTIONS(227), 3, sym_imaginary_literal, sym_rune_literal, sym_raw_string_literal, - ACTIONS(225), 5, + ACTIONS(219), 4, + anon_sym_PIPE, + anon_sym_RPAREN, + anon_sym_RBRACE_RBRACE, + anon_sym_DASH_RBRACE_RBRACE, + ACTIONS(221), 5, sym_int_literal, sym_float_literal, sym_true, sym_false, sym_nil, - STATE(265), 13, + STATE(258), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -19551,43 +19891,46 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [4409] = 12, + [4789] = 14, ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, sym_identifier, ACTIONS(39), 1, anon_sym_DQUOTE, + STATE(58), 1, + sym_unfished_selector_expression, STATE(111), 1, sym__right_delimiter, - STATE(195), 1, + STATE(201), 1, sym_variable, - ACTIONS(235), 2, + STATE(260), 1, + sym_parenthesized_pipeline, + ACTIONS(233), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, STATE(57), 2, sym_selector_expression, sym_field, - ACTIONS(233), 3, + ACTIONS(231), 3, sym_imaginary_literal, sym_rune_literal, sym_raw_string_literal, - ACTIONS(231), 5, + ACTIONS(229), 5, sym_int_literal, sym_float_literal, sym_true, sym_false, sym_nil, - STATE(267), 13, + STATE(267), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -19596,43 +19939,46 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [4466] = 12, + [4851] = 14, ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, sym_identifier, ACTIONS(39), 1, anon_sym_DQUOTE, - STATE(195), 1, - sym_variable, - STATE(217), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(173), 1, sym__right_delimiter, - ACTIONS(241), 2, + STATE(201), 1, + sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, + ACTIONS(239), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, STATE(57), 2, sym_selector_expression, sym_field, - ACTIONS(239), 3, + ACTIONS(237), 3, sym_imaginary_literal, sym_rune_literal, sym_raw_string_literal, - ACTIONS(237), 5, + ACTIONS(235), 5, sym_int_literal, sym_float_literal, sym_true, sym_false, sym_nil, - STATE(264), 13, + STATE(279), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -19641,43 +19987,46 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [4523] = 12, + [4913] = 14, ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, sym_identifier, ACTIONS(39), 1, anon_sym_DQUOTE, - STATE(109), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(113), 1, sym__right_delimiter, - STATE(195), 1, + STATE(201), 1, sym_variable, - ACTIONS(247), 2, + STATE(260), 1, + sym_parenthesized_pipeline, + ACTIONS(245), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, STATE(57), 2, sym_selector_expression, sym_field, - ACTIONS(245), 3, + ACTIONS(243), 3, sym_imaginary_literal, sym_rune_literal, sym_raw_string_literal, - ACTIONS(243), 5, + ACTIONS(241), 5, sym_int_literal, sym_float_literal, sym_true, sym_false, sym_nil, - STATE(262), 13, + STATE(280), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -19686,40 +20035,46 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [4580] = 11, + [4975] = 14, ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, sym_identifier, ACTIONS(39), 1, anon_sym_DQUOTE, - STATE(196), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, - STATE(352), 1, - sym_range_variable_definition, + STATE(226), 1, + sym__right_delimiter, + STATE(260), 1, + sym_parenthesized_pipeline, + ACTIONS(251), 2, + anon_sym_RBRACE_RBRACE, + anon_sym_DASH_RBRACE_RBRACE, STATE(57), 2, sym_selector_expression, sym_field, - ACTIONS(251), 3, + ACTIONS(249), 3, sym_imaginary_literal, sym_rune_literal, sym_raw_string_literal, - ACTIONS(249), 5, + ACTIONS(247), 5, sym_int_literal, sym_float_literal, sym_true, sym_false, sym_nil, - STATE(258), 13, + STATE(289), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -19728,20 +20083,24 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [4633] = 11, + [5037] = 13, ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, sym_identifier, ACTIONS(39), 1, anon_sym_DQUOTE, - STATE(196), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(202), 1, sym_variable, - STATE(359), 1, + STATE(260), 1, + sym_parenthesized_pipeline, + STATE(355), 1, sym_range_variable_definition, STATE(57), 2, sym_selector_expression, @@ -19756,12 +20115,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(272), 13, + STATE(274), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -19770,20 +20128,26 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [4686] = 10, - ACTIONS(189), 1, - anon_sym_DOT, - ACTIONS(193), 1, - sym_identifier, - ACTIONS(209), 1, + [5095] = 13, + ACTIONS(27), 1, anon_sym_LPAREN, - ACTIONS(211), 1, + ACTIONS(29), 1, + anon_sym_DOT2, + ACTIONS(31), 1, anon_sym_DOLLAR, - ACTIONS(217), 1, + ACTIONS(33), 1, + sym_identifier, + ACTIONS(39), 1, anon_sym_DQUOTE, - STATE(176), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(202), 1, sym_variable, - STATE(55), 2, + STATE(260), 1, + sym_parenthesized_pipeline, + STATE(363), 1, + sym_range_variable_definition, + STATE(57), 2, sym_selector_expression, sym_field, ACTIONS(259), 3, @@ -19796,12 +20160,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(251), 13, + STATE(270), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -19810,38 +20173,41 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [4736] = 10, - ACTIONS(27), 1, + [5153] = 12, + ACTIONS(173), 1, + anon_sym_DOT2, + ACTIONS(177), 1, + sym_identifier, + ACTIONS(205), 1, anon_sym_LPAREN, - ACTIONS(29), 1, - anon_sym_DOT, - ACTIONS(31), 1, + ACTIONS(209), 1, anon_sym_DOLLAR, - ACTIONS(33), 1, - sym_identifier, - ACTIONS(39), 1, + ACTIONS(215), 1, anon_sym_DQUOTE, - STATE(195), 1, + STATE(56), 1, + sym_unfished_selector_expression, + STATE(178), 1, sym_variable, - STATE(57), 2, + STATE(243), 1, + sym_parenthesized_pipeline, + STATE(53), 2, sym_selector_expression, sym_field, - ACTIONS(263), 3, + ACTIONS(227), 3, sym_imaginary_literal, sym_rune_literal, sym_raw_string_literal, - ACTIONS(261), 5, + ACTIONS(221), 5, sym_int_literal, sym_float_literal, sym_true, sym_false, sym_nil, - STATE(266), 13, + STATE(258), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -19850,38 +20216,41 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [4786] = 10, + [5208] = 12, ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, sym_identifier, ACTIONS(39), 1, anon_sym_DQUOTE, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, - ACTIONS(267), 3, + ACTIONS(263), 3, sym_imaginary_literal, sym_rune_literal, sym_raw_string_literal, - ACTIONS(265), 5, + ACTIONS(261), 5, sym_int_literal, sym_float_literal, sym_true, sym_false, sym_nil, - STATE(253), 13, + STATE(282), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -19890,38 +20259,41 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [4836] = 10, - ACTIONS(27), 1, + [5263] = 12, + ACTIONS(173), 1, + anon_sym_DOT2, + ACTIONS(177), 1, + sym_identifier, + ACTIONS(205), 1, anon_sym_LPAREN, - ACTIONS(29), 1, - anon_sym_DOT, - ACTIONS(31), 1, + ACTIONS(209), 1, anon_sym_DOLLAR, - ACTIONS(33), 1, - sym_identifier, - ACTIONS(39), 1, + ACTIONS(215), 1, anon_sym_DQUOTE, - STATE(195), 1, + STATE(56), 1, + sym_unfished_selector_expression, + STATE(178), 1, sym_variable, - STATE(57), 2, + STATE(243), 1, + sym_parenthesized_pipeline, + STATE(53), 2, sym_selector_expression, sym_field, - ACTIONS(271), 3, + ACTIONS(267), 3, sym_imaginary_literal, sym_rune_literal, sym_raw_string_literal, - ACTIONS(269), 5, + ACTIONS(265), 5, sym_int_literal, sym_float_literal, sym_true, sym_false, sym_nil, - STATE(271), 13, + STATE(249), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -19930,38 +20302,41 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [4886] = 10, - ACTIONS(27), 1, + [5318] = 12, + ACTIONS(173), 1, + anon_sym_DOT2, + ACTIONS(177), 1, + sym_identifier, + ACTIONS(205), 1, anon_sym_LPAREN, - ACTIONS(29), 1, - anon_sym_DOT, - ACTIONS(31), 1, + ACTIONS(209), 1, anon_sym_DOLLAR, - ACTIONS(33), 1, - sym_identifier, - ACTIONS(39), 1, + ACTIONS(215), 1, anon_sym_DQUOTE, - STATE(195), 1, + STATE(56), 1, + sym_unfished_selector_expression, + STATE(178), 1, sym_variable, - STATE(57), 2, + STATE(243), 1, + sym_parenthesized_pipeline, + STATE(53), 2, sym_selector_expression, sym_field, - ACTIONS(275), 3, + ACTIONS(271), 3, sym_imaginary_literal, sym_rune_literal, sym_raw_string_literal, - ACTIONS(273), 5, + ACTIONS(269), 5, sym_int_literal, sym_float_literal, sym_true, sym_false, sym_nil, - STATE(270), 13, + STATE(250), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -19970,38 +20345,41 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [4936] = 10, + [5373] = 12, ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, sym_identifier, ACTIONS(39), 1, anon_sym_DQUOTE, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, - ACTIONS(279), 3, + ACTIONS(275), 3, sym_imaginary_literal, sym_rune_literal, sym_raw_string_literal, - ACTIONS(277), 5, + ACTIONS(273), 5, sym_int_literal, sym_float_literal, sym_true, sym_false, sym_nil, - STATE(283), 13, + STATE(333), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -20010,38 +20388,41 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [4986] = 10, - ACTIONS(189), 1, - anon_sym_DOT, - ACTIONS(193), 1, + [5428] = 12, + ACTIONS(173), 1, + anon_sym_DOT2, + ACTIONS(177), 1, sym_identifier, - ACTIONS(209), 1, + ACTIONS(205), 1, anon_sym_LPAREN, - ACTIONS(211), 1, + ACTIONS(209), 1, anon_sym_DOLLAR, - ACTIONS(217), 1, + ACTIONS(215), 1, anon_sym_DQUOTE, - STATE(176), 1, + STATE(56), 1, + sym_unfished_selector_expression, + STATE(178), 1, sym_variable, - STATE(55), 2, + STATE(243), 1, + sym_parenthesized_pipeline, + STATE(53), 2, sym_selector_expression, sym_field, - ACTIONS(223), 3, + ACTIONS(279), 3, sym_imaginary_literal, sym_rune_literal, sym_raw_string_literal, - ACTIONS(207), 5, + ACTIONS(277), 5, sym_int_literal, sym_float_literal, sym_true, sym_false, sym_nil, - STATE(247), 13, + STATE(252), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -20050,20 +20431,24 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [5036] = 10, - ACTIONS(189), 1, - anon_sym_DOT, - ACTIONS(193), 1, - sym_identifier, - ACTIONS(209), 1, + [5483] = 12, + ACTIONS(27), 1, anon_sym_LPAREN, - ACTIONS(211), 1, + ACTIONS(29), 1, + anon_sym_DOT2, + ACTIONS(31), 1, anon_sym_DOLLAR, - ACTIONS(217), 1, + ACTIONS(33), 1, + sym_identifier, + ACTIONS(39), 1, anon_sym_DQUOTE, - STATE(176), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, - STATE(55), 2, + STATE(260), 1, + sym_parenthesized_pipeline, + STATE(57), 2, sym_selector_expression, sym_field, ACTIONS(283), 3, @@ -20076,12 +20461,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(243), 13, + STATE(370), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -20090,19 +20474,23 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [5086] = 10, + [5538] = 12, ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, sym_identifier, ACTIONS(39), 1, anon_sym_DQUOTE, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -20116,12 +20504,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(259), 13, + STATE(275), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -20130,20 +20517,24 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [5136] = 10, - ACTIONS(189), 1, - anon_sym_DOT, - ACTIONS(193), 1, - sym_identifier, - ACTIONS(209), 1, + [5593] = 12, + ACTIONS(27), 1, anon_sym_LPAREN, - ACTIONS(211), 1, + ACTIONS(29), 1, + anon_sym_DOT2, + ACTIONS(31), 1, anon_sym_DOLLAR, - ACTIONS(217), 1, + ACTIONS(33), 1, + sym_identifier, + ACTIONS(39), 1, anon_sym_DQUOTE, - STATE(176), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, - STATE(55), 2, + STATE(260), 1, + sym_parenthesized_pipeline, + STATE(57), 2, sym_selector_expression, sym_field, ACTIONS(291), 3, @@ -20156,12 +20547,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(250), 13, + STATE(284), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -20170,19 +20560,23 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [5186] = 10, + [5648] = 12, ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, sym_identifier, ACTIONS(39), 1, anon_sym_DQUOTE, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -20196,12 +20590,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(347), 13, + STATE(373), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -20210,19 +20603,23 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [5236] = 10, + [5703] = 12, ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, sym_identifier, ACTIONS(39), 1, anon_sym_DQUOTE, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -20236,12 +20633,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(296), 13, + STATE(285), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -20250,19 +20646,23 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [5286] = 10, + [5758] = 12, ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, sym_identifier, ACTIONS(39), 1, anon_sym_DQUOTE, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -20276,12 +20676,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(269), 13, + STATE(283), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -20290,19 +20689,23 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [5336] = 10, + [5813] = 12, ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, sym_identifier, ACTIONS(39), 1, anon_sym_DQUOTE, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -20316,12 +20719,11 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(254), 13, + STATE(273), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, - sym_parenthesized_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -20330,19 +20732,23 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [5386] = 10, + [5868] = 12, ACTIONS(27), 1, anon_sym_LPAREN, ACTIONS(29), 1, - anon_sym_DOT, + anon_sym_DOT2, ACTIONS(31), 1, anon_sym_DOLLAR, ACTIONS(33), 1, sym_identifier, ACTIONS(39), 1, anon_sym_DQUOTE, - STATE(195), 1, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, sym_variable, + STATE(260), 1, + sym_parenthesized_pipeline, STATE(57), 2, sym_selector_expression, sym_field, @@ -20356,12 +20762,54 @@ static const uint16_t ts_small_parse_table[] = { sym_true, sym_false, sym_nil, - STATE(257), 13, + STATE(268), 12, sym__pipeline, sym_variable_definition, sym_assignment, sym_chained_pipeline, + sym_method_call, + sym_function_call, + sym__expression, + sym__literal, + sym__boolean_literal, + sym_dot, + sym__string_literal, + sym_interpreted_string_literal, + [5923] = 12, + ACTIONS(27), 1, + anon_sym_LPAREN, + ACTIONS(29), 1, + anon_sym_DOT2, + ACTIONS(31), 1, + anon_sym_DOLLAR, + ACTIONS(33), 1, + sym_identifier, + ACTIONS(39), 1, + anon_sym_DQUOTE, + STATE(58), 1, + sym_unfished_selector_expression, + STATE(201), 1, + sym_variable, + STATE(260), 1, sym_parenthesized_pipeline, + STATE(57), 2, + sym_selector_expression, + sym_field, + ACTIONS(315), 3, + sym_imaginary_literal, + sym_rune_literal, + sym_raw_string_literal, + ACTIONS(313), 5, + sym_int_literal, + sym_float_literal, + sym_true, + sym_false, + sym_nil, + STATE(296), 12, + sym__pipeline, + sym_variable_definition, + sym_assignment, + sym_chained_pipeline, sym_method_call, sym_function_call, sym__expression, @@ -20370,21 +20818,21 @@ static const uint16_t ts_small_parse_table[] = { sym_dot, sym__string_literal, sym_interpreted_string_literal, - [5436] = 8, - STATE(3), 1, + [5978] = 8, + STATE(2), 1, sym__left_delimiter, - STATE(180), 1, - sym__if_actions_end, - STATE(193), 1, + STATE(210), 1, aux_sym_if_action_repeat1, - STATE(280), 1, + STATE(234), 1, + sym__if_actions_end, + STATE(291), 1, sym__else_clause, - STATE(368), 1, + STATE(372), 1, sym__else_if_clause, - ACTIONS(315), 2, + ACTIONS(319), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -20403,27 +20851,27 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [5477] = 8, - STATE(3), 1, + [6019] = 8, + STATE(2), 1, sym__left_delimiter, - STATE(153), 1, - sym__if_actions_end, - STATE(201), 1, + STATE(211), 1, aux_sym_if_action_repeat1, - STATE(277), 1, + STATE(230), 1, + sym__if_actions_end, + STATE(293), 1, sym__else_clause, - STATE(368), 1, + STATE(372), 1, sym__else_if_clause, - ACTIONS(315), 2, + ACTIONS(319), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(83), 12, + STATE(85), 12, sym__block, sym_text, sym__action, @@ -20436,21 +20884,21 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [5518] = 8, - STATE(2), 1, + [6060] = 8, + STATE(3), 1, sym__left_delimiter, - STATE(203), 1, - aux_sym_if_action_repeat1, - STATE(231), 1, + STATE(188), 1, sym__if_actions_end, - STATE(290), 1, + STATE(197), 1, + aux_sym_if_action_repeat1, + STATE(287), 1, sym__else_clause, - STATE(368), 1, + STATE(372), 1, sym__else_if_clause, - ACTIONS(317), 2, + ACTIONS(321), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -20469,27 +20917,27 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [5559] = 8, - STATE(2), 1, + [6101] = 8, + STATE(3), 1, sym__left_delimiter, - STATE(204), 1, - aux_sym_if_action_repeat1, - STATE(226), 1, + STATE(179), 1, sym__if_actions_end, - STATE(292), 1, + STATE(206), 1, + aux_sym_if_action_repeat1, + STATE(261), 1, sym__else_clause, - STATE(368), 1, + STATE(372), 1, sym__else_if_clause, - ACTIONS(317), 2, + ACTIONS(321), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(85), 12, + STATE(87), 12, sym__block, sym_text, sym__action, @@ -20502,21 +20950,21 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [5600] = 5, - ACTIONS(319), 1, + [6142] = 5, + ACTIONS(323), 1, ts_builtin_sym_end, - STATE(53), 1, + STATE(54), 1, sym__left_delimiter, - ACTIONS(7), 2, + ACTIONS(328), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(5), 5, + ACTIONS(325), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(88), 12, + STATE(89), 12, sym__block, sym_text, sym__action, @@ -20529,21 +20977,21 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [5632] = 5, - ACTIONS(321), 1, + [6174] = 5, + ACTIONS(331), 1, ts_builtin_sym_end, - STATE(53), 1, + STATE(54), 1, sym__left_delimiter, - ACTIONS(326), 2, + ACTIONS(7), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(323), 5, + ACTIONS(5), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(88), 12, + STATE(89), 12, sym__block, sym_text, sym__action, @@ -20556,13 +21004,13 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [5664] = 4, - STATE(11), 1, + [6206] = 4, + STATE(6), 1, sym__left_delimiter, - ACTIONS(329), 2, + ACTIONS(333), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -20581,13 +21029,13 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [5693] = 4, - STATE(17), 1, + [6235] = 4, + STATE(52), 1, sym__left_delimiter, - ACTIONS(331), 2, + ACTIONS(335), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -20606,13 +21054,13 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [5722] = 4, - STATE(13), 1, + [6264] = 4, + STATE(44), 1, sym__left_delimiter, - ACTIONS(333), 2, + ACTIONS(338), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -20631,13 +21079,13 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [5751] = 4, - STATE(46), 1, + [6293] = 4, + STATE(9), 1, sym__left_delimiter, - ACTIONS(335), 2, + ACTIONS(340), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -20656,13 +21104,13 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [5780] = 4, - STATE(44), 1, + [6322] = 4, + STATE(43), 1, sym__left_delimiter, - ACTIONS(337), 2, + ACTIONS(342), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -20681,13 +21129,13 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [5809] = 4, - STATE(43), 1, + [6351] = 4, + STATE(41), 1, sym__left_delimiter, - ACTIONS(339), 2, + ACTIONS(344), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -20706,19 +21154,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [5838] = 4, - STATE(41), 1, + [6380] = 4, + STATE(17), 1, sym__left_delimiter, - ACTIONS(341), 2, + ACTIONS(346), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(92), 12, + STATE(103), 12, sym__block, sym_text, sym__action, @@ -20731,19 +21179,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [5867] = 4, - STATE(30), 1, + [6409] = 4, + STATE(18), 1, sym__left_delimiter, - ACTIONS(343), 2, + ACTIONS(348), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(108), 12, + STATE(95), 12, sym__block, sym_text, sym__action, @@ -20756,13 +21204,13 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [5896] = 4, - STATE(18), 1, + [6438] = 4, + STATE(25), 1, sym__left_delimiter, - ACTIONS(345), 2, + ACTIONS(350), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -20781,13 +21229,13 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [5925] = 4, - STATE(45), 1, + [6467] = 4, + STATE(34), 1, sym__left_delimiter, - ACTIONS(347), 2, + ACTIONS(352), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -20806,13 +21254,13 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [5954] = 4, - STATE(9), 1, + [6496] = 4, + STATE(5), 1, sym__left_delimiter, - ACTIONS(349), 2, + ACTIONS(354), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -20831,13 +21279,13 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [5983] = 4, - STATE(8), 1, + [6525] = 4, + STATE(13), 1, sym__left_delimiter, - ACTIONS(351), 2, + ACTIONS(356), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -20856,13 +21304,13 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6012] = 4, - STATE(27), 1, + [6554] = 4, + STATE(52), 1, sym__left_delimiter, - ACTIONS(353), 2, + ACTIONS(361), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(358), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -20881,19 +21329,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6041] = 4, - STATE(22), 1, + [6583] = 4, + STATE(39), 1, sym__left_delimiter, - ACTIONS(355), 2, + ACTIONS(364), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(93), 12, + STATE(103), 12, sym__block, sym_text, sym__action, @@ -20906,19 +21354,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6070] = 4, - STATE(52), 1, + [6612] = 4, + STATE(22), 1, sym__left_delimiter, - ACTIONS(360), 2, + ACTIONS(366), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(357), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(103), 12, + STATE(96), 12, sym__block, sym_text, sym__action, @@ -20931,19 +21379,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6099] = 4, - STATE(15), 1, + [6641] = 4, + STATE(12), 1, sym__left_delimiter, - ACTIONS(363), 2, + ACTIONS(368), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(89), 12, + STATE(117), 12, sym__block, sym_text, sym__action, @@ -20956,19 +21404,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6128] = 4, - STATE(14), 1, + [6670] = 4, + STATE(11), 1, sym__left_delimiter, - ACTIONS(365), 2, + ACTIONS(370), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(119), 12, + STATE(91), 12, sym__block, sym_text, sym__action, @@ -20981,13 +21429,13 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6157] = 4, - STATE(51), 1, + [6699] = 4, + STATE(32), 1, sym__left_delimiter, - ACTIONS(367), 2, + ACTIONS(372), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -21006,19 +21454,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6186] = 4, - STATE(47), 1, + [6728] = 4, + STATE(48), 1, sym__left_delimiter, - ACTIONS(369), 2, + ACTIONS(374), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(126), 12, + STATE(128), 12, sym__block, sym_text, sym__action, @@ -21031,13 +21479,13 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6215] = 4, - STATE(36), 1, + [6757] = 4, + STATE(38), 1, sym__left_delimiter, - ACTIONS(371), 2, + ACTIONS(376), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -21056,19 +21504,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6244] = 4, + [6786] = 4, STATE(50), 1, sym__left_delimiter, - ACTIONS(373), 2, + ACTIONS(378), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(135), 12, + STATE(137), 12, sym__block, sym_text, sym__action, @@ -21081,19 +21529,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6273] = 4, - STATE(4), 1, + [6815] = 4, + STATE(10), 1, sym__left_delimiter, - ACTIONS(375), 2, + ACTIONS(380), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(91), 12, + STATE(94), 12, sym__block, sym_text, sym__action, @@ -21106,19 +21554,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6302] = 4, - STATE(34), 1, + [6844] = 4, + STATE(47), 1, sym__left_delimiter, - ACTIONS(377), 2, + ACTIONS(382), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(117), 12, + STATE(93), 12, sym__block, sym_text, sym__action, @@ -21131,19 +21579,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6331] = 4, - STATE(12), 1, + [6873] = 4, + STATE(8), 1, sym__left_delimiter, - ACTIONS(379), 2, + ACTIONS(384), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(140), 12, + STATE(134), 12, sym__block, sym_text, sym__action, @@ -21156,19 +21604,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6360] = 4, - STATE(39), 1, + [6902] = 4, + STATE(40), 1, sym__left_delimiter, - ACTIONS(381), 2, + ACTIONS(386), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(97), 12, + STATE(99), 12, sym__block, sym_text, sym__action, @@ -21181,19 +21629,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6389] = 4, - STATE(20), 1, + [6931] = 4, + STATE(23), 1, sym__left_delimiter, - ACTIONS(383), 2, + ACTIONS(388), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(103), 12, + STATE(97), 12, sym__block, sym_text, sym__action, @@ -21206,19 +21654,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6418] = 4, - STATE(24), 1, + [6960] = 4, + STATE(7), 1, sym__left_delimiter, - ACTIONS(385), 2, + ACTIONS(390), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(94), 12, + STATE(103), 12, sym__block, sym_text, sym__action, @@ -21231,19 +21679,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6447] = 4, + [6989] = 4, STATE(52), 1, sym__left_delimiter, - ACTIONS(387), 2, + ACTIONS(392), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(137), 12, + STATE(92), 12, sym__block, sym_text, sym__action, @@ -21256,13 +21704,13 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6476] = 4, - STATE(49), 1, + [7018] = 4, + STATE(19), 1, sym__left_delimiter, - ACTIONS(390), 2, + ACTIONS(395), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -21281,13 +21729,13 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6505] = 4, - STATE(33), 1, + [7047] = 4, + STATE(46), 1, sym__left_delimiter, - ACTIONS(392), 2, + ACTIONS(397), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -21306,19 +21754,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6534] = 4, - STATE(10), 1, + [7076] = 4, + STATE(49), 1, sym__left_delimiter, - ACTIONS(394), 2, + ACTIONS(399), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(103), 12, + STATE(100), 12, sym__block, sym_text, sym__action, @@ -21331,19 +21779,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6563] = 4, - STATE(29), 1, + [7105] = 4, + STATE(51), 1, sym__left_delimiter, - ACTIONS(396), 2, + ACTIONS(401), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(101), 12, + STATE(108), 12, sym__block, sym_text, sym__action, @@ -21356,13 +21804,13 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6592] = 4, - STATE(52), 1, + [7134] = 4, + STATE(20), 1, sym__left_delimiter, - ACTIONS(398), 2, + ACTIONS(403), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -21381,19 +21829,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6621] = 4, - STATE(6), 1, + [7163] = 4, + STATE(4), 1, sym__left_delimiter, - ACTIONS(401), 2, + ACTIONS(405), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(99), 12, + STATE(101), 12, sym__block, sym_text, sym__action, @@ -21406,19 +21854,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6650] = 4, - STATE(32), 1, + [7192] = 4, + STATE(36), 1, sym__left_delimiter, - ACTIONS(403), 2, + ACTIONS(407), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(136), 12, + STATE(139), 12, sym__block, sym_text, sym__action, @@ -21431,19 +21879,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6679] = 4, - STATE(19), 1, + [7221] = 4, + STATE(42), 1, sym__left_delimiter, - ACTIONS(405), 2, + ACTIONS(409), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(98), 12, + STATE(120), 12, sym__block, sym_text, sym__action, @@ -21456,19 +21904,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6708] = 4, - STATE(7), 1, + [7250] = 4, + STATE(14), 1, sym__left_delimiter, - ACTIONS(407), 2, + ACTIONS(411), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(100), 12, + STATE(102), 12, sym__block, sym_text, sym__action, @@ -21481,13 +21929,13 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6737] = 4, - STATE(35), 1, + [7279] = 4, + STATE(29), 1, sym__left_delimiter, - ACTIONS(409), 2, + ACTIONS(413), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -21506,19 +21954,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6766] = 4, - STATE(31), 1, + [7308] = 4, + STATE(35), 1, sym__left_delimiter, - ACTIONS(411), 2, + ACTIONS(415), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(133), 12, + STATE(136), 12, sym__block, sym_text, sym__action, @@ -21531,13 +21979,13 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6795] = 4, - STATE(21), 1, + [7337] = 4, + STATE(37), 1, sym__left_delimiter, - ACTIONS(413), 2, + ACTIONS(417), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -21556,19 +22004,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6824] = 4, - STATE(37), 1, + [7366] = 4, + STATE(52), 1, sym__left_delimiter, - ACTIONS(415), 2, + ACTIONS(419), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(114), 12, + STATE(103), 12, sym__block, sym_text, sym__action, @@ -21581,19 +22029,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6853] = 4, - STATE(48), 1, + [7395] = 4, + STATE(21), 1, sym__left_delimiter, - ACTIONS(417), 2, + ACTIONS(422), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(141), 12, + STATE(104), 12, sym__block, sym_text, sym__action, @@ -21606,19 +22054,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6882] = 4, - STATE(38), 1, + [7424] = 4, + STATE(24), 1, sym__left_delimiter, - ACTIONS(419), 2, + ACTIONS(424), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(118), 12, + STATE(103), 12, sym__block, sym_text, sym__action, @@ -21631,19 +22079,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6911] = 4, - STATE(23), 1, + [7453] = 4, + STATE(15), 1, sym__left_delimiter, - ACTIONS(421), 2, + ACTIONS(426), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(106), 12, + STATE(103), 12, sym__block, sym_text, sym__action, @@ -21656,19 +22104,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6940] = 4, - STATE(16), 1, + [7482] = 4, + STATE(26), 1, sym__left_delimiter, - ACTIONS(423), 2, + ACTIONS(428), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(103), 12, + STATE(119), 12, sym__block, sym_text, sym__action, @@ -21681,19 +22129,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6969] = 4, - STATE(25), 1, + [7511] = 4, + STATE(27), 1, sym__left_delimiter, - ACTIONS(425), 2, + ACTIONS(430), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(90), 12, + STATE(103), 12, sym__block, sym_text, sym__action, @@ -21706,13 +22154,13 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [6998] = 4, - STATE(42), 1, + [7540] = 4, + STATE(30), 1, sym__left_delimiter, - ACTIONS(427), 2, + ACTIONS(432), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -21731,19 +22179,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [7027] = 4, - STATE(26), 1, + [7569] = 4, + STATE(16), 1, sym__left_delimiter, - ACTIONS(429), 2, + ACTIONS(434), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(103), 12, + STATE(123), 12, sym__block, sym_text, sym__action, @@ -21756,13 +22204,13 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [7056] = 4, - STATE(52), 1, + [7598] = 4, + STATE(31), 1, sym__left_delimiter, - ACTIONS(431), 2, + ACTIONS(436), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -21781,19 +22229,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [7085] = 4, - STATE(28), 1, + [7627] = 4, + STATE(33), 1, sym__left_delimiter, - ACTIONS(434), 2, + ACTIONS(438), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(128), 12, + STATE(133), 12, sym__block, sym_text, sym__action, @@ -21806,19 +22254,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [7114] = 4, - STATE(52), 1, + [7656] = 4, + STATE(28), 1, sym__left_delimiter, - ACTIONS(436), 2, + ACTIONS(440), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(121), 12, + STATE(110), 12, sym__block, sym_text, sym__action, @@ -21831,19 +22279,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [7143] = 4, - STATE(5), 1, + [7685] = 4, + STATE(45), 1, sym__left_delimiter, - ACTIONS(439), 2, + ACTIONS(442), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(103), 12, + STATE(130), 12, sym__block, sym_text, sym__action, @@ -21856,19 +22304,19 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [7172] = 4, - STATE(40), 1, + [7714] = 4, + STATE(52), 1, sym__left_delimiter, - ACTIONS(441), 2, + ACTIONS(444), 2, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - ACTIONS(313), 5, + ACTIONS(317), 5, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, aux_sym_text_token4, aux_sym_text_token5, - STATE(103), 12, + STATE(131), 12, sym__block, sym_text, sym__action, @@ -21881,14 +22329,15 @@ static const uint16_t ts_small_parse_table[] = { sym_block_action, sym_with_action, aux_sym_template_repeat1, - [7201] = 2, - ACTIONS(445), 1, + [7743] = 2, + ACTIONS(449), 1, anon_sym_, - ACTIONS(443), 17, + ACTIONS(447), 18, anon_sym_PIPE, anon_sym_LPAREN, anon_sym_RPAREN, anon_sym_DOT, + anon_sym_DOT2, anon_sym_DOLLAR, sym_identifier, sym_int_literal, @@ -21902,14 +22351,39 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_DQUOTE, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [7224] = 2, - ACTIONS(449), 1, + [7767] = 4, + ACTIONS(453), 1, + anon_sym_, + ACTIONS(455), 1, + sym_identifier, + STATE(144), 1, + sym__field_identifier, + ACTIONS(451), 16, + anon_sym_PIPE, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_DOT2, + anon_sym_DOLLAR, + sym_int_literal, + sym_float_literal, + sym_imaginary_literal, + sym_rune_literal, + sym_true, + sym_false, + sym_nil, + sym_raw_string_literal, + anon_sym_DQUOTE, + anon_sym_RBRACE_RBRACE, + anon_sym_DASH_RBRACE_RBRACE, + [7795] = 2, + ACTIONS(459), 1, anon_sym_, - ACTIONS(447), 17, + ACTIONS(457), 18, anon_sym_PIPE, anon_sym_LPAREN, anon_sym_RPAREN, anon_sym_DOT, + anon_sym_DOT2, anon_sym_DOLLAR, sym_identifier, sym_int_literal, @@ -21923,14 +22397,15 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_DQUOTE, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [7247] = 2, - ACTIONS(453), 1, + [7819] = 2, + ACTIONS(463), 1, anon_sym_, - ACTIONS(451), 17, + ACTIONS(461), 18, anon_sym_PIPE, anon_sym_LPAREN, anon_sym_RPAREN, anon_sym_DOT, + anon_sym_DOT2, anon_sym_DOLLAR, sym_identifier, sym_int_literal, @@ -21944,16 +22419,17 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_DQUOTE, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [7270] = 2, - ACTIONS(457), 7, + [7843] = 2, + ACTIONS(457), 8, anon_sym_DOT, + anon_sym_DOT2, sym_identifier, sym_int_literal, sym_float_literal, sym_true, sym_false, sym_nil, - ACTIONS(455), 10, + ACTIONS(459), 10, anon_sym_PIPE, anon_sym_LPAREN, anon_sym_RPAREN, @@ -21964,16 +22440,17 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_DQUOTE, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [7292] = 2, - ACTIONS(451), 7, + [7866] = 2, + ACTIONS(447), 8, anon_sym_DOT, + anon_sym_DOT2, sym_identifier, sym_int_literal, sym_float_literal, sym_true, sym_false, sym_nil, - ACTIONS(453), 10, + ACTIONS(449), 10, anon_sym_PIPE, anon_sym_LPAREN, anon_sym_RPAREN, @@ -21984,16 +22461,17 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_DQUOTE, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [7314] = 2, - ACTIONS(447), 7, + [7889] = 2, + ACTIONS(461), 8, anon_sym_DOT, + anon_sym_DOT2, sym_identifier, sym_int_literal, sym_float_literal, sym_true, sym_false, sym_nil, - ACTIONS(449), 10, + ACTIONS(463), 10, anon_sym_PIPE, anon_sym_LPAREN, anon_sym_RPAREN, @@ -22004,16 +22482,19 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_DQUOTE, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [7336] = 2, - ACTIONS(443), 7, - anon_sym_DOT, + [7912] = 4, + ACTIONS(465), 1, sym_identifier, + STATE(149), 1, + sym__field_identifier, + ACTIONS(451), 6, + anon_sym_DOT2, sym_int_literal, sym_float_literal, sym_true, sym_false, sym_nil, - ACTIONS(445), 10, + ACTIONS(453), 10, anon_sym_PIPE, anon_sym_LPAREN, anon_sym_RPAREN, @@ -22024,16 +22505,16 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_DQUOTE, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [7358] = 2, - ACTIONS(461), 7, - anon_sym_DOT, + [7939] = 2, + ACTIONS(469), 7, + anon_sym_DOT2, sym_identifier, sym_int_literal, sym_float_literal, sym_true, sym_false, sym_nil, - ACTIONS(459), 10, + ACTIONS(467), 10, anon_sym_PIPE, anon_sym_LPAREN, anon_sym_RPAREN, @@ -22044,10 +22525,30 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_DQUOTE, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [7380] = 2, - ACTIONS(465), 1, + [7961] = 2, + ACTIONS(473), 7, + anon_sym_DOT2, + sym_identifier, + sym_int_literal, + sym_float_literal, + sym_true, + sym_false, + sym_nil, + ACTIONS(471), 10, + anon_sym_PIPE, + anon_sym_LPAREN, + anon_sym_RPAREN, + anon_sym_DOLLAR, + sym_imaginary_literal, + sym_rune_literal, + sym_raw_string_literal, + anon_sym_DQUOTE, + anon_sym_RBRACE_RBRACE, + anon_sym_DASH_RBRACE_RBRACE, + [7983] = 2, + ACTIONS(477), 1, sym_identifier, - ACTIONS(463), 8, + ACTIONS(475), 8, anon_sym_COMMA, anon_sym_COLON_EQ, anon_sym_EQ, @@ -22056,53 +22557,20 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_DOT, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [7394] = 3, - ACTIONS(463), 1, - anon_sym_, - ACTIONS(469), 1, + [7997] = 3, + ACTIONS(481), 1, sym_identifier, - ACTIONS(467), 7, + ACTIONS(475), 2, + anon_sym_, + anon_sym_DOT, + ACTIONS(479), 6, anon_sym_COLON_EQ, anon_sym_EQ, anon_sym_PIPE, anon_sym_RPAREN, - anon_sym_DOT, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [7410] = 2, - ACTIONS(471), 1, - ts_builtin_sym_end, - ACTIONS(473), 7, - aux_sym_text_token1, - aux_sym_text_token2, - aux_sym_text_token3, - aux_sym_text_token4, - aux_sym_text_token5, - anon_sym_LBRACE_LBRACE, - anon_sym_LBRACE_LBRACE_DASH, - [7423] = 2, - ACTIONS(475), 1, - ts_builtin_sym_end, - ACTIONS(477), 7, - aux_sym_text_token1, - aux_sym_text_token2, - aux_sym_text_token3, - aux_sym_text_token4, - aux_sym_text_token5, - anon_sym_LBRACE_LBRACE, - anon_sym_LBRACE_LBRACE_DASH, - [7436] = 2, - ACTIONS(479), 1, - ts_builtin_sym_end, - ACTIONS(481), 7, - aux_sym_text_token1, - aux_sym_text_token2, - aux_sym_text_token3, - aux_sym_text_token4, - aux_sym_text_token5, - anon_sym_LBRACE_LBRACE, - anon_sym_LBRACE_LBRACE_DASH, - [7449] = 2, + [8013] = 2, ACTIONS(483), 1, ts_builtin_sym_end, ACTIONS(485), 7, @@ -22113,7 +22581,7 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7462] = 2, + [8026] = 2, ACTIONS(487), 1, ts_builtin_sym_end, ACTIONS(489), 7, @@ -22124,7 +22592,7 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7475] = 2, + [8039] = 2, ACTIONS(491), 1, ts_builtin_sym_end, ACTIONS(493), 7, @@ -22135,7 +22603,7 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7488] = 2, + [8052] = 2, ACTIONS(495), 1, ts_builtin_sym_end, ACTIONS(497), 7, @@ -22146,7 +22614,7 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7501] = 2, + [8065] = 2, ACTIONS(499), 1, ts_builtin_sym_end, ACTIONS(501), 7, @@ -22157,10 +22625,20 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7514] = 2, - ACTIONS(503), 1, + [8078] = 1, + ACTIONS(503), 8, + anon_sym_COMMA, + anon_sym_COLON_EQ, + anon_sym_EQ, + anon_sym_PIPE, + anon_sym_RPAREN, + anon_sym_DOT, + anon_sym_RBRACE_RBRACE, + anon_sym_DASH_RBRACE_RBRACE, + [8089] = 2, + ACTIONS(505), 1, ts_builtin_sym_end, - ACTIONS(505), 7, + ACTIONS(507), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22168,10 +22646,10 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7527] = 2, - ACTIONS(507), 1, + [8102] = 2, + ACTIONS(509), 1, ts_builtin_sym_end, - ACTIONS(509), 7, + ACTIONS(511), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22179,21 +22657,10 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7540] = 2, + [8115] = 2, ACTIONS(513), 1, - anon_sym_, - ACTIONS(511), 7, - anon_sym_COLON_EQ, - anon_sym_EQ, - anon_sym_PIPE, - anon_sym_RPAREN, - anon_sym_DOT, - anon_sym_RBRACE_RBRACE, - anon_sym_DASH_RBRACE_RBRACE, - [7553] = 2, - ACTIONS(515), 1, ts_builtin_sym_end, - ACTIONS(517), 7, + ACTIONS(515), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22201,10 +22668,10 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7566] = 2, - ACTIONS(519), 1, + [8128] = 2, + ACTIONS(517), 1, ts_builtin_sym_end, - ACTIONS(521), 7, + ACTIONS(519), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22212,10 +22679,10 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7579] = 2, - ACTIONS(523), 1, + [8141] = 2, + ACTIONS(521), 1, ts_builtin_sym_end, - ACTIONS(525), 7, + ACTIONS(523), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22223,10 +22690,10 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7592] = 2, - ACTIONS(527), 1, + [8154] = 2, + ACTIONS(525), 1, ts_builtin_sym_end, - ACTIONS(529), 7, + ACTIONS(527), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22234,10 +22701,10 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7605] = 2, - ACTIONS(531), 1, + [8167] = 2, + ACTIONS(529), 1, ts_builtin_sym_end, - ACTIONS(533), 7, + ACTIONS(531), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22245,10 +22712,10 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7618] = 2, - ACTIONS(535), 1, + [8180] = 2, + ACTIONS(533), 1, ts_builtin_sym_end, - ACTIONS(537), 7, + ACTIONS(535), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22256,10 +22723,10 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7631] = 2, - ACTIONS(539), 1, + [8193] = 2, + ACTIONS(537), 1, ts_builtin_sym_end, - ACTIONS(541), 7, + ACTIONS(539), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22267,10 +22734,10 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7644] = 2, - ACTIONS(543), 1, + [8206] = 2, + ACTIONS(541), 1, ts_builtin_sym_end, - ACTIONS(545), 7, + ACTIONS(543), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22278,10 +22745,10 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7657] = 2, - ACTIONS(547), 1, + [8219] = 2, + ACTIONS(545), 1, ts_builtin_sym_end, - ACTIONS(549), 7, + ACTIONS(547), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22289,10 +22756,10 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7670] = 2, - ACTIONS(551), 1, + [8232] = 2, + ACTIONS(549), 1, ts_builtin_sym_end, - ACTIONS(553), 7, + ACTIONS(551), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22300,10 +22767,10 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7683] = 2, - ACTIONS(555), 1, + [8245] = 2, + ACTIONS(553), 1, ts_builtin_sym_end, - ACTIONS(557), 7, + ACTIONS(555), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22311,10 +22778,10 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7696] = 2, - ACTIONS(559), 1, + [8258] = 2, + ACTIONS(557), 1, ts_builtin_sym_end, - ACTIONS(561), 7, + ACTIONS(559), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22322,10 +22789,10 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7709] = 2, - ACTIONS(563), 1, + [8271] = 2, + ACTIONS(561), 1, ts_builtin_sym_end, - ACTIONS(565), 7, + ACTIONS(563), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22333,23 +22800,10 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7722] = 4, - ACTIONS(201), 1, - anon_sym_, - ACTIONS(567), 1, - anon_sym_COLON_EQ, - ACTIONS(569), 1, - anon_sym_EQ, - ACTIONS(199), 5, - anon_sym_PIPE, - anon_sym_RPAREN, - anon_sym_DOT, - anon_sym_RBRACE_RBRACE, - anon_sym_DASH_RBRACE_RBRACE, - [7739] = 2, - ACTIONS(571), 1, + [8284] = 2, + ACTIONS(565), 1, ts_builtin_sym_end, - ACTIONS(573), 7, + ACTIONS(567), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22357,7 +22811,21 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7752] = 2, + [8297] = 5, + ACTIONS(169), 1, + anon_sym_, + ACTIONS(569), 1, + anon_sym_COLON_EQ, + ACTIONS(571), 1, + anon_sym_EQ, + ACTIONS(573), 1, + anon_sym_DOT, + ACTIONS(165), 4, + anon_sym_PIPE, + anon_sym_RPAREN, + anon_sym_RBRACE_RBRACE, + anon_sym_DASH_RBRACE_RBRACE, + [8316] = 2, ACTIONS(575), 1, ts_builtin_sym_end, ACTIONS(577), 7, @@ -22368,7 +22836,7 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7765] = 2, + [8329] = 2, ACTIONS(579), 1, ts_builtin_sym_end, ACTIONS(581), 7, @@ -22379,7 +22847,7 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7778] = 2, + [8342] = 2, ACTIONS(583), 1, ts_builtin_sym_end, ACTIONS(585), 7, @@ -22390,7 +22858,7 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7791] = 2, + [8355] = 2, ACTIONS(587), 1, ts_builtin_sym_end, ACTIONS(589), 7, @@ -22401,20 +22869,21 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7804] = 1, - ACTIONS(513), 8, - anon_sym_COMMA, + [8368] = 2, + ACTIONS(503), 2, + anon_sym_, + anon_sym_DOT, + ACTIONS(591), 6, anon_sym_COLON_EQ, anon_sym_EQ, anon_sym_PIPE, anon_sym_RPAREN, - anon_sym_DOT, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [7815] = 2, - ACTIONS(591), 1, + [8381] = 2, + ACTIONS(593), 1, ts_builtin_sym_end, - ACTIONS(593), 7, + ACTIONS(595), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22422,10 +22891,10 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7828] = 2, - ACTIONS(595), 1, + [8394] = 2, + ACTIONS(597), 1, ts_builtin_sym_end, - ACTIONS(597), 7, + ACTIONS(599), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22433,10 +22902,10 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7841] = 2, - ACTIONS(599), 1, + [8407] = 2, + ACTIONS(601), 1, ts_builtin_sym_end, - ACTIONS(601), 7, + ACTIONS(603), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22444,10 +22913,10 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7854] = 2, - ACTIONS(603), 1, + [8420] = 2, + ACTIONS(605), 1, ts_builtin_sym_end, - ACTIONS(605), 7, + ACTIONS(607), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22455,10 +22924,10 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7867] = 2, - ACTIONS(607), 1, + [8433] = 2, + ACTIONS(609), 1, ts_builtin_sym_end, - ACTIONS(609), 7, + ACTIONS(611), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22466,10 +22935,10 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7880] = 2, - ACTIONS(611), 1, + [8446] = 2, + ACTIONS(613), 1, ts_builtin_sym_end, - ACTIONS(613), 7, + ACTIONS(615), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22477,10 +22946,10 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7893] = 2, - ACTIONS(615), 1, + [8459] = 2, + ACTIONS(617), 1, ts_builtin_sym_end, - ACTIONS(617), 7, + ACTIONS(619), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22488,10 +22957,10 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7906] = 2, - ACTIONS(619), 1, + [8472] = 2, + ACTIONS(621), 1, ts_builtin_sym_end, - ACTIONS(621), 7, + ACTIONS(623), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22499,10 +22968,10 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7919] = 2, - ACTIONS(623), 1, + [8485] = 2, + ACTIONS(625), 1, ts_builtin_sym_end, - ACTIONS(625), 7, + ACTIONS(627), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22510,10 +22979,10 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7932] = 2, - ACTIONS(627), 1, + [8498] = 2, + ACTIONS(629), 1, ts_builtin_sym_end, - ACTIONS(629), 7, + ACTIONS(631), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22521,23 +22990,56 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7945] = 7, - ACTIONS(631), 1, - anon_sym_LBRACE_LBRACE, + [8511] = 2, ACTIONS(633), 1, + ts_builtin_sym_end, + ACTIONS(635), 7, + aux_sym_text_token1, + aux_sym_text_token2, + aux_sym_text_token3, + aux_sym_text_token4, + aux_sym_text_token5, + anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - STATE(163), 1, + [8524] = 2, + ACTIONS(637), 1, + ts_builtin_sym_end, + ACTIONS(639), 7, + aux_sym_text_token1, + aux_sym_text_token2, + aux_sym_text_token3, + aux_sym_text_token4, + aux_sym_text_token5, + anon_sym_LBRACE_LBRACE, + anon_sym_LBRACE_LBRACE_DASH, + [8537] = 2, + ACTIONS(641), 1, + ts_builtin_sym_end, + ACTIONS(643), 7, + aux_sym_text_token1, + aux_sym_text_token2, + aux_sym_text_token3, + aux_sym_text_token4, + aux_sym_text_token5, + anon_sym_LBRACE_LBRACE, + anon_sym_LBRACE_LBRACE_DASH, + [8550] = 7, + ACTIONS(645), 1, + anon_sym_LBRACE_LBRACE, + ACTIONS(647), 1, + anon_sym_LBRACE_LBRACE_DASH, + STATE(168), 1, sym__if_actions_end, - STATE(260), 1, + STATE(257), 1, aux_sym_if_action_repeat1, - STATE(291), 1, + STATE(295), 1, sym__else_clause, - STATE(319), 1, + STATE(323), 1, sym__left_delimiter, - STATE(368), 1, + STATE(372), 1, sym__else_if_clause, - [7967] = 1, - ACTIONS(601), 7, + [8572] = 1, + ACTIONS(515), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22545,55 +23047,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [7977] = 3, - ACTIONS(635), 1, - anon_sym_COLON_EQ, - ACTIONS(637), 1, - anon_sym_EQ, - ACTIONS(201), 5, - anon_sym_PIPE, - anon_sym_RPAREN, - anon_sym_DOT, - anon_sym_RBRACE_RBRACE, - anon_sym_DASH_RBRACE_RBRACE, - [7991] = 4, - ACTIONS(635), 1, - anon_sym_COLON_EQ, - ACTIONS(637), 1, - anon_sym_EQ, - ACTIONS(639), 1, - anon_sym_COMMA, - ACTIONS(201), 4, - anon_sym_PIPE, - anon_sym_DOT, - anon_sym_RBRACE_RBRACE, - anon_sym_DASH_RBRACE_RBRACE, - [8007] = 5, - ACTIONS(641), 1, - anon_sym_PIPE, - ACTIONS(645), 1, - anon_sym_, - ACTIONS(647), 1, - anon_sym_DOT, - STATE(198), 1, - aux_sym_argument_list_repeat1, - ACTIONS(643), 3, - anon_sym_RPAREN, - anon_sym_RBRACE_RBRACE, - anon_sym_DASH_RBRACE_RBRACE, - [8025] = 3, - ACTIONS(649), 1, - anon_sym_, - STATE(202), 1, - aux_sym_argument_list_repeat1, - ACTIONS(203), 5, - anon_sym_PIPE, - anon_sym_RPAREN, - anon_sym_DOT, - anon_sym_RBRACE_RBRACE, - anon_sym_DASH_RBRACE_RBRACE, - [8039] = 1, - ACTIONS(577), 7, + [8582] = 1, + ACTIONS(589), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22601,99 +23056,42 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8049] = 3, - ACTIONS(653), 1, - anon_sym_, - ACTIONS(655), 1, - sym_identifier, - ACTIONS(651), 5, - anon_sym_PIPE, - anon_sym_RPAREN, - anon_sym_DOT, - anon_sym_RBRACE_RBRACE, - anon_sym_DASH_RBRACE_RBRACE, - [8063] = 7, - ACTIONS(631), 1, + [8592] = 1, + ACTIONS(623), 7, + aux_sym_text_token1, + aux_sym_text_token2, + aux_sym_text_token3, + aux_sym_text_token4, + aux_sym_text_token5, anon_sym_LBRACE_LBRACE, - ACTIONS(633), 1, anon_sym_LBRACE_LBRACE_DASH, - STATE(177), 1, - sym__if_actions_end, - STATE(260), 1, - aux_sym_if_action_repeat1, - STATE(281), 1, - sym__else_clause, - STATE(319), 1, - sym__left_delimiter, - STATE(368), 1, - sym__else_if_clause, - [8085] = 3, - ACTIONS(659), 1, - anon_sym_, - STATE(202), 1, - aux_sym_argument_list_repeat1, - ACTIONS(657), 5, - anon_sym_PIPE, - anon_sym_RPAREN, + [8602] = 4, + ACTIONS(649), 1, + anon_sym_COLON_EQ, + ACTIONS(651), 1, + anon_sym_EQ, + ACTIONS(653), 1, anon_sym_DOT, - anon_sym_RBRACE_RBRACE, - anon_sym_DASH_RBRACE_RBRACE, - [8099] = 7, - ACTIONS(662), 1, - anon_sym_LBRACE_LBRACE, - ACTIONS(664), 1, - anon_sym_LBRACE_LBRACE_DASH, - STATE(241), 1, - sym__if_actions_end, - STATE(260), 1, - aux_sym_if_action_repeat1, - STATE(285), 1, - sym__else_clause, - STATE(357), 1, - sym__left_delimiter, - STATE(368), 1, - sym__else_if_clause, - [8121] = 7, - ACTIONS(662), 1, - anon_sym_LBRACE_LBRACE, - ACTIONS(664), 1, - anon_sym_LBRACE_LBRACE_DASH, - STATE(232), 1, - sym__if_actions_end, - STATE(260), 1, - aux_sym_if_action_repeat1, - STATE(286), 1, - sym__else_clause, - STATE(357), 1, - sym__left_delimiter, - STATE(368), 1, - sym__else_if_clause, - [8143] = 3, - ACTIONS(666), 1, - anon_sym_, - STATE(202), 1, - aux_sym_argument_list_repeat1, - ACTIONS(203), 5, + ACTIONS(169), 4, anon_sym_PIPE, anon_sym_RPAREN, - anon_sym_DOT, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8157] = 5, - ACTIONS(641), 1, - anon_sym_PIPE, - ACTIONS(647), 1, + [8618] = 5, + ACTIONS(649), 1, + anon_sym_COLON_EQ, + ACTIONS(651), 1, + anon_sym_EQ, + ACTIONS(653), 1, anon_sym_DOT, - ACTIONS(668), 1, - anon_sym_, - STATE(205), 1, - aux_sym_argument_list_repeat1, - ACTIONS(643), 3, - anon_sym_RPAREN, + ACTIONS(655), 1, + anon_sym_COMMA, + ACTIONS(169), 3, + anon_sym_PIPE, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8175] = 1, - ACTIONS(597), 7, + [8636] = 1, + ACTIONS(585), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22701,8 +23099,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8185] = 1, - ACTIONS(533), 7, + [8646] = 1, + ACTIONS(535), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22710,8 +23108,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8195] = 1, - ACTIONS(629), 7, + [8656] = 1, + ACTIONS(547), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22719,17 +23117,23 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8205] = 1, - ACTIONS(485), 7, - aux_sym_text_token1, - aux_sym_text_token2, - aux_sym_text_token3, - aux_sym_text_token4, - aux_sym_text_token5, + [8666] = 7, + ACTIONS(645), 1, anon_sym_LBRACE_LBRACE, + ACTIONS(647), 1, anon_sym_LBRACE_LBRACE_DASH, - [8215] = 1, - ACTIONS(625), 7, + STATE(186), 1, + sym__if_actions_end, + STATE(257), 1, + aux_sym_if_action_repeat1, + STATE(288), 1, + sym__else_clause, + STATE(323), 1, + sym__left_delimiter, + STATE(372), 1, + sym__else_if_clause, + [8688] = 1, + ACTIONS(607), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22737,8 +23141,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8225] = 1, - ACTIONS(493), 7, + [8698] = 1, + ACTIONS(615), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22746,8 +23150,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8235] = 1, - ACTIONS(621), 7, + [8708] = 1, + ACTIONS(599), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22755,8 +23159,38 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8245] = 1, - ACTIONS(617), 7, + [8718] = 7, + ACTIONS(657), 1, + anon_sym_LBRACE_LBRACE, + ACTIONS(659), 1, + anon_sym_LBRACE_LBRACE_DASH, + STATE(227), 1, + sym__if_actions_end, + STATE(257), 1, + aux_sym_if_action_repeat1, + STATE(297), 1, + sym__else_clause, + STATE(336), 1, + sym__left_delimiter, + STATE(372), 1, + sym__else_if_clause, + [8740] = 7, + ACTIONS(657), 1, + anon_sym_LBRACE_LBRACE, + ACTIONS(659), 1, + anon_sym_LBRACE_LBRACE_DASH, + STATE(235), 1, + sym__if_actions_end, + STATE(257), 1, + aux_sym_if_action_repeat1, + STATE(290), 1, + sym__else_clause, + STATE(336), 1, + sym__left_delimiter, + STATE(372), 1, + sym__else_if_clause, + [8762] = 1, + ACTIONS(539), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22764,8 +23198,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8255] = 1, - ACTIONS(613), 7, + [8772] = 1, + ACTIONS(497), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22773,8 +23207,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8265] = 1, - ACTIONS(609), 7, + [8782] = 1, + ACTIONS(559), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22782,8 +23216,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8275] = 1, - ACTIONS(537), 7, + [8792] = 1, + ACTIONS(555), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22791,8 +23225,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8285] = 1, - ACTIONS(593), 7, + [8802] = 1, + ACTIONS(507), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22800,8 +23234,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8295] = 1, - ACTIONS(581), 7, + [8812] = 1, + ACTIONS(595), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22809,8 +23243,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8305] = 1, - ACTIONS(565), 7, + [8822] = 1, + ACTIONS(489), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22818,8 +23252,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8315] = 1, - ACTIONS(545), 7, + [8832] = 1, + ACTIONS(519), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22827,8 +23261,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8325] = 1, - ACTIONS(505), 7, + [8842] = 1, + ACTIONS(501), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22836,8 +23270,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8335] = 1, - ACTIONS(489), 7, + [8852] = 1, + ACTIONS(523), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22845,8 +23279,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8345] = 1, - ACTIONS(481), 7, + [8862] = 1, + ACTIONS(563), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22854,8 +23288,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8355] = 1, - ACTIONS(557), 7, + [8872] = 1, + ACTIONS(511), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22863,8 +23297,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8365] = 1, - ACTIONS(477), 7, + [8882] = 1, + ACTIONS(527), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22872,8 +23306,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8375] = 1, - ACTIONS(561), 7, + [8892] = 1, + ACTIONS(643), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22881,8 +23315,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8385] = 1, - ACTIONS(541), 7, + [8902] = 1, + ACTIONS(551), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22890,8 +23324,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8395] = 1, - ACTIONS(605), 7, + [8912] = 1, + ACTIONS(531), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22899,8 +23333,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8405] = 1, - ACTIONS(553), 7, + [8922] = 1, + ACTIONS(485), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22908,8 +23342,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8415] = 1, - ACTIONS(585), 7, + [8932] = 1, + ACTIONS(543), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22917,8 +23351,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8425] = 1, - ACTIONS(573), 7, + [8942] = 1, + ACTIONS(577), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22926,8 +23360,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8435] = 1, - ACTIONS(529), 7, + [8952] = 1, + ACTIONS(581), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22935,8 +23369,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8445] = 1, - ACTIONS(525), 7, + [8962] = 1, + ACTIONS(635), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22944,8 +23378,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8455] = 1, - ACTIONS(589), 7, + [8972] = 1, + ACTIONS(639), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22953,8 +23387,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8465] = 1, - ACTIONS(549), 7, + [8982] = 1, + ACTIONS(611), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22962,8 +23396,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8475] = 1, - ACTIONS(497), 7, + [8992] = 1, + ACTIONS(603), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22971,8 +23405,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8485] = 1, - ACTIONS(501), 7, + [9002] = 1, + ACTIONS(631), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22980,8 +23414,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8495] = 1, - ACTIONS(473), 7, + [9012] = 1, + ACTIONS(627), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22989,8 +23423,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8505] = 1, - ACTIONS(521), 7, + [9022] = 1, + ACTIONS(619), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -22998,8 +23432,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8515] = 1, - ACTIONS(517), 7, + [9032] = 1, + ACTIONS(567), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -23007,8 +23441,8 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8525] = 1, - ACTIONS(509), 7, + [9042] = 1, + ACTIONS(493), 7, aux_sym_text_token1, aux_sym_text_token2, aux_sym_text_token3, @@ -23016,1774 +23450,1803 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_text_token5, anon_sym_LBRACE_LBRACE, anon_sym_LBRACE_LBRACE_DASH, - [8535] = 4, - ACTIONS(641), 1, - anon_sym_PIPE, - ACTIONS(647), 1, - anon_sym_DOT, - ACTIONS(672), 1, + [9052] = 3, + ACTIONS(661), 1, anon_sym_, - ACTIONS(670), 3, + STATE(246), 1, + aux_sym_argument_list_repeat1, + ACTIONS(217), 4, + anon_sym_PIPE, anon_sym_RPAREN, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8550] = 2, - ACTIONS(676), 1, + [9065] = 2, + ACTIONS(665), 2, anon_sym_, - ACTIONS(674), 5, + anon_sym_DOT, + ACTIONS(663), 4, anon_sym_PIPE, anon_sym_RPAREN, - anon_sym_DOT, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8561] = 2, - ACTIONS(455), 1, + [9076] = 3, + ACTIONS(573), 1, + anon_sym_DOT, + ACTIONS(669), 1, anon_sym_, - ACTIONS(457), 5, + ACTIONS(667), 4, anon_sym_PIPE, anon_sym_RPAREN, - anon_sym_DOT, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8572] = 2, - ACTIONS(680), 1, + [9089] = 3, + ACTIONS(673), 1, anon_sym_, - ACTIONS(678), 5, + ACTIONS(675), 1, + sym_identifier, + ACTIONS(671), 4, anon_sym_PIPE, anon_sym_RPAREN, - anon_sym_DOT, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8583] = 4, - ACTIONS(641), 1, + [9102] = 4, + ACTIONS(677), 1, anon_sym_PIPE, - ACTIONS(647), 1, - anon_sym_DOT, - ACTIONS(682), 1, + ACTIONS(681), 1, anon_sym_, - ACTIONS(657), 3, + STATE(241), 1, + aux_sym_argument_list_repeat1, + ACTIONS(679), 3, anon_sym_RPAREN, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8598] = 2, - ACTIONS(686), 1, + [9117] = 3, + ACTIONS(685), 1, anon_sym_, - ACTIONS(684), 5, + STATE(246), 1, + aux_sym_argument_list_repeat1, + ACTIONS(683), 4, anon_sym_PIPE, anon_sym_RPAREN, - anon_sym_DOT, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8609] = 2, - ACTIONS(459), 1, + [9130] = 3, + ACTIONS(688), 1, anon_sym_, - ACTIONS(461), 5, + STATE(246), 1, + aux_sym_argument_list_repeat1, + ACTIONS(217), 4, anon_sym_PIPE, anon_sym_RPAREN, - anon_sym_DOT, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8620] = 2, + [9143] = 4, + ACTIONS(677), 1, + anon_sym_PIPE, ACTIONS(690), 1, anon_sym_, - ACTIONS(688), 5, - anon_sym_PIPE, + STATE(247), 1, + aux_sym_argument_list_repeat1, + ACTIONS(679), 3, anon_sym_RPAREN, - anon_sym_DOT, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8631] = 4, - ACTIONS(641), 1, + [9158] = 3, + ACTIONS(677), 1, anon_sym_PIPE, - ACTIONS(647), 1, - anon_sym_DOT, ACTIONS(694), 1, anon_sym_, ACTIONS(692), 3, anon_sym_RPAREN, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8646] = 2, - ACTIONS(696), 1, - sym_identifier, - ACTIONS(653), 5, + [9170] = 3, + ACTIONS(677), 1, anon_sym_PIPE, + ACTIONS(698), 1, + anon_sym_, + ACTIONS(696), 3, anon_sym_RPAREN, - anon_sym_DOT, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8657] = 4, - ACTIONS(698), 1, - anon_sym_PIPE, + [9182] = 2, ACTIONS(700), 1, - anon_sym_DOT, - STATE(84), 1, - sym__right_delimiter, - ACTIONS(702), 2, - anon_sym_RBRACE_RBRACE, - anon_sym_DASH_RBRACE_RBRACE, - [8671] = 4, - ACTIONS(698), 1, + sym_identifier, + ACTIONS(673), 4, anon_sym_PIPE, - ACTIONS(700), 1, - anon_sym_DOT, - STATE(112), 1, - sym__right_delimiter, - ACTIONS(704), 2, + anon_sym_RPAREN, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8685] = 4, - ACTIONS(698), 1, + [9192] = 2, + ACTIONS(704), 1, + anon_sym_, + ACTIONS(702), 4, anon_sym_PIPE, - ACTIONS(700), 1, - anon_sym_DOT, - STATE(212), 1, - sym__right_delimiter, - ACTIONS(706), 2, + anon_sym_RPAREN, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8699] = 4, - ACTIONS(698), 1, + [9202] = 2, + ACTIONS(471), 1, + anon_sym_, + ACTIONS(473), 4, anon_sym_PIPE, - ACTIONS(700), 1, - anon_sym_DOT, - STATE(157), 1, - sym__right_delimiter, - ACTIONS(708), 2, + anon_sym_RPAREN, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8713] = 4, - ACTIONS(698), 1, + [9212] = 2, + ACTIONS(708), 1, + anon_sym_, + ACTIONS(706), 4, anon_sym_PIPE, - ACTIONS(700), 1, - anon_sym_DOT, - STATE(139), 1, - sym__right_delimiter, - ACTIONS(710), 2, + anon_sym_RPAREN, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8727] = 4, - ACTIONS(698), 1, + [9222] = 2, + ACTIONS(467), 1, + anon_sym_, + ACTIONS(469), 4, anon_sym_PIPE, - ACTIONS(700), 1, - anon_sym_DOT, - STATE(122), 1, - sym__right_delimiter, - ACTIONS(712), 2, + anon_sym_RPAREN, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8741] = 4, - ACTIONS(698), 1, + [9232] = 2, + ACTIONS(712), 1, + anon_sym_, + ACTIONS(710), 4, anon_sym_PIPE, - ACTIONS(700), 1, - anon_sym_DOT, - STATE(110), 1, - sym__right_delimiter, - ACTIONS(714), 2, + anon_sym_RPAREN, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8755] = 5, - ACTIONS(716), 1, + [9242] = 5, + ACTIONS(714), 1, anon_sym_LBRACE_LBRACE, - ACTIONS(719), 1, + ACTIONS(717), 1, anon_sym_LBRACE_LBRACE_DASH, - STATE(260), 1, + STATE(257), 1, aux_sym_if_action_repeat1, - STATE(368), 1, + STATE(372), 1, sym__else_if_clause, - STATE(373), 1, + STATE(377), 1, sym__left_delimiter, - [8771] = 1, - ACTIONS(676), 5, + [9258] = 3, + ACTIONS(677), 1, anon_sym_PIPE, + ACTIONS(720), 1, + anon_sym_, + ACTIONS(683), 3, anon_sym_RPAREN, - anon_sym_DOT, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8779] = 4, - ACTIONS(698), 1, + [9270] = 1, + ACTIONS(665), 5, anon_sym_PIPE, - ACTIONS(700), 1, + anon_sym_RPAREN, anon_sym_DOT, - STATE(130), 1, - sym__right_delimiter, - ACTIONS(722), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8793] = 1, - ACTIONS(680), 5, + [9278] = 2, + ACTIONS(653), 1, + anon_sym_DOT, + ACTIONS(669), 4, anon_sym_PIPE, anon_sym_RPAREN, - anon_sym_DOT, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8801] = 4, - ACTIONS(698), 1, - anon_sym_PIPE, - ACTIONS(700), 1, - anon_sym_DOT, - STATE(227), 1, + [9288] = 4, + ACTIONS(722), 1, + anon_sym_LBRACE_LBRACE, + ACTIONS(724), 1, + anon_sym_LBRACE_LBRACE_DASH, + STATE(194), 1, + sym__if_actions_end, + STATE(375), 1, + sym__left_delimiter, + [9301] = 3, + ACTIONS(726), 1, + anon_sym_DQUOTE, + STATE(281), 1, + aux_sym_interpreted_string_literal_repeat1, + ACTIONS(728), 2, + aux_sym_interpreted_string_literal_token1, + sym_escape_sequence, + [9312] = 3, + ACTIONS(39), 1, + anon_sym_DQUOTE, + ACTIONS(730), 1, + sym_raw_string_literal, + STATE(65), 2, + sym__string_literal, + sym_interpreted_string_literal, + [9323] = 3, + ACTIONS(39), 1, + anon_sym_DQUOTE, + ACTIONS(732), 1, + sym_raw_string_literal, + STATE(64), 2, + sym__string_literal, + sym_interpreted_string_literal, + [9334] = 3, + ACTIONS(39), 1, + anon_sym_DQUOTE, + ACTIONS(734), 1, + sym_raw_string_literal, + STATE(365), 2, + sym__string_literal, + sym_interpreted_string_literal, + [9345] = 3, + ACTIONS(39), 1, + anon_sym_DQUOTE, + ACTIONS(736), 1, + sym_raw_string_literal, + STATE(352), 2, + sym__string_literal, + sym_interpreted_string_literal, + [9356] = 3, + ACTIONS(738), 1, + anon_sym_PIPE, + STATE(142), 1, sym__right_delimiter, - ACTIONS(724), 2, + ACTIONS(740), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8815] = 4, - ACTIONS(698), 1, + [9367] = 3, + ACTIONS(738), 1, anon_sym_PIPE, - ACTIONS(700), 1, - anon_sym_DOT, - STATE(174), 1, + STATE(114), 1, sym__right_delimiter, - ACTIONS(726), 2, + ACTIONS(742), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8829] = 3, - ACTIONS(698), 1, + [9378] = 3, + ACTIONS(39), 1, + anon_sym_DQUOTE, + ACTIONS(744), 1, + sym_raw_string_literal, + STATE(66), 2, + sym__string_literal, + sym_interpreted_string_literal, + [9389] = 3, + ACTIONS(738), 1, anon_sym_PIPE, - ACTIONS(700), 1, - anon_sym_DOT, - ACTIONS(672), 3, - anon_sym_RPAREN, + STATE(107), 1, + sym__right_delimiter, + ACTIONS(746), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8841] = 4, - ACTIONS(698), 1, + [9400] = 3, + ACTIONS(748), 1, + anon_sym_DQUOTE, + STATE(277), 1, + aux_sym_interpreted_string_literal_repeat1, + ACTIONS(750), 2, + aux_sym_interpreted_string_literal_token1, + sym_escape_sequence, + [9411] = 3, + ACTIONS(738), 1, anon_sym_PIPE, - ACTIONS(700), 1, - anon_sym_DOT, - STATE(96), 1, + STATE(163), 1, sym__right_delimiter, - ACTIONS(728), 2, + ACTIONS(752), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8855] = 1, - ACTIONS(686), 5, + [9422] = 3, + ACTIONS(738), 1, anon_sym_PIPE, - anon_sym_RPAREN, - anon_sym_DOT, + STATE(88), 1, + sym__right_delimiter, + ACTIONS(754), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8863] = 4, - ACTIONS(698), 1, + [9433] = 3, + ACTIONS(738), 1, anon_sym_PIPE, - ACTIONS(700), 1, - anon_sym_DOT, - STATE(86), 1, + STATE(124), 1, sym__right_delimiter, - ACTIONS(730), 2, + ACTIONS(756), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8877] = 1, - ACTIONS(690), 5, + [9444] = 3, + ACTIONS(738), 1, + anon_sym_PIPE, + STATE(112), 1, + sym__right_delimiter, + ACTIONS(758), 2, + anon_sym_RBRACE_RBRACE, + anon_sym_DASH_RBRACE_RBRACE, + [9455] = 1, + ACTIONS(712), 4, anon_sym_PIPE, anon_sym_RPAREN, - anon_sym_DOT, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8885] = 3, - ACTIONS(698), 1, + [9462] = 3, + ACTIONS(760), 1, + anon_sym_DQUOTE, + STATE(281), 1, + aux_sym_interpreted_string_literal_repeat1, + ACTIONS(728), 2, + aux_sym_interpreted_string_literal_token1, + sym_escape_sequence, + [9473] = 1, + ACTIONS(708), 4, anon_sym_PIPE, - ACTIONS(700), 1, - anon_sym_DOT, - ACTIONS(694), 3, anon_sym_RPAREN, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8897] = 4, - ACTIONS(698), 1, + [9480] = 3, + ACTIONS(738), 1, anon_sym_PIPE, - ACTIONS(700), 1, - anon_sym_DOT, - STATE(105), 1, + STATE(180), 1, sym__right_delimiter, - ACTIONS(732), 2, + ACTIONS(762), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [8911] = 3, - ACTIONS(734), 1, - anon_sym_DQUOTE, - STATE(273), 1, - aux_sym_interpreted_string_literal_repeat1, - ACTIONS(736), 2, - aux_sym_interpreted_string_literal_token1, - sym_escape_sequence, - [8922] = 3, - ACTIONS(39), 1, - anon_sym_DQUOTE, - ACTIONS(739), 1, - sym_raw_string_literal, - STATE(63), 2, - sym__string_literal, - sym_interpreted_string_literal, - [8933] = 3, - ACTIONS(39), 1, - anon_sym_DQUOTE, - ACTIONS(741), 1, - sym_raw_string_literal, - STATE(62), 2, - sym__string_literal, - sym_interpreted_string_literal, - [8944] = 3, - ACTIONS(39), 1, - anon_sym_DQUOTE, - ACTIONS(743), 1, - sym_raw_string_literal, - STATE(64), 2, - sym__string_literal, - sym_interpreted_string_literal, - [8955] = 4, - ACTIONS(745), 1, - anon_sym_LBRACE_LBRACE, - ACTIONS(747), 1, - anon_sym_LBRACE_LBRACE_DASH, - STATE(186), 1, - sym__if_actions_end, - STATE(371), 1, - sym__left_delimiter, - [8968] = 3, - ACTIONS(749), 1, + [9491] = 3, + ACTIONS(738), 1, + anon_sym_PIPE, + STATE(141), 1, + sym__right_delimiter, + ACTIONS(764), 2, + anon_sym_RBRACE_RBRACE, + anon_sym_DASH_RBRACE_RBRACE, + [9502] = 3, + ACTIONS(766), 1, anon_sym_DQUOTE, - STATE(273), 1, + STATE(281), 1, aux_sym_interpreted_string_literal_repeat1, - ACTIONS(751), 2, + ACTIONS(768), 2, aux_sym_interpreted_string_literal_token1, sym_escape_sequence, - [8979] = 3, + [9513] = 1, + ACTIONS(704), 4, + anon_sym_PIPE, + anon_sym_RPAREN, + anon_sym_RBRACE_RBRACE, + anon_sym_DASH_RBRACE_RBRACE, + [9520] = 2, + ACTIONS(738), 1, + anon_sym_PIPE, + ACTIONS(694), 3, + anon_sym_RPAREN, + anon_sym_RBRACE_RBRACE, + anon_sym_DASH_RBRACE_RBRACE, + [9529] = 3, + ACTIONS(738), 1, + anon_sym_PIPE, + STATE(86), 1, + sym__right_delimiter, + ACTIONS(771), 2, + anon_sym_RBRACE_RBRACE, + anon_sym_DASH_RBRACE_RBRACE, + [9540] = 2, + ACTIONS(738), 1, + anon_sym_PIPE, + ACTIONS(698), 3, + anon_sym_RPAREN, + anon_sym_RBRACE_RBRACE, + anon_sym_DASH_RBRACE_RBRACE, + [9549] = 3, ACTIONS(39), 1, anon_sym_DQUOTE, - ACTIONS(753), 1, + ACTIONS(773), 1, sym_raw_string_literal, - STATE(349), 2, + STATE(67), 2, sym__string_literal, sym_interpreted_string_literal, - [8990] = 4, - ACTIONS(745), 1, + [9560] = 4, + ACTIONS(722), 1, anon_sym_LBRACE_LBRACE, - ACTIONS(747), 1, + ACTIONS(724), 1, anon_sym_LBRACE_LBRACE_DASH, - STATE(164), 1, + STATE(156), 1, sym__if_actions_end, - STATE(371), 1, + STATE(375), 1, sym__left_delimiter, - [9003] = 4, - ACTIONS(745), 1, + [9573] = 4, + ACTIONS(722), 1, anon_sym_LBRACE_LBRACE, - ACTIONS(747), 1, + ACTIONS(724), 1, anon_sym_LBRACE_LBRACE_DASH, - STATE(161), 1, + STATE(167), 1, sym__if_actions_end, - STATE(371), 1, + STATE(375), 1, sym__left_delimiter, - [9016] = 3, - ACTIONS(39), 1, - anon_sym_DQUOTE, - ACTIONS(755), 1, - sym_raw_string_literal, - STATE(361), 2, - sym__string_literal, - sym_interpreted_string_literal, - [9027] = 3, - ACTIONS(698), 1, + [9586] = 3, + ACTIONS(738), 1, anon_sym_PIPE, - ACTIONS(700), 1, - anon_sym_DOT, - ACTIONS(757), 2, + STATE(231), 1, + sym__right_delimiter, + ACTIONS(775), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9038] = 3, - ACTIONS(39), 1, - anon_sym_DQUOTE, - ACTIONS(759), 1, - sym_raw_string_literal, - STATE(65), 2, - sym__string_literal, - sym_interpreted_string_literal, - [9049] = 4, - ACTIONS(761), 1, + [9597] = 4, + ACTIONS(777), 1, anon_sym_LBRACE_LBRACE, - ACTIONS(763), 1, + ACTIONS(779), 1, anon_sym_LBRACE_LBRACE_DASH, - STATE(233), 1, + STATE(224), 1, sym__if_actions_end, - STATE(372), 1, + STATE(376), 1, sym__left_delimiter, - [9062] = 4, - ACTIONS(761), 1, + [9610] = 4, + ACTIONS(777), 1, anon_sym_LBRACE_LBRACE, - ACTIONS(763), 1, + ACTIONS(779), 1, anon_sym_LBRACE_LBRACE_DASH, - STATE(242), 1, + STATE(228), 1, sym__if_actions_end, - STATE(372), 1, + STATE(376), 1, sym__left_delimiter, - [9075] = 3, - ACTIONS(765), 1, - anon_sym_DQUOTE, - STATE(288), 1, - aux_sym_interpreted_string_literal_repeat1, - ACTIONS(767), 2, - aux_sym_interpreted_string_literal_token1, - sym_escape_sequence, - [9086] = 3, - ACTIONS(769), 1, - anon_sym_DQUOTE, - STATE(273), 1, - aux_sym_interpreted_string_literal_repeat1, - ACTIONS(751), 2, - aux_sym_interpreted_string_literal_token1, - sym_escape_sequence, - [9097] = 3, - ACTIONS(771), 1, + [9623] = 3, + ACTIONS(781), 1, anon_sym_DQUOTE, - STATE(278), 1, + STATE(262), 1, aux_sym_interpreted_string_literal_repeat1, - ACTIONS(773), 2, + ACTIONS(783), 2, aux_sym_interpreted_string_literal_token1, sym_escape_sequence, - [9108] = 4, - ACTIONS(761), 1, + [9634] = 4, + ACTIONS(777), 1, anon_sym_LBRACE_LBRACE, - ACTIONS(763), 1, + ACTIONS(779), 1, anon_sym_LBRACE_LBRACE_DASH, - STATE(240), 1, - sym__if_actions_end, - STATE(372), 1, - sym__left_delimiter, - [9121] = 4, - ACTIONS(745), 1, - anon_sym_LBRACE_LBRACE, - ACTIONS(747), 1, - anon_sym_LBRACE_LBRACE_DASH, - STATE(166), 1, + STATE(232), 1, sym__if_actions_end, - STATE(371), 1, + STATE(376), 1, sym__left_delimiter, - [9134] = 4, - ACTIONS(761), 1, - anon_sym_LBRACE_LBRACE, - ACTIONS(763), 1, - anon_sym_LBRACE_LBRACE_DASH, - STATE(229), 1, - sym__if_actions_end, - STATE(372), 1, - sym__left_delimiter, - [9147] = 2, - STATE(189), 1, - sym__right_delimiter, - ACTIONS(775), 2, - anon_sym_RBRACE_RBRACE, - anon_sym_DASH_RBRACE_RBRACE, - [9155] = 2, - STATE(211), 1, - sym__right_delimiter, - ACTIONS(777), 2, - anon_sym_RBRACE_RBRACE, - anon_sym_DASH_RBRACE_RBRACE, - [9163] = 2, - STATE(235), 1, - sym__right_delimiter, - ACTIONS(779), 2, - anon_sym_RBRACE_RBRACE, - anon_sym_DASH_RBRACE_RBRACE, - [9171] = 3, - ACTIONS(698), 1, + [9647] = 3, + ACTIONS(738), 1, anon_sym_PIPE, - ACTIONS(700), 1, - anon_sym_DOT, - ACTIONS(781), 1, - anon_sym_RPAREN, - [9181] = 2, - STATE(183), 1, - sym__right_delimiter, - ACTIONS(783), 2, - anon_sym_RBRACE_RBRACE, - anon_sym_DASH_RBRACE_RBRACE, - [9189] = 2, - STATE(228), 1, + STATE(223), 1, sym__right_delimiter, ACTIONS(785), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9197] = 2, - STATE(169), 1, + [9658] = 4, + ACTIONS(722), 1, + anon_sym_LBRACE_LBRACE, + ACTIONS(724), 1, + anon_sym_LBRACE_LBRACE_DASH, + STATE(191), 1, + sym__if_actions_end, + STATE(375), 1, + sym__left_delimiter, + [9671] = 3, + ACTIONS(738), 1, + anon_sym_PIPE, + STATE(143), 1, sym__right_delimiter, ACTIONS(787), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9205] = 2, - STATE(187), 1, + [9682] = 4, + ACTIONS(777), 1, + anon_sym_LBRACE_LBRACE, + ACTIONS(779), 1, + anon_sym_LBRACE_LBRACE_DASH, + STATE(200), 1, + sym__if_actions_end, + STATE(376), 1, + sym__left_delimiter, + [9695] = 2, + STATE(166), 1, sym__right_delimiter, ACTIONS(789), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9213] = 2, - STATE(132), 1, + [9703] = 2, + STATE(219), 1, sym__right_delimiter, ACTIONS(791), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9221] = 2, - STATE(172), 1, + [9711] = 2, + STATE(174), 1, sym__right_delimiter, ACTIONS(793), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9229] = 2, - STATE(188), 1, + [9719] = 2, + STATE(222), 1, sym__right_delimiter, ACTIONS(795), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9237] = 2, - STATE(134), 1, + [9727] = 2, + STATE(135), 1, sym__right_delimiter, ACTIONS(797), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9245] = 2, - STATE(236), 1, + [9735] = 2, + STATE(204), 1, sym__right_delimiter, ACTIONS(799), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9253] = 2, - STATE(165), 1, + [9743] = 2, + STATE(162), 1, sym__right_delimiter, ACTIONS(801), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9261] = 2, - STATE(225), 1, + [9751] = 2, + STATE(170), 1, sym__right_delimiter, ACTIONS(803), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9269] = 2, - STATE(234), 1, + [9759] = 2, + STATE(196), 1, sym__right_delimiter, ACTIONS(805), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9277] = 2, - STATE(230), 1, + [9767] = 2, + STATE(199), 1, sym__right_delimiter, ACTIONS(807), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9285] = 2, - STATE(138), 1, + [9775] = 2, + STATE(182), 1, sym__right_delimiter, ACTIONS(809), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9293] = 2, - STATE(181), 1, + [9783] = 2, + STATE(195), 1, sym__right_delimiter, ACTIONS(811), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9301] = 2, - STATE(171), 1, + [9791] = 2, + STATE(138), 1, sym__right_delimiter, ACTIONS(813), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9309] = 2, - STATE(158), 1, + [9799] = 2, + STATE(212), 1, sym__right_delimiter, ACTIONS(815), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9317] = 2, - STATE(127), 1, + [9807] = 2, + STATE(169), 1, sym__right_delimiter, ACTIONS(817), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9325] = 2, - STATE(159), 1, + [9815] = 2, + STATE(239), 1, sym__right_delimiter, ACTIONS(819), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9333] = 2, - STATE(179), 1, + [9823] = 2, + STATE(193), 1, sym__right_delimiter, ACTIONS(821), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9341] = 2, - STATE(123), 1, + [9831] = 2, + STATE(140), 1, sym__right_delimiter, ACTIONS(823), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9349] = 2, - STATE(185), 1, + [9839] = 2, + STATE(187), 1, sym__right_delimiter, ACTIONS(825), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9357] = 3, - ACTIONS(11), 1, - anon_sym_elseif, - ACTIONS(13), 1, - anon_sym_else, - ACTIONS(827), 1, - anon_sym_end, - [9367] = 2, - STATE(207), 1, + [9847] = 2, + STATE(159), 1, + sym__right_delimiter, + ACTIONS(827), 2, + anon_sym_RBRACE_RBRACE, + anon_sym_DASH_RBRACE_RBRACE, + [9855] = 2, + STATE(165), 1, sym__right_delimiter, ACTIONS(829), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9375] = 2, - STATE(175), 1, + [9863] = 2, + STATE(129), 1, sym__right_delimiter, ACTIONS(831), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9383] = 2, - STATE(152), 1, + [9871] = 2, + STATE(213), 1, sym__right_delimiter, ACTIONS(833), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9391] = 2, - STATE(116), 1, + [9879] = 2, + STATE(125), 1, sym__right_delimiter, ACTIONS(835), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9399] = 2, - STATE(95), 1, + [9887] = 2, + STATE(192), 1, sym__right_delimiter, ACTIONS(837), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9407] = 2, - STATE(190), 1, - sym__right_delimiter, - ACTIONS(839), 2, - anon_sym_RBRACE_RBRACE, - anon_sym_DASH_RBRACE_RBRACE, - [9415] = 2, - STATE(194), 1, + [9895] = 3, + ACTIONS(11), 1, + anon_sym_elseif, + ACTIONS(13), 1, + anon_sym_else, + ACTIONS(839), 1, + anon_sym_end, + [9905] = 2, + STATE(207), 1, sym__right_delimiter, ACTIONS(841), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9423] = 2, - STATE(102), 1, + [9913] = 2, + STATE(214), 1, sym__right_delimiter, ACTIONS(843), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9431] = 2, - STATE(191), 1, + [9921] = 2, + STATE(171), 1, sym__right_delimiter, ACTIONS(845), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9439] = 2, - STATE(170), 1, + [9929] = 2, + STATE(118), 1, sym__right_delimiter, ACTIONS(847), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9447] = 2, - STATE(115), 1, + [9937] = 2, + STATE(98), 1, sym__right_delimiter, ACTIONS(849), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9455] = 2, - STATE(208), 1, + [9945] = 2, + STATE(190), 1, sym__right_delimiter, ACTIONS(851), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9463] = 2, - STATE(160), 1, + [9953] = 2, + STATE(189), 1, sym__right_delimiter, ACTIONS(853), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9471] = 2, - STATE(209), 1, + [9961] = 2, + STATE(105), 1, sym__right_delimiter, ACTIONS(855), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9479] = 2, - STATE(210), 1, + [9969] = 2, + STATE(185), 1, sym__right_delimiter, ACTIONS(857), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9487] = 2, - STATE(213), 1, - sym__right_delimiter, + [9977] = 2, + ACTIONS(738), 1, + anon_sym_PIPE, ACTIONS(859), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9495] = 2, - STATE(214), 1, + [9985] = 2, + STATE(116), 1, sym__right_delimiter, ACTIONS(861), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9503] = 2, - STATE(192), 1, + [9993] = 2, + STATE(229), 1, sym__right_delimiter, ACTIONS(863), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9511] = 2, - STATE(124), 1, - sym__right_delimiter, - ACTIONS(865), 2, - anon_sym_RBRACE_RBRACE, - anon_sym_DASH_RBRACE_RBRACE, - [9519] = 2, + [10001] = 3, + ACTIONS(11), 1, + anon_sym_elseif, + ACTIONS(13), 1, + anon_sym_else, + ACTIONS(865), 1, + anon_sym_end, + [10011] = 2, STATE(215), 1, sym__right_delimiter, ACTIONS(867), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9527] = 2, - STATE(216), 1, + [10019] = 2, + STATE(221), 1, sym__right_delimiter, ACTIONS(869), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9535] = 2, - STATE(120), 1, + [10027] = 2, + STATE(175), 1, sym__right_delimiter, ACTIONS(871), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9543] = 2, - STATE(218), 1, + [10035] = 2, + STATE(216), 1, sym__right_delimiter, ACTIONS(873), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9551] = 2, - STATE(129), 1, + [10043] = 2, + STATE(181), 1, sym__right_delimiter, ACTIONS(875), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9559] = 2, - STATE(219), 1, + [10051] = 2, + STATE(157), 1, sym__right_delimiter, ACTIONS(877), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9567] = 2, - STATE(220), 1, + [10059] = 2, + STATE(177), 1, sym__right_delimiter, ACTIONS(879), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9575] = 2, - STATE(156), 1, + [10067] = 2, + STATE(225), 1, sym__right_delimiter, ACTIONS(881), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9583] = 3, - ACTIONS(698), 1, - anon_sym_PIPE, - ACTIONS(700), 1, - anon_sym_DOT, - ACTIONS(883), 1, - anon_sym_RPAREN, - [9593] = 2, - STATE(154), 1, + [10075] = 2, + STATE(121), 1, + sym__right_delimiter, + ACTIONS(883), 2, + anon_sym_RBRACE_RBRACE, + anon_sym_DASH_RBRACE_RBRACE, + [10083] = 2, + STATE(233), 1, sym__right_delimiter, ACTIONS(885), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9601] = 2, - STATE(113), 1, + [10091] = 2, + STATE(236), 1, sym__right_delimiter, ACTIONS(887), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9609] = 2, - STATE(167), 1, + [10099] = 2, + STATE(237), 1, sym__right_delimiter, ACTIONS(889), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9617] = 2, - STATE(221), 1, + [10107] = 2, + STATE(172), 1, sym__right_delimiter, ACTIONS(891), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9625] = 2, - STATE(125), 1, + [10115] = 2, + STATE(122), 1, sym__right_delimiter, ACTIONS(893), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9633] = 2, - STATE(173), 1, + [10123] = 2, + STATE(238), 1, sym__right_delimiter, ACTIONS(895), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9641] = 2, - STATE(184), 1, + [10131] = 2, + STATE(115), 1, sym__right_delimiter, ACTIONS(897), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9649] = 2, - STATE(239), 1, + [10139] = 2, + STATE(164), 1, sym__right_delimiter, ACTIONS(899), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9657] = 2, - STATE(155), 1, + [10147] = 2, + STATE(208), 1, sym__right_delimiter, ACTIONS(901), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9665] = 3, - ACTIONS(11), 1, - anon_sym_elseif, - ACTIONS(13), 1, - anon_sym_else, - ACTIONS(903), 1, - anon_sym_end, - [9675] = 2, - STATE(222), 1, + [10155] = 2, + STATE(127), 1, + sym__right_delimiter, + ACTIONS(903), 2, + anon_sym_RBRACE_RBRACE, + anon_sym_DASH_RBRACE_RBRACE, + [10163] = 2, + STATE(209), 1, sym__right_delimiter, ACTIONS(905), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9683] = 2, - STATE(104), 1, + [10171] = 2, + STATE(132), 1, sym__right_delimiter, ACTIONS(907), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9691] = 2, - STATE(238), 1, + [10179] = 2, + STATE(160), 1, sym__right_delimiter, ACTIONS(909), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9699] = 2, - STATE(107), 1, + [10187] = 2, + STATE(203), 1, sym__right_delimiter, ACTIONS(911), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9707] = 2, - STATE(223), 1, + [10195] = 2, + STATE(176), 1, sym__right_delimiter, ACTIONS(913), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9715] = 2, - STATE(237), 1, + [10203] = 2, + STATE(218), 1, sym__right_delimiter, ACTIONS(915), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9723] = 2, - STATE(224), 1, + [10211] = 2, + STATE(158), 1, sym__right_delimiter, ACTIONS(917), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9731] = 2, - STATE(131), 1, + [10219] = 2, + STATE(106), 1, sym__right_delimiter, ACTIONS(919), 2, anon_sym_RBRACE_RBRACE, anon_sym_DASH_RBRACE_RBRACE, - [9739] = 2, - ACTIONS(921), 1, - sym_identifier, - STATE(146), 1, - sym__field_identifier, - [9746] = 2, + [10227] = 2, + STATE(205), 1, + sym__right_delimiter, + ACTIONS(921), 2, + anon_sym_RBRACE_RBRACE, + anon_sym_DASH_RBRACE_RBRACE, + [10235] = 2, + STATE(109), 1, + sym__right_delimiter, + ACTIONS(923), 2, + anon_sym_RBRACE_RBRACE, + anon_sym_DASH_RBRACE_RBRACE, + [10243] = 2, + STATE(198), 1, + sym__right_delimiter, + ACTIONS(925), 2, + anon_sym_RBRACE_RBRACE, + anon_sym_DASH_RBRACE_RBRACE, + [10251] = 2, + STATE(220), 1, + sym__right_delimiter, + ACTIONS(927), 2, + anon_sym_RBRACE_RBRACE, + anon_sym_DASH_RBRACE_RBRACE, + [10259] = 2, + STATE(240), 1, + sym__right_delimiter, + ACTIONS(929), 2, + anon_sym_RBRACE_RBRACE, + anon_sym_DASH_RBRACE_RBRACE, + [10267] = 2, + STATE(126), 1, + sym__right_delimiter, + ACTIONS(931), 2, + anon_sym_RBRACE_RBRACE, + anon_sym_DASH_RBRACE_RBRACE, + [10275] = 2, + ACTIONS(738), 1, + anon_sym_PIPE, + ACTIONS(933), 1, + anon_sym_RPAREN, + [10282] = 2, ACTIONS(31), 1, anon_sym_DOLLAR, - STATE(370), 1, + STATE(374), 1, sym_variable, - [9753] = 2, - ACTIONS(923), 1, + [10289] = 2, + ACTIONS(935), 1, anon_sym_LBRACE_LBRACE, - ACTIONS(925), 1, + ACTIONS(937), 1, anon_sym_LBRACE_LBRACE_DASH, - [9760] = 2, - ACTIONS(927), 1, - sym_identifier, - STATE(144), 1, - sym__field_identifier, - [9767] = 1, - ACTIONS(929), 1, + [10296] = 2, + ACTIONS(738), 1, + anon_sym_PIPE, + ACTIONS(939), 1, + anon_sym_RPAREN, + [10303] = 1, + ACTIONS(941), 1, anon_sym_COLON_EQ, - [9771] = 1, - ACTIONS(827), 1, + [10307] = 1, + ACTIONS(839), 1, anon_sym_end, - [9775] = 1, - ACTIONS(903), 1, + [10311] = 1, + ACTIONS(865), 1, anon_sym_end, - [9779] = 1, + [10315] = 1, ACTIONS(11), 1, anon_sym_elseif, - [9783] = 1, - ACTIONS(931), 1, + [10319] = 1, + ACTIONS(943), 1, ts_builtin_sym_end, }; static const uint32_t ts_small_parse_table_map[] = { [SMALL_STATE(2)] = 0, - [SMALL_STATE(3)] = 80, - [SMALL_STATE(4)] = 160, - [SMALL_STATE(5)] = 237, - [SMALL_STATE(6)] = 314, - [SMALL_STATE(7)] = 391, - [SMALL_STATE(8)] = 468, - [SMALL_STATE(9)] = 545, - [SMALL_STATE(10)] = 622, - [SMALL_STATE(11)] = 699, - [SMALL_STATE(12)] = 776, - [SMALL_STATE(13)] = 853, - [SMALL_STATE(14)] = 930, - [SMALL_STATE(15)] = 1007, - [SMALL_STATE(16)] = 1084, - [SMALL_STATE(17)] = 1158, - [SMALL_STATE(18)] = 1232, - [SMALL_STATE(19)] = 1306, - [SMALL_STATE(20)] = 1380, - [SMALL_STATE(21)] = 1454, - [SMALL_STATE(22)] = 1528, - [SMALL_STATE(23)] = 1602, - [SMALL_STATE(24)] = 1676, - [SMALL_STATE(25)] = 1750, - [SMALL_STATE(26)] = 1824, - [SMALL_STATE(27)] = 1898, - [SMALL_STATE(28)] = 1972, - [SMALL_STATE(29)] = 2046, - [SMALL_STATE(30)] = 2120, - [SMALL_STATE(31)] = 2194, - [SMALL_STATE(32)] = 2268, - [SMALL_STATE(33)] = 2342, - [SMALL_STATE(34)] = 2416, - [SMALL_STATE(35)] = 2490, - [SMALL_STATE(36)] = 2564, - [SMALL_STATE(37)] = 2638, - [SMALL_STATE(38)] = 2712, - [SMALL_STATE(39)] = 2786, - [SMALL_STATE(40)] = 2860, - [SMALL_STATE(41)] = 2934, - [SMALL_STATE(42)] = 3008, - [SMALL_STATE(43)] = 3082, - [SMALL_STATE(44)] = 3156, - [SMALL_STATE(45)] = 3230, - [SMALL_STATE(46)] = 3304, - [SMALL_STATE(47)] = 3378, - [SMALL_STATE(48)] = 3452, - [SMALL_STATE(49)] = 3526, - [SMALL_STATE(50)] = 3600, - [SMALL_STATE(51)] = 3674, - [SMALL_STATE(52)] = 3748, - [SMALL_STATE(53)] = 3819, - [SMALL_STATE(54)] = 3890, - [SMALL_STATE(55)] = 3950, - [SMALL_STATE(56)] = 4008, - [SMALL_STATE(57)] = 4065, - [SMALL_STATE(58)] = 4124, - [SMALL_STATE(59)] = 4181, - [SMALL_STATE(60)] = 4240, - [SMALL_STATE(61)] = 4296, - [SMALL_STATE(62)] = 4352, - [SMALL_STATE(63)] = 4409, - [SMALL_STATE(64)] = 4466, - [SMALL_STATE(65)] = 4523, - [SMALL_STATE(66)] = 4580, - [SMALL_STATE(67)] = 4633, - [SMALL_STATE(68)] = 4686, - [SMALL_STATE(69)] = 4736, - [SMALL_STATE(70)] = 4786, - [SMALL_STATE(71)] = 4836, - [SMALL_STATE(72)] = 4886, - [SMALL_STATE(73)] = 4936, - [SMALL_STATE(74)] = 4986, - [SMALL_STATE(75)] = 5036, - [SMALL_STATE(76)] = 5086, - [SMALL_STATE(77)] = 5136, - [SMALL_STATE(78)] = 5186, - [SMALL_STATE(79)] = 5236, - [SMALL_STATE(80)] = 5286, - [SMALL_STATE(81)] = 5336, - [SMALL_STATE(82)] = 5386, - [SMALL_STATE(83)] = 5436, - [SMALL_STATE(84)] = 5477, - [SMALL_STATE(85)] = 5518, - [SMALL_STATE(86)] = 5559, - [SMALL_STATE(87)] = 5600, - [SMALL_STATE(88)] = 5632, - [SMALL_STATE(89)] = 5664, - [SMALL_STATE(90)] = 5693, - [SMALL_STATE(91)] = 5722, - [SMALL_STATE(92)] = 5751, - [SMALL_STATE(93)] = 5780, - [SMALL_STATE(94)] = 5809, - [SMALL_STATE(95)] = 5838, - [SMALL_STATE(96)] = 5867, - [SMALL_STATE(97)] = 5896, - [SMALL_STATE(98)] = 5925, - [SMALL_STATE(99)] = 5954, - [SMALL_STATE(100)] = 5983, - [SMALL_STATE(101)] = 6012, - [SMALL_STATE(102)] = 6041, - [SMALL_STATE(103)] = 6070, - [SMALL_STATE(104)] = 6099, - [SMALL_STATE(105)] = 6128, - [SMALL_STATE(106)] = 6157, - [SMALL_STATE(107)] = 6186, - [SMALL_STATE(108)] = 6215, - [SMALL_STATE(109)] = 6244, - [SMALL_STATE(110)] = 6273, - [SMALL_STATE(111)] = 6302, - [SMALL_STATE(112)] = 6331, - [SMALL_STATE(113)] = 6360, - [SMALL_STATE(114)] = 6389, - [SMALL_STATE(115)] = 6418, - [SMALL_STATE(116)] = 6447, - [SMALL_STATE(117)] = 6476, - [SMALL_STATE(118)] = 6505, - [SMALL_STATE(119)] = 6534, - [SMALL_STATE(120)] = 6563, - [SMALL_STATE(121)] = 6592, - [SMALL_STATE(122)] = 6621, - [SMALL_STATE(123)] = 6650, - [SMALL_STATE(124)] = 6679, - [SMALL_STATE(125)] = 6708, - [SMALL_STATE(126)] = 6737, - [SMALL_STATE(127)] = 6766, - [SMALL_STATE(128)] = 6795, - [SMALL_STATE(129)] = 6824, - [SMALL_STATE(130)] = 6853, - [SMALL_STATE(131)] = 6882, - [SMALL_STATE(132)] = 6911, - [SMALL_STATE(133)] = 6940, - [SMALL_STATE(134)] = 6969, - [SMALL_STATE(135)] = 6998, - [SMALL_STATE(136)] = 7027, - [SMALL_STATE(137)] = 7056, - [SMALL_STATE(138)] = 7085, - [SMALL_STATE(139)] = 7114, - [SMALL_STATE(140)] = 7143, - [SMALL_STATE(141)] = 7172, - [SMALL_STATE(142)] = 7201, - [SMALL_STATE(143)] = 7224, - [SMALL_STATE(144)] = 7247, - [SMALL_STATE(145)] = 7270, - [SMALL_STATE(146)] = 7292, - [SMALL_STATE(147)] = 7314, - [SMALL_STATE(148)] = 7336, - [SMALL_STATE(149)] = 7358, - [SMALL_STATE(150)] = 7380, - [SMALL_STATE(151)] = 7394, - [SMALL_STATE(152)] = 7410, - [SMALL_STATE(153)] = 7423, - [SMALL_STATE(154)] = 7436, - [SMALL_STATE(155)] = 7449, - [SMALL_STATE(156)] = 7462, - [SMALL_STATE(157)] = 7475, - [SMALL_STATE(158)] = 7488, - [SMALL_STATE(159)] = 7501, - [SMALL_STATE(160)] = 7514, - [SMALL_STATE(161)] = 7527, - [SMALL_STATE(162)] = 7540, - [SMALL_STATE(163)] = 7553, - [SMALL_STATE(164)] = 7566, - [SMALL_STATE(165)] = 7579, - [SMALL_STATE(166)] = 7592, - [SMALL_STATE(167)] = 7605, - [SMALL_STATE(168)] = 7618, - [SMALL_STATE(169)] = 7631, - [SMALL_STATE(170)] = 7644, - [SMALL_STATE(171)] = 7657, - [SMALL_STATE(172)] = 7670, - [SMALL_STATE(173)] = 7683, - [SMALL_STATE(174)] = 7696, - [SMALL_STATE(175)] = 7709, - [SMALL_STATE(176)] = 7722, - [SMALL_STATE(177)] = 7739, - [SMALL_STATE(178)] = 7752, - [SMALL_STATE(179)] = 7765, - [SMALL_STATE(180)] = 7778, - [SMALL_STATE(181)] = 7791, - [SMALL_STATE(182)] = 7804, - [SMALL_STATE(183)] = 7815, - [SMALL_STATE(184)] = 7828, - [SMALL_STATE(185)] = 7841, - [SMALL_STATE(186)] = 7854, - [SMALL_STATE(187)] = 7867, - [SMALL_STATE(188)] = 7880, - [SMALL_STATE(189)] = 7893, - [SMALL_STATE(190)] = 7906, - [SMALL_STATE(191)] = 7919, - [SMALL_STATE(192)] = 7932, - [SMALL_STATE(193)] = 7945, - [SMALL_STATE(194)] = 7967, - [SMALL_STATE(195)] = 7977, - [SMALL_STATE(196)] = 7991, - [SMALL_STATE(197)] = 8007, - [SMALL_STATE(198)] = 8025, - [SMALL_STATE(199)] = 8039, - [SMALL_STATE(200)] = 8049, - [SMALL_STATE(201)] = 8063, - [SMALL_STATE(202)] = 8085, - [SMALL_STATE(203)] = 8099, - [SMALL_STATE(204)] = 8121, - [SMALL_STATE(205)] = 8143, - [SMALL_STATE(206)] = 8157, - [SMALL_STATE(207)] = 8175, - [SMALL_STATE(208)] = 8185, - [SMALL_STATE(209)] = 8195, - [SMALL_STATE(210)] = 8205, - [SMALL_STATE(211)] = 8215, - [SMALL_STATE(212)] = 8225, - [SMALL_STATE(213)] = 8235, - [SMALL_STATE(214)] = 8245, - [SMALL_STATE(215)] = 8255, - [SMALL_STATE(216)] = 8265, - [SMALL_STATE(217)] = 8275, - [SMALL_STATE(218)] = 8285, - [SMALL_STATE(219)] = 8295, - [SMALL_STATE(220)] = 8305, - [SMALL_STATE(221)] = 8315, - [SMALL_STATE(222)] = 8325, - [SMALL_STATE(223)] = 8335, - [SMALL_STATE(224)] = 8345, - [SMALL_STATE(225)] = 8355, - [SMALL_STATE(226)] = 8365, - [SMALL_STATE(227)] = 8375, - [SMALL_STATE(228)] = 8385, - [SMALL_STATE(229)] = 8395, - [SMALL_STATE(230)] = 8405, - [SMALL_STATE(231)] = 8415, - [SMALL_STATE(232)] = 8425, - [SMALL_STATE(233)] = 8435, - [SMALL_STATE(234)] = 8445, - [SMALL_STATE(235)] = 8455, - [SMALL_STATE(236)] = 8465, - [SMALL_STATE(237)] = 8475, - [SMALL_STATE(238)] = 8485, - [SMALL_STATE(239)] = 8495, - [SMALL_STATE(240)] = 8505, - [SMALL_STATE(241)] = 8515, - [SMALL_STATE(242)] = 8525, - [SMALL_STATE(243)] = 8535, - [SMALL_STATE(244)] = 8550, - [SMALL_STATE(245)] = 8561, - [SMALL_STATE(246)] = 8572, - [SMALL_STATE(247)] = 8583, - [SMALL_STATE(248)] = 8598, - [SMALL_STATE(249)] = 8609, - [SMALL_STATE(250)] = 8620, - [SMALL_STATE(251)] = 8631, - [SMALL_STATE(252)] = 8646, - [SMALL_STATE(253)] = 8657, - [SMALL_STATE(254)] = 8671, - [SMALL_STATE(255)] = 8685, - [SMALL_STATE(256)] = 8699, - [SMALL_STATE(257)] = 8713, - [SMALL_STATE(258)] = 8727, - [SMALL_STATE(259)] = 8741, - [SMALL_STATE(260)] = 8755, - [SMALL_STATE(261)] = 8771, - [SMALL_STATE(262)] = 8779, - [SMALL_STATE(263)] = 8793, - [SMALL_STATE(264)] = 8801, - [SMALL_STATE(265)] = 8815, - [SMALL_STATE(266)] = 8829, - [SMALL_STATE(267)] = 8841, - [SMALL_STATE(268)] = 8855, - [SMALL_STATE(269)] = 8863, - [SMALL_STATE(270)] = 8877, - [SMALL_STATE(271)] = 8885, - [SMALL_STATE(272)] = 8897, - [SMALL_STATE(273)] = 8911, - [SMALL_STATE(274)] = 8922, - [SMALL_STATE(275)] = 8933, - [SMALL_STATE(276)] = 8944, - [SMALL_STATE(277)] = 8955, - [SMALL_STATE(278)] = 8968, - [SMALL_STATE(279)] = 8979, - [SMALL_STATE(280)] = 8990, - [SMALL_STATE(281)] = 9003, - [SMALL_STATE(282)] = 9016, - [SMALL_STATE(283)] = 9027, - [SMALL_STATE(284)] = 9038, - [SMALL_STATE(285)] = 9049, - [SMALL_STATE(286)] = 9062, - [SMALL_STATE(287)] = 9075, - [SMALL_STATE(288)] = 9086, - [SMALL_STATE(289)] = 9097, - [SMALL_STATE(290)] = 9108, - [SMALL_STATE(291)] = 9121, - [SMALL_STATE(292)] = 9134, - [SMALL_STATE(293)] = 9147, - [SMALL_STATE(294)] = 9155, - [SMALL_STATE(295)] = 9163, - [SMALL_STATE(296)] = 9171, - [SMALL_STATE(297)] = 9181, - [SMALL_STATE(298)] = 9189, - [SMALL_STATE(299)] = 9197, - [SMALL_STATE(300)] = 9205, - [SMALL_STATE(301)] = 9213, - [SMALL_STATE(302)] = 9221, - [SMALL_STATE(303)] = 9229, - [SMALL_STATE(304)] = 9237, - [SMALL_STATE(305)] = 9245, - [SMALL_STATE(306)] = 9253, - [SMALL_STATE(307)] = 9261, - [SMALL_STATE(308)] = 9269, - [SMALL_STATE(309)] = 9277, - [SMALL_STATE(310)] = 9285, - [SMALL_STATE(311)] = 9293, - [SMALL_STATE(312)] = 9301, - [SMALL_STATE(313)] = 9309, - [SMALL_STATE(314)] = 9317, - [SMALL_STATE(315)] = 9325, - [SMALL_STATE(316)] = 9333, - [SMALL_STATE(317)] = 9341, - [SMALL_STATE(318)] = 9349, - [SMALL_STATE(319)] = 9357, - [SMALL_STATE(320)] = 9367, - [SMALL_STATE(321)] = 9375, - [SMALL_STATE(322)] = 9383, - [SMALL_STATE(323)] = 9391, - [SMALL_STATE(324)] = 9399, - [SMALL_STATE(325)] = 9407, - [SMALL_STATE(326)] = 9415, - [SMALL_STATE(327)] = 9423, - [SMALL_STATE(328)] = 9431, - [SMALL_STATE(329)] = 9439, - [SMALL_STATE(330)] = 9447, - [SMALL_STATE(331)] = 9455, - [SMALL_STATE(332)] = 9463, - [SMALL_STATE(333)] = 9471, - [SMALL_STATE(334)] = 9479, - [SMALL_STATE(335)] = 9487, - [SMALL_STATE(336)] = 9495, - [SMALL_STATE(337)] = 9503, - [SMALL_STATE(338)] = 9511, - [SMALL_STATE(339)] = 9519, - [SMALL_STATE(340)] = 9527, - [SMALL_STATE(341)] = 9535, - [SMALL_STATE(342)] = 9543, - [SMALL_STATE(343)] = 9551, - [SMALL_STATE(344)] = 9559, - [SMALL_STATE(345)] = 9567, - [SMALL_STATE(346)] = 9575, - [SMALL_STATE(347)] = 9583, - [SMALL_STATE(348)] = 9593, - [SMALL_STATE(349)] = 9601, - [SMALL_STATE(350)] = 9609, - [SMALL_STATE(351)] = 9617, - [SMALL_STATE(352)] = 9625, - [SMALL_STATE(353)] = 9633, - [SMALL_STATE(354)] = 9641, - [SMALL_STATE(355)] = 9649, - [SMALL_STATE(356)] = 9657, - [SMALL_STATE(357)] = 9665, - [SMALL_STATE(358)] = 9675, - [SMALL_STATE(359)] = 9683, - [SMALL_STATE(360)] = 9691, - [SMALL_STATE(361)] = 9699, - [SMALL_STATE(362)] = 9707, - [SMALL_STATE(363)] = 9715, - [SMALL_STATE(364)] = 9723, - [SMALL_STATE(365)] = 9731, - [SMALL_STATE(366)] = 9739, - [SMALL_STATE(367)] = 9746, - [SMALL_STATE(368)] = 9753, - [SMALL_STATE(369)] = 9760, - [SMALL_STATE(370)] = 9767, - [SMALL_STATE(371)] = 9771, - [SMALL_STATE(372)] = 9775, - [SMALL_STATE(373)] = 9779, - [SMALL_STATE(374)] = 9783, + [SMALL_STATE(3)] = 85, + [SMALL_STATE(4)] = 170, + [SMALL_STATE(5)] = 252, + [SMALL_STATE(6)] = 334, + [SMALL_STATE(7)] = 416, + [SMALL_STATE(8)] = 498, + [SMALL_STATE(9)] = 580, + [SMALL_STATE(10)] = 662, + [SMALL_STATE(11)] = 744, + [SMALL_STATE(12)] = 826, + [SMALL_STATE(13)] = 908, + [SMALL_STATE(14)] = 990, + [SMALL_STATE(15)] = 1072, + [SMALL_STATE(16)] = 1154, + [SMALL_STATE(17)] = 1233, + [SMALL_STATE(18)] = 1312, + [SMALL_STATE(19)] = 1391, + [SMALL_STATE(20)] = 1470, + [SMALL_STATE(21)] = 1549, + [SMALL_STATE(22)] = 1628, + [SMALL_STATE(23)] = 1707, + [SMALL_STATE(24)] = 1786, + [SMALL_STATE(25)] = 1865, + [SMALL_STATE(26)] = 1944, + [SMALL_STATE(27)] = 2023, + [SMALL_STATE(28)] = 2102, + [SMALL_STATE(29)] = 2181, + [SMALL_STATE(30)] = 2260, + [SMALL_STATE(31)] = 2339, + [SMALL_STATE(32)] = 2418, + [SMALL_STATE(33)] = 2497, + [SMALL_STATE(34)] = 2576, + [SMALL_STATE(35)] = 2655, + [SMALL_STATE(36)] = 2734, + [SMALL_STATE(37)] = 2813, + [SMALL_STATE(38)] = 2892, + [SMALL_STATE(39)] = 2971, + [SMALL_STATE(40)] = 3050, + [SMALL_STATE(41)] = 3129, + [SMALL_STATE(42)] = 3208, + [SMALL_STATE(43)] = 3287, + [SMALL_STATE(44)] = 3366, + [SMALL_STATE(45)] = 3445, + [SMALL_STATE(46)] = 3524, + [SMALL_STATE(47)] = 3603, + [SMALL_STATE(48)] = 3682, + [SMALL_STATE(49)] = 3761, + [SMALL_STATE(50)] = 3840, + [SMALL_STATE(51)] = 3919, + [SMALL_STATE(52)] = 3998, + [SMALL_STATE(53)] = 4074, + [SMALL_STATE(54)] = 4142, + [SMALL_STATE(55)] = 4218, + [SMALL_STATE(56)] = 4283, + [SMALL_STATE(57)] = 4348, + [SMALL_STATE(58)] = 4415, + [SMALL_STATE(59)] = 4479, + [SMALL_STATE(60)] = 4541, + [SMALL_STATE(61)] = 4603, + [SMALL_STATE(62)] = 4667, + [SMALL_STATE(63)] = 4728, + [SMALL_STATE(64)] = 4789, + [SMALL_STATE(65)] = 4851, + [SMALL_STATE(66)] = 4913, + [SMALL_STATE(67)] = 4975, + [SMALL_STATE(68)] = 5037, + [SMALL_STATE(69)] = 5095, + [SMALL_STATE(70)] = 5153, + [SMALL_STATE(71)] = 5208, + [SMALL_STATE(72)] = 5263, + [SMALL_STATE(73)] = 5318, + [SMALL_STATE(74)] = 5373, + [SMALL_STATE(75)] = 5428, + [SMALL_STATE(76)] = 5483, + [SMALL_STATE(77)] = 5538, + [SMALL_STATE(78)] = 5593, + [SMALL_STATE(79)] = 5648, + [SMALL_STATE(80)] = 5703, + [SMALL_STATE(81)] = 5758, + [SMALL_STATE(82)] = 5813, + [SMALL_STATE(83)] = 5868, + [SMALL_STATE(84)] = 5923, + [SMALL_STATE(85)] = 5978, + [SMALL_STATE(86)] = 6019, + [SMALL_STATE(87)] = 6060, + [SMALL_STATE(88)] = 6101, + [SMALL_STATE(89)] = 6142, + [SMALL_STATE(90)] = 6174, + [SMALL_STATE(91)] = 6206, + [SMALL_STATE(92)] = 6235, + [SMALL_STATE(93)] = 6264, + [SMALL_STATE(94)] = 6293, + [SMALL_STATE(95)] = 6322, + [SMALL_STATE(96)] = 6351, + [SMALL_STATE(97)] = 6380, + [SMALL_STATE(98)] = 6409, + [SMALL_STATE(99)] = 6438, + [SMALL_STATE(100)] = 6467, + [SMALL_STATE(101)] = 6496, + [SMALL_STATE(102)] = 6525, + [SMALL_STATE(103)] = 6554, + [SMALL_STATE(104)] = 6583, + [SMALL_STATE(105)] = 6612, + [SMALL_STATE(106)] = 6641, + [SMALL_STATE(107)] = 6670, + [SMALL_STATE(108)] = 6699, + [SMALL_STATE(109)] = 6728, + [SMALL_STATE(110)] = 6757, + [SMALL_STATE(111)] = 6786, + [SMALL_STATE(112)] = 6815, + [SMALL_STATE(113)] = 6844, + [SMALL_STATE(114)] = 6873, + [SMALL_STATE(115)] = 6902, + [SMALL_STATE(116)] = 6931, + [SMALL_STATE(117)] = 6960, + [SMALL_STATE(118)] = 6989, + [SMALL_STATE(119)] = 7018, + [SMALL_STATE(120)] = 7047, + [SMALL_STATE(121)] = 7076, + [SMALL_STATE(122)] = 7105, + [SMALL_STATE(123)] = 7134, + [SMALL_STATE(124)] = 7163, + [SMALL_STATE(125)] = 7192, + [SMALL_STATE(126)] = 7221, + [SMALL_STATE(127)] = 7250, + [SMALL_STATE(128)] = 7279, + [SMALL_STATE(129)] = 7308, + [SMALL_STATE(130)] = 7337, + [SMALL_STATE(131)] = 7366, + [SMALL_STATE(132)] = 7395, + [SMALL_STATE(133)] = 7424, + [SMALL_STATE(134)] = 7453, + [SMALL_STATE(135)] = 7482, + [SMALL_STATE(136)] = 7511, + [SMALL_STATE(137)] = 7540, + [SMALL_STATE(138)] = 7569, + [SMALL_STATE(139)] = 7598, + [SMALL_STATE(140)] = 7627, + [SMALL_STATE(141)] = 7656, + [SMALL_STATE(142)] = 7685, + [SMALL_STATE(143)] = 7714, + [SMALL_STATE(144)] = 7743, + [SMALL_STATE(145)] = 7767, + [SMALL_STATE(146)] = 7795, + [SMALL_STATE(147)] = 7819, + [SMALL_STATE(148)] = 7843, + [SMALL_STATE(149)] = 7866, + [SMALL_STATE(150)] = 7889, + [SMALL_STATE(151)] = 7912, + [SMALL_STATE(152)] = 7939, + [SMALL_STATE(153)] = 7961, + [SMALL_STATE(154)] = 7983, + [SMALL_STATE(155)] = 7997, + [SMALL_STATE(156)] = 8013, + [SMALL_STATE(157)] = 8026, + [SMALL_STATE(158)] = 8039, + [SMALL_STATE(159)] = 8052, + [SMALL_STATE(160)] = 8065, + [SMALL_STATE(161)] = 8078, + [SMALL_STATE(162)] = 8089, + [SMALL_STATE(163)] = 8102, + [SMALL_STATE(164)] = 8115, + [SMALL_STATE(165)] = 8128, + [SMALL_STATE(166)] = 8141, + [SMALL_STATE(167)] = 8154, + [SMALL_STATE(168)] = 8167, + [SMALL_STATE(169)] = 8180, + [SMALL_STATE(170)] = 8193, + [SMALL_STATE(171)] = 8206, + [SMALL_STATE(172)] = 8219, + [SMALL_STATE(173)] = 8232, + [SMALL_STATE(174)] = 8245, + [SMALL_STATE(175)] = 8258, + [SMALL_STATE(176)] = 8271, + [SMALL_STATE(177)] = 8284, + [SMALL_STATE(178)] = 8297, + [SMALL_STATE(179)] = 8316, + [SMALL_STATE(180)] = 8329, + [SMALL_STATE(181)] = 8342, + [SMALL_STATE(182)] = 8355, + [SMALL_STATE(183)] = 8368, + [SMALL_STATE(184)] = 8381, + [SMALL_STATE(185)] = 8394, + [SMALL_STATE(186)] = 8407, + [SMALL_STATE(187)] = 8420, + [SMALL_STATE(188)] = 8433, + [SMALL_STATE(189)] = 8446, + [SMALL_STATE(190)] = 8459, + [SMALL_STATE(191)] = 8472, + [SMALL_STATE(192)] = 8485, + [SMALL_STATE(193)] = 8498, + [SMALL_STATE(194)] = 8511, + [SMALL_STATE(195)] = 8524, + [SMALL_STATE(196)] = 8537, + [SMALL_STATE(197)] = 8550, + [SMALL_STATE(198)] = 8572, + [SMALL_STATE(199)] = 8582, + [SMALL_STATE(200)] = 8592, + [SMALL_STATE(201)] = 8602, + [SMALL_STATE(202)] = 8618, + [SMALL_STATE(203)] = 8636, + [SMALL_STATE(204)] = 8646, + [SMALL_STATE(205)] = 8656, + [SMALL_STATE(206)] = 8666, + [SMALL_STATE(207)] = 8688, + [SMALL_STATE(208)] = 8698, + [SMALL_STATE(209)] = 8708, + [SMALL_STATE(210)] = 8718, + [SMALL_STATE(211)] = 8740, + [SMALL_STATE(212)] = 8762, + [SMALL_STATE(213)] = 8772, + [SMALL_STATE(214)] = 8782, + [SMALL_STATE(215)] = 8792, + [SMALL_STATE(216)] = 8802, + [SMALL_STATE(217)] = 8812, + [SMALL_STATE(218)] = 8822, + [SMALL_STATE(219)] = 8832, + [SMALL_STATE(220)] = 8842, + [SMALL_STATE(221)] = 8852, + [SMALL_STATE(222)] = 8862, + [SMALL_STATE(223)] = 8872, + [SMALL_STATE(224)] = 8882, + [SMALL_STATE(225)] = 8892, + [SMALL_STATE(226)] = 8902, + [SMALL_STATE(227)] = 8912, + [SMALL_STATE(228)] = 8922, + [SMALL_STATE(229)] = 8932, + [SMALL_STATE(230)] = 8942, + [SMALL_STATE(231)] = 8952, + [SMALL_STATE(232)] = 8962, + [SMALL_STATE(233)] = 8972, + [SMALL_STATE(234)] = 8982, + [SMALL_STATE(235)] = 8992, + [SMALL_STATE(236)] = 9002, + [SMALL_STATE(237)] = 9012, + [SMALL_STATE(238)] = 9022, + [SMALL_STATE(239)] = 9032, + [SMALL_STATE(240)] = 9042, + [SMALL_STATE(241)] = 9052, + [SMALL_STATE(242)] = 9065, + [SMALL_STATE(243)] = 9076, + [SMALL_STATE(244)] = 9089, + [SMALL_STATE(245)] = 9102, + [SMALL_STATE(246)] = 9117, + [SMALL_STATE(247)] = 9130, + [SMALL_STATE(248)] = 9143, + [SMALL_STATE(249)] = 9158, + [SMALL_STATE(250)] = 9170, + [SMALL_STATE(251)] = 9182, + [SMALL_STATE(252)] = 9192, + [SMALL_STATE(253)] = 9202, + [SMALL_STATE(254)] = 9212, + [SMALL_STATE(255)] = 9222, + [SMALL_STATE(256)] = 9232, + [SMALL_STATE(257)] = 9242, + [SMALL_STATE(258)] = 9258, + [SMALL_STATE(259)] = 9270, + [SMALL_STATE(260)] = 9278, + [SMALL_STATE(261)] = 9288, + [SMALL_STATE(262)] = 9301, + [SMALL_STATE(263)] = 9312, + [SMALL_STATE(264)] = 9323, + [SMALL_STATE(265)] = 9334, + [SMALL_STATE(266)] = 9345, + [SMALL_STATE(267)] = 9356, + [SMALL_STATE(268)] = 9367, + [SMALL_STATE(269)] = 9378, + [SMALL_STATE(270)] = 9389, + [SMALL_STATE(271)] = 9400, + [SMALL_STATE(272)] = 9411, + [SMALL_STATE(273)] = 9422, + [SMALL_STATE(274)] = 9433, + [SMALL_STATE(275)] = 9444, + [SMALL_STATE(276)] = 9455, + [SMALL_STATE(277)] = 9462, + [SMALL_STATE(278)] = 9473, + [SMALL_STATE(279)] = 9480, + [SMALL_STATE(280)] = 9491, + [SMALL_STATE(281)] = 9502, + [SMALL_STATE(282)] = 9513, + [SMALL_STATE(283)] = 9520, + [SMALL_STATE(284)] = 9529, + [SMALL_STATE(285)] = 9540, + [SMALL_STATE(286)] = 9549, + [SMALL_STATE(287)] = 9560, + [SMALL_STATE(288)] = 9573, + [SMALL_STATE(289)] = 9586, + [SMALL_STATE(290)] = 9597, + [SMALL_STATE(291)] = 9610, + [SMALL_STATE(292)] = 9623, + [SMALL_STATE(293)] = 9634, + [SMALL_STATE(294)] = 9647, + [SMALL_STATE(295)] = 9658, + [SMALL_STATE(296)] = 9671, + [SMALL_STATE(297)] = 9682, + [SMALL_STATE(298)] = 9695, + [SMALL_STATE(299)] = 9703, + [SMALL_STATE(300)] = 9711, + [SMALL_STATE(301)] = 9719, + [SMALL_STATE(302)] = 9727, + [SMALL_STATE(303)] = 9735, + [SMALL_STATE(304)] = 9743, + [SMALL_STATE(305)] = 9751, + [SMALL_STATE(306)] = 9759, + [SMALL_STATE(307)] = 9767, + [SMALL_STATE(308)] = 9775, + [SMALL_STATE(309)] = 9783, + [SMALL_STATE(310)] = 9791, + [SMALL_STATE(311)] = 9799, + [SMALL_STATE(312)] = 9807, + [SMALL_STATE(313)] = 9815, + [SMALL_STATE(314)] = 9823, + [SMALL_STATE(315)] = 9831, + [SMALL_STATE(316)] = 9839, + [SMALL_STATE(317)] = 9847, + [SMALL_STATE(318)] = 9855, + [SMALL_STATE(319)] = 9863, + [SMALL_STATE(320)] = 9871, + [SMALL_STATE(321)] = 9879, + [SMALL_STATE(322)] = 9887, + [SMALL_STATE(323)] = 9895, + [SMALL_STATE(324)] = 9905, + [SMALL_STATE(325)] = 9913, + [SMALL_STATE(326)] = 9921, + [SMALL_STATE(327)] = 9929, + [SMALL_STATE(328)] = 9937, + [SMALL_STATE(329)] = 9945, + [SMALL_STATE(330)] = 9953, + [SMALL_STATE(331)] = 9961, + [SMALL_STATE(332)] = 9969, + [SMALL_STATE(333)] = 9977, + [SMALL_STATE(334)] = 9985, + [SMALL_STATE(335)] = 9993, + [SMALL_STATE(336)] = 10001, + [SMALL_STATE(337)] = 10011, + [SMALL_STATE(338)] = 10019, + [SMALL_STATE(339)] = 10027, + [SMALL_STATE(340)] = 10035, + [SMALL_STATE(341)] = 10043, + [SMALL_STATE(342)] = 10051, + [SMALL_STATE(343)] = 10059, + [SMALL_STATE(344)] = 10067, + [SMALL_STATE(345)] = 10075, + [SMALL_STATE(346)] = 10083, + [SMALL_STATE(347)] = 10091, + [SMALL_STATE(348)] = 10099, + [SMALL_STATE(349)] = 10107, + [SMALL_STATE(350)] = 10115, + [SMALL_STATE(351)] = 10123, + [SMALL_STATE(352)] = 10131, + [SMALL_STATE(353)] = 10139, + [SMALL_STATE(354)] = 10147, + [SMALL_STATE(355)] = 10155, + [SMALL_STATE(356)] = 10163, + [SMALL_STATE(357)] = 10171, + [SMALL_STATE(358)] = 10179, + [SMALL_STATE(359)] = 10187, + [SMALL_STATE(360)] = 10195, + [SMALL_STATE(361)] = 10203, + [SMALL_STATE(362)] = 10211, + [SMALL_STATE(363)] = 10219, + [SMALL_STATE(364)] = 10227, + [SMALL_STATE(365)] = 10235, + [SMALL_STATE(366)] = 10243, + [SMALL_STATE(367)] = 10251, + [SMALL_STATE(368)] = 10259, + [SMALL_STATE(369)] = 10267, + [SMALL_STATE(370)] = 10275, + [SMALL_STATE(371)] = 10282, + [SMALL_STATE(372)] = 10289, + [SMALL_STATE(373)] = 10296, + [SMALL_STATE(374)] = 10303, + [SMALL_STATE(375)] = 10307, + [SMALL_STATE(376)] = 10311, + [SMALL_STATE(377)] = 10315, + [SMALL_STATE(378)] = 10319, }; static const TSParseActionEntry ts_parse_actions[] = { [0] = {.entry = {.count = 0, .reusable = false}}, [1] = {.entry = {.count = 1, .reusable = false}}, RECOVER(), [3] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_template, 0), - [5] = {.entry = {.count = 1, .reusable = false}}, SHIFT(178), - [7] = {.entry = {.count = 1, .reusable = false}}, SHIFT(53), - [9] = {.entry = {.count = 1, .reusable = false}}, SHIFT(80), - [11] = {.entry = {.count = 1, .reusable = true}}, SHIFT(82), - [13] = {.entry = {.count = 1, .reusable = false}}, SHIFT(323), - [15] = {.entry = {.count = 1, .reusable = false}}, SHIFT(355), - [17] = {.entry = {.count = 1, .reusable = false}}, SHIFT(67), - [19] = {.entry = {.count = 1, .reusable = false}}, SHIFT(276), - [21] = {.entry = {.count = 1, .reusable = false}}, SHIFT(282), - [23] = {.entry = {.count = 1, .reusable = false}}, SHIFT(284), - [25] = {.entry = {.count = 1, .reusable = false}}, SHIFT(81), - [27] = {.entry = {.count = 1, .reusable = true}}, SHIFT(78), - [29] = {.entry = {.count = 1, .reusable = false}}, SHIFT(252), - [31] = {.entry = {.count = 1, .reusable = true}}, SHIFT(150), - [33] = {.entry = {.count = 1, .reusable = false}}, SHIFT(59), - [35] = {.entry = {.count = 1, .reusable = false}}, SHIFT(255), - [37] = {.entry = {.count = 1, .reusable = true}}, SHIFT(255), - [39] = {.entry = {.count = 1, .reusable = true}}, SHIFT(289), - [41] = {.entry = {.count = 1, .reusable = true}}, SHIFT(334), - [43] = {.entry = {.count = 1, .reusable = false}}, SHIFT(322), - [45] = {.entry = {.count = 1, .reusable = false}}, SHIFT(310), - [47] = {.entry = {.count = 1, .reusable = false}}, SHIFT(306), - [49] = {.entry = {.count = 1, .reusable = false}}, SHIFT(324), - [51] = {.entry = {.count = 1, .reusable = false}}, SHIFT(358), - [53] = {.entry = {.count = 1, .reusable = false}}, SHIFT(314), - [55] = {.entry = {.count = 1, .reusable = false}}, SHIFT(313), - [57] = {.entry = {.count = 1, .reusable = false}}, SHIFT(317), - [59] = {.entry = {.count = 1, .reusable = false}}, SHIFT(315), - [61] = {.entry = {.count = 1, .reusable = false}}, SHIFT(304), - [63] = {.entry = {.count = 1, .reusable = false}}, SHIFT(302), - [65] = {.entry = {.count = 1, .reusable = false}}, SHIFT(301), - [67] = {.entry = {.count = 1, .reusable = false}}, SHIFT(299), - [69] = {.entry = {.count = 1, .reusable = false}}, SHIFT(327), - [71] = {.entry = {.count = 1, .reusable = false}}, SHIFT(298), - [73] = {.entry = {.count = 1, .reusable = false}}, SHIFT(330), - [75] = {.entry = {.count = 1, .reusable = false}}, SHIFT(309), - [77] = {.entry = {.count = 1, .reusable = false}}, SHIFT(341), - [79] = {.entry = {.count = 1, .reusable = false}}, SHIFT(308), - [81] = {.entry = {.count = 1, .reusable = false}}, SHIFT(338), - [83] = {.entry = {.count = 1, .reusable = false}}, SHIFT(332), - [85] = {.entry = {.count = 1, .reusable = false}}, SHIFT(343), - [87] = {.entry = {.count = 1, .reusable = false}}, SHIFT(363), - [89] = {.entry = {.count = 1, .reusable = false}}, SHIFT(365), - [91] = {.entry = {.count = 1, .reusable = false}}, SHIFT(360), - [93] = {.entry = {.count = 1, .reusable = false}}, SHIFT(293), - [95] = {.entry = {.count = 1, .reusable = false}}, SHIFT(350), - [97] = {.entry = {.count = 1, .reusable = false}}, SHIFT(353), - [99] = {.entry = {.count = 1, .reusable = false}}, SHIFT(337), - [101] = {.entry = {.count = 1, .reusable = false}}, SHIFT(336), - [103] = {.entry = {.count = 1, .reusable = false}}, SHIFT(328), - [105] = {.entry = {.count = 1, .reusable = false}}, SHIFT(335), - [107] = {.entry = {.count = 1, .reusable = false}}, SHIFT(325), - [109] = {.entry = {.count = 1, .reusable = false}}, SHIFT(339), - [111] = {.entry = {.count = 1, .reusable = false}}, SHIFT(303), - [113] = {.entry = {.count = 1, .reusable = false}}, SHIFT(300), - [115] = {.entry = {.count = 1, .reusable = false}}, SHIFT(294), - [117] = {.entry = {.count = 1, .reusable = false}}, SHIFT(297), - [119] = {.entry = {.count = 1, .reusable = false}}, SHIFT(342), - [121] = {.entry = {.count = 1, .reusable = false}}, SHIFT(348), - [123] = {.entry = {.count = 1, .reusable = false}}, SHIFT(316), - [125] = {.entry = {.count = 1, .reusable = false}}, SHIFT(321), - [127] = {.entry = {.count = 1, .reusable = false}}, SHIFT(340), - [129] = {.entry = {.count = 1, .reusable = false}}, SHIFT(311), - [131] = {.entry = {.count = 1, .reusable = false}}, SHIFT(307), - [133] = {.entry = {.count = 1, .reusable = false}}, SHIFT(329), - [135] = {.entry = {.count = 1, .reusable = false}}, SHIFT(344), - [137] = {.entry = {.count = 1, .reusable = false}}, SHIFT(345), - [139] = {.entry = {.count = 1, .reusable = false}}, SHIFT(312), - [141] = {.entry = {.count = 1, .reusable = false}}, SHIFT(351), - [143] = {.entry = {.count = 1, .reusable = false}}, SHIFT(333), - [145] = {.entry = {.count = 1, .reusable = false}}, SHIFT(362), - [147] = {.entry = {.count = 1, .reusable = false}}, SHIFT(331), - [149] = {.entry = {.count = 1, .reusable = false}}, SHIFT(326), - [151] = {.entry = {.count = 1, .reusable = false}}, SHIFT(354), - [153] = {.entry = {.count = 1, .reusable = false}}, SHIFT(320), - [155] = {.entry = {.count = 1, .reusable = false}}, SHIFT(305), - [157] = {.entry = {.count = 1, .reusable = false}}, SHIFT(364), - [159] = {.entry = {.count = 1, .reusable = false}}, SHIFT(346), - [161] = {.entry = {.count = 1, .reusable = false}}, SHIFT(295), - [163] = {.entry = {.count = 1, .reusable = false}}, SHIFT(318), - [165] = {.entry = {.count = 1, .reusable = false}}, SHIFT(70), - [167] = {.entry = {.count = 1, .reusable = false}}, SHIFT(66), - [169] = {.entry = {.count = 1, .reusable = false}}, SHIFT(275), - [171] = {.entry = {.count = 1, .reusable = false}}, SHIFT(279), - [173] = {.entry = {.count = 1, .reusable = false}}, SHIFT(274), - [175] = {.entry = {.count = 1, .reusable = false}}, SHIFT(76), - [177] = {.entry = {.count = 1, .reusable = false}}, SHIFT(256), - [179] = {.entry = {.count = 1, .reusable = true}}, SHIFT(256), - [181] = {.entry = {.count = 1, .reusable = true}}, SHIFT(356), - [183] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_function_call, 1, .production_id = 1), - [185] = {.entry = {.count = 1, .reusable = false}}, SHIFT(79), - [187] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_function_call, 1, .production_id = 1), - [189] = {.entry = {.count = 1, .reusable = false}}, SHIFT(200), - [191] = {.entry = {.count = 1, .reusable = false}}, SHIFT(151), - [193] = {.entry = {.count = 1, .reusable = false}}, SHIFT(54), - [195] = {.entry = {.count = 1, .reusable = false}}, SHIFT(206), - [197] = {.entry = {.count = 1, .reusable = false}}, SHIFT(287), - [199] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym__expression, 1), - [201] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym__expression, 1), - [203] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_argument_list, 2), - [205] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_argument_list, 2), - [207] = {.entry = {.count = 1, .reusable = false}}, SHIFT(247), - [209] = {.entry = {.count = 1, .reusable = true}}, SHIFT(79), - [211] = {.entry = {.count = 1, .reusable = true}}, SHIFT(151), - [213] = {.entry = {.count = 1, .reusable = false}}, SHIFT(197), - [215] = {.entry = {.count = 1, .reusable = true}}, SHIFT(197), - [217] = {.entry = {.count = 1, .reusable = true}}, SHIFT(287), - [219] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_argument_list, 3), - [221] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_argument_list, 3), - [223] = {.entry = {.count = 1, .reusable = true}}, SHIFT(247), - [225] = {.entry = {.count = 1, .reusable = false}}, SHIFT(265), - [227] = {.entry = {.count = 1, .reusable = true}}, SHIFT(265), - [229] = {.entry = {.count = 1, .reusable = true}}, SHIFT(168), - [231] = {.entry = {.count = 1, .reusable = false}}, SHIFT(267), - [233] = {.entry = {.count = 1, .reusable = true}}, SHIFT(267), - [235] = {.entry = {.count = 1, .reusable = true}}, SHIFT(111), - [237] = {.entry = {.count = 1, .reusable = false}}, SHIFT(264), - [239] = {.entry = {.count = 1, .reusable = true}}, SHIFT(264), - [241] = {.entry = {.count = 1, .reusable = true}}, SHIFT(217), - [243] = {.entry = {.count = 1, .reusable = false}}, SHIFT(262), - [245] = {.entry = {.count = 1, .reusable = true}}, SHIFT(262), - [247] = {.entry = {.count = 1, .reusable = true}}, SHIFT(109), - [249] = {.entry = {.count = 1, .reusable = false}}, SHIFT(258), - [251] = {.entry = {.count = 1, .reusable = true}}, SHIFT(258), - [253] = {.entry = {.count = 1, .reusable = false}}, SHIFT(272), - [255] = {.entry = {.count = 1, .reusable = true}}, SHIFT(272), - [257] = {.entry = {.count = 1, .reusable = false}}, SHIFT(251), - [259] = {.entry = {.count = 1, .reusable = true}}, SHIFT(251), - [261] = {.entry = {.count = 1, .reusable = false}}, SHIFT(266), - [263] = {.entry = {.count = 1, .reusable = true}}, SHIFT(266), - [265] = {.entry = {.count = 1, .reusable = false}}, SHIFT(253), - [267] = {.entry = {.count = 1, .reusable = true}}, SHIFT(253), - [269] = {.entry = {.count = 1, .reusable = false}}, SHIFT(271), - [271] = {.entry = {.count = 1, .reusable = true}}, SHIFT(271), - [273] = {.entry = {.count = 1, .reusable = false}}, SHIFT(270), - [275] = {.entry = {.count = 1, .reusable = true}}, SHIFT(270), - [277] = {.entry = {.count = 1, .reusable = false}}, SHIFT(283), - [279] = {.entry = {.count = 1, .reusable = true}}, SHIFT(283), - [281] = {.entry = {.count = 1, .reusable = false}}, SHIFT(243), - [283] = {.entry = {.count = 1, .reusable = true}}, SHIFT(243), - [285] = {.entry = {.count = 1, .reusable = false}}, SHIFT(259), - [287] = {.entry = {.count = 1, .reusable = true}}, SHIFT(259), - [289] = {.entry = {.count = 1, .reusable = false}}, SHIFT(250), - [291] = {.entry = {.count = 1, .reusable = true}}, SHIFT(250), - [293] = {.entry = {.count = 1, .reusable = false}}, SHIFT(347), - [295] = {.entry = {.count = 1, .reusable = true}}, SHIFT(347), - [297] = {.entry = {.count = 1, .reusable = false}}, SHIFT(296), - [299] = {.entry = {.count = 1, .reusable = true}}, SHIFT(296), - [301] = {.entry = {.count = 1, .reusable = false}}, SHIFT(269), - [303] = {.entry = {.count = 1, .reusable = true}}, SHIFT(269), - [305] = {.entry = {.count = 1, .reusable = false}}, SHIFT(254), - [307] = {.entry = {.count = 1, .reusable = true}}, SHIFT(254), - [309] = {.entry = {.count = 1, .reusable = false}}, SHIFT(257), - [311] = {.entry = {.count = 1, .reusable = true}}, SHIFT(257), - [313] = {.entry = {.count = 1, .reusable = false}}, SHIFT(199), - [315] = {.entry = {.count = 1, .reusable = false}}, SHIFT(3), - [317] = {.entry = {.count = 1, .reusable = false}}, SHIFT(2), - [319] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_template, 1), - [321] = {.entry = {.count = 1, .reusable = true}}, REDUCE(aux_sym_template_repeat1, 2), - [323] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_template_repeat1, 2), SHIFT_REPEAT(178), - [326] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_template_repeat1, 2), SHIFT_REPEAT(53), - [329] = {.entry = {.count = 1, .reusable = false}}, SHIFT(11), - [331] = {.entry = {.count = 1, .reusable = false}}, SHIFT(17), - [333] = {.entry = {.count = 1, .reusable = false}}, SHIFT(13), - [335] = {.entry = {.count = 1, .reusable = false}}, SHIFT(46), - [337] = {.entry = {.count = 1, .reusable = false}}, SHIFT(44), - [339] = {.entry = {.count = 1, .reusable = false}}, SHIFT(43), - [341] = {.entry = {.count = 1, .reusable = false}}, SHIFT(41), - [343] = {.entry = {.count = 1, .reusable = false}}, SHIFT(30), - [345] = {.entry = {.count = 1, .reusable = false}}, SHIFT(18), - [347] = {.entry = {.count = 1, .reusable = false}}, SHIFT(45), - [349] = {.entry = {.count = 1, .reusable = false}}, SHIFT(9), - [351] = {.entry = {.count = 1, .reusable = false}}, SHIFT(8), - [353] = {.entry = {.count = 1, .reusable = false}}, SHIFT(27), - [355] = {.entry = {.count = 1, .reusable = false}}, SHIFT(22), - [357] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_template_repeat1, 2), SHIFT_REPEAT(199), - [360] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_template_repeat1, 2), SHIFT_REPEAT(52), - [363] = {.entry = {.count = 1, .reusable = false}}, SHIFT(15), - [365] = {.entry = {.count = 1, .reusable = false}}, SHIFT(14), - [367] = {.entry = {.count = 1, .reusable = false}}, SHIFT(51), - [369] = {.entry = {.count = 1, .reusable = false}}, SHIFT(47), - [371] = {.entry = {.count = 1, .reusable = false}}, SHIFT(36), - [373] = {.entry = {.count = 1, .reusable = false}}, SHIFT(50), - [375] = {.entry = {.count = 1, .reusable = false}}, SHIFT(4), - [377] = {.entry = {.count = 1, .reusable = false}}, SHIFT(34), - [379] = {.entry = {.count = 1, .reusable = false}}, SHIFT(12), - [381] = {.entry = {.count = 1, .reusable = false}}, SHIFT(39), - [383] = {.entry = {.count = 1, .reusable = false}}, SHIFT(20), - [385] = {.entry = {.count = 1, .reusable = false}}, SHIFT(24), - [387] = {.entry = {.count = 2, .reusable = false}}, REDUCE(sym__else_clause, 3, .dynamic_precedence = 2), SHIFT(52), - [390] = {.entry = {.count = 1, .reusable = false}}, SHIFT(49), - [392] = {.entry = {.count = 1, .reusable = false}}, SHIFT(33), - [394] = {.entry = {.count = 1, .reusable = false}}, SHIFT(10), - [396] = {.entry = {.count = 1, .reusable = false}}, SHIFT(29), - [398] = {.entry = {.count = 2, .reusable = false}}, REDUCE(sym__else_if_clause, 5, .dynamic_precedence = 1, .production_id = 26), SHIFT(52), - [401] = {.entry = {.count = 1, .reusable = false}}, SHIFT(6), - [403] = {.entry = {.count = 1, .reusable = false}}, SHIFT(32), - [405] = {.entry = {.count = 1, .reusable = false}}, SHIFT(19), - [407] = {.entry = {.count = 1, .reusable = false}}, SHIFT(7), - [409] = {.entry = {.count = 1, .reusable = false}}, SHIFT(35), - [411] = {.entry = {.count = 1, .reusable = false}}, SHIFT(31), - [413] = {.entry = {.count = 1, .reusable = false}}, SHIFT(21), - [415] = {.entry = {.count = 1, .reusable = false}}, SHIFT(37), - [417] = {.entry = {.count = 1, .reusable = false}}, SHIFT(48), - [419] = {.entry = {.count = 1, .reusable = false}}, SHIFT(38), - [421] = {.entry = {.count = 1, .reusable = false}}, SHIFT(23), - [423] = {.entry = {.count = 1, .reusable = false}}, SHIFT(16), - [425] = {.entry = {.count = 1, .reusable = false}}, SHIFT(25), - [427] = {.entry = {.count = 1, .reusable = false}}, SHIFT(42), - [429] = {.entry = {.count = 1, .reusable = false}}, SHIFT(26), - [431] = {.entry = {.count = 2, .reusable = false}}, REDUCE(sym__else_clause, 4, .dynamic_precedence = 2, .production_id = 21), SHIFT(52), - [434] = {.entry = {.count = 1, .reusable = false}}, SHIFT(28), - [436] = {.entry = {.count = 2, .reusable = false}}, REDUCE(sym__else_if_clause, 4, .dynamic_precedence = 1, .production_id = 10), SHIFT(52), - [439] = {.entry = {.count = 1, .reusable = false}}, SHIFT(5), - [441] = {.entry = {.count = 1, .reusable = false}}, SHIFT(40), - [443] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_field, 2, .production_id = 2), - [445] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_field, 2, .production_id = 2), - [447] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym__field_identifier, 1, .production_id = 6), - [449] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym__field_identifier, 1, .production_id = 6), - [451] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_selector_expression, 3, .production_id = 7), - [453] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_selector_expression, 3, .production_id = 7), - [455] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_interpreted_string_literal, 2), - [457] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_interpreted_string_literal, 2), - [459] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_interpreted_string_literal, 3), - [461] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_interpreted_string_literal, 3), - [463] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_variable, 1), - [465] = {.entry = {.count = 1, .reusable = true}}, SHIFT(182), - [467] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_variable, 1), - [469] = {.entry = {.count = 1, .reusable = false}}, SHIFT(162), - [471] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym__if_actions_end, 3), - [473] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym__if_actions_end, 3), - [475] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_if_action, 5, .production_id = 10), - [477] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_if_action, 5, .production_id = 10), - [479] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_block_action, 8, .production_id = 11), - [481] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_block_action, 8, .production_id = 11), - [483] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym__comment_action, 3), - [485] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym__comment_action, 3), - [487] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_block_action, 8, .production_id = 25), - [489] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_block_action, 8, .production_id = 25), - [491] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym__pipeline_action, 3), - [493] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym__pipeline_action, 3), - [495] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_range_action, 7, .production_id = 19), - [497] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_range_action, 7, .production_id = 19), - [499] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_range_action, 7), - [501] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_range_action, 7), - [503] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_with_action, 8, .production_id = 13), - [505] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_with_action, 8, .production_id = 13), - [507] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_if_action, 7, .production_id = 18), - [509] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_if_action, 7, .production_id = 18), - [511] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_variable, 2, .production_id = 2), - [513] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_variable, 2, .production_id = 2), - [515] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_if_action, 7, .production_id = 17), - [517] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_if_action, 7, .production_id = 17), - [519] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_if_action, 7, .production_id = 16), - [521] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_if_action, 7, .production_id = 16), - [523] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_with_action, 7, .production_id = 10), - [525] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_with_action, 7, .production_id = 10), - [527] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_if_action, 8, .production_id = 22), - [529] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_if_action, 8, .production_id = 22), - [531] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_range_action, 12, .production_id = 31), - [533] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_range_action, 12, .production_id = 31), - [535] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_template_action, 4, .production_id = 5), - [537] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_template_action, 4, .production_id = 5), - [539] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_range_action, 8, .production_id = 24), - [541] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_range_action, 8, .production_id = 24), - [543] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_block_action, 9, .production_id = 27), - [545] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_block_action, 9, .production_id = 27), - [547] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_define_action, 7, .production_id = 5), - [549] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_define_action, 7, .production_id = 5), - [551] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_range_action, 8, .production_id = 23), - [553] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_range_action, 8, .production_id = 23), - [555] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_define_action, 8, .production_id = 25), - [557] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_define_action, 8, .production_id = 25), - [559] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_template_action, 5, .production_id = 11), - [561] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_template_action, 5, .production_id = 11), - [563] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_range_action, 10), - [565] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_range_action, 10), - [567] = {.entry = {.count = 1, .reusable = false}}, SHIFT(68), - [569] = {.entry = {.count = 1, .reusable = false}}, SHIFT(75), - [571] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_if_action, 6, .production_id = 14), - [573] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_if_action, 6, .production_id = 14), - [575] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_text, 1), - [577] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_text, 1), - [579] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_range_action, 10, .production_id = 19), - [581] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_range_action, 10, .production_id = 19), - [583] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_if_action, 6, .production_id = 13), - [585] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_if_action, 6, .production_id = 13), - [587] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_block_action, 7, .production_id = 5), - [589] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_block_action, 7, .production_id = 5), - [591] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_with_action, 10, .production_id = 10), - [593] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_with_action, 10, .production_id = 10), - [595] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_with_action, 12, .production_id = 33), - [597] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_with_action, 12, .production_id = 33), - [599] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_range_action, 12, .production_id = 32), - [601] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_range_action, 12, .production_id = 32), - [603] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_if_action, 6, .production_id = 12), - [605] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_if_action, 6, .production_id = 12), - [607] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_range_action, 11, .production_id = 28), - [609] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_range_action, 11, .production_id = 28), - [611] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_range_action, 11, .production_id = 23), - [613] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_range_action, 11, .production_id = 23), - [615] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_range_action, 11, .production_id = 29), - [617] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_range_action, 11, .production_id = 29), - [619] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_range_action, 11, .production_id = 24), - [621] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_range_action, 11, .production_id = 24), - [623] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_with_action, 11, .production_id = 30), - [625] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_with_action, 11, .production_id = 30), - [627] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_with_action, 11, .production_id = 13), - [629] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_with_action, 11, .production_id = 13), - [631] = {.entry = {.count = 1, .reusable = false}}, SHIFT(319), - [633] = {.entry = {.count = 1, .reusable = true}}, SHIFT(319), - [635] = {.entry = {.count = 1, .reusable = true}}, SHIFT(71), - [637] = {.entry = {.count = 1, .reusable = true}}, SHIFT(69), - [639] = {.entry = {.count = 1, .reusable = true}}, SHIFT(367), - [641] = {.entry = {.count = 1, .reusable = false}}, SHIFT(77), - [643] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_argument_list, 1), - [645] = {.entry = {.count = 1, .reusable = true}}, SHIFT(61), - [647] = {.entry = {.count = 1, .reusable = false}}, SHIFT(369), - [649] = {.entry = {.count = 1, .reusable = true}}, SHIFT(60), - [651] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_dot, 1), - [653] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_dot, 1), - [655] = {.entry = {.count = 1, .reusable = false}}, SHIFT(142), - [657] = {.entry = {.count = 1, .reusable = false}}, REDUCE(aux_sym_argument_list_repeat1, 2), - [659] = {.entry = {.count = 2, .reusable = true}}, REDUCE(aux_sym_argument_list_repeat1, 2), SHIFT_REPEAT(74), - [662] = {.entry = {.count = 1, .reusable = false}}, SHIFT(357), - [664] = {.entry = {.count = 1, .reusable = true}}, SHIFT(357), - [666] = {.entry = {.count = 1, .reusable = true}}, SHIFT(58), - [668] = {.entry = {.count = 1, .reusable = true}}, SHIFT(56), - [670] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_assignment, 3, .production_id = 8), - [672] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_assignment, 3, .production_id = 8), - [674] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_function_call, 2, .production_id = 3), - [676] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_function_call, 2, .production_id = 3), - [678] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_method_call, 2, .production_id = 4), - [680] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_method_call, 2, .production_id = 4), - [682] = {.entry = {.count = 1, .reusable = true}}, REDUCE(aux_sym_argument_list_repeat1, 2), - [684] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_parenthesized_pipeline, 3), - [686] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_parenthesized_pipeline, 3), - [688] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_chained_pipeline, 3), - [690] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_chained_pipeline, 3), - [692] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_variable_definition, 3, .production_id = 8), - [694] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_variable_definition, 3, .production_id = 8), - [696] = {.entry = {.count = 1, .reusable = true}}, SHIFT(148), - [698] = {.entry = {.count = 1, .reusable = true}}, SHIFT(72), - [700] = {.entry = {.count = 1, .reusable = true}}, SHIFT(366), - [702] = {.entry = {.count = 1, .reusable = true}}, SHIFT(84), - [704] = {.entry = {.count = 1, .reusable = true}}, SHIFT(112), - [706] = {.entry = {.count = 1, .reusable = true}}, SHIFT(212), - [708] = {.entry = {.count = 1, .reusable = true}}, SHIFT(157), - [710] = {.entry = {.count = 1, .reusable = true}}, SHIFT(139), - [712] = {.entry = {.count = 1, .reusable = true}}, SHIFT(122), - [714] = {.entry = {.count = 1, .reusable = true}}, SHIFT(110), - [716] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_if_action_repeat1, 2, .production_id = 15), SHIFT_REPEAT(373), - [719] = {.entry = {.count = 2, .reusable = true}}, REDUCE(aux_sym_if_action_repeat1, 2, .production_id = 15), SHIFT_REPEAT(373), - [722] = {.entry = {.count = 1, .reusable = true}}, SHIFT(130), - [724] = {.entry = {.count = 1, .reusable = true}}, SHIFT(227), - [726] = {.entry = {.count = 1, .reusable = true}}, SHIFT(174), - [728] = {.entry = {.count = 1, .reusable = true}}, SHIFT(96), - [730] = {.entry = {.count = 1, .reusable = true}}, SHIFT(86), - [732] = {.entry = {.count = 1, .reusable = true}}, SHIFT(105), - [734] = {.entry = {.count = 1, .reusable = false}}, REDUCE(aux_sym_interpreted_string_literal_repeat1, 2), - [736] = {.entry = {.count = 2, .reusable = true}}, REDUCE(aux_sym_interpreted_string_literal_repeat1, 2), SHIFT_REPEAT(273), - [739] = {.entry = {.count = 1, .reusable = true}}, SHIFT(63), - [741] = {.entry = {.count = 1, .reusable = true}}, SHIFT(62), - [743] = {.entry = {.count = 1, .reusable = true}}, SHIFT(64), - [745] = {.entry = {.count = 1, .reusable = false}}, SHIFT(371), - [747] = {.entry = {.count = 1, .reusable = true}}, SHIFT(371), - [749] = {.entry = {.count = 1, .reusable = false}}, SHIFT(149), - [751] = {.entry = {.count = 1, .reusable = true}}, SHIFT(273), - [753] = {.entry = {.count = 1, .reusable = true}}, SHIFT(349), - [755] = {.entry = {.count = 1, .reusable = true}}, SHIFT(361), - [757] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_range_variable_definition, 5, .production_id = 20), - [759] = {.entry = {.count = 1, .reusable = true}}, SHIFT(65), - [761] = {.entry = {.count = 1, .reusable = false}}, SHIFT(372), - [763] = {.entry = {.count = 1, .reusable = true}}, SHIFT(372), - [765] = {.entry = {.count = 1, .reusable = false}}, SHIFT(245), - [767] = {.entry = {.count = 1, .reusable = true}}, SHIFT(288), - [769] = {.entry = {.count = 1, .reusable = false}}, SHIFT(249), - [771] = {.entry = {.count = 1, .reusable = false}}, SHIFT(145), - [773] = {.entry = {.count = 1, .reusable = true}}, SHIFT(278), - [775] = {.entry = {.count = 1, .reusable = true}}, SHIFT(189), - [777] = {.entry = {.count = 1, .reusable = true}}, SHIFT(211), - [779] = {.entry = {.count = 1, .reusable = true}}, SHIFT(235), - [781] = {.entry = {.count = 1, .reusable = true}}, SHIFT(248), - [783] = {.entry = {.count = 1, .reusable = true}}, SHIFT(183), - [785] = {.entry = {.count = 1, .reusable = true}}, SHIFT(228), - [787] = {.entry = {.count = 1, .reusable = true}}, SHIFT(169), - [789] = {.entry = {.count = 1, .reusable = true}}, SHIFT(187), - [791] = {.entry = {.count = 1, .reusable = true}}, SHIFT(132), - [793] = {.entry = {.count = 1, .reusable = true}}, SHIFT(172), - [795] = {.entry = {.count = 1, .reusable = true}}, SHIFT(188), - [797] = {.entry = {.count = 1, .reusable = true}}, SHIFT(134), - [799] = {.entry = {.count = 1, .reusable = true}}, SHIFT(236), - [801] = {.entry = {.count = 1, .reusable = true}}, SHIFT(165), - [803] = {.entry = {.count = 1, .reusable = true}}, SHIFT(225), - [805] = {.entry = {.count = 1, .reusable = true}}, SHIFT(234), - [807] = {.entry = {.count = 1, .reusable = true}}, SHIFT(230), - [809] = {.entry = {.count = 1, .reusable = true}}, SHIFT(138), - [811] = {.entry = {.count = 1, .reusable = true}}, SHIFT(181), - [813] = {.entry = {.count = 1, .reusable = true}}, SHIFT(171), - [815] = {.entry = {.count = 1, .reusable = true}}, SHIFT(158), - [817] = {.entry = {.count = 1, .reusable = true}}, SHIFT(127), - [819] = {.entry = {.count = 1, .reusable = true}}, SHIFT(159), - [821] = {.entry = {.count = 1, .reusable = true}}, SHIFT(179), - [823] = {.entry = {.count = 1, .reusable = true}}, SHIFT(123), - [825] = {.entry = {.count = 1, .reusable = true}}, SHIFT(185), - [827] = {.entry = {.count = 1, .reusable = true}}, SHIFT(322), - [829] = {.entry = {.count = 1, .reusable = true}}, SHIFT(207), - [831] = {.entry = {.count = 1, .reusable = true}}, SHIFT(175), - [833] = {.entry = {.count = 1, .reusable = true}}, SHIFT(152), - [835] = {.entry = {.count = 1, .reusable = true}}, SHIFT(116), - [837] = {.entry = {.count = 1, .reusable = true}}, SHIFT(95), - [839] = {.entry = {.count = 1, .reusable = true}}, SHIFT(190), - [841] = {.entry = {.count = 1, .reusable = true}}, SHIFT(194), - [843] = {.entry = {.count = 1, .reusable = true}}, SHIFT(102), - [845] = {.entry = {.count = 1, .reusable = true}}, SHIFT(191), - [847] = {.entry = {.count = 1, .reusable = true}}, SHIFT(170), - [849] = {.entry = {.count = 1, .reusable = true}}, SHIFT(115), - [851] = {.entry = {.count = 1, .reusable = true}}, SHIFT(208), - [853] = {.entry = {.count = 1, .reusable = true}}, SHIFT(160), - [855] = {.entry = {.count = 1, .reusable = true}}, SHIFT(209), - [857] = {.entry = {.count = 1, .reusable = true}}, SHIFT(210), - [859] = {.entry = {.count = 1, .reusable = true}}, SHIFT(213), - [861] = {.entry = {.count = 1, .reusable = true}}, SHIFT(214), - [863] = {.entry = {.count = 1, .reusable = true}}, SHIFT(192), - [865] = {.entry = {.count = 1, .reusable = true}}, SHIFT(124), + [5] = {.entry = {.count = 1, .reusable = false}}, SHIFT(184), + [7] = {.entry = {.count = 1, .reusable = false}}, SHIFT(54), + [9] = {.entry = {.count = 1, .reusable = false}}, SHIFT(78), + [11] = {.entry = {.count = 1, .reusable = true}}, SHIFT(84), + [13] = {.entry = {.count = 1, .reusable = false}}, SHIFT(327), + [15] = {.entry = {.count = 1, .reusable = false}}, SHIFT(335), + [17] = {.entry = {.count = 1, .reusable = false}}, SHIFT(69), + [19] = {.entry = {.count = 1, .reusable = false}}, SHIFT(286), + [21] = {.entry = {.count = 1, .reusable = false}}, SHIFT(265), + [23] = {.entry = {.count = 1, .reusable = false}}, SHIFT(264), + [25] = {.entry = {.count = 1, .reusable = false}}, SHIFT(83), + [27] = {.entry = {.count = 1, .reusable = true}}, SHIFT(76), + [29] = {.entry = {.count = 1, .reusable = false}}, SHIFT(251), + [31] = {.entry = {.count = 1, .reusable = true}}, SHIFT(154), + [33] = {.entry = {.count = 1, .reusable = false}}, SHIFT(61), + [35] = {.entry = {.count = 1, .reusable = false}}, SHIFT(294), + [37] = {.entry = {.count = 1, .reusable = true}}, SHIFT(294), + [39] = {.entry = {.count = 1, .reusable = true}}, SHIFT(271), + [41] = {.entry = {.count = 1, .reusable = true}}, SHIFT(301), + [43] = {.entry = {.count = 1, .reusable = false}}, SHIFT(326), + [45] = {.entry = {.count = 1, .reusable = false}}, SHIFT(319), + [47] = {.entry = {.count = 1, .reusable = false}}, SHIFT(318), + [49] = {.entry = {.count = 1, .reusable = false}}, SHIFT(302), + [51] = {.entry = {.count = 1, .reusable = false}}, SHIFT(305), + [53] = {.entry = {.count = 1, .reusable = false}}, SHIFT(331), + [55] = {.entry = {.count = 1, .reusable = false}}, SHIFT(311), + [57] = {.entry = {.count = 1, .reusable = false}}, SHIFT(334), + [59] = {.entry = {.count = 1, .reusable = false}}, SHIFT(307), + [61] = {.entry = {.count = 1, .reusable = false}}, SHIFT(345), + [63] = {.entry = {.count = 1, .reusable = false}}, SHIFT(303), + [65] = {.entry = {.count = 1, .reusable = false}}, SHIFT(357), + [67] = {.entry = {.count = 1, .reusable = false}}, SHIFT(353), + [69] = {.entry = {.count = 1, .reusable = false}}, SHIFT(315), + [71] = {.entry = {.count = 1, .reusable = false}}, SHIFT(312), + [73] = {.entry = {.count = 1, .reusable = false}}, SHIFT(350), + [75] = {.entry = {.count = 1, .reusable = false}}, SHIFT(299), + [77] = {.entry = {.count = 1, .reusable = false}}, SHIFT(369), + [79] = {.entry = {.count = 1, .reusable = false}}, SHIFT(338), + [81] = {.entry = {.count = 1, .reusable = false}}, SHIFT(310), + [83] = {.entry = {.count = 1, .reusable = false}}, SHIFT(308), + [85] = {.entry = {.count = 1, .reusable = false}}, SHIFT(321), + [87] = {.entry = {.count = 1, .reusable = false}}, SHIFT(298), + [89] = {.entry = {.count = 1, .reusable = false}}, SHIFT(328), + [91] = {.entry = {.count = 1, .reusable = false}}, SHIFT(366), + [93] = {.entry = {.count = 1, .reusable = false}}, SHIFT(329), + [95] = {.entry = {.count = 1, .reusable = false}}, SHIFT(340), + [97] = {.entry = {.count = 1, .reusable = false}}, SHIFT(344), + [99] = {.entry = {.count = 1, .reusable = false}}, SHIFT(300), + [101] = {.entry = {.count = 1, .reusable = false}}, SHIFT(304), + [103] = {.entry = {.count = 1, .reusable = false}}, SHIFT(306), + [105] = {.entry = {.count = 1, .reusable = false}}, SHIFT(347), + [107] = {.entry = {.count = 1, .reusable = false}}, SHIFT(351), + [109] = {.entry = {.count = 1, .reusable = false}}, SHIFT(309), + [111] = {.entry = {.count = 1, .reusable = false}}, SHIFT(343), + [113] = {.entry = {.count = 1, .reusable = false}}, SHIFT(314), + [115] = {.entry = {.count = 1, .reusable = false}}, SHIFT(322), + [117] = {.entry = {.count = 1, .reusable = false}}, SHIFT(362), + [119] = {.entry = {.count = 1, .reusable = false}}, SHIFT(313), + [121] = {.entry = {.count = 1, .reusable = false}}, SHIFT(367), + [123] = {.entry = {.count = 1, .reusable = false}}, SHIFT(330), + [125] = {.entry = {.count = 1, .reusable = false}}, SHIFT(348), + [127] = {.entry = {.count = 1, .reusable = false}}, SHIFT(332), + [129] = {.entry = {.count = 1, .reusable = false}}, SHIFT(346), + [131] = {.entry = {.count = 1, .reusable = false}}, SHIFT(341), + [133] = {.entry = {.count = 1, .reusable = false}}, SHIFT(342), + [135] = {.entry = {.count = 1, .reusable = false}}, SHIFT(364), + [137] = {.entry = {.count = 1, .reusable = false}}, SHIFT(349), + [139] = {.entry = {.count = 1, .reusable = false}}, SHIFT(339), + [141] = {.entry = {.count = 1, .reusable = false}}, SHIFT(317), + [143] = {.entry = {.count = 1, .reusable = false}}, SHIFT(337), + [145] = {.entry = {.count = 1, .reusable = false}}, SHIFT(361), + [147] = {.entry = {.count = 1, .reusable = false}}, SHIFT(325), + [149] = {.entry = {.count = 1, .reusable = false}}, SHIFT(358), + [151] = {.entry = {.count = 1, .reusable = false}}, SHIFT(368), + [153] = {.entry = {.count = 1, .reusable = false}}, SHIFT(354), + [155] = {.entry = {.count = 1, .reusable = false}}, SHIFT(316), + [157] = {.entry = {.count = 1, .reusable = false}}, SHIFT(320), + [159] = {.entry = {.count = 1, .reusable = false}}, SHIFT(356), + [161] = {.entry = {.count = 1, .reusable = false}}, SHIFT(324), + [163] = {.entry = {.count = 1, .reusable = false}}, SHIFT(359), + [165] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym__expression, 1), + [167] = {.entry = {.count = 1, .reusable = false}}, SHIFT(79), + [169] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym__expression, 1), + [171] = {.entry = {.count = 1, .reusable = false}}, SHIFT(145), + [173] = {.entry = {.count = 1, .reusable = false}}, SHIFT(244), + [175] = {.entry = {.count = 1, .reusable = false}}, SHIFT(155), + [177] = {.entry = {.count = 1, .reusable = false}}, SHIFT(55), + [179] = {.entry = {.count = 1, .reusable = false}}, SHIFT(245), + [181] = {.entry = {.count = 1, .reusable = false}}, SHIFT(292), + [183] = {.entry = {.count = 1, .reusable = false}}, SHIFT(82), + [185] = {.entry = {.count = 1, .reusable = false}}, SHIFT(68), + [187] = {.entry = {.count = 1, .reusable = false}}, SHIFT(263), + [189] = {.entry = {.count = 1, .reusable = false}}, SHIFT(266), + [191] = {.entry = {.count = 1, .reusable = false}}, SHIFT(269), + [193] = {.entry = {.count = 1, .reusable = false}}, SHIFT(77), + [195] = {.entry = {.count = 1, .reusable = false}}, SHIFT(272), + [197] = {.entry = {.count = 1, .reusable = true}}, SHIFT(272), + [199] = {.entry = {.count = 1, .reusable = true}}, SHIFT(360), + [201] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_function_call, 1, .production_id = 1), + [203] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_function_call, 1, .production_id = 1), + [205] = {.entry = {.count = 1, .reusable = true}}, SHIFT(79), + [207] = {.entry = {.count = 1, .reusable = false}}, SHIFT(151), + [209] = {.entry = {.count = 1, .reusable = true}}, SHIFT(155), + [211] = {.entry = {.count = 1, .reusable = false}}, SHIFT(248), + [213] = {.entry = {.count = 1, .reusable = true}}, SHIFT(248), + [215] = {.entry = {.count = 1, .reusable = true}}, SHIFT(292), + [217] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_argument_list, 2), + [219] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_argument_list, 2), + [221] = {.entry = {.count = 1, .reusable = false}}, SHIFT(258), + [223] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_argument_list, 3), + [225] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_argument_list, 3), + [227] = {.entry = {.count = 1, .reusable = true}}, SHIFT(258), + [229] = {.entry = {.count = 1, .reusable = false}}, SHIFT(267), + [231] = {.entry = {.count = 1, .reusable = true}}, SHIFT(267), + [233] = {.entry = {.count = 1, .reusable = true}}, SHIFT(111), + [235] = {.entry = {.count = 1, .reusable = false}}, SHIFT(279), + [237] = {.entry = {.count = 1, .reusable = true}}, SHIFT(279), + [239] = {.entry = {.count = 1, .reusable = true}}, SHIFT(173), + [241] = {.entry = {.count = 1, .reusable = false}}, SHIFT(280), + [243] = {.entry = {.count = 1, .reusable = true}}, SHIFT(280), + [245] = {.entry = {.count = 1, .reusable = true}}, SHIFT(113), + [247] = {.entry = {.count = 1, .reusable = false}}, SHIFT(289), + [249] = {.entry = {.count = 1, .reusable = true}}, SHIFT(289), + [251] = {.entry = {.count = 1, .reusable = true}}, SHIFT(226), + [253] = {.entry = {.count = 1, .reusable = false}}, SHIFT(274), + [255] = {.entry = {.count = 1, .reusable = true}}, SHIFT(274), + [257] = {.entry = {.count = 1, .reusable = false}}, SHIFT(270), + [259] = {.entry = {.count = 1, .reusable = true}}, SHIFT(270), + [261] = {.entry = {.count = 1, .reusable = false}}, SHIFT(282), + [263] = {.entry = {.count = 1, .reusable = true}}, SHIFT(282), + [265] = {.entry = {.count = 1, .reusable = false}}, SHIFT(249), + [267] = {.entry = {.count = 1, .reusable = true}}, SHIFT(249), + [269] = {.entry = {.count = 1, .reusable = false}}, SHIFT(250), + [271] = {.entry = {.count = 1, .reusable = true}}, SHIFT(250), + [273] = {.entry = {.count = 1, .reusable = false}}, SHIFT(333), + [275] = {.entry = {.count = 1, .reusable = true}}, SHIFT(333), + [277] = {.entry = {.count = 1, .reusable = false}}, SHIFT(252), + [279] = {.entry = {.count = 1, .reusable = true}}, SHIFT(252), + [281] = {.entry = {.count = 1, .reusable = false}}, SHIFT(370), + [283] = {.entry = {.count = 1, .reusable = true}}, SHIFT(370), + [285] = {.entry = {.count = 1, .reusable = false}}, SHIFT(275), + [287] = {.entry = {.count = 1, .reusable = true}}, SHIFT(275), + [289] = {.entry = {.count = 1, .reusable = false}}, SHIFT(284), + [291] = {.entry = {.count = 1, .reusable = true}}, SHIFT(284), + [293] = {.entry = {.count = 1, .reusable = false}}, SHIFT(373), + [295] = {.entry = {.count = 1, .reusable = true}}, SHIFT(373), + [297] = {.entry = {.count = 1, .reusable = false}}, SHIFT(285), + [299] = {.entry = {.count = 1, .reusable = true}}, SHIFT(285), + [301] = {.entry = {.count = 1, .reusable = false}}, SHIFT(283), + [303] = {.entry = {.count = 1, .reusable = true}}, SHIFT(283), + [305] = {.entry = {.count = 1, .reusable = false}}, SHIFT(273), + [307] = {.entry = {.count = 1, .reusable = true}}, SHIFT(273), + [309] = {.entry = {.count = 1, .reusable = false}}, SHIFT(268), + [311] = {.entry = {.count = 1, .reusable = true}}, SHIFT(268), + [313] = {.entry = {.count = 1, .reusable = false}}, SHIFT(296), + [315] = {.entry = {.count = 1, .reusable = true}}, SHIFT(296), + [317] = {.entry = {.count = 1, .reusable = false}}, SHIFT(217), + [319] = {.entry = {.count = 1, .reusable = false}}, SHIFT(2), + [321] = {.entry = {.count = 1, .reusable = false}}, SHIFT(3), + [323] = {.entry = {.count = 1, .reusable = true}}, REDUCE(aux_sym_template_repeat1, 2), + [325] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_template_repeat1, 2), SHIFT_REPEAT(184), + [328] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_template_repeat1, 2), SHIFT_REPEAT(54), + [331] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_template, 1), + [333] = {.entry = {.count = 1, .reusable = false}}, SHIFT(6), + [335] = {.entry = {.count = 2, .reusable = false}}, REDUCE(sym__else_clause, 4, .dynamic_precedence = 2, .production_id = 22), SHIFT(52), + [338] = {.entry = {.count = 1, .reusable = false}}, SHIFT(44), + [340] = {.entry = {.count = 1, .reusable = false}}, SHIFT(9), + [342] = {.entry = {.count = 1, .reusable = false}}, SHIFT(43), + [344] = {.entry = {.count = 1, .reusable = false}}, SHIFT(41), + [346] = {.entry = {.count = 1, .reusable = false}}, SHIFT(17), + [348] = {.entry = {.count = 1, .reusable = false}}, SHIFT(18), + [350] = {.entry = {.count = 1, .reusable = false}}, SHIFT(25), + [352] = {.entry = {.count = 1, .reusable = false}}, SHIFT(34), + [354] = {.entry = {.count = 1, .reusable = false}}, SHIFT(5), + [356] = {.entry = {.count = 1, .reusable = false}}, SHIFT(13), + [358] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_template_repeat1, 2), SHIFT_REPEAT(217), + [361] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_template_repeat1, 2), SHIFT_REPEAT(52), + [364] = {.entry = {.count = 1, .reusable = false}}, SHIFT(39), + [366] = {.entry = {.count = 1, .reusable = false}}, SHIFT(22), + [368] = {.entry = {.count = 1, .reusable = false}}, SHIFT(12), + [370] = {.entry = {.count = 1, .reusable = false}}, SHIFT(11), + [372] = {.entry = {.count = 1, .reusable = false}}, SHIFT(32), + [374] = {.entry = {.count = 1, .reusable = false}}, SHIFT(48), + [376] = {.entry = {.count = 1, .reusable = false}}, SHIFT(38), + [378] = {.entry = {.count = 1, .reusable = false}}, SHIFT(50), + [380] = {.entry = {.count = 1, .reusable = false}}, SHIFT(10), + [382] = {.entry = {.count = 1, .reusable = false}}, SHIFT(47), + [384] = {.entry = {.count = 1, .reusable = false}}, SHIFT(8), + [386] = {.entry = {.count = 1, .reusable = false}}, SHIFT(40), + [388] = {.entry = {.count = 1, .reusable = false}}, SHIFT(23), + [390] = {.entry = {.count = 1, .reusable = false}}, SHIFT(7), + [392] = {.entry = {.count = 2, .reusable = false}}, REDUCE(sym__else_clause, 3, .dynamic_precedence = 2), SHIFT(52), + [395] = {.entry = {.count = 1, .reusable = false}}, SHIFT(19), + [397] = {.entry = {.count = 1, .reusable = false}}, SHIFT(46), + [399] = {.entry = {.count = 1, .reusable = false}}, SHIFT(49), + [401] = {.entry = {.count = 1, .reusable = false}}, SHIFT(51), + [403] = {.entry = {.count = 1, .reusable = false}}, SHIFT(20), + [405] = {.entry = {.count = 1, .reusable = false}}, SHIFT(4), + [407] = {.entry = {.count = 1, .reusable = false}}, SHIFT(36), + [409] = {.entry = {.count = 1, .reusable = false}}, SHIFT(42), + [411] = {.entry = {.count = 1, .reusable = false}}, SHIFT(14), + [413] = {.entry = {.count = 1, .reusable = false}}, SHIFT(29), + [415] = {.entry = {.count = 1, .reusable = false}}, SHIFT(35), + [417] = {.entry = {.count = 1, .reusable = false}}, SHIFT(37), + [419] = {.entry = {.count = 2, .reusable = false}}, REDUCE(sym__else_if_clause, 5, .dynamic_precedence = 1, .production_id = 27), SHIFT(52), + [422] = {.entry = {.count = 1, .reusable = false}}, SHIFT(21), + [424] = {.entry = {.count = 1, .reusable = false}}, SHIFT(24), + [426] = {.entry = {.count = 1, .reusable = false}}, SHIFT(15), + [428] = {.entry = {.count = 1, .reusable = false}}, SHIFT(26), + [430] = {.entry = {.count = 1, .reusable = false}}, SHIFT(27), + [432] = {.entry = {.count = 1, .reusable = false}}, SHIFT(30), + [434] = {.entry = {.count = 1, .reusable = false}}, SHIFT(16), + [436] = {.entry = {.count = 1, .reusable = false}}, SHIFT(31), + [438] = {.entry = {.count = 1, .reusable = false}}, SHIFT(33), + [440] = {.entry = {.count = 1, .reusable = false}}, SHIFT(28), + [442] = {.entry = {.count = 1, .reusable = false}}, SHIFT(45), + [444] = {.entry = {.count = 2, .reusable = false}}, REDUCE(sym__else_if_clause, 4, .dynamic_precedence = 1, .production_id = 11), SHIFT(52), + [447] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_selector_expression, 3, .production_id = 8), + [449] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_selector_expression, 3, .production_id = 8), + [451] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_unfished_selector_expression, 2, .production_id = 4), + [453] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_unfished_selector_expression, 2, .production_id = 4), + [455] = {.entry = {.count = 1, .reusable = false}}, SHIFT(147), + [457] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_field, 2, .production_id = 2), + [459] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_field, 2, .production_id = 2), + [461] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym__field_identifier, 1, .production_id = 7), + [463] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym__field_identifier, 1, .production_id = 7), + [465] = {.entry = {.count = 1, .reusable = false}}, SHIFT(150), + [467] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_interpreted_string_literal, 2), + [469] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_interpreted_string_literal, 2), + [471] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_interpreted_string_literal, 3), + [473] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_interpreted_string_literal, 3), + [475] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_variable, 1), + [477] = {.entry = {.count = 1, .reusable = true}}, SHIFT(161), + [479] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_variable, 1), + [481] = {.entry = {.count = 1, .reusable = false}}, SHIFT(183), + [483] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_if_action, 7, .production_id = 17), + [485] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_if_action, 7, .production_id = 17), + [487] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_range_action, 10), + [489] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_range_action, 10), + [491] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_block_action, 8, .production_id = 12), + [493] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_block_action, 8, .production_id = 12), + [495] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_define_action, 7, .production_id = 6), + [497] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_define_action, 7, .production_id = 6), + [499] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_block_action, 8, .production_id = 26), + [501] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_block_action, 8, .production_id = 26), + [503] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_variable, 2, .production_id = 2), + [505] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_range_action, 12, .production_id = 32), + [507] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_range_action, 12, .production_id = 32), + [509] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym__pipeline_action, 3), + [511] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym__pipeline_action, 3), + [513] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_with_action, 8, .production_id = 14), + [515] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_with_action, 8, .production_id = 14), + [517] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_range_action, 7, .production_id = 20), + [519] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_range_action, 7, .production_id = 20), + [521] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_range_action, 7), + [523] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_range_action, 7), + [525] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_if_action, 7, .production_id = 19), + [527] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_if_action, 7, .production_id = 19), + [529] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_if_action, 7, .production_id = 18), + [531] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_if_action, 7, .production_id = 18), + [533] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_with_action, 7, .production_id = 11), + [535] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_with_action, 7, .production_id = 11), + [537] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_range_action, 8, .production_id = 25), + [539] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_range_action, 8, .production_id = 25), + [541] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym__if_actions_end, 3), + [543] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym__if_actions_end, 3), + [545] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_block_action, 9, .production_id = 28), + [547] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_block_action, 9, .production_id = 28), + [549] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_template_action, 4, .production_id = 6), + [551] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_template_action, 4, .production_id = 6), + [553] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_range_action, 12, .production_id = 33), + [555] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_range_action, 12, .production_id = 33), + [557] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_with_action, 12, .production_id = 34), + [559] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_with_action, 12, .production_id = 34), + [561] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym__comment_action, 3), + [563] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym__comment_action, 3), + [565] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_define_action, 8, .production_id = 26), + [567] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_define_action, 8, .production_id = 26), + [569] = {.entry = {.count = 1, .reusable = false}}, SHIFT(72), + [571] = {.entry = {.count = 1, .reusable = false}}, SHIFT(73), + [573] = {.entry = {.count = 1, .reusable = true}}, SHIFT(145), + [575] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_if_action, 5, .production_id = 11), + [577] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_if_action, 5, .production_id = 11), + [579] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_template_action, 5, .production_id = 12), + [581] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_template_action, 5, .production_id = 12), + [583] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_range_action, 10, .production_id = 20), + [585] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_range_action, 10, .production_id = 20), + [587] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_range_action, 8, .production_id = 24), + [589] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_range_action, 8, .production_id = 24), + [591] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_variable, 2, .production_id = 2), + [593] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_text, 1), + [595] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_text, 1), + [597] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_with_action, 10, .production_id = 11), + [599] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_with_action, 10, .production_id = 11), + [601] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_if_action, 6, .production_id = 15), + [603] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_if_action, 6, .production_id = 15), + [605] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_block_action, 7, .production_id = 6), + [607] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_block_action, 7, .production_id = 6), + [609] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_if_action, 6, .production_id = 14), + [611] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_if_action, 6, .production_id = 14), + [613] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_range_action, 11, .production_id = 29), + [615] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_range_action, 11, .production_id = 29), + [617] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_range_action, 11, .production_id = 24), + [619] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_range_action, 11, .production_id = 24), + [621] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_if_action, 8, .production_id = 23), + [623] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_if_action, 8, .production_id = 23), + [625] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_range_action, 11, .production_id = 30), + [627] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_range_action, 11, .production_id = 30), + [629] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_range_action, 11, .production_id = 25), + [631] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_range_action, 11, .production_id = 25), + [633] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_if_action, 6, .production_id = 13), + [635] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_if_action, 6, .production_id = 13), + [637] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_with_action, 11, .production_id = 31), + [639] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_with_action, 11, .production_id = 31), + [641] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_with_action, 11, .production_id = 14), + [643] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_with_action, 11, .production_id = 14), + [645] = {.entry = {.count = 1, .reusable = false}}, SHIFT(323), + [647] = {.entry = {.count = 1, .reusable = true}}, SHIFT(323), + [649] = {.entry = {.count = 1, .reusable = true}}, SHIFT(81), + [651] = {.entry = {.count = 1, .reusable = true}}, SHIFT(80), + [653] = {.entry = {.count = 1, .reusable = true}}, SHIFT(151), + [655] = {.entry = {.count = 1, .reusable = true}}, SHIFT(371), + [657] = {.entry = {.count = 1, .reusable = false}}, SHIFT(336), + [659] = {.entry = {.count = 1, .reusable = true}}, SHIFT(336), + [661] = {.entry = {.count = 1, .reusable = true}}, SHIFT(60), + [663] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_parenthesized_pipeline, 3), + [665] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_parenthesized_pipeline, 3), + [667] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym__pipeline, 1), + [669] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym__pipeline, 1), + [671] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_dot, 1), + [673] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_dot, 1), + [675] = {.entry = {.count = 1, .reusable = false}}, SHIFT(146), + [677] = {.entry = {.count = 1, .reusable = false}}, SHIFT(75), + [679] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_argument_list, 1), + [681] = {.entry = {.count = 1, .reusable = true}}, SHIFT(59), + [683] = {.entry = {.count = 1, .reusable = false}}, REDUCE(aux_sym_argument_list_repeat1, 2), + [685] = {.entry = {.count = 2, .reusable = true}}, REDUCE(aux_sym_argument_list_repeat1, 2), SHIFT_REPEAT(70), + [688] = {.entry = {.count = 1, .reusable = true}}, SHIFT(62), + [690] = {.entry = {.count = 1, .reusable = true}}, SHIFT(63), + [692] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_variable_definition, 3, .production_id = 9), + [694] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_variable_definition, 3, .production_id = 9), + [696] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_assignment, 3, .production_id = 9), + [698] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_assignment, 3, .production_id = 9), + [700] = {.entry = {.count = 1, .reusable = true}}, SHIFT(148), + [702] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_chained_pipeline, 3), + [704] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_chained_pipeline, 3), + [706] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_method_call, 2, .production_id = 5), + [708] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_method_call, 2, .production_id = 5), + [710] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_function_call, 2, .production_id = 3), + [712] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_function_call, 2, .production_id = 3), + [714] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_if_action_repeat1, 2, .production_id = 16), SHIFT_REPEAT(377), + [717] = {.entry = {.count = 2, .reusable = true}}, REDUCE(aux_sym_if_action_repeat1, 2, .production_id = 16), SHIFT_REPEAT(377), + [720] = {.entry = {.count = 1, .reusable = true}}, REDUCE(aux_sym_argument_list_repeat1, 2), + [722] = {.entry = {.count = 1, .reusable = false}}, SHIFT(375), + [724] = {.entry = {.count = 1, .reusable = true}}, SHIFT(375), + [726] = {.entry = {.count = 1, .reusable = false}}, SHIFT(253), + [728] = {.entry = {.count = 1, .reusable = true}}, SHIFT(281), + [730] = {.entry = {.count = 1, .reusable = true}}, SHIFT(65), + [732] = {.entry = {.count = 1, .reusable = true}}, SHIFT(64), + [734] = {.entry = {.count = 1, .reusable = true}}, SHIFT(365), + [736] = {.entry = {.count = 1, .reusable = true}}, SHIFT(352), + [738] = {.entry = {.count = 1, .reusable = true}}, SHIFT(71), + [740] = {.entry = {.count = 1, .reusable = true}}, SHIFT(142), + [742] = {.entry = {.count = 1, .reusable = true}}, SHIFT(114), + [744] = {.entry = {.count = 1, .reusable = true}}, SHIFT(66), + [746] = {.entry = {.count = 1, .reusable = true}}, SHIFT(107), + [748] = {.entry = {.count = 1, .reusable = false}}, SHIFT(152), + [750] = {.entry = {.count = 1, .reusable = true}}, SHIFT(277), + [752] = {.entry = {.count = 1, .reusable = true}}, SHIFT(163), + [754] = {.entry = {.count = 1, .reusable = true}}, SHIFT(88), + [756] = {.entry = {.count = 1, .reusable = true}}, SHIFT(124), + [758] = {.entry = {.count = 1, .reusable = true}}, SHIFT(112), + [760] = {.entry = {.count = 1, .reusable = false}}, SHIFT(153), + [762] = {.entry = {.count = 1, .reusable = true}}, SHIFT(180), + [764] = {.entry = {.count = 1, .reusable = true}}, SHIFT(141), + [766] = {.entry = {.count = 1, .reusable = false}}, REDUCE(aux_sym_interpreted_string_literal_repeat1, 2), + [768] = {.entry = {.count = 2, .reusable = true}}, REDUCE(aux_sym_interpreted_string_literal_repeat1, 2), SHIFT_REPEAT(281), + [771] = {.entry = {.count = 1, .reusable = true}}, SHIFT(86), + [773] = {.entry = {.count = 1, .reusable = true}}, SHIFT(67), + [775] = {.entry = {.count = 1, .reusable = true}}, SHIFT(231), + [777] = {.entry = {.count = 1, .reusable = false}}, SHIFT(376), + [779] = {.entry = {.count = 1, .reusable = true}}, SHIFT(376), + [781] = {.entry = {.count = 1, .reusable = false}}, SHIFT(255), + [783] = {.entry = {.count = 1, .reusable = true}}, SHIFT(262), + [785] = {.entry = {.count = 1, .reusable = true}}, SHIFT(223), + [787] = {.entry = {.count = 1, .reusable = true}}, SHIFT(143), + [789] = {.entry = {.count = 1, .reusable = true}}, SHIFT(166), + [791] = {.entry = {.count = 1, .reusable = true}}, SHIFT(219), + [793] = {.entry = {.count = 1, .reusable = true}}, SHIFT(174), + [795] = {.entry = {.count = 1, .reusable = true}}, SHIFT(222), + [797] = {.entry = {.count = 1, .reusable = true}}, SHIFT(135), + [799] = {.entry = {.count = 1, .reusable = true}}, SHIFT(204), + [801] = {.entry = {.count = 1, .reusable = true}}, SHIFT(162), + [803] = {.entry = {.count = 1, .reusable = true}}, SHIFT(170), + [805] = {.entry = {.count = 1, .reusable = true}}, SHIFT(196), + [807] = {.entry = {.count = 1, .reusable = true}}, SHIFT(199), + [809] = {.entry = {.count = 1, .reusable = true}}, SHIFT(182), + [811] = {.entry = {.count = 1, .reusable = true}}, SHIFT(195), + [813] = {.entry = {.count = 1, .reusable = true}}, SHIFT(138), + [815] = {.entry = {.count = 1, .reusable = true}}, SHIFT(212), + [817] = {.entry = {.count = 1, .reusable = true}}, SHIFT(169), + [819] = {.entry = {.count = 1, .reusable = true}}, SHIFT(239), + [821] = {.entry = {.count = 1, .reusable = true}}, SHIFT(193), + [823] = {.entry = {.count = 1, .reusable = true}}, SHIFT(140), + [825] = {.entry = {.count = 1, .reusable = true}}, SHIFT(187), + [827] = {.entry = {.count = 1, .reusable = true}}, SHIFT(159), + [829] = {.entry = {.count = 1, .reusable = true}}, SHIFT(165), + [831] = {.entry = {.count = 1, .reusable = true}}, SHIFT(129), + [833] = {.entry = {.count = 1, .reusable = true}}, SHIFT(213), + [835] = {.entry = {.count = 1, .reusable = true}}, SHIFT(125), + [837] = {.entry = {.count = 1, .reusable = true}}, SHIFT(192), + [839] = {.entry = {.count = 1, .reusable = true}}, SHIFT(326), + [841] = {.entry = {.count = 1, .reusable = true}}, SHIFT(207), + [843] = {.entry = {.count = 1, .reusable = true}}, SHIFT(214), + [845] = {.entry = {.count = 1, .reusable = true}}, SHIFT(171), + [847] = {.entry = {.count = 1, .reusable = true}}, SHIFT(118), + [849] = {.entry = {.count = 1, .reusable = true}}, SHIFT(98), + [851] = {.entry = {.count = 1, .reusable = true}}, SHIFT(190), + [853] = {.entry = {.count = 1, .reusable = true}}, SHIFT(189), + [855] = {.entry = {.count = 1, .reusable = true}}, SHIFT(105), + [857] = {.entry = {.count = 1, .reusable = true}}, SHIFT(185), + [859] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_range_variable_definition, 5, .production_id = 21), + [861] = {.entry = {.count = 1, .reusable = true}}, SHIFT(116), + [863] = {.entry = {.count = 1, .reusable = true}}, SHIFT(229), + [865] = {.entry = {.count = 1, .reusable = true}}, SHIFT(335), [867] = {.entry = {.count = 1, .reusable = true}}, SHIFT(215), - [869] = {.entry = {.count = 1, .reusable = true}}, SHIFT(216), - [871] = {.entry = {.count = 1, .reusable = true}}, SHIFT(120), - [873] = {.entry = {.count = 1, .reusable = true}}, SHIFT(218), - [875] = {.entry = {.count = 1, .reusable = true}}, SHIFT(129), - [877] = {.entry = {.count = 1, .reusable = true}}, SHIFT(219), - [879] = {.entry = {.count = 1, .reusable = true}}, SHIFT(220), - [881] = {.entry = {.count = 1, .reusable = true}}, SHIFT(156), - [883] = {.entry = {.count = 1, .reusable = true}}, SHIFT(268), - [885] = {.entry = {.count = 1, .reusable = true}}, SHIFT(154), - [887] = {.entry = {.count = 1, .reusable = true}}, SHIFT(113), - [889] = {.entry = {.count = 1, .reusable = true}}, SHIFT(167), - [891] = {.entry = {.count = 1, .reusable = true}}, SHIFT(221), - [893] = {.entry = {.count = 1, .reusable = true}}, SHIFT(125), - [895] = {.entry = {.count = 1, .reusable = true}}, SHIFT(173), - [897] = {.entry = {.count = 1, .reusable = true}}, SHIFT(184), - [899] = {.entry = {.count = 1, .reusable = true}}, SHIFT(239), - [901] = {.entry = {.count = 1, .reusable = true}}, SHIFT(155), - [903] = {.entry = {.count = 1, .reusable = true}}, SHIFT(355), - [905] = {.entry = {.count = 1, .reusable = true}}, SHIFT(222), - [907] = {.entry = {.count = 1, .reusable = true}}, SHIFT(104), - [909] = {.entry = {.count = 1, .reusable = true}}, SHIFT(238), - [911] = {.entry = {.count = 1, .reusable = true}}, SHIFT(107), - [913] = {.entry = {.count = 1, .reusable = true}}, SHIFT(223), - [915] = {.entry = {.count = 1, .reusable = true}}, SHIFT(237), - [917] = {.entry = {.count = 1, .reusable = true}}, SHIFT(224), - [919] = {.entry = {.count = 1, .reusable = true}}, SHIFT(131), - [921] = {.entry = {.count = 1, .reusable = true}}, SHIFT(147), - [923] = {.entry = {.count = 1, .reusable = false}}, REDUCE(aux_sym_if_action_repeat1, 1, .production_id = 9), - [925] = {.entry = {.count = 1, .reusable = true}}, REDUCE(aux_sym_if_action_repeat1, 1, .production_id = 9), - [927] = {.entry = {.count = 1, .reusable = true}}, SHIFT(143), - [929] = {.entry = {.count = 1, .reusable = true}}, SHIFT(73), - [931] = {.entry = {.count = 1, .reusable = true}}, ACCEPT_INPUT(), + [869] = {.entry = {.count = 1, .reusable = true}}, SHIFT(221), + [871] = {.entry = {.count = 1, .reusable = true}}, SHIFT(175), + [873] = {.entry = {.count = 1, .reusable = true}}, SHIFT(216), + [875] = {.entry = {.count = 1, .reusable = true}}, SHIFT(181), + [877] = {.entry = {.count = 1, .reusable = true}}, SHIFT(157), + [879] = {.entry = {.count = 1, .reusable = true}}, SHIFT(177), + [881] = {.entry = {.count = 1, .reusable = true}}, SHIFT(225), + [883] = {.entry = {.count = 1, .reusable = true}}, SHIFT(121), + [885] = {.entry = {.count = 1, .reusable = true}}, SHIFT(233), + [887] = {.entry = {.count = 1, .reusable = true}}, SHIFT(236), + [889] = {.entry = {.count = 1, .reusable = true}}, SHIFT(237), + [891] = {.entry = {.count = 1, .reusable = true}}, SHIFT(172), + [893] = {.entry = {.count = 1, .reusable = true}}, SHIFT(122), + [895] = {.entry = {.count = 1, .reusable = true}}, SHIFT(238), + [897] = {.entry = {.count = 1, .reusable = true}}, SHIFT(115), + [899] = {.entry = {.count = 1, .reusable = true}}, SHIFT(164), + [901] = {.entry = {.count = 1, .reusable = true}}, SHIFT(208), + [903] = {.entry = {.count = 1, .reusable = true}}, SHIFT(127), + [905] = {.entry = {.count = 1, .reusable = true}}, SHIFT(209), + [907] = {.entry = {.count = 1, .reusable = true}}, SHIFT(132), + [909] = {.entry = {.count = 1, .reusable = true}}, SHIFT(160), + [911] = {.entry = {.count = 1, .reusable = true}}, SHIFT(203), + [913] = {.entry = {.count = 1, .reusable = true}}, SHIFT(176), + [915] = {.entry = {.count = 1, .reusable = true}}, SHIFT(218), + [917] = {.entry = {.count = 1, .reusable = true}}, SHIFT(158), + [919] = {.entry = {.count = 1, .reusable = true}}, SHIFT(106), + [921] = {.entry = {.count = 1, .reusable = true}}, SHIFT(205), + [923] = {.entry = {.count = 1, .reusable = true}}, SHIFT(109), + [925] = {.entry = {.count = 1, .reusable = true}}, SHIFT(198), + [927] = {.entry = {.count = 1, .reusable = true}}, SHIFT(220), + [929] = {.entry = {.count = 1, .reusable = true}}, SHIFT(240), + [931] = {.entry = {.count = 1, .reusable = true}}, SHIFT(126), + [933] = {.entry = {.count = 1, .reusable = true}}, SHIFT(259), + [935] = {.entry = {.count = 1, .reusable = false}}, REDUCE(aux_sym_if_action_repeat1, 1, .production_id = 10), + [937] = {.entry = {.count = 1, .reusable = true}}, REDUCE(aux_sym_if_action_repeat1, 1, .production_id = 10), + [939] = {.entry = {.count = 1, .reusable = true}}, SHIFT(242), + [941] = {.entry = {.count = 1, .reusable = true}}, SHIFT(74), + [943] = {.entry = {.count = 1, .reusable = true}}, ACCEPT_INPUT(), }; #ifdef __cplusplus diff --git a/internal/util/slices.go b/internal/util/slices.go index ef449802..4004c24c 100644 --- a/internal/util/slices.go +++ b/internal/util/slices.go @@ -1,5 +1,7 @@ package util +// TODO: update this to use slices.Concat once we update +// from go 1.21 to go 1.22 func ConcatMultipleSlices[T any](slices [][]T) []T { var totalLen int From 88706f04df6b23d2b03fc10d7171c4c1e3374c4c Mon Sep 17 00:00:00 2001 From: qvalentin Date: Sat, 4 May 2024 10:16:41 +0200 Subject: [PATCH 29/35] feat: completion for unfinished_selector_expression --- .../yamlls/completion_integration_test.go | 2 +- internal/adapter/yamlls/diagnostics.go | 7 + internal/adapter/yamlls/documentSync.go | 3 + internal/adapter/yamlls/hover.go | 22 +- .../adapter/yamlls/hover_integration_test.go | 2 +- internal/charts/chart.go | 5 +- .../documentation/helm/helm-documentation.go | 8 +- internal/handler/completion.go | 109 +---- internal/handler/completion_main_test.go | 158 ++++--- internal/handler/definition.go | 3 +- internal/handler/generic_document_usecase.go | 38 +- internal/handler/hover.go | 13 +- internal/handler/initialization.go | 7 +- internal/handler/references.go | 5 +- internal/handler/text_document.go | 15 + internal/language_features/function_call.go | 5 +- .../generic_document_usecase.go | 10 - internal/language_features/includes.go | 44 +- .../language_features/template_context.go | 61 +-- .../template_context_completion.go | 73 ++++ internal/language_features/text.go | 54 +++ internal/language_features/usecases.go | 3 + internal/lsp/ast.go | 9 + internal/lsp/document.go | 52 --- internal/lsp/document_store.go | 82 ++++ internal/lsp/symbol_table.go | 24 +- internal/lsp/symbol_table_includes.go | 6 +- ...es.go => symbol_table_template_context.go} | 24 +- internal/lsp/symbol_table_test.go | 42 ++ internal/lsp/visitor.go | 2 +- internal/protocol/completion.go | 55 ++- internal/{util/lsp.go => protocol/hover.go} | 6 +- internal/tree-sitter/gotemplate/README.md | 2 +- internal/tree-sitter/gotemplate/node-types.go | 73 ++-- internal/tree-sitter/gotemplate/parser.c | 396 +++++++++--------- internal/util/values.go | 10 +- internal/util/yaml_path.go | 59 --- .../example/templates/completion-test.yaml | 6 +- 38 files changed, 836 insertions(+), 659 deletions(-) create mode 100644 internal/language_features/template_context_completion.go create mode 100644 internal/language_features/text.go create mode 100644 internal/lsp/document_store.go rename internal/lsp/{symbol_table_values.go => symbol_table_template_context.go} (82%) rename internal/{util/lsp.go => protocol/hover.go} (88%) delete mode 100644 internal/util/yaml_path.go diff --git a/internal/adapter/yamlls/completion_integration_test.go b/internal/adapter/yamlls/completion_integration_test.go index 4b73c2f4..3e3687b4 100644 --- a/internal/adapter/yamlls/completion_integration_test.go +++ b/internal/adapter/yamlls/completion_integration_test.go @@ -24,7 +24,7 @@ func TestYamllsCompletionIntegration(t *testing.T) { expected []lsp.CompletionItem }{ { - desc: "test hover on deployment.yaml", + desc: "test completion on deployment.yaml", file: "../../../testdata/example/templates/deployment.yaml", position: lsp.Position{ Line: 42, diff --git a/internal/adapter/yamlls/diagnostics.go b/internal/adapter/yamlls/diagnostics.go index 7b92f6f6..b140103d 100644 --- a/internal/adapter/yamlls/diagnostics.go +++ b/internal/adapter/yamlls/diagnostics.go @@ -2,6 +2,8 @@ package yamlls import ( "context" + "runtime" + "strings" lsplocal "github.com/mrjosh/helm-ls/internal/lsp" sitter "github.com/smacker/go-tree-sitter" @@ -71,6 +73,11 @@ func diagnisticIsRelevant(diagnostic lsp.Diagnostic, node *sitter.Node) bool { // {{- end }} return false default: + // TODO: remove this once the tree-sitter grammar behavior for windows newlines is the same as for unix + if strings.HasPrefix(diagnostic.Message, "Incorrect type. Expected") && runtime.GOOS == "windows" { + return false + } + return true } } diff --git a/internal/adapter/yamlls/documentSync.go b/internal/adapter/yamlls/documentSync.go index 02c9bae7..1d566b58 100644 --- a/internal/adapter/yamlls/documentSync.go +++ b/internal/adapter/yamlls/documentSync.go @@ -14,6 +14,9 @@ func (yamllsConnector Connector) InitiallySyncOpenDocuments(docs []*lsplocal.Doc return } for _, doc := range docs { + if !doc.IsOpen { + continue + } yamllsConnector.DocumentDidOpen(doc.Ast, lsp.DidOpenTextDocumentParams{ TextDocument: lsp.TextDocumentItem{ URI: doc.URI, diff --git a/internal/adapter/yamlls/hover.go b/internal/adapter/yamlls/hover.go index 92b12453..b6f85df2 100644 --- a/internal/adapter/yamlls/hover.go +++ b/internal/adapter/yamlls/hover.go @@ -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 @@ -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) @@ -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 } diff --git a/internal/adapter/yamlls/hover_integration_test.go b/internal/adapter/yamlls/hover_integration_test.go index c4b97754..2cc474e5 100644 --- a/internal/adapter/yamlls/hover_integration_test.go +++ b/internal/adapter/yamlls/hover_integration_test.go @@ -73,7 +73,7 @@ func TestYamllsHoverIntegration(t *testing.T) { }, }, tt.word) return err == nil && strings.Contains(result.Contents.Value, tt.expected) - }, time.Second*10, time.Second/2) + }, time.Second*40, time.Second*2) }) } } diff --git a/internal/charts/chart.go b/internal/charts/chart.go index 4e4f16b4..2ca97c9f 100644 --- a/internal/charts/chart.go +++ b/internal/charts/chart.go @@ -60,8 +60,9 @@ func (c *Chart) ResolveValueFiles(query []string, chartStore *ChartStore) []*Que } func (c *Chart) GetValueLocation(templateContext []string) (lsp.Location, error) { - modifyedVar := make([]string, 0) - // for Charts, we make the first letter lowercase + modifyedVar := make([]string, len(templateContext)) + // make the first letter lowercase since in the template the first letter is + // capitalized, but it is not in the Chart.yaml file for _, value := range templateContext { restOfString := "" if (len(value)) > 1 { diff --git a/internal/documentation/helm/helm-documentation.go b/internal/documentation/helm/helm-documentation.go index d1e0773d..d770eec1 100644 --- a/internal/documentation/helm/helm-documentation.go +++ b/internal/documentation/helm/helm-documentation.go @@ -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 } diff --git a/internal/handler/completion.go b/internal/handler/completion.go index fb6a0c9a..2a7104ce 100644 --- a/internal/handler/completion.go +++ b/internal/handler/completion.go @@ -2,48 +2,27 @@ 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" + "github.com/mrjosh/helm-ls/internal/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, ¶ms.TextDocumentPositionParams), + languagefeatures.NewIncludesCallFeature(genericDocumentUseCase), } for _, usecase := range usecases { @@ -52,79 +31,15 @@ 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{} + // If no usecase matched, we assume we are at {{ }} + // and provide the basic BuiltInObjects and functions + items := []helmdocs.HelmDocumentation{} for _, v := range helmdocs.BuiltInObjects { - items = append(items, lsp.CompletionItem{ - Label: v.Name, - InsertText: "." + v.Name, - Detail: v.Detail, - Documentation: v.Doc, - }) + v.Name = "." + v.Name + items = append(items, v) } - 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, - } + return protocol.CompletionResults{}. + WithDocs(items, lsp.CompletionItemKindConstant). + WithDocs(helmdocs.AllFuncs, lsp.CompletionItemKindFunction).ToList(), nil } diff --git a/internal/handler/completion_main_test.go b/internal/handler/completion_main_test.go index d3b3fd5e..7b6e66e9 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" ) @@ -23,6 +26,31 @@ func TestCompletionMain(t *testing.T) { notExpectedInsertTexts []string expectedError error }{ + { + desc: "Test completion on {{ not }}", + position: lsp.Position{ + Line: 11, + Character: 7, + }, + expectedInsertText: "toYaml", + notExpectedInsertTexts: []string{ + "replicaCount", + }, + 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 {{ if (and .Values. ) }}", position: lsp.Position{ @@ -70,6 +98,7 @@ func TestCompletionMain(t *testing.T) { expectedInsertText: "Chart", notExpectedInsertTexts: []string{ helmdocs.HelmFuncs[0].Name, + "js", "replicaCount", "toYaml", }, @@ -99,36 +128,9 @@ 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{ - // Line: 4, - // Character: 3, - // }, - // expectedInsertText: "toYaml", - // notExpectedInsertTexts: []string{ - // "replicaCount", - // }, - // expectedError: nil, - // }, } for _, tt := range testCases { t.Run(tt.desc, func(t *testing.T) { - documents := lsplocal.NewDocumentStore() - path := "../../testdata/example/templates/completion-test.yaml" fileURI := uri.File(path) @@ -136,31 +138,14 @@ func TestCompletionMain(t *testing.T) { if err != nil { t.Fatal("Could not read test file", err) } - d := lsp.DidOpenTextDocumentParams{ - TextDocument: lsp.TextDocumentItem{ - URI: fileURI, - LanguageID: "", - Version: 0, - Text: string(content), - }, - } - 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: tt.position, - }, - }) + result, err := completionTestCall(fileURI, string(content), tt.position) assert.Equal(t, tt.expectedError, err) assert.NotNil(t, result) + if result == nil { + return + } + insertTexts := []string{} for _, item := range result.Items { insertTexts = append(insertTexts, item.InsertText) @@ -173,3 +158,78 @@ 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)} + // to get the correct values file + fileURI := uri.File("../../testdata/example/templates/completion-test.yaml") + + result, err := completionTestCall(fileURI, buf, 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) + } + }) + } +} + +func completionTestCall(fileURI uri.URI, buf string, pos lsp.Position) (*lsp.CompletionList, error) { + documents := lsplocal.NewDocumentStore() + 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, + }, + }) + return result, err +} diff --git a/internal/handler/definition.go b/internal/handler/definition.go index a3d5f5e6..c62560d9 100644 --- a/internal/handler/definition.go +++ b/internal/handler/definition.go @@ -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 } diff --git a/internal/handler/generic_document_usecase.go b/internal/handler/generic_document_usecase.go index 841e521e..14e1ec61 100644 --- a/internal/handler/generic_document_usecase.go +++ b/internal/handler/generic_document_usecase.go @@ -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()) @@ -18,19 +20,29 @@ 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 ( + nodeType = node.Type() + parentNode = node.Parent() + parentNodeType string + ) + 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 } diff --git a/internal/handler/hover.go b/internal/handler/hover.go index 7df10878..7c60374a 100644 --- a/internal/handler/hover.go +++ b/internal/handler/hover.go @@ -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 @@ -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 } diff --git a/internal/handler/initialization.go b/internal/handler/initialization.go index 15ed6b32..40f3923d 100644 --- a/internal/handler/initialization.go +++ b/internal/handler/initialization.go @@ -27,7 +27,7 @@ func (h *langHandler) Initialize(ctx context.Context, params *lsp.InitializePara } logger.Debug("Initializing chartStore") - h.chartStore = charts.NewChartStore(workspaceURI, h.NewChartWithWatchedFiles) + h.chartStore = charts.NewChartStore(workspaceURI, h.NewChartWithInitActions) logger.Debug("Initializing done") return &lsp.InitializeResult{ @@ -83,3 +83,8 @@ func configureLogLevel(helmlsConfig util.HelmlsConfiguration) { logger.SetLevel(logrus.DebugLevel) } } + +func (h *langHandler) NewChartWithInitActions(rootURI uri.URI, valuesFilesConfig util.ValuesFilesConfig) *charts.Chart { + go h.LoadDocsOnNewChart(rootURI) + return h.NewChartWithWatchedFiles(rootURI, valuesFilesConfig) +} diff --git a/internal/handler/references.go b/internal/handler/references.go index d7be4e9d..f41b3a14 100644 --- a/internal/handler/references.go +++ b/internal/handler/references.go @@ -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) +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/handler/text_document.go b/internal/handler/text_document.go index 8f7724c7..d8c75a8e 100644 --- a/internal/handler/text_document.go +++ b/internal/handler/text_document.go @@ -3,9 +3,12 @@ package handler import ( "context" "errors" + "io/fs" + "path/filepath" lsplocal "github.com/mrjosh/helm-ls/internal/lsp" lsp "go.lsp.dev/protocol" + "go.lsp.dev/uri" ) func (h *langHandler) DidOpen(ctx context.Context, params *lsp.DidOpenTextDocumentParams) (err error) { @@ -94,3 +97,15 @@ func (h *langHandler) DidRenameFiles(ctx context.Context, params *lsp.RenameFile logger.Error("DidRenameFiles unimplemented") return nil } + +// TODO: maybe use the helm implementation of this once https://github.com/mrjosh/helm-ls/pull/77 is resolved +func (h *langHandler) LoadDocsOnNewChart(rootURI uri.URI) { + _ = filepath.WalkDir(filepath.Join(rootURI.Filename(), "templates"), + func(path string, d fs.DirEntry, err error) error { + if !d.IsDir() { + return h.documents.Store(uri.File(path), h.helmlsConfig) + } + return nil + }, + ) +} diff --git a/internal/language_features/function_call.go b/internal/language_features/function_call.go index a47cf650..80f651b4 100644 --- a/internal/language_features/function_call.go +++ b/internal/language_features/function_call.go @@ -20,7 +20,8 @@ func NewFunctionCallFeature(genericDocumentUseCase *GenericDocumentUseCase) *Fun } func (f *FunctionCallFeature) AppropriateForNode() bool { - return f.NodeType == gotemplate.NodeTypeIdentifier && f.ParentNodeType == gotemplate.NodeTypeFunctionCall + return f.NodeType == gotemplate.NodeTypeIdentifier && + f.ParentNodeType == gotemplate.NodeTypeFunctionCall } func (f *FunctionCallFeature) Hover() (string, error) { @@ -32,5 +33,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 } diff --git a/internal/language_features/generic_document_usecase.go b/internal/language_features/generic_document_usecase.go index 90b369c2..23afbbf9 100644 --- a/internal/language_features/generic_document_usecase.go +++ b/internal/language_features/generic_document_usecase.go @@ -20,13 +20,3 @@ type GenericDocumentUseCase struct { func (u *GenericDocumentUseCase) NodeContent() string { return u.Node.Content([]byte(u.Document.Content)) } - -func (u GenericDocumentUseCase) WithNode(node *sitter.Node) *GenericDocumentUseCase { - u.Node = node - u.NodeType = node.Type() - u.ParentNode = node.Parent() - if u.ParentNode != nil { - u.ParentNodeType = u.ParentNode.Type() - } - return &u -} diff --git a/internal/language_features/includes.go b/internal/language_features/includes.go index 84b0fbd9..656a59ad 100644 --- a/internal/language_features/includes.go +++ b/internal/language_features/includes.go @@ -4,8 +4,10 @@ import ( lsp "go.lsp.dev/protocol" 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" + sitter "github.com/smacker/go-tree-sitter" ) type IncludesFeature struct { @@ -18,14 +20,29 @@ type IncludesCallFeature struct { // should be called on {{ include "name" . }} func (f *IncludesCallFeature) AppropriateForNode() bool { - if f.ParentNodeType != gotemplate.NodeTypeArgumentList { + functionCallNode := f.getFunctionCallNode() + + if functionCallNode == nil { return false } - functionCallNode := f.Node.Parent().Parent() _, err := lsplocal.ParseIncludeFunctionCall(functionCallNode, []byte(f.GenericDocumentUseCase.Document.Content)) return err == nil } +func (f *IncludesCallFeature) getFunctionCallNode() *sitter.Node { + var functionCallNode *sitter.Node + if f.ParentNodeType == gotemplate.NodeTypeArgumentList { + functionCallNode = f.Node.Parent().Parent() + } + if f.ParentNodeType == gotemplate.NodeTypeInterpretedStringLiteral { + parentParent := f.ParentNode.Parent() + if parentParent != nil && parentParent.Type() == gotemplate.NodeTypeArgumentList { + functionCallNode = parentParent.Parent() + } + } + return functionCallNode +} + type IncludesDefinitionFeature struct { *IncludesFeature } @@ -57,7 +74,7 @@ func (f *IncludesCallFeature) References() (result []lsp.Location, err error) { } func (f *IncludesCallFeature) getIncludeName() (string, error) { - functionCallNode := f.Node.Parent().Parent() + functionCallNode := f.getFunctionCallNode() return lsplocal.ParseIncludeFunctionCall(functionCallNode, []byte(f.GenericDocumentUseCase.Document.Content)) } @@ -90,14 +107,14 @@ func (f *IncludesFeature) getDefinitionLocations(includeName string) []lsp.Locat return locations } -func (f *IncludesFeature) getDefinitionsHover(includeName string) util.HoverResultsWithFiles { - result := util.HoverResultsWithFiles{} +func (f *IncludesFeature) getDefinitionsHover(includeName string) protocol.HoverResultsWithFiles { + result := protocol.HoverResultsWithFiles{} for _, doc := range f.GenericDocumentUseCase.DocumentStore.GetAllDocs() { referenceRanges := doc.SymbolTable.GetIncludeDefinitions(includeName) for _, referenceRange := range referenceRanges { node := doc.Ast.RootNode().NamedDescendantForPointRange(referenceRange.StartPoint, referenceRange.EndPoint) if node != nil { - result = append(result, util.HoverResultWithFile{ + result = append(result, protocol.HoverResultWithFile{ Value: node.Content([]byte(doc.Content)), URI: doc.URI, }) @@ -125,3 +142,18 @@ func (f *IncludesCallFeature) Definition() (result []lsp.Location, err error) { } return f.getDefinitionLocations(includeName), nil } + +func (f *IncludesCallFeature) Completion() (*lsp.CompletionList, error) { + items := []lsp.CompletionItem{} + for _, doc := range f.GenericDocumentUseCase.DocumentStore.GetAllDocs() { + inlcudeDefinitionNames := doc.SymbolTable.GetAllIncludeDefinitionsNames() + for _, includeDefinitionName := range inlcudeDefinitionNames { + items = append(items, lsp.CompletionItem{ + InsertText: includeDefinitionName, + Kind: lsp.CompletionItemKindFunction, + Label: includeDefinitionName, + }) + } + } + return &lsp.CompletionList{IsIncomplete: false, Items: items}, nil +} diff --git a/internal/language_features/template_context.go b/internal/language_features/template_context.go index 26cfb80c..8dcf15af 100644 --- a/internal/language_features/template_context.go +++ b/internal/language_features/template_context.go @@ -26,9 +26,6 @@ func NewTemplateContextFeature(genericDocumentUseCase *GenericDocumentUseCase) * } func (f *TemplateContextFeature) AppropriateForNode() bool { - nodeContent := f.NodeContent() - println(nodeContent) - if f.NodeType == gotemplate.NodeTypeDot || f.NodeType == gotemplate.NodeTypeDotSymbol { return true } @@ -104,13 +101,13 @@ func (f *TemplateContextFeature) Hover() (string, error) { func (f *TemplateContextFeature) valuesHover(templateContext lsplocal.TemplateContext) (string, error) { var ( valuesFiles = f.Chart.ResolveValueFiles(templateContext, f.ChartStore) - hoverResults = util.HoverResultsWithFiles{} + hoverResults = protocol.HoverResultsWithFiles{} ) for _, valuesFiles := range valuesFiles { for _, valuesFile := range valuesFiles.ValuesFiles.AllValuesFiles() { result, err := util.GetTableOrValueForSelector(valuesFile.Values, strings.Join(valuesFiles.Selector, ".")) if err == nil { - hoverResults = append(hoverResults, util.HoverResultWithFile{URI: valuesFile.URI, Value: result}) + hoverResults = append(hoverResults, protocol.HoverResultWithFile{URI: valuesFile.URI, Value: result}) } } } @@ -122,57 +119,3 @@ func (f *TemplateContextFeature) getMetadataField(v *chart.Metadata, fieldName s field := reflect.Indirect(r).FieldByName(fieldName) return util.FormatToYAML(field, fieldName) } - -func (f *TemplateContextFeature) Completion() (result *lsp.CompletionList, err error) { - templateContext, err := f.getTemplateContext() - if err != nil { - return nil, err - } - - if len(templateContext) == 0 { - result := helmdocs.BuiltInObjects - return protocol.NewCompletionResults(result).ToLSP(), nil - } - - if len(templateContext) == 1 { - result, ok := helmdocs.BuiltInOjectVals[templateContext[0]] - if !ok { - result := helmdocs.BuiltInObjects - return protocol.NewCompletionResults(result).ToLSP(), nil - } - return protocol.NewCompletionResults(result).ToLSP(), nil - } - - switch templateContext[0] { - case "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 { - result := helmdocs.BuiltInObjects - return protocol.NewCompletionResults(result).ToLSP(), nil - } - return protocol.NewCompletionResults(result).ToLSP(), nil - - } - - return nil, nil -} - -func (f *TemplateContextFeature) valuesCompletion(templateContext lsplocal.TemplateContext) (*lsp.CompletionList, error) { - m := make(map[string]lsp.CompletionItem) - for _, queriedValuesFiles := range f.Chart.ResolveValueFiles(templateContext.Tail(), f.ChartStore) { - for _, valuesFile := range queriedValuesFiles.ValuesFiles.AllValuesFiles() { - for _, item := range util.GetValueCompletion(valuesFile.Values, queriedValuesFiles.Selector) { - m[item.InsertText] = item - } - } - } - completions := []lsp.CompletionItem{} - for _, item := range m { - completions = append(completions, item) - } - - return &lsp.CompletionList{Items: completions, IsIncomplete: false}, nil -} diff --git a/internal/language_features/template_context_completion.go b/internal/language_features/template_context_completion.go new file mode 100644 index 00000000..815539d1 --- /dev/null +++ b/internal/language_features/template_context_completion.go @@ -0,0 +1,73 @@ +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" + "github.com/mrjosh/helm-ls/internal/util" + lsp "go.lsp.dev/protocol" +) + +func (f *TemplateContextFeature) Completion() (*lsp.CompletionList, error) { + templateContext, err := f.getTemplateContext() + if err != nil { + return nil, err + } + + if len(templateContext) == 0 { + return protocol.CompletionResults{}.WithDocs(helmdocs.BuiltInObjects, lsp.CompletionItemKindConstant).ToList(), nil + } + + if len(templateContext) == 1 { + result, ok := helmdocs.BuiltInOjectVals[templateContext[0]] + if !ok { + return protocol.CompletionResults{}.WithDocs(helmdocs.BuiltInObjects, lsp.CompletionItemKindConstant).ToList(), nil + } + return protocol.CompletionResults{}.WithDocs(result, lsp.CompletionItemKindValue).ToList(), nil + } + if templateContext[0] == "Values" { + return f.valuesCompletion(templateContext) + } + nestedDocs, ok := helmdocs.BuiltInOjectVals[templateContext[0]] + if ok { + if len(templateContext) < 3 { + return protocol.CompletionResults{}.WithDocs(nestedDocs, 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) +} + +// 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) { + m := make(map[string]lsp.CompletionItem) + for _, queriedValuesFiles := range f.Chart.ResolveValueFiles(templateContext.Tail(), f.ChartStore) { + for _, valuesFile := range queriedValuesFiles.ValuesFiles.AllValuesFiles() { + for _, item := range util.GetValueCompletion(valuesFile.Values, queriedValuesFiles.Selector) { + m[item.InsertText] = item + } + } + } + completions := []lsp.CompletionItem{} + for _, item := range m { + completions = append(completions, item) + } + + return &lsp.CompletionList{Items: completions, IsIncomplete: false}, nil +} diff --git a/internal/language_features/text.go b/internal/language_features/text.go new file mode 100644 index 00000000..8b603543 --- /dev/null +++ b/internal/language_features/text.go @@ -0,0 +1,54 @@ +package languagefeatures + +import ( + "context" + + "github.com/mrjosh/helm-ls/internal/adapter/yamlls" + "github.com/mrjosh/helm-ls/internal/documentation/godocs" + "github.com/mrjosh/helm-ls/internal/protocol" + "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" + lsp "go.lsp.dev/protocol" +) + +type TextFeature struct { + *GenericDocumentUseCase + textDocumentPosition *lsp.TextDocumentPositionParams + ctx context.Context + yamllsConnector *yamlls.Connector +} + +func NewTextFeature( + ctx context.Context, + genericDocumentUseCase *GenericDocumentUseCase, + yamllsConnector *yamlls.Connector, + textDocumentPosition *lsp.TextDocumentPositionParams, +) *TextFeature { + return &TextFeature{ + GenericDocumentUseCase: genericDocumentUseCase, + textDocumentPosition: textDocumentPosition, + ctx: ctx, + yamllsConnector: yamllsConnector, + } +} + +func (f *TextFeature) AppropriateForNode() bool { + return f.NodeType == gotemplate.NodeTypeText || f.NodeType == gotemplate.NodeTypeTemplate +} + +func (f *TextFeature) Completion() (result *lsp.CompletionList, err error) { + comletions := f.yamllsCompletions(&lsp.CompletionParams{ + TextDocumentPositionParams: *f.textDocumentPosition, + }) + + return protocol.CompletionResults{Items: comletions}.WithSnippets(godocs.TextSnippets).ToList(), nil +} + +func (f *TextFeature) yamllsCompletions(params *lsp.CompletionParams) []lsp.CompletionItem { + response, err := f.yamllsConnector.CallCompletion(f.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 +} diff --git a/internal/language_features/usecases.go b/internal/language_features/usecases.go index 0fa4fa16..f1449bfa 100644 --- a/internal/language_features/usecases.go +++ b/internal/language_features/usecases.go @@ -1,9 +1,12 @@ package languagefeatures import ( + "github.com/mrjosh/helm-ls/internal/log" lsp "go.lsp.dev/protocol" ) +var logger = log.GetLogger() + // interface for use cases type UseCase interface { AppropriateForNode() bool diff --git a/internal/lsp/ast.go b/internal/lsp/ast.go index 00a134b1..c90ccea7 100644 --- a/internal/lsp/ast.go +++ b/internal/lsp/ast.go @@ -20,6 +20,15 @@ func NodeAtPosition(tree *sitter.Tree, position lsp.Position) *sitter.Node { return tree.RootNode().NamedDescendantForPointRange(start, start) } +func NestedNodeAtPositionForCompletion(tree *sitter.Tree, position lsp.Position) *sitter.Node { + currentNode := NodeAtPosition(tree, position) + pointToLoopUp := sitter.Point{ + Row: position.Line, + Column: position.Character, + } + return FindRelevantChildNodeCompletion(currentNode, pointToLoopUp) +} + func FindDirectChildNodeByStart(currentNode *sitter.Node, pointToLookUp sitter.Point) *sitter.Node { for i := 0; i < int(currentNode.ChildCount()); i++ { child := currentNode.Child(i) diff --git a/internal/lsp/document.go b/internal/lsp/document.go index 5a5f2395..99b8172c 100644 --- a/internal/lsp/document.go +++ b/internal/lsp/document.go @@ -2,65 +2,13 @@ package lsp import ( "bytes" - "fmt" "strings" - "sync" "github.com/mrjosh/helm-ls/internal/util" sitter "github.com/smacker/go-tree-sitter" lsp "go.lsp.dev/protocol" - "go.lsp.dev/uri" ) -// documentStore holds opened documents. -type DocumentStore struct { - documents sync.Map -} - -func NewDocumentStore() *DocumentStore { - return &DocumentStore{ - documents: sync.Map{}, - } -} - -func (s *DocumentStore) GetAllDocs() []*Document { - var docs []*Document - s.documents.Range(func(_, v interface{}) bool { - docs = append(docs, v.(*Document)) - return true - }) - return docs -} - -func (s *DocumentStore) DidOpen(params *lsp.DidOpenTextDocumentParams, helmlsConfig util.HelmlsConfiguration) (*Document, error) { - logger.Debug(fmt.Sprintf("Opening document %s with langID %s", params.TextDocument.URI, params.TextDocument.LanguageID)) - - uri := params.TextDocument.URI - path := uri.Filename() - ast := ParseAst(nil, params.TextDocument.Text) - doc := &Document{ - URI: uri, - Path: path, - Content: params.TextDocument.Text, - Ast: ast, - DiagnosticsCache: NewDiagnosticsCache(helmlsConfig), - IsOpen: true, - SymbolTable: NewSymbolTable(ast, []byte(params.TextDocument.Text)), - } - logger.Debug("Storing doc ", path) - s.documents.Store(path, doc) - return doc, nil -} - -func (s *DocumentStore) Get(docuri uri.URI) (*Document, bool) { - path := docuri.Filename() - d, ok := s.documents.Load(path) - if !ok { - return nil, false - } - return d.(*Document), ok -} - // Document represents an opened file. type Document struct { URI lsp.DocumentURI diff --git a/internal/lsp/document_store.go b/internal/lsp/document_store.go new file mode 100644 index 00000000..d7c8428d --- /dev/null +++ b/internal/lsp/document_store.go @@ -0,0 +1,82 @@ +package lsp + +import ( + "fmt" + "os" + "sync" + + "github.com/mrjosh/helm-ls/internal/util" + lsp "go.lsp.dev/protocol" + "go.lsp.dev/uri" +) + +// documentStore holds opened documents. +type DocumentStore struct { + documents sync.Map +} + +func NewDocumentStore() *DocumentStore { + return &DocumentStore{ + documents: sync.Map{}, + } +} + +func (s *DocumentStore) GetAllDocs() []*Document { + var docs []*Document + s.documents.Range(func(_, v interface{}) bool { + docs = append(docs, v.(*Document)) + return true + }) + return docs +} + +func (s *DocumentStore) DidOpen(params *lsp.DidOpenTextDocumentParams, helmlsConfig util.HelmlsConfiguration) (*Document, error) { + logger.Debug(fmt.Sprintf("Opening document %s with langID %s", params.TextDocument.URI, params.TextDocument.LanguageID)) + + uri := params.TextDocument.URI + path := uri.Filename() + ast := ParseAst(nil, params.TextDocument.Text) + doc := &Document{ + URI: uri, + Path: path, + Content: params.TextDocument.Text, + Ast: ast, + DiagnosticsCache: NewDiagnosticsCache(helmlsConfig), + IsOpen: true, + SymbolTable: NewSymbolTable(ast, []byte(params.TextDocument.Text)), + } + logger.Debug("Storing doc ", path) + s.documents.Store(path, doc) + return doc, nil +} + +func (s *DocumentStore) Store(uri uri.URI, helmlsConfig util.HelmlsConfiguration) error { + content, err := os.ReadFile(uri.Filename()) + if err != nil { + logger.Error("Could not open file ", uri.Filename(), " ", err) + return err + } + + ast := ParseAst(nil, string(content)) + s.documents.Store(uri.Filename(), + &Document{ + URI: uri, + Path: uri.Filename(), + Content: string(content), + Ast: ast, + DiagnosticsCache: NewDiagnosticsCache(helmlsConfig), + IsOpen: false, + SymbolTable: NewSymbolTable(ast, content), + }, + ) + return nil +} + +func (s *DocumentStore) Get(docuri uri.URI) (*Document, bool) { + path := docuri.Filename() + d, ok := s.documents.Load(path) + if !ok { + return nil, false + } + return d.(*Document), ok +} diff --git a/internal/lsp/symbol_table.go b/internal/lsp/symbol_table.go index 2f3d6719..8588c57e 100644 --- a/internal/lsp/symbol_table.go +++ b/internal/lsp/symbol_table.go @@ -17,6 +17,15 @@ func (t TemplateContext) Tail() TemplateContext { return t[1:] } +func (t TemplateContext) IsVariable() bool { + return len(t) > 0 && t[0] == "$" +} + +func (t TemplateContext) AppendSuffix(suffix string) TemplateContext { + t[len(t)-1] = t[len(t)-1] + suffix + return t +} + type SymbolTable struct { contexts map[string][]sitter.Range contextsReversed map[sitter.Range]TemplateContext @@ -26,10 +35,10 @@ type SymbolTable struct { func NewSymbolTable(ast *sitter.Tree, content []byte) *SymbolTable { s := &SymbolTable{ - contexts: make(map[string][]sitter.Range), - contextsReversed: make(map[sitter.Range]TemplateContext), - includeDefinitions: make(map[string][]sitter.Range), - includeUseages: make(map[string][]sitter.Range), + contexts: map[string][]sitter.Range{}, + contextsReversed: map[sitter.Range]TemplateContext{}, + includeDefinitions: map[string][]sitter.Range{}, + includeUseages: map[string][]sitter.Range{}, } s.parseTree(ast, content) return s @@ -66,6 +75,13 @@ func (s *SymbolTable) GetIncludeDefinitions(symbol string) []sitter.Range { return s.includeDefinitions[symbol] } +func (s *SymbolTable) GetAllIncludeDefinitionsNames() (result []string) { + for k := range s.includeDefinitions { + result = append(result, k) + } + return result +} + func (s *SymbolTable) GetIncludeReference(symbol string) []sitter.Range { result := s.includeUseages[symbol] definitions := s.includeDefinitions[symbol] diff --git a/internal/lsp/symbol_table_includes.go b/internal/lsp/symbol_table_includes.go index 9e35c76e..b1d2d7de 100644 --- a/internal/lsp/symbol_table_includes.go +++ b/internal/lsp/symbol_table_includes.go @@ -59,6 +59,6 @@ func ParseIncludeFunctionCall(node *sitter.Node, content []byte) (string, error) return util.RemoveQuotes(firstArgument.Content(content)), nil } -func (v *IncludeDefinitionsVisitor) Exit(node *sitter.Node) {} -func (v *IncludeDefinitionsVisitor) EnterContextShift(node *sitter.Node, suffix string) {} -func (v *IncludeDefinitionsVisitor) ExitContextShift(node *sitter.Node) {} +func (v *IncludeDefinitionsVisitor) Exit(_ *sitter.Node) {} +func (v *IncludeDefinitionsVisitor) EnterContextShift(_ *sitter.Node, _ string) {} +func (v *IncludeDefinitionsVisitor) ExitContextShift(_ *sitter.Node) {} diff --git a/internal/lsp/symbol_table_values.go b/internal/lsp/symbol_table_template_context.go similarity index 82% rename from internal/lsp/symbol_table_values.go rename to internal/lsp/symbol_table_template_context.go index 4b81e7dd..b77cdcbf 100644 --- a/internal/lsp/symbol_table_values.go +++ b/internal/lsp/symbol_table_template_context.go @@ -48,7 +48,8 @@ func (v *TemplateContextVisitor) RestoreStashedContext() { } func (v *TemplateContextVisitor) Enter(node *sitter.Node) { - switch node.Type() { + nodeType := node.Type() + switch nodeType { case gotemplate.NodeTypeDot: v.symbolTable.AddTemplateContext(v.currentContext, GetRangeForNode(node)) case gotemplate.NodeTypeFieldIdentifier: @@ -57,6 +58,13 @@ func (v *TemplateContextVisitor) Enter(node *sitter.Node) { case gotemplate.NodeTypeField: content := node.ChildByFieldName("name").Content(v.content) v.symbolTable.AddTemplateContext(append(v.currentContext, content), GetRangeForNode(node.ChildByFieldName("name"))) + case gotemplate.NodeTypeUnfinishedSelectorExpression: + operandNode := node.ChildByFieldName("operand") + if operandNode.Type() == gotemplate.NodeTypeVariable && operandNode.Content(v.content) == "$" { + v.StashContext() + } + v.symbolTable.AddTemplateContext(append(getContextForSelectorExpression(operandNode, v.content), ""), + GetRangeForNode(node.Child(int(node.ChildCount())-1))) case gotemplate.NodeTypeSelectorExpression: operandNode := node.ChildByFieldName("operand") if operandNode.Type() == gotemplate.NodeTypeVariable && operandNode.Content(v.content) == "$" { @@ -67,7 +75,7 @@ func (v *TemplateContextVisitor) Enter(node *sitter.Node) { func (v *TemplateContextVisitor) Exit(node *sitter.Node) { switch node.Type() { - case gotemplate.NodeTypeSelectorExpression: + case gotemplate.NodeTypeSelectorExpression, gotemplate.NodeTypeUnfinishedSelectorExpression: operandNode := node.ChildByFieldName("operand") if operandNode.Type() == gotemplate.NodeTypeVariable && operandNode.Content(v.content) == "$" { v.RestoreStashedContext() @@ -83,13 +91,13 @@ func (v *TemplateContextVisitor) EnterContextShift(node *sitter.Node, suffix str case gotemplate.NodeTypeField: content := node.ChildByFieldName("name").Content(v.content) + suffix v.PushContext(content) - case gotemplate.NodeTypeSelectorExpression: + case gotemplate.NodeTypeSelectorExpression, gotemplate.NodeTypeUnfinishedSelectorExpression: s := getContextForSelectorExpression(node, v.content) if len(s) > 0 { - s[len(s)-1] = s[len(s)-1] + suffix - if s[0] == "$" { + s = s.AppendSuffix(suffix) + if s.IsVariable() { v.StashContext() - s = s[1:] + s = s.Tail() } } v.PushContextMany(s) @@ -100,9 +108,9 @@ func (v *TemplateContextVisitor) ExitContextShift(node *sitter.Node) { switch node.Type() { case gotemplate.NodeTypeField, gotemplate.NodeTypeFieldIdentifier: v.PopContext() - case gotemplate.NodeTypeSelectorExpression: + case gotemplate.NodeTypeSelectorExpression, gotemplate.NodeTypeUnfinishedSelectorExpression: s := getContextForSelectorExpression(node, v.content) - if len(s) > 0 && s[0] == "$" { + if s.IsVariable() { v.RestoreStashedContext() } else { v.PopContextN(len(s)) diff --git a/internal/lsp/symbol_table_test.go b/internal/lsp/symbol_table_test.go index 62ab6fe4..530bf7e1 100644 --- a/internal/lsp/symbol_table_test.go +++ b/internal/lsp/symbol_table_test.go @@ -62,6 +62,10 @@ func TestSymbolTableForValues(t *testing.T) { {{ .Test }} {{ . }} + +{{ if (and .Values. ) }} + +{{ end }} ` ast := ParseAst(nil, content) @@ -210,3 +214,41 @@ func TestSymbolTableForValuesTestFile(t *testing.T) { assert.Contains(t, points, v.startPoint) } } + +func TestSymbolTableForValuesSingleTests(t *testing.T) { + type testCase struct { + template string + path []string + startPoint sitter.Point + } + + testCases := []testCase{ + { + template: `{{ if (and .Values. ) }} {{ end }} `, + path: []string{"Values"}, + startPoint: sitter.Point{ + Row: 0, + Column: 12, + }, + }, + { + template: `{{ if (and .Values. ) }} {{ end }} `, + path: []string{"Values", ""}, + startPoint: sitter.Point{ + Row: 0, + Column: 18, + }, + }, + } + + for _, v := range testCases { + ast := ParseAst(nil, v.template) + symbolTable := NewSymbolTable(ast, []byte(v.template)) + values := symbolTable.GetTemplateContextRanges(v.path) + points := []sitter.Point{} + for _, v := range values { + points = append(points, v.StartPoint) + } + assert.Contains(t, points, v.startPoint) + } +} diff --git a/internal/lsp/visitor.go b/internal/lsp/visitor.go index 4d652498..7c96e639 100644 --- a/internal/lsp/visitor.go +++ b/internal/lsp/visitor.go @@ -56,7 +56,7 @@ func (v *Visitors) visitNodesRecursiveWithScopeShift(node *sitter.Node) { for _, visitor := range v.visitors { visitor.ExitContextShift(rangeNode) } - case gotemplate.NodeTypeSelectorExpression: + case gotemplate.NodeTypeSelectorExpression, gotemplate.NodeTypeUnfinishedSelectorExpression: operand := node.ChildByFieldName("operand") v.visitNodesRecursiveWithScopeShift(operand) for _, visitor := range v.visitors { diff --git a/internal/protocol/completion.go b/internal/protocol/completion.go index 3e1efe5f..5ff66033 100644 --- a/internal/protocol/completion.go +++ b/internal/protocol/completion.go @@ -1,42 +1,57 @@ package protocol import ( + "github.com/mrjosh/helm-ls/internal/documentation/godocs" helmdocs "github.com/mrjosh/helm-ls/internal/documentation/helm" lsp "go.lsp.dev/protocol" ) -type CompletionResults []CompletionResult +type CompletionResults struct { + Items []lsp.CompletionItem +} -func NewCompletionResults(docs []helmdocs.HelmDocumentation) *CompletionResults { - result := CompletionResults{} +func (c CompletionResults) ToList() (result *lsp.CompletionList) { + return &lsp.CompletionList{Items: c.Items, IsIncomplete: false} +} +func (c CompletionResults) WithDocs(docs []helmdocs.HelmDocumentation, kind lsp.CompletionItemKind) CompletionResults { + items := c.Items for _, doc := range docs { - result = append(result, CompletionResult{doc}) + items = append(items, + lsp.CompletionItem{ + Label: doc.Name, + Detail: doc.Detail, + InsertText: doc.Name, + InsertTextFormat: lsp.InsertTextFormatPlainText, + Kind: kind, + }, + ) } - - return &result + return CompletionResults{Items: items} } -type CompletionResult struct { - Documentation helmdocs.HelmDocumentation +func (c CompletionResults) WithSnippets(snippets []godocs.GoTemplateSnippet) CompletionResults { + items := c.Items + for _, snippet := range snippets { + items = append(items, textCompletionItem(snippet)) + } + return CompletionResults{Items: items} } -func (c *CompletionResult) ToLSP() (result lsp.CompletionItem) { +func textCompletionItem(gotemplateSnippet godocs.GoTemplateSnippet) lsp.CompletionItem { return lsp.CompletionItem{ - Label: c.Documentation.Name, - Detail: c.Documentation.Detail, - InsertText: c.Documentation.Name, + Label: gotemplateSnippet.Name, + InsertText: gotemplateSnippet.Snippet, + Detail: gotemplateSnippet.Detail, + Documentation: gotemplateSnippet.Doc, + Kind: lsp.CompletionItemKindText, InsertTextFormat: lsp.InsertTextFormatSnippet, - Kind: lsp.CompletionItemKindConstant, // TODO: make this more variable } } -func (c *CompletionResults) ToLSP() (result *lsp.CompletionList) { - items := []lsp.CompletionItem{} - - for _, completion := range *c { - items = append(items, completion.ToLSP()) +func GetTextCompletionItems(gotemplateSnippet []godocs.GoTemplateSnippet) (result []lsp.CompletionItem) { + for _, item := range gotemplateSnippet { + result = append(result, textCompletionItem(item)) } - - return &lsp.CompletionList{Items: items} + return result } diff --git a/internal/util/lsp.go b/internal/protocol/hover.go similarity index 88% rename from internal/util/lsp.go rename to internal/protocol/hover.go index c77636fa..a075fdab 100644 --- a/internal/util/lsp.go +++ b/internal/protocol/hover.go @@ -1,4 +1,4 @@ -package util +package protocol import ( "fmt" @@ -36,10 +36,6 @@ func (h HoverResultsWithFiles) Format(rootURI uri.URI) string { return formatted } -func (h *HoverResultsWithFiles) Add(hoverResult HoverResultWithFile) { - *h = append(*h, hoverResult) -} - func BuildHoverResponse(value string, wordRange lsp.Range) *lsp.Hover { content := lsp.MarkupContent{ Kind: lsp.Markdown, diff --git a/internal/tree-sitter/gotemplate/README.md b/internal/tree-sitter/gotemplate/README.md index 45f71268..b306f859 100644 --- a/internal/tree-sitter/gotemplate/README.md +++ b/internal/tree-sitter/gotemplate/README.md @@ -4,4 +4,4 @@ Taken from https://github.com/ngalaiko/tree-sitter-go-template, or currently thi ## Binding -Using this libary: https://github.com/smacker/go-tree-sitter +Using this library: https://github.com/smacker/go-tree-sitter diff --git a/internal/tree-sitter/gotemplate/node-types.go b/internal/tree-sitter/gotemplate/node-types.go index 085a8413..281f10e1 100644 --- a/internal/tree-sitter/gotemplate/node-types.go +++ b/internal/tree-sitter/gotemplate/node-types.go @@ -1,42 +1,43 @@ package gotemplate const ( - NodeTypeAssignment = "assignment" - NodeTypeArgumentList = "argument_list" - NodeTypeBlock = "block" - NodeTypeBlockAction = "block_action" - NodeTypeChainedPipeline = "chained_pipeline" - NodeTypeCloseBraces = "}}" - NodeTypeCloseBracesDash = "-}}" - NodeTypeComment = "comment" - NodeTypeDefine = "define" - NodeTypeDefineAction = "define_action" - NodeTypeDollar = "$" - NodeTypeDot = "dot" - NodeTypeDotSymbol = "." - NodeTypeElse = "else" - NodeTypeElseIf = "else if" - NodeTypeEnd = "end" - NodeTypeError = "ERROR" - NodeTypeField = "field" - NodeTypeFieldIdentifier = "field_identifier" - NodeTypeFunctionCall = "function_call" - NodeTypeIdentifier = "identifier" - NodeTypeIf = "if" - NodeTypeIfAction = "if_action" - NodeTypeInterpretedStringLiteral = "interpreted_string_literal" - NodeTypeOpenBraces = "{{" - NodeTypeOpenBracesDash = "{{-" - NodeTypeRange = "range" - NodeTypeRangeAction = "range_action" - NodeTypeRangeVariableDefinition = "range_variable_definition" - NodeTypeSelectorExpression = "selector_expression" - NodeTypeTemplate = "template" - NodeTypeText = "text" - NodeTypeVariable = "variable" - NodeTypeVariableDefinition = "variable_definition" - NodeTypeWith = "with" - NodeTypeWithAction = "with_action" + NodeTypeAssignment = "assignment" + NodeTypeArgumentList = "argument_list" + NodeTypeBlock = "block" + NodeTypeBlockAction = "block_action" + NodeTypeChainedPipeline = "chained_pipeline" + NodeTypeCloseBraces = "}}" + NodeTypeCloseBracesDash = "-}}" + NodeTypeComment = "comment" + NodeTypeDefine = "define" + NodeTypeDefineAction = "define_action" + NodeTypeDollar = "$" + NodeTypeDot = "dot" + NodeTypeDotSymbol = "." + NodeTypeElse = "else" + NodeTypeElseIf = "else if" + NodeTypeEnd = "end" + NodeTypeError = "ERROR" + NodeTypeField = "field" + NodeTypeFieldIdentifier = "field_identifier" + NodeTypeFunctionCall = "function_call" + NodeTypeIdentifier = "identifier" + NodeTypeIf = "if" + NodeTypeIfAction = "if_action" + NodeTypeInterpretedStringLiteral = "interpreted_string_literal" + NodeTypeOpenBraces = "{{" + NodeTypeOpenBracesDash = "{{-" + NodeTypeRange = "range" + NodeTypeRangeAction = "range_action" + NodeTypeRangeVariableDefinition = "range_variable_definition" + NodeTypeSelectorExpression = "selector_expression" + NodeTypeUnfinishedSelectorExpression = "unfinished_selector_expression" + NodeTypeTemplate = "template" + NodeTypeText = "text" + NodeTypeVariable = "variable" + NodeTypeVariableDefinition = "variable_definition" + NodeTypeWith = "with" + NodeTypeWithAction = "with_action" FieldNameAlternative = "alternative" FieldNameCondition = "condition" diff --git a/internal/tree-sitter/gotemplate/parser.c b/internal/tree-sitter/gotemplate/parser.c index ed4c264d..dce287c1 100644 --- a/internal/tree-sitter/gotemplate/parser.c +++ b/internal/tree-sitter/gotemplate/parser.c @@ -1,7 +1,6 @@ -#include +#include "tree_sitter/parser.h" #if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmissing-field-initializers" #endif @@ -16,7 +15,7 @@ #define MAX_ALIAS_SEQUENCE_LENGTH 12 #define PRODUCTION_ID_COUNT 35 -enum { +enum ts_symbol_identifiers { aux_sym_text_token1 = 1, aux_sym_text_token2 = 2, aux_sym_text_token3 = 3, @@ -37,7 +36,7 @@ enum { anon_sym_PIPE = 18, anon_sym_LPAREN = 19, anon_sym_RPAREN = 20, - anon_sym_ = 21, + anon_sym_SPACE = 21, sym_pipeline_stub = 22, anon_sym_DOT = 23, anon_sym_DOT2 = 24, @@ -85,7 +84,7 @@ enum { sym_argument_list = 66, sym__expression = 67, sym_selector_expression = 68, - sym_unfished_selector_expression = 69, + sym_unfinished_selector_expression = 69, sym__field_identifier = 70, sym_field = 71, sym_variable = 72, @@ -125,7 +124,7 @@ static const char * const ts_symbol_names[] = { [anon_sym_PIPE] = "|", [anon_sym_LPAREN] = "(", [anon_sym_RPAREN] = ")", - [anon_sym_] = " ", + [anon_sym_SPACE] = " ", [sym_pipeline_stub] = "pipeline_stub", [anon_sym_DOT] = ".", [anon_sym_DOT2] = ".", @@ -173,7 +172,7 @@ static const char * const ts_symbol_names[] = { [sym_argument_list] = "argument_list", [sym__expression] = "_expression", [sym_selector_expression] = "selector_expression", - [sym_unfished_selector_expression] = "unfished_selector_expression", + [sym_unfinished_selector_expression] = "unfinished_selector_expression", [sym__field_identifier] = "_field_identifier", [sym_field] = "field", [sym_variable] = "variable", @@ -213,7 +212,7 @@ static const TSSymbol ts_symbol_map[] = { [anon_sym_PIPE] = anon_sym_PIPE, [anon_sym_LPAREN] = anon_sym_LPAREN, [anon_sym_RPAREN] = anon_sym_RPAREN, - [anon_sym_] = anon_sym_, + [anon_sym_SPACE] = anon_sym_SPACE, [sym_pipeline_stub] = sym_pipeline_stub, [anon_sym_DOT] = anon_sym_DOT, [anon_sym_DOT2] = anon_sym_DOT, @@ -261,7 +260,7 @@ static const TSSymbol ts_symbol_map[] = { [sym_argument_list] = sym_argument_list, [sym__expression] = sym__expression, [sym_selector_expression] = sym_selector_expression, - [sym_unfished_selector_expression] = sym_unfished_selector_expression, + [sym_unfinished_selector_expression] = sym_unfinished_selector_expression, [sym__field_identifier] = sym__field_identifier, [sym_field] = sym_field, [sym_variable] = sym_variable, @@ -364,7 +363,7 @@ static const TSSymbolMetadata ts_symbol_metadata[] = { .visible = true, .named = false, }, - [anon_sym_] = { + [anon_sym_SPACE] = { .visible = true, .named = false, }, @@ -556,7 +555,7 @@ static const TSSymbolMetadata ts_symbol_metadata[] = { .visible = true, .named = true, }, - [sym_unfished_selector_expression] = { + [sym_unfinished_selector_expression] = { .visible = true, .named = true, }, @@ -622,7 +621,7 @@ static const TSSymbolMetadata ts_symbol_metadata[] = { }, }; -enum { +enum ts_field_identifiers { field_alternative = 1, field_argument = 2, field_arguments = 3, @@ -14613,9 +14612,7 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead == '{') ADVANCE(33); if (lookahead == '|') ADVANCE(107); if (lookahead == '}') ADVANCE(34); - if (lookahead == '\t' || - lookahead == '\n' || - lookahead == '\r' || + if (('\t' <= lookahead && lookahead <= '\r') || lookahead == ' ') SKIP(76) if (('1' <= lookahead && lookahead <= '9')) ADVANCE(167); END_STATE(); @@ -14623,8 +14620,7 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead == '\n') SKIP(12) if (lookahead == '"') ADVANCE(180); if (lookahead == '\\') ADVANCE(24); - if (lookahead == '\t' || - lookahead == '\r' || + if (('\t' <= lookahead && lookahead <= '\r') || lookahead == ' ') ADVANCE(181); if (lookahead != 0) ADVANCE(182); END_STATE(); @@ -14645,9 +14641,7 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead == 't') ADVANCE(154); if (lookahead == '|') ADVANCE(107); if (lookahead == '}') ADVANCE(34); - if (lookahead == '\t' || - lookahead == '\n' || - lookahead == '\r') SKIP(3) + if (('\t' <= lookahead && lookahead <= '\r')) SKIP(3) if (('1' <= lookahead && lookahead <= '9')) ADVANCE(167); END_STATE(); case 3: @@ -14667,9 +14661,7 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead == 't') ADVANCE(154); if (lookahead == '|') ADVANCE(107); if (lookahead == '}') ADVANCE(34); - if (lookahead == '\t' || - lookahead == '\n' || - lookahead == '\r') SKIP(3) + if (('\t' <= lookahead && lookahead <= '\r')) SKIP(3) if (('1' <= lookahead && lookahead <= '9')) ADVANCE(167); END_STATE(); case 4: @@ -14682,9 +14674,7 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (sym_identifier_character_set_3(lookahead)) ADVANCE(164); if (lookahead == '|') ADVANCE(107); if (lookahead == '}') ADVANCE(34); - if (lookahead == '\t' || - lookahead == '\n' || - lookahead == '\r') SKIP(5) + if (('\t' <= lookahead && lookahead <= '\r')) SKIP(5) END_STATE(); case 5: if (lookahead == ' ') ADVANCE(110); @@ -14695,9 +14685,7 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (sym_identifier_character_set_3(lookahead)) ADVANCE(164); if (lookahead == '|') ADVANCE(107); if (lookahead == '}') ADVANCE(34); - if (lookahead == '\t' || - lookahead == '\n' || - lookahead == '\r') SKIP(5) + if (('\t' <= lookahead && lookahead <= '\r')) SKIP(5) END_STATE(); case 6: if (lookahead == '"') ADVANCE(180); @@ -14715,9 +14703,7 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead == 't') ADVANCE(154); if (lookahead == '|') ADVANCE(107); if (lookahead == '}') ADVANCE(34); - if (lookahead == '\t' || - lookahead == '\n' || - lookahead == '\r' || + if (('\t' <= lookahead && lookahead <= '\r') || lookahead == ' ') SKIP(7) if (('1' <= lookahead && lookahead <= '9')) ADVANCE(167); END_STATE(); @@ -14737,9 +14723,7 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead == 't') ADVANCE(154); if (lookahead == '|') ADVANCE(107); if (lookahead == '}') ADVANCE(34); - if (lookahead == '\t' || - lookahead == '\n' || - lookahead == '\r' || + if (('\t' <= lookahead && lookahead <= '\r') || lookahead == ' ') SKIP(7) if (('1' <= lookahead && lookahead <= '9')) ADVANCE(167); END_STATE(); @@ -14762,9 +14746,7 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead == 'r') ADVANCE(161); if (lookahead == 't') ADVANCE(116); if (lookahead == 'w') ADVANCE(136); - if (lookahead == '\t' || - lookahead == '\n' || - lookahead == '\r' || + if (('\t' <= lookahead && lookahead <= '\r') || lookahead == ' ') SKIP(8) if (('1' <= lookahead && lookahead <= '9')) ADVANCE(167); END_STATE(); @@ -14787,9 +14769,7 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead == 'r') ADVANCE(161); if (lookahead == 't') ADVANCE(116); if (lookahead == 'w') ADVANCE(136); - if (lookahead == '\t' || - lookahead == '\n' || - lookahead == '\r' || + if (('\t' <= lookahead && lookahead <= '\r') || lookahead == ' ') SKIP(9) if (('1' <= lookahead && lookahead <= '9')) ADVANCE(167); END_STATE(); @@ -14812,9 +14792,7 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead == 'r') ADVANCE(161); if (lookahead == 't') ADVANCE(116); if (lookahead == 'w') ADVANCE(136); - if (lookahead == '\t' || - lookahead == '\n' || - lookahead == '\r' || + if (('\t' <= lookahead && lookahead <= '\r') || lookahead == ' ') SKIP(10) if (('1' <= lookahead && lookahead <= '9')) ADVANCE(167); END_STATE(); @@ -14836,24 +14814,21 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead == 'r') ADVANCE(161); if (lookahead == 't') ADVANCE(116); if (lookahead == 'w') ADVANCE(136); - if (lookahead == '\t' || - lookahead == '\n' || - lookahead == '\r' || + if (('\t' <= lookahead && lookahead <= '\r') || lookahead == ' ') SKIP(11) if (('1' <= lookahead && lookahead <= '9')) ADVANCE(167); END_STATE(); case 12: if (lookahead == '"') ADVANCE(180); - if (lookahead == '\t' || - lookahead == '\n' || - lookahead == '\r' || + if (('\t' <= lookahead && lookahead <= '\r') || lookahead == ' ') SKIP(12) END_STATE(); case 13: if (lookahead == '"') ADVANCE(80); if (lookahead == '{') ADVANCE(73); if (lookahead != 0 && - lookahead != '\n') ADVANCE(13); + lookahead != '\n' && + lookahead != '\r') ADVANCE(13); END_STATE(); case 14: if (lookahead == '\'') ADVANCE(175); @@ -14862,7 +14837,8 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead == '\'') ADVANCE(82); if (lookahead == '{') ADVANCE(74); if (lookahead != 0 && - lookahead != '\n') ADVANCE(15); + lookahead != '\n' && + lookahead != '\r') ADVANCE(15); END_STATE(); case 16: if (lookahead == ')') ADVANCE(109); @@ -14874,9 +14850,7 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (sym_identifier_character_set_3(lookahead)) ADVANCE(164); if (lookahead == '|') ADVANCE(107); if (lookahead == '}') ADVANCE(34); - if (lookahead == '\t' || - lookahead == '\n' || - lookahead == '\r' || + if (('\t' <= lookahead && lookahead <= '\r') || lookahead == ' ') SKIP(17) END_STATE(); case 17: @@ -14888,9 +14862,7 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (sym_identifier_character_set_3(lookahead)) ADVANCE(164); if (lookahead == '|') ADVANCE(107); if (lookahead == '}') ADVANCE(34); - if (lookahead == '\t' || - lookahead == '\n' || - lookahead == '\r' || + if (('\t' <= lookahead && lookahead <= '\r') || lookahead == ' ') SKIP(17) END_STATE(); case 18: @@ -14940,7 +14912,8 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead == ']') ADVANCE(78); if (lookahead == '{') ADVANCE(72); if (lookahead != 0 && - lookahead != '\n') ADVANCE(26); + lookahead != '\n' && + lookahead != '\r') ADVANCE(26); END_STATE(); case 27: if (lookahead == '_') ADVANCE(43); @@ -14964,7 +14937,8 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { case 32: if (lookahead == '{') ADVANCE(72); if (lookahead != 0 && - lookahead != '\n') ADVANCE(26); + lookahead != '\n' && + lookahead != '\r') ADVANCE(26); END_STATE(); case 33: if (lookahead == '{') ADVANCE(188); @@ -14983,6 +14957,7 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { lookahead == ' ') ADVANCE(26); if (lookahead != 0 && lookahead != '\n' && + lookahead != '\r' && lookahead != '{') ADVANCE(87); END_STATE(); case 38: @@ -14990,6 +14965,7 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { lookahead == ' ') ADVANCE(13); if (lookahead != 0 && lookahead != '\n' && + lookahead != '\r' && lookahead != '{') ADVANCE(85); END_STATE(); case 39: @@ -14997,6 +14973,7 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { lookahead == ' ') ADVANCE(15); if (lookahead != 0 && lookahead != '\n' && + lookahead != '\r' && lookahead != '{') ADVANCE(86); END_STATE(); case 40: @@ -15150,33 +15127,38 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead != 0 && lookahead != '\t' && lookahead != '\n' && + lookahead != '\r' && lookahead != ' ' && lookahead != '{') ADVANCE(89); END_STATE(); case 72: if (lookahead != 0 && lookahead != '\n' && + lookahead != '\r' && lookahead != '{') ADVANCE(26); END_STATE(); case 73: if (lookahead != 0 && lookahead != '\n' && + lookahead != '\r' && lookahead != '{') ADVANCE(13); END_STATE(); case 74: if (lookahead != 0 && lookahead != '\n' && + lookahead != '\r' && lookahead != '{') ADVANCE(15); END_STATE(); case 75: if (eof) ADVANCE(77); if (lookahead == '\n') SKIP(75) - if (lookahead == '\r') ADVANCE(84); if (lookahead == '"') ADVANCE(88); if (lookahead == '\'') ADVANCE(90); if (lookahead == '[') ADVANCE(91); if (lookahead == '{') ADVANCE(93); - if (lookahead == '\t' || + if (lookahead == 11 || + lookahead == '\f') ADVANCE(84); + if (('\t' <= lookahead && lookahead <= '\r') || lookahead == ' ') ADVANCE(92); if (lookahead != 0) ADVANCE(89); END_STATE(); @@ -15210,9 +15192,7 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead == '{') ADVANCE(33); if (lookahead == '|') ADVANCE(107); if (lookahead == '}') ADVANCE(34); - if (lookahead == '\t' || - lookahead == '\n' || - lookahead == '\r' || + if (('\t' <= lookahead && lookahead <= '\r') || lookahead == ' ') SKIP(76) if (('1' <= lookahead && lookahead <= '9')) ADVANCE(167); END_STATE(); @@ -15224,7 +15204,8 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead == ']') ADVANCE(78); if (lookahead == '{') ADVANCE(72); if (lookahead != 0 && - lookahead != '\n') ADVANCE(26); + lookahead != '\n' && + lookahead != '\r') ADVANCE(26); END_STATE(); case 79: ACCEPT_TOKEN(aux_sym_text_token1); @@ -15233,14 +15214,16 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead == '\t' || lookahead == ' ') ADVANCE(26); if (lookahead != 0 && - lookahead != '\n') ADVANCE(87); + lookahead != '\n' && + lookahead != '\r') ADVANCE(87); END_STATE(); case 80: ACCEPT_TOKEN(aux_sym_text_token2); if (lookahead == '"') ADVANCE(80); if (lookahead == '{') ADVANCE(73); if (lookahead != 0 && - lookahead != '\n') ADVANCE(13); + lookahead != '\n' && + lookahead != '\r') ADVANCE(13); END_STATE(); case 81: ACCEPT_TOKEN(aux_sym_text_token2); @@ -15249,14 +15232,16 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead == '\t' || lookahead == ' ') ADVANCE(13); if (lookahead != 0 && - lookahead != '\n') ADVANCE(85); + lookahead != '\n' && + lookahead != '\r') ADVANCE(85); END_STATE(); case 82: ACCEPT_TOKEN(aux_sym_text_token3); if (lookahead == '\'') ADVANCE(82); if (lookahead == '{') ADVANCE(74); if (lookahead != 0 && - lookahead != '\n') ADVANCE(15); + lookahead != '\n' && + lookahead != '\r') ADVANCE(15); END_STATE(); case 83: ACCEPT_TOKEN(aux_sym_text_token3); @@ -15265,16 +15250,19 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead == '\t' || lookahead == ' ') ADVANCE(15); if (lookahead != 0 && - lookahead != '\n') ADVANCE(86); + lookahead != '\n' && + lookahead != '\r') ADVANCE(86); END_STATE(); case 84: ACCEPT_TOKEN(aux_sym_text_token4); - if (lookahead == '\r') ADVANCE(84); if (lookahead == '"') ADVANCE(88); if (lookahead == '\'') ADVANCE(90); if (lookahead == '[') ADVANCE(91); if (lookahead == '{') ADVANCE(93); + if (lookahead == 11 || + lookahead == '\f') ADVANCE(84); if (lookahead == '\t' || + lookahead == '\r' || lookahead == ' ') ADVANCE(92); if (lookahead != 0 && lookahead != '\n') ADVANCE(89); @@ -15286,7 +15274,8 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead == '\t' || lookahead == ' ') ADVANCE(13); if (lookahead != 0 && - lookahead != '\n') ADVANCE(85); + lookahead != '\n' && + lookahead != '\r') ADVANCE(85); END_STATE(); case 86: ACCEPT_TOKEN(aux_sym_text_token4); @@ -15295,7 +15284,8 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead == '\t' || lookahead == ' ') ADVANCE(15); if (lookahead != 0 && - lookahead != '\n') ADVANCE(86); + lookahead != '\n' && + lookahead != '\r') ADVANCE(86); END_STATE(); case 87: ACCEPT_TOKEN(aux_sym_text_token4); @@ -15304,7 +15294,8 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead == '\t' || lookahead == ' ') ADVANCE(26); if (lookahead != 0 && - lookahead != '\n') ADVANCE(87); + lookahead != '\n' && + lookahead != '\r') ADVANCE(87); END_STATE(); case 88: ACCEPT_TOKEN(aux_sym_text_token4); @@ -15312,7 +15303,8 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead == '\t' || lookahead == ' ') ADVANCE(13); if (lookahead != 0 && - lookahead != '\n') ADVANCE(85); + lookahead != '\n' && + lookahead != '\r') ADVANCE(85); END_STATE(); case 89: ACCEPT_TOKEN(aux_sym_text_token4); @@ -15320,6 +15312,7 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead != 0 && lookahead != '\t' && lookahead != '\n' && + lookahead != '\r' && lookahead != ' ') ADVANCE(89); END_STATE(); case 90: @@ -15328,7 +15321,8 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead == '\t' || lookahead == ' ') ADVANCE(15); if (lookahead != 0 && - lookahead != '\n') ADVANCE(86); + lookahead != '\n' && + lookahead != '\r') ADVANCE(86); END_STATE(); case 91: ACCEPT_TOKEN(aux_sym_text_token4); @@ -15336,16 +15330,19 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead == '\t' || lookahead == ' ') ADVANCE(26); if (lookahead != 0 && - lookahead != '\n') ADVANCE(87); + lookahead != '\n' && + lookahead != '\r') ADVANCE(87); END_STATE(); case 92: ACCEPT_TOKEN(aux_sym_text_token5); - if (lookahead == '\r') ADVANCE(84); if (lookahead == '"') ADVANCE(88); if (lookahead == '\'') ADVANCE(90); if (lookahead == '[') ADVANCE(91); if (lookahead == '{') ADVANCE(93); + if (lookahead == 11 || + lookahead == '\f') ADVANCE(84); if (lookahead == '\t' || + lookahead == '\r' || lookahead == ' ') ADVANCE(92); if (lookahead != 0 && lookahead != '\n') ADVANCE(89); @@ -15356,6 +15353,7 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead != 0 && lookahead != '\t' && lookahead != '\n' && + lookahead != '\r' && lookahead != ' ') ADVANCE(89); END_STATE(); case 94: @@ -15417,7 +15415,7 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { ACCEPT_TOKEN(anon_sym_RPAREN); END_STATE(); case 110: - ACCEPT_TOKEN(anon_sym_); + ACCEPT_TOKEN(anon_sym_SPACE); if (lookahead == ' ') ADVANCE(110); END_STATE(); case 111: @@ -15794,7 +15792,7 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { case 181: ACCEPT_TOKEN(aux_sym_interpreted_string_literal_token1); if (lookahead == '\t' || - lookahead == '\r' || + (11 <= lookahead && lookahead <= '\r') || lookahead == ' ') ADVANCE(181); if (lookahead != 0 && lookahead != '\n' && @@ -16326,7 +16324,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(41), 1, sym_comment, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -16389,7 +16387,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(43), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -16450,7 +16448,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(47), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -16511,7 +16509,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(51), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -16572,7 +16570,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(55), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -16633,7 +16631,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(59), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -16694,7 +16692,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(63), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -16755,7 +16753,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(67), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -16816,7 +16814,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(71), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -16877,7 +16875,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(75), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -16938,7 +16936,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(79), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -16999,7 +16997,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(83), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -17060,7 +17058,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(87), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -17121,7 +17119,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(91), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -17180,7 +17178,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(93), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -17239,7 +17237,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(95), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -17298,7 +17296,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(97), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -17357,7 +17355,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(99), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -17416,7 +17414,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(101), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -17475,7 +17473,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(103), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -17534,7 +17532,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(105), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -17593,7 +17591,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(107), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -17652,7 +17650,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(109), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -17711,7 +17709,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(111), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -17770,7 +17768,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(113), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -17829,7 +17827,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(115), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -17888,7 +17886,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(117), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -17947,7 +17945,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(119), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -18006,7 +18004,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(121), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -18065,7 +18063,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(123), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -18124,7 +18122,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(125), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -18183,7 +18181,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(127), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -18242,7 +18240,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(129), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -18301,7 +18299,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(131), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -18360,7 +18358,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(133), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -18419,7 +18417,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(135), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -18478,7 +18476,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(137), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -18537,7 +18535,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(139), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -18596,7 +18594,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(141), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -18655,7 +18653,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(143), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -18714,7 +18712,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(145), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -18773,7 +18771,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(147), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -18832,7 +18830,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(149), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -18891,7 +18889,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(151), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -18950,7 +18948,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(153), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -19009,7 +19007,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(155), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -19068,7 +19066,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(157), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -19127,7 +19125,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(159), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -19186,7 +19184,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(161), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -19245,7 +19243,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(163), 1, anon_sym_end, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -19302,7 +19300,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(41), 1, sym_comment, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -19337,7 +19335,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(167), 1, anon_sym_LPAREN, ACTIONS(169), 1, - anon_sym_, + anon_sym_SPACE, ACTIONS(171), 1, anon_sym_DOT, ACTIONS(173), 1, @@ -19349,7 +19347,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(181), 1, anon_sym_DQUOTE, STATE(56), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(178), 1, sym_variable, STATE(243), 1, @@ -19412,7 +19410,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(199), 1, sym_comment, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -19455,9 +19453,9 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(181), 1, anon_sym_DQUOTE, ACTIONS(203), 1, - anon_sym_, + anon_sym_SPACE, STATE(56), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(178), 1, sym_variable, STATE(243), 1, @@ -19498,7 +19496,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(167), 1, anon_sym_LPAREN, ACTIONS(169), 1, - anon_sym_, + anon_sym_SPACE, ACTIONS(173), 1, anon_sym_DOT2, ACTIONS(175), 1, @@ -19508,7 +19506,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(181), 1, anon_sym_DQUOTE, STATE(56), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(178), 1, sym_variable, STATE(243), 1, @@ -19559,7 +19557,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(215), 1, anon_sym_DQUOTE, STATE(56), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(178), 1, sym_variable, STATE(243), 1, @@ -19609,7 +19607,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(215), 1, anon_sym_DQUOTE, STATE(56), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(178), 1, sym_variable, STATE(243), 1, @@ -19659,9 +19657,9 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(181), 1, anon_sym_DQUOTE, ACTIONS(219), 1, - anon_sym_, + anon_sym_SPACE, STATE(56), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(178), 1, sym_variable, STATE(243), 1, @@ -19708,9 +19706,9 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(181), 1, anon_sym_DQUOTE, ACTIONS(225), 1, - anon_sym_, + anon_sym_SPACE, STATE(56), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(178), 1, sym_variable, STATE(243), 1, @@ -19757,7 +19755,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(215), 1, anon_sym_DQUOTE, STATE(56), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(178), 1, sym_variable, STATE(243), 1, @@ -19807,7 +19805,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(215), 1, anon_sym_DQUOTE, STATE(56), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(178), 1, sym_variable, STATE(243), 1, @@ -19855,7 +19853,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(215), 1, anon_sym_DQUOTE, STATE(56), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(178), 1, sym_variable, STATE(243), 1, @@ -19903,7 +19901,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(39), 1, anon_sym_DQUOTE, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(111), 1, sym__right_delimiter, STATE(201), 1, @@ -19951,7 +19949,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(39), 1, anon_sym_DQUOTE, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(173), 1, sym__right_delimiter, STATE(201), 1, @@ -19999,7 +19997,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(39), 1, anon_sym_DQUOTE, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(113), 1, sym__right_delimiter, STATE(201), 1, @@ -20047,7 +20045,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(39), 1, anon_sym_DQUOTE, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(226), 1, @@ -20095,7 +20093,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(39), 1, anon_sym_DQUOTE, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(202), 1, sym_variable, STATE(260), 1, @@ -20140,7 +20138,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(39), 1, anon_sym_DQUOTE, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(202), 1, sym_variable, STATE(260), 1, @@ -20185,7 +20183,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(215), 1, anon_sym_DQUOTE, STATE(56), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(178), 1, sym_variable, STATE(243), 1, @@ -20228,7 +20226,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(39), 1, anon_sym_DQUOTE, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -20271,7 +20269,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(215), 1, anon_sym_DQUOTE, STATE(56), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(178), 1, sym_variable, STATE(243), 1, @@ -20314,7 +20312,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(215), 1, anon_sym_DQUOTE, STATE(56), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(178), 1, sym_variable, STATE(243), 1, @@ -20357,7 +20355,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(39), 1, anon_sym_DQUOTE, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -20400,7 +20398,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(215), 1, anon_sym_DQUOTE, STATE(56), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(178), 1, sym_variable, STATE(243), 1, @@ -20443,7 +20441,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(39), 1, anon_sym_DQUOTE, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -20486,7 +20484,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(39), 1, anon_sym_DQUOTE, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -20529,7 +20527,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(39), 1, anon_sym_DQUOTE, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -20572,7 +20570,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(39), 1, anon_sym_DQUOTE, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -20615,7 +20613,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(39), 1, anon_sym_DQUOTE, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -20658,7 +20656,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(39), 1, anon_sym_DQUOTE, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -20701,7 +20699,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(39), 1, anon_sym_DQUOTE, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -20744,7 +20742,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(39), 1, anon_sym_DQUOTE, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -20787,7 +20785,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(39), 1, anon_sym_DQUOTE, STATE(58), 1, - sym_unfished_selector_expression, + sym_unfinished_selector_expression, STATE(201), 1, sym_variable, STATE(260), 1, @@ -22331,7 +22329,7 @@ static const uint16_t ts_small_parse_table[] = { aux_sym_template_repeat1, [7743] = 2, ACTIONS(449), 1, - anon_sym_, + anon_sym_SPACE, ACTIONS(447), 18, anon_sym_PIPE, anon_sym_LPAREN, @@ -22353,7 +22351,7 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_DASH_RBRACE_RBRACE, [7767] = 4, ACTIONS(453), 1, - anon_sym_, + anon_sym_SPACE, ACTIONS(455), 1, sym_identifier, STATE(144), 1, @@ -22377,7 +22375,7 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_DASH_RBRACE_RBRACE, [7795] = 2, ACTIONS(459), 1, - anon_sym_, + anon_sym_SPACE, ACTIONS(457), 18, anon_sym_PIPE, anon_sym_LPAREN, @@ -22399,7 +22397,7 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_DASH_RBRACE_RBRACE, [7819] = 2, ACTIONS(463), 1, - anon_sym_, + anon_sym_SPACE, ACTIONS(461), 18, anon_sym_PIPE, anon_sym_LPAREN, @@ -22561,7 +22559,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(481), 1, sym_identifier, ACTIONS(475), 2, - anon_sym_, + anon_sym_SPACE, anon_sym_DOT, ACTIONS(479), 6, anon_sym_COLON_EQ, @@ -22813,7 +22811,7 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_LBRACE_LBRACE_DASH, [8297] = 5, ACTIONS(169), 1, - anon_sym_, + anon_sym_SPACE, ACTIONS(569), 1, anon_sym_COLON_EQ, ACTIONS(571), 1, @@ -22871,7 +22869,7 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_LBRACE_LBRACE_DASH, [8368] = 2, ACTIONS(503), 2, - anon_sym_, + anon_sym_SPACE, anon_sym_DOT, ACTIONS(591), 6, anon_sym_COLON_EQ, @@ -23452,7 +23450,7 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_LBRACE_LBRACE_DASH, [9052] = 3, ACTIONS(661), 1, - anon_sym_, + anon_sym_SPACE, STATE(246), 1, aux_sym_argument_list_repeat1, ACTIONS(217), 4, @@ -23462,7 +23460,7 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_DASH_RBRACE_RBRACE, [9065] = 2, ACTIONS(665), 2, - anon_sym_, + anon_sym_SPACE, anon_sym_DOT, ACTIONS(663), 4, anon_sym_PIPE, @@ -23473,7 +23471,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(573), 1, anon_sym_DOT, ACTIONS(669), 1, - anon_sym_, + anon_sym_SPACE, ACTIONS(667), 4, anon_sym_PIPE, anon_sym_RPAREN, @@ -23481,7 +23479,7 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_DASH_RBRACE_RBRACE, [9089] = 3, ACTIONS(673), 1, - anon_sym_, + anon_sym_SPACE, ACTIONS(675), 1, sym_identifier, ACTIONS(671), 4, @@ -23493,7 +23491,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(677), 1, anon_sym_PIPE, ACTIONS(681), 1, - anon_sym_, + anon_sym_SPACE, STATE(241), 1, aux_sym_argument_list_repeat1, ACTIONS(679), 3, @@ -23502,7 +23500,7 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_DASH_RBRACE_RBRACE, [9117] = 3, ACTIONS(685), 1, - anon_sym_, + anon_sym_SPACE, STATE(246), 1, aux_sym_argument_list_repeat1, ACTIONS(683), 4, @@ -23512,7 +23510,7 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_DASH_RBRACE_RBRACE, [9130] = 3, ACTIONS(688), 1, - anon_sym_, + anon_sym_SPACE, STATE(246), 1, aux_sym_argument_list_repeat1, ACTIONS(217), 4, @@ -23524,7 +23522,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(677), 1, anon_sym_PIPE, ACTIONS(690), 1, - anon_sym_, + anon_sym_SPACE, STATE(247), 1, aux_sym_argument_list_repeat1, ACTIONS(679), 3, @@ -23535,7 +23533,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(677), 1, anon_sym_PIPE, ACTIONS(694), 1, - anon_sym_, + anon_sym_SPACE, ACTIONS(692), 3, anon_sym_RPAREN, anon_sym_RBRACE_RBRACE, @@ -23544,7 +23542,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(677), 1, anon_sym_PIPE, ACTIONS(698), 1, - anon_sym_, + anon_sym_SPACE, ACTIONS(696), 3, anon_sym_RPAREN, anon_sym_RBRACE_RBRACE, @@ -23559,7 +23557,7 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_DASH_RBRACE_RBRACE, [9192] = 2, ACTIONS(704), 1, - anon_sym_, + anon_sym_SPACE, ACTIONS(702), 4, anon_sym_PIPE, anon_sym_RPAREN, @@ -23567,7 +23565,7 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_DASH_RBRACE_RBRACE, [9202] = 2, ACTIONS(471), 1, - anon_sym_, + anon_sym_SPACE, ACTIONS(473), 4, anon_sym_PIPE, anon_sym_RPAREN, @@ -23575,7 +23573,7 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_DASH_RBRACE_RBRACE, [9212] = 2, ACTIONS(708), 1, - anon_sym_, + anon_sym_SPACE, ACTIONS(706), 4, anon_sym_PIPE, anon_sym_RPAREN, @@ -23583,7 +23581,7 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_DASH_RBRACE_RBRACE, [9222] = 2, ACTIONS(467), 1, - anon_sym_, + anon_sym_SPACE, ACTIONS(469), 4, anon_sym_PIPE, anon_sym_RPAREN, @@ -23591,7 +23589,7 @@ static const uint16_t ts_small_parse_table[] = { anon_sym_DASH_RBRACE_RBRACE, [9232] = 2, ACTIONS(712), 1, - anon_sym_, + anon_sym_SPACE, ACTIONS(710), 4, anon_sym_PIPE, anon_sym_RPAREN, @@ -23612,7 +23610,7 @@ static const uint16_t ts_small_parse_table[] = { ACTIONS(677), 1, anon_sym_PIPE, ACTIONS(720), 1, - anon_sym_, + anon_sym_SPACE, ACTIONS(683), 3, anon_sym_RPAREN, anon_sym_RBRACE_RBRACE, @@ -25002,8 +25000,8 @@ static const TSParseActionEntry ts_parse_actions[] = { [444] = {.entry = {.count = 2, .reusable = false}}, REDUCE(sym__else_if_clause, 4, .dynamic_precedence = 1, .production_id = 11), SHIFT(52), [447] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_selector_expression, 3, .production_id = 8), [449] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_selector_expression, 3, .production_id = 8), - [451] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_unfished_selector_expression, 2, .production_id = 4), - [453] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_unfished_selector_expression, 2, .production_id = 4), + [451] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_unfinished_selector_expression, 2, .production_id = 4), + [453] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_unfinished_selector_expression, 2, .production_id = 4), [455] = {.entry = {.count = 1, .reusable = false}}, SHIFT(147), [457] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_field, 2, .production_id = 2), [459] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_field, 2, .production_id = 2), @@ -25252,11 +25250,15 @@ static const TSParseActionEntry ts_parse_actions[] = { #ifdef __cplusplus extern "C" { #endif -#ifdef _WIN32 -#define extern __declspec(dllexport) +#ifdef TREE_SITTER_HIDE_SYMBOLS +#define TS_PUBLIC +#elif defined(_WIN32) +#define TS_PUBLIC __declspec(dllexport) +#else +#define TS_PUBLIC __attribute__((visibility("default"))) #endif -extern const TSLanguage *tree_sitter_gotmpl(void) { +TS_PUBLIC const TSLanguage *tree_sitter_gotmpl() { static const TSLanguage language = { .version = LANGUAGE_VERSION, .symbol_count = SYMBOL_COUNT, 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 { diff --git a/internal/util/yaml_path.go b/internal/util/yaml_path.go deleted file mode 100644 index d07c4e84..00000000 --- a/internal/util/yaml_path.go +++ /dev/null @@ -1,59 +0,0 @@ -package util - -import ( - "fmt" - "strings" -) - -type YamlPath struct { - TableNames []string -} - -func NewYamlPath(yamlPathString string) (YamlPath, error) { - var ( - splitted = strings.Split(yamlPathString, ".") - variableSplitted = []string{} - ) - - // filter out empty strings, that were added by the split - for _, s := range splitted { - if s != "" { - variableSplitted = append(variableSplitted, s) - } - } - if len(variableSplitted) == 0 { - return YamlPath{}, fmt.Errorf("Could not parse yaml path: %s", yamlPathString) - } - // $ always points to the root context so we can safely remove it - // as long the LSP does not know about ranges - if variableSplitted[0] == "$" && len(variableSplitted) > 1 { - variableSplitted = variableSplitted[1:] - } - - return YamlPath{ - TableNames: variableSplitted, - }, nil -} - -func (path YamlPath) GetTail() []string { - return path.TableNames[1:] -} - -func (path YamlPath) IsValuesPath() bool { - return path.TableNames[0] == "Values" -} - -func (path YamlPath) IsChartPath() bool { - return path.TableNames[0] == "Chart" -} - -func (path YamlPath) IsReleasePath() bool { - return path.TableNames[0] == "Release" -} - -func (path YamlPath) IsFilesPath() bool { - return path.TableNames[0] == "Files" -} -func (path YamlPath) IsCapabilitiesPath() bool { - return path.TableNames[0] == "Capabilities" -} diff --git a/testdata/example/templates/completion-test.yaml b/testdata/example/templates/completion-test.yaml index cea82b73..0944f3dd 100644 --- a/testdata/example/templates/completion-test.yaml +++ b/testdata/example/templates/completion-test.yaml @@ -6,7 +6,7 @@ {{ .Chart.N }} {{ . }} -{{ toYaml .Release. }} -{{ toYaml (.Release. ) }} -{{ .Release. }} +{{ if (and .Values. ) }} +{{ end }} +{{ if }} From c50176c33401d548da76333eb866fd5ca20db7ed Mon Sep 17 00:00:00 2001 From: qvalentin <36446499+qvalentin@users.noreply.github.com> Date: Fri, 17 May 2024 17:06:47 +0200 Subject: [PATCH 30/35] feat: use helm as a libary instead of copying code (#77) * feat: use helm as a libary instead of copying code * fix(values): don't use helms values lookup * fix: cleanup variable go-to-definition * chore: delete dead code * feat(lint): rework diagnostics --- go.mod | 110 ++- go.sum | 354 ++++++++- internal/adapter/yamlls/documentSync.go | 2 +- internal/charts/chart_for_document.go | 2 +- internal/charts/chart_store_test.go | 2 +- internal/charts/chart_test.go | 2 +- internal/charts/metadata.go | 7 +- internal/charts/values_file.go | 5 +- internal/charts/values_file_test.go | 2 +- internal/handler/completion_main_test.go | 5 +- internal/handler/diagnostics.go | 17 + internal/handler/hover_main_test.go | 21 +- internal/handler/text_document.go | 14 +- .../language_features/template_context.go | 5 +- .../template_context_completion_test.go | 3 +- .../template_context_hover_test.go | 24 +- internal/language_features/variables.go | 2 +- internal/lsp/ast.go | 10 - internal/lsp/ast_variable.go | 12 - internal/lsp/ast_variable_test.go | 19 +- internal/lsp/lint.go | 170 ++--- internal/lsp/lint_test.go | 65 ++ internal/protocol/completion.go | 11 +- internal/util/values.go | 93 ++- internal/util/values_test.go | 39 +- internal/util/yaml.go | 12 + pkg/action/lint.go | 106 --- pkg/chart/chart.go | 173 ----- pkg/chart/dependency.go | 79 -- pkg/chart/errors.go | 30 - pkg/chart/file.go | 27 - pkg/chart/loader/archive.go | 204 ------ pkg/chart/loader/directory.go | 119 --- pkg/chart/loader/load.go | 201 ----- pkg/chart/metadata.go | 161 ---- pkg/chartutil/capabilities.go | 132 ---- pkg/chartutil/chartfile.go | 92 --- pkg/chartutil/coalesce.go | 227 ------ pkg/chartutil/compatible.go | 36 - pkg/chartutil/create.go | 686 ------------------ pkg/chartutil/dependencies.go | 285 -------- pkg/chartutil/errors.go | 41 -- pkg/chartutil/expand.go | 90 --- pkg/chartutil/jsonschema.go | 87 --- pkg/chartutil/save.go | 244 ------- pkg/chartutil/validate_name.go | 112 --- pkg/chartutil/values.go | 255 ------- pkg/engine/engine.go | 416 ----------- pkg/engine/error.go | 25 - pkg/engine/files.go | 160 ---- pkg/engine/files_test.go | 98 --- pkg/engine/funcs.go | 179 ----- pkg/engine/funcs_test.go | 178 ----- pkg/engine/lookup_func.go | 124 ---- pkg/lint/lint.go | 37 - pkg/lint/rules/chartfile.go | 209 ------ pkg/lint/rules/dependencies.go | 82 --- pkg/lint/rules/deprecations.go | 98 --- pkg/lint/rules/template.go | 385 ---------- pkg/lint/rules/values.go | 86 --- pkg/lint/support/error.go | 1 - pkg/lint/support/message.go | 97 --- testdata/example/templates/lint.yaml | 1 + 63 files changed, 806 insertions(+), 5765 deletions(-) create mode 100644 internal/handler/diagnostics.go create mode 100644 internal/lsp/lint_test.go delete mode 100644 pkg/action/lint.go delete mode 100644 pkg/chart/chart.go delete mode 100644 pkg/chart/dependency.go delete mode 100644 pkg/chart/errors.go delete mode 100644 pkg/chart/file.go delete mode 100644 pkg/chart/loader/archive.go delete mode 100644 pkg/chart/loader/directory.go delete mode 100644 pkg/chart/loader/load.go delete mode 100644 pkg/chart/metadata.go delete mode 100644 pkg/chartutil/capabilities.go delete mode 100644 pkg/chartutil/chartfile.go delete mode 100644 pkg/chartutil/coalesce.go delete mode 100644 pkg/chartutil/compatible.go delete mode 100644 pkg/chartutil/create.go delete mode 100644 pkg/chartutil/dependencies.go delete mode 100644 pkg/chartutil/errors.go delete mode 100644 pkg/chartutil/expand.go delete mode 100644 pkg/chartutil/jsonschema.go delete mode 100644 pkg/chartutil/save.go delete mode 100644 pkg/chartutil/validate_name.go delete mode 100644 pkg/chartutil/values.go delete mode 100644 pkg/engine/engine.go delete mode 100644 pkg/engine/error.go delete mode 100644 pkg/engine/files.go delete mode 100644 pkg/engine/files_test.go delete mode 100644 pkg/engine/funcs.go delete mode 100644 pkg/engine/funcs_test.go delete mode 100644 pkg/engine/lookup_func.go delete mode 100644 pkg/lint/lint.go delete mode 100644 pkg/lint/rules/chartfile.go delete mode 100644 pkg/lint/rules/dependencies.go delete mode 100644 pkg/lint/rules/deprecations.go delete mode 100644 pkg/lint/rules/template.go delete mode 100644 pkg/lint/rules/values.go delete mode 100644 pkg/lint/support/error.go delete mode 100644 pkg/lint/support/message.go create mode 100644 testdata/example/templates/lint.yaml diff --git a/go.mod b/go.mod index 9c3b5017..ae19c435 100644 --- a/go.mod +++ b/go.mod @@ -3,84 +3,158 @@ module github.com/mrjosh/helm-ls go 1.21 require ( - github.com/BurntSushi/toml v1.3.2 - github.com/Masterminds/semver/v3 v3.2.1 - github.com/Masterminds/sprig/v3 v3.2.3 - github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 - github.com/cyphar/filepath-securejoin v0.2.4 - github.com/gobwas/glob v0.2.3 - github.com/mitchellh/copystructure v1.2.0 github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.9.3 github.com/smacker/go-tree-sitter v0.0.0-20240214120134-1f283e24f560 github.com/spf13/cobra v1.8.0 github.com/stretchr/testify v1.9.0 - github.com/xeipuuv/gojsonschema v1.2.0 go.lsp.dev/jsonrpc2 v0.10.0 go.lsp.dev/protocol v0.12.0 go.lsp.dev/uri v0.3.0 go.uber.org/zap v1.24.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/apiextensions-apiserver v0.29.2 - k8s.io/apimachinery v0.29.2 - k8s.io/apiserver v0.29.2 - k8s.io/client-go v0.29.2 - k8s.io/helm v2.17.0+incompatible - sigs.k8s.io/yaml v1.4.0 + helm.sh/helm/v3 v3.14.4 ) require ( + github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect + github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect + github.com/Masterminds/semver/v3 v3.2.1 // indirect + github.com/Masterminds/sprig/v3 v3.2.3 // indirect + github.com/Masterminds/squirrel v1.5.4 // indirect + github.com/Microsoft/hcsshim v0.11.4 // indirect + github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/chai2010/gettext-go v1.0.2 // indirect + github.com/containerd/containerd v1.7.12 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/docker/cli v24.0.6+incompatible // indirect + github.com/docker/distribution v2.8.2+incompatible // indirect + github.com/docker/docker v24.0.9+incompatible // indirect + github.com/docker/docker-credential-helpers v0.7.0 // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-metrics v0.0.1 // indirect + github.com/docker/go-units v0.5.0 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/evanphx/json-patch v5.7.0+incompatible // indirect + github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect + github.com/fatih/color v1.13.0 // indirect + github.com/felixge/httpsnoop v1.0.3 // indirect github.com/frankban/quicktest v1.14.4 // indirect + github.com/go-errors/errors v1.4.2 // indirect + github.com/go-gorp/gorp/v3 v3.1.0 // indirect github.com/go-logr/logr v1.3.0 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.3 // indirect + github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.3 // indirect + github.com/google/btree v1.0.1 // indirect github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.3.0 // indirect - github.com/huandu/xstrings v1.3.3 // indirect - github.com/imdario/mergo v0.3.11 // indirect + github.com/gorilla/mux v1.8.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/gosuri/uitable v0.0.4 // indirect + github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/huandu/xstrings v1.4.0 // indirect + github.com/imdario/mergo v0.3.13 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jmoiron/sqlx v1.3.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.16.0 // indirect + github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect + github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect + github.com/lib/pq v1.10.9 // indirect + github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mattn/go-runewidth v0.0.9 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/moby/locker v1.0.1 // indirect + github.com/moby/spdystream v0.2.0 // indirect + github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect + github.com/morikuni/aec v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0-rc5 // indirect + github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.44.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/rubenv/sql-migrate v1.5.2 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/segmentio/asm v1.1.3 // indirect github.com/segmentio/encoding v0.3.4 // indirect - github.com/shopspring/decimal v1.2.0 // indirect + github.com/shopspring/decimal v1.3.1 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.2 // indirect - github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect + github.com/xlab/treeprint v1.2.0 // indirect go.lsp.dev/pkg v0.0.0-20210717090340-384b27a52fb2 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 // indirect + go.opentelemetry.io/otel v1.19.0 // indirect + go.opentelemetry.io/otel/metric v1.19.0 // indirect + go.opentelemetry.io/otel/trace v1.19.0 // indirect + go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect go.uber.org/atomic v1.10.0 // indirect go.uber.org/goleak v1.2.1 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.21.0 // indirect golang.org/x/net v0.23.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect + golang.org/x/sync v0.5.0 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/term v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect + google.golang.org/grpc v1.58.3 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect k8s.io/api v0.29.2 // indirect + k8s.io/apiextensions-apiserver v0.29.2 // indirect + k8s.io/apimachinery v0.29.2 // indirect + k8s.io/apiserver v0.29.2 // indirect + k8s.io/cli-runtime v0.29.0 // indirect + k8s.io/client-go v0.29.2 // indirect + k8s.io/component-base v0.29.2 // indirect k8s.io/klog/v2 v2.110.1 // indirect k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect + k8s.io/kubectl v0.29.0 // indirect k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect + oras.land/oras-go v1.2.4 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect + sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index 4a1ddbd2..40131f6f 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,15 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= +github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= +github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= +github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= @@ -7,42 +17,158 @@ github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0 github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= +github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8= +github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w= +github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= +github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= +github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= +github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng= +github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= +github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= +github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= +github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= +github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= +github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= +github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= +github.com/containerd/containerd v1.7.12 h1:+KQsnv4VnzyxWcfO9mlxxELaoztsDEjOuCMPAuPqgU0= +github.com/containerd/containerd v1.7.12/go.mod h1:/5OMpE1p0ylxtEUGY8kuCYkDRzJm9NO1TFMWjUpdevk= +github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= +github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc= +github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2/go.mod h1:WHNsWjnIn2V1LYOrME7e8KxSeKunYHsxEm4am0BUtcI= +github.com/docker/cli v24.0.6+incompatible h1:fF+XCQCgJjjQNIMjzaSmiKJSCcfcXb3TWTcc7GAneOY= +github.com/docker/cli v24.0.6+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= +github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v24.0.9+incompatible h1:HPGzNmwfLZWdxHqK9/II92pyi1EpYKsAqcl4G0Of9v0= +github.com/docker/docker v24.0.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= +github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= +github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= +github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= +github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= +github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= +github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI= +github.com/foxcpp/go-mockdns v1.0.0/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= +github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/gobuffalo/logger v1.0.6 h1:nnZNpxYo0zx+Aj9RfMPBm+x9zAU2OayFh/xrAWi34HU= +github.com/gobuffalo/logger v1.0.6/go.mod h1:J31TBEHR1QLV2683OXTAItYIg8pv2JMHnF/quuAbMjs= +github.com/gobuffalo/packd v1.0.1 h1:U2wXfRr4E9DH8IdsDLlRFwTZTK7hLfq9qT/QHXGVe/0= +github.com/gobuffalo/packd v1.0.1/go.mod h1:PP2POP3p3RXGz7Jh6eYEf93S7vA2za6xM7QT85L4+VY= +github.com/gobuffalo/packr/v2 v2.8.3 h1:xE1yzvnO56cUC0sTpKR3DIbxZgB54AftTFMhB2XEWlY= +github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXsOdiU5KwbKc= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k= +github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= +github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= @@ -52,21 +178,54 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= +github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= +github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= +github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= +github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= +github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= +github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= +github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= +github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -74,40 +233,129 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= +github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI= +github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc= +github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY= +github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI= +github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= +github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/miekg/dns v1.1.25 h1:dFwPR6SfLtrSwgDcIq2bcU/gVutB4sNApq2HBdqcakg= +github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= +github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= +github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= +github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= +github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rubenv/sql-migrate v1.5.2 h1:bMDqOnrJVV/6JQgQ/MxOpU+AdO8uzYYA/TxFUBzFtS0= +github.com/rubenv/sql-migrate v1.5.2/go.mod h1:H38GW8Vqf8F0Su5XignRyaRcbXbJunSWxs+kmzlg0Is= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/segmentio/asm v1.1.3 h1:WM03sfUOENvvKexOLp+pCqgb/WDjsi7EK8gIsICtzhc= github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg= github.com/segmentio/encoding v0.3.4 h1:WM4IBnxH8B9TakiM2QD5LyNl9JSndh88QbHqVC+Pauc= github.com/segmentio/encoding v0.3.4/go.mod h1:n0JeuIqEQrQoPDGsjo8UNd1iA0U8d8+oHAA4E3G3OxM= -github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smacker/go-tree-sitter v0.0.0-20240214120134-1f283e24f560 h1:i1kygzBpj4bIXk+ztDJCnmywZrbpsRJG1QCMrMI0P3o= @@ -120,6 +368,7 @@ github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyh github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= @@ -134,15 +383,24 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= +github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= +github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= +github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= +github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= +github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= +github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= go.lsp.dev/jsonrpc2 v0.10.0 h1:Pr/YcXJoEOTMc/b6OTmcR1DPJ3mSWl/SWiU1Cct6VmI= go.lsp.dev/jsonrpc2 v0.10.0/go.mod h1:fmEzIdXPi/rf6d4uFcayi8HpFP1nBF99ERP1htC72Ac= go.lsp.dev/pkg v0.0.0-20210717090340-384b27a52fb2 h1:hCzQgh6UcwbKgNSRurYWSqh8MufqRRPODRBblutn4TE= @@ -151,6 +409,18 @@ go.lsp.dev/protocol v0.12.0 h1:tNprUI9klQW5FAFVM4Sa+AbPFuVQByWhP1ttNUAjIWg= go.lsp.dev/protocol v0.12.0/go.mod h1:Qb11/HgZQ72qQbeyPfJbu3hZBH23s1sr4st8czGeDMQ= go.lsp.dev/uri v0.3.0 h1:KcZJmh6nFIBeJzTugn5JTU6OOyG0lDOo3R9KwTxTYbo= go.lsp.dev/uri v0.3.0/go.mod h1:P5sbO1IQR+qySTWOCnhnK7phBx+W3zbLqSMDJNTw88I= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZx8cOF0+Kkazoc7lwUNMGy0LrzEMxTm4BbTxg= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0/go.mod h1:62CPTSry9QZtOaSsE3tOzhx6LzDhHnXJ6xHeMNNiM6Q= +go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= +go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= +go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= +go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= +go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= +go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= +go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY= +go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= @@ -159,6 +429,7 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -166,11 +437,23 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= @@ -179,26 +462,42 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= @@ -212,6 +511,10 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -222,25 +525,54 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= +gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= +helm.sh/helm/v3 v3.14.4 h1:6FSpEfqyDalHq3kUr4gOMThhgY55kXUEjdQoyODYnrM= +helm.sh/helm/v3 v3.14.4/go.mod h1:Tje7LL4gprZpuBNTbG34d1Xn5NmRT3OWfBRwpOSer9I= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.29.2 h1:hBC7B9+MU+ptchxEqTNW2DkUosJpp1P+Wn6YncZ474A= k8s.io/api v0.29.2/go.mod h1:sdIaaKuU7P44aoyyLlikSLayT6Vb7bvJNCX105xZXY0= k8s.io/apiextensions-apiserver v0.29.2 h1:UK3xB5lOWSnhaCk0RFZ0LUacPZz9RY4wi/yt2Iu+btg= @@ -249,18 +581,28 @@ k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8= k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU= k8s.io/apiserver v0.29.2 h1:+Z9S0dSNr+CjnVXQePG8TcBWHr3Q7BmAr7NraHvsMiQ= k8s.io/apiserver v0.29.2/go.mod h1:B0LieKVoyU7ykQvPFm7XSdIHaCHSzCzQWPFa5bqbeMQ= +k8s.io/cli-runtime v0.29.0 h1:q2kC3cex4rOBLfPOnMSzV2BIrrQlx97gxHJs21KxKS4= +k8s.io/cli-runtime v0.29.0/go.mod h1:VKudXp3X7wR45L+nER85YUzOQIru28HQpXr0mTdeCrk= k8s.io/client-go v0.29.2 h1:FEg85el1TeZp+/vYJM7hkDlSTFZ+c5nnK44DJ4FyoRg= k8s.io/client-go v0.29.2/go.mod h1:knlvFZE58VpqbQpJNbCbctTVXcd35mMyAAwBdpt4jrA= -k8s.io/helm v2.17.0+incompatible h1:Bpn6o1wKLYqKM3+Osh8e+1/K2g/GsQJ4F4yNF2+deao= -k8s.io/helm v2.17.0+incompatible/go.mod h1:LZzlS4LQBHfciFOurYBFkCMTaZ0D1l+p0teMg7TSULI= +k8s.io/component-base v0.29.2 h1:lpiLyuvPA9yV1aQwGLENYyK7n/8t6l3nn3zAtFTJYe8= +k8s.io/component-base v0.29.2/go.mod h1:BfB3SLrefbZXiBfbM+2H1dlat21Uewg/5qtKOl8degM= k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= +k8s.io/kubectl v0.29.0 h1:Oqi48gXjikDhrBF67AYuZRTcJV4lg2l42GmvsP7FmYI= +k8s.io/kubectl v0.29.0/go.mod h1:0jMjGWIcMIQzmUaMgAzhSELv5WtHo2a8pq67DtviAJs= k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +oras.land/oras-go v1.2.4 h1:djpBY2/2Cs1PV87GSJlxv4voajVOMZxqqtq9AB8YNvY= +oras.land/oras-go v1.2.4/go.mod h1:DYcGfb3YF1nKjcezfX2SNlDAeQFKSXmf+qrFmrh4324= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 h1:XX3Ajgzov2RKUdc5jW3t5jwY7Bo7dcRm+tFxT+NfgY0= +sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3/go.mod h1:9n16EZKMhXBNSiUC5kSdFQJkdH3zbxS/JoO619G1VAY= +sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 h1:W6cLQc5pnqM7vh3b7HvGNfXrJ/xL6BDMS0v1V/HHg5U= +sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3/go.mod h1:JWP1Fj0VWGHyw3YUPjXSQnRnrwezrZSrApfX5S0nIag= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= diff --git a/internal/adapter/yamlls/documentSync.go b/internal/adapter/yamlls/documentSync.go index 1d566b58..9c10e054 100644 --- a/internal/adapter/yamlls/documentSync.go +++ b/internal/adapter/yamlls/documentSync.go @@ -91,7 +91,7 @@ func (yamllsConnector Connector) DocumentDidChangeFullSync(doc *lsplocal.Documen return } - logger.Println("Sending DocumentDidChange with full sync, current content:", doc.Content) + logger.Debug("Sending DocumentDidChange with full sync, current content:", doc.Content) trimmedText := lsplocal.TrimTemplate(doc.Ast.Copy(), doc.Content) params.ContentChanges = []lsp.TextDocumentContentChangeEvent{ diff --git a/internal/charts/chart_for_document.go b/internal/charts/chart_for_document.go index c6f46915..de7fd68c 100644 --- a/internal/charts/chart_for_document.go +++ b/internal/charts/chart_for_document.go @@ -6,9 +6,9 @@ import ( "path/filepath" "strings" - "github.com/mrjosh/helm-ls/pkg/chartutil" lsp "go.lsp.dev/protocol" "go.lsp.dev/uri" + "helm.sh/helm/v3/pkg/chartutil" ) func (s *ChartStore) GetChartForDoc(uri lsp.DocumentURI) (*Chart, error) { diff --git a/internal/charts/chart_store_test.go b/internal/charts/chart_store_test.go index 49646a78..3cd00468 100644 --- a/internal/charts/chart_store_test.go +++ b/internal/charts/chart_store_test.go @@ -6,10 +6,10 @@ import ( "testing" "github.com/mrjosh/helm-ls/internal/util" - "github.com/mrjosh/helm-ls/pkg/chartutil" "github.com/stretchr/testify/assert" "go.lsp.dev/uri" "gopkg.in/yaml.v3" + "helm.sh/helm/v3/pkg/chartutil" ) func TestSetValuesFilesConfigOverwrites(t *testing.T) { diff --git a/internal/charts/chart_test.go b/internal/charts/chart_test.go index d9f88998..d2057304 100644 --- a/internal/charts/chart_test.go +++ b/internal/charts/chart_test.go @@ -7,8 +7,8 @@ import ( "github.com/mrjosh/helm-ls/internal/charts" "github.com/mrjosh/helm-ls/internal/util" - "github.com/mrjosh/helm-ls/pkg/chart" "go.lsp.dev/uri" + "helm.sh/helm/v3/pkg/chart" "github.com/stretchr/testify/assert" ) diff --git a/internal/charts/metadata.go b/internal/charts/metadata.go index 53c47ab4..f5dd8e11 100644 --- a/internal/charts/metadata.go +++ b/internal/charts/metadata.go @@ -3,10 +3,11 @@ package charts import ( "path/filepath" - "github.com/mrjosh/helm-ls/pkg/chart" - "github.com/mrjosh/helm-ls/pkg/chartutil" + "github.com/mrjosh/helm-ls/internal/util" "go.lsp.dev/uri" "gopkg.in/yaml.v3" + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/chartutil" ) type ChartMetadata struct { @@ -17,7 +18,7 @@ type ChartMetadata struct { func NewChartMetadata(rootURI uri.URI) *ChartMetadata { filePath := filepath.Join(rootURI.Filename(), chartutil.ChartfileName) - chartNode, err := chartutil.ReadYamlFileToNode(filePath) + chartNode, err := util.ReadYamlFileToNode(filePath) if err != nil { logger.Error("Error loading Chart.yaml file", rootURI, err) } diff --git a/internal/charts/values_file.go b/internal/charts/values_file.go index 812882dc..a27385eb 100644 --- a/internal/charts/values_file.go +++ b/internal/charts/values_file.go @@ -1,8 +1,9 @@ package charts import ( - "github.com/mrjosh/helm-ls/pkg/chartutil" + "github.com/mrjosh/helm-ls/internal/util" "go.lsp.dev/uri" + "helm.sh/helm/v3/pkg/chartutil" "gopkg.in/yaml.v3" ) @@ -37,7 +38,7 @@ func readInValuesFile(filePath string) (chartutil.Values, yaml.Node) { logger.Error("Error loading values file ", filePath, err) } - valueNodes, err := chartutil.ReadYamlFileToNode(filePath) + valueNodes, err := util.ReadYamlFileToNode(filePath) if err != nil { logger.Error("Error loading values file ", filePath, err) } diff --git a/internal/charts/values_file_test.go b/internal/charts/values_file_test.go index 58efbe56..64830f96 100644 --- a/internal/charts/values_file_test.go +++ b/internal/charts/values_file_test.go @@ -6,9 +6,9 @@ import ( "testing" "github.com/mrjosh/helm-ls/internal/charts" - "github.com/mrjosh/helm-ls/pkg/chartutil" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" + "helm.sh/helm/v3/pkg/chartutil" ) func TestNewValuesFile(t *testing.T) { diff --git a/internal/handler/completion_main_test.go b/internal/handler/completion_main_test.go index 7b6e66e9..481cbab5 100644 --- a/internal/handler/completion_main_test.go +++ b/internal/handler/completion_main_test.go @@ -173,6 +173,9 @@ func TestCompletionMainSingleLines(t *testing.T) { {"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}, + {`Test completion on {{ range .Values.ingress.hosts }} {{ .^ }} {{ end }}`, []string{"host", "paths"}, []string{}, nil}, + {`Test completion on {{ range .Values.ingress.hosts }} {{ .ho^ }} {{ end }}`, []string{"host", "paths"}, []string{}, nil}, + {`Test completion on {{ range .Values.ingress.hosts }} {{ range .paths }} {{ .^ }} {{ end }} {{ end }}`, []string{"pathType", "path"}, []string{}, nil}, } for _, tt := range testCases { @@ -181,7 +184,7 @@ func TestCompletionMainSingleLines(t *testing.T) { col := strings.Index(tt.templateWithMark, "^") buf := strings.Replace(tt.templateWithMark, "^", "", 1) pos := protocol.Position{Line: 0, Character: uint32(col)} - // to get the correct values file + // to get the correct values file ../../testdata/example/values.yaml fileURI := uri.File("../../testdata/example/templates/completion-test.yaml") result, err := completionTestCall(fileURI, buf, pos) diff --git a/internal/handler/diagnostics.go b/internal/handler/diagnostics.go new file mode 100644 index 00000000..f10293d8 --- /dev/null +++ b/internal/handler/diagnostics.go @@ -0,0 +1,17 @@ +package handler + +import ( + "context" + + lsp "go.lsp.dev/protocol" +) + +func (h *langHandler) publishDiagnostics(ctx context.Context, notifications []lsp.PublishDiagnosticsParams) { + for _, notification := range notifications { + logger.Debug("Publishing diagnostics notification ", notification) + err := h.client.PublishDiagnostics(ctx, ¬ification) + if err != nil { + logger.Error("Error publishing diagnostics ", err) + } + } +} diff --git a/internal/handler/hover_main_test.go b/internal/handler/hover_main_test.go index f0dde49e..8eeba435 100644 --- a/internal/handler/hover_main_test.go +++ b/internal/handler/hover_main_test.go @@ -23,6 +23,15 @@ func TestHoverMain(t *testing.T) { expected string expectedError error }{ + { + desc: "Test hover on dot", + position: lsp.Position{ + Line: 17, + Character: 19, + }, + expected: fmt.Sprintf("### %s\n%s\n\n\n", filepath.Join("..", "..", "testdata", "example", "values.yaml"), "{}"), + expectedError: nil, + }, { desc: "Test hover on function", position: lsp.Position{ @@ -59,15 +68,6 @@ func TestHoverMain(t *testing.T) { expected: "Name of the chart\n\nexample\n", expectedError: nil, }, - { - desc: "Test hover on dot", - position: lsp.Position{ - Line: 17, - Character: 19, - }, - expected: fmt.Sprintf("### %s\n%s\n\n\n", filepath.Join("..", "..", "testdata", "example", "values.yaml"), "{}"), - expectedError: nil, - }, { desc: "Test hover on .Files function", position: lsp.Position{ @@ -148,6 +148,9 @@ func TestHoverMain(t *testing.T) { }, }) assert.Equal(t, tt.expectedError, err) + if result == nil { + t.Fatal("Result is nil") + } assert.Equal(t, tt.expected, result.Contents.Value) }) } diff --git a/internal/handler/text_document.go b/internal/handler/text_document.go index d8c75a8e..a2ac6b2a 100644 --- a/internal/handler/text_document.go +++ b/internal/handler/text_document.go @@ -28,12 +28,14 @@ func (h *langHandler) DidOpen(ctx context.Context, params *lsp.DidOpenTextDocume if err != nil { logger.Error("Error getting chart info for file", doc.URI, err) } - notification := lsplocal.GetDiagnosticsNotification(chart, doc) + notifications := lsplocal.GetDiagnosticsNotifications(chart, doc) - return h.client.PublishDiagnostics(ctx, notification) + defer h.publishDiagnostics(ctx, notifications) + + return nil } -func (h *langHandler) DidClose(ctx context.Context, params *lsp.DidCloseTextDocumentParams) (err error) { +func (h *langHandler) DidClose(_ context.Context, _ *lsp.DidCloseTextDocumentParams) (err error) { return nil } @@ -48,9 +50,11 @@ func (h *langHandler) DidSave(ctx context.Context, params *lsp.DidSaveTextDocume } h.yamllsConnector.DocumentDidSave(doc, *params) - notification := lsplocal.GetDiagnosticsNotification(chart, doc) + notifications := lsplocal.GetDiagnosticsNotifications(chart, doc) - return h.client.PublishDiagnostics(ctx, notification) + defer h.publishDiagnostics(ctx, notifications) + + return nil } func (h *langHandler) DidChange(ctx context.Context, params *lsp.DidChangeTextDocumentParams) (err error) { diff --git a/internal/language_features/template_context.go b/internal/language_features/template_context.go index 8dcf15af..01da658e 100644 --- a/internal/language_features/template_context.go +++ b/internal/language_features/template_context.go @@ -3,7 +3,6 @@ package languagefeatures import ( "fmt" "reflect" - "strings" lsp "go.lsp.dev/protocol" @@ -12,7 +11,7 @@ import ( "github.com/mrjosh/helm-ls/internal/protocol" "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" "github.com/mrjosh/helm-ls/internal/util" - "github.com/mrjosh/helm-ls/pkg/chart" + "helm.sh/helm/v3/pkg/chart" ) type TemplateContextFeature struct { @@ -105,7 +104,7 @@ func (f *TemplateContextFeature) valuesHover(templateContext lsplocal.TemplateCo ) for _, valuesFiles := range valuesFiles { for _, valuesFile := range valuesFiles.ValuesFiles.AllValuesFiles() { - result, err := util.GetTableOrValueForSelector(valuesFile.Values, strings.Join(valuesFiles.Selector, ".")) + result, err := util.GetTableOrValueForSelector(valuesFile.Values, valuesFiles.Selector) if err == nil { hoverResults = append(hoverResults, protocol.HoverResultWithFile{URI: valuesFile.URI, Value: result}) } diff --git a/internal/language_features/template_context_completion_test.go b/internal/language_features/template_context_completion_test.go index 86cb17f9..3ee0a594 100644 --- a/internal/language_features/template_context_completion_test.go +++ b/internal/language_features/template_context_completion_test.go @@ -4,9 +4,10 @@ import ( "testing" "github.com/mrjosh/helm-ls/internal/charts" - "github.com/mrjosh/helm-ls/pkg/chart" + "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" + "helm.sh/helm/v3/pkg/chart" ) func TestGetValuesCompletions(t *testing.T) { diff --git a/internal/language_features/template_context_hover_test.go b/internal/language_features/template_context_hover_test.go index 47af0098..159035df 100644 --- a/internal/language_features/template_context_hover_test.go +++ b/internal/language_features/template_context_hover_test.go @@ -5,9 +5,9 @@ import ( "testing" "github.com/mrjosh/helm-ls/internal/charts" - "github.com/mrjosh/helm-ls/pkg/chart" "github.com/stretchr/testify/assert" "go.lsp.dev/uri" + "helm.sh/helm/v3/pkg/chart" ) func Test_langHandler_getValueHover(t *testing.T) { @@ -234,6 +234,28 @@ value want: `### values.yaml 1.2345 +`, + wantErr: false, + }, + { + name: "Lookup in list", + args: args{ + chart: &charts.Chart{ + ChartMetadata: &charts.ChartMetadata{}, + ValuesFiles: &charts.ValuesFiles{ + MainValuesFile: &charts.ValuesFile{ + Values: map[string]interface{}{ + "key": []interface{}{"hello"}, + }, + URI: "file://tmp/values.yaml", + }, + }, + }, + splittedVar: []string{"key[]"}, + }, + want: `### values.yaml +hello + `, wantErr: false, }, diff --git a/internal/language_features/variables.go b/internal/language_features/variables.go index e6ebca61..19d493d9 100644 --- a/internal/language_features/variables.go +++ b/internal/language_features/variables.go @@ -26,7 +26,7 @@ func (f *VariablesFeature) AppropriateForNode() bool { func (f *VariablesFeature) Definition() (result []lsp.Location, err error) { variableName := f.GenericDocumentUseCase.NodeContent() - definitionNode := lsplocal.GetVariableDefinition(variableName, f.GenericDocumentUseCase.ParentNode, f.Document.Content) + definitionNode := lsplocal.GetVariableDefinition(variableName, f.GenericDocumentUseCase.Node, f.Document.Content) if definitionNode == nil { return []lsp.Location{}, fmt.Errorf("Could not find definition for %s. Variable definition not found", variableName) } diff --git a/internal/lsp/ast.go b/internal/lsp/ast.go index c90ccea7..c258829b 100644 --- a/internal/lsp/ast.go +++ b/internal/lsp/ast.go @@ -29,16 +29,6 @@ func NestedNodeAtPositionForCompletion(tree *sitter.Tree, position lsp.Position) return FindRelevantChildNodeCompletion(currentNode, pointToLoopUp) } -func FindDirectChildNodeByStart(currentNode *sitter.Node, pointToLookUp sitter.Point) *sitter.Node { - for i := 0; i < int(currentNode.ChildCount()); i++ { - child := currentNode.Child(i) - if child.StartPoint().Column == pointToLookUp.Column && child.StartPoint().Row == pointToLookUp.Row { - return child - } - } - return currentNode -} - func FindRelevantChildNode(currentNode *sitter.Node, pointToLookUp sitter.Point) *sitter.Node { for i := 0; i < int(currentNode.ChildCount()); i++ { child := currentNode.Child(i) diff --git a/internal/lsp/ast_variable.go b/internal/lsp/ast_variable.go index 696c9540..f0fb2cfa 100644 --- a/internal/lsp/ast_variable.go +++ b/internal/lsp/ast_variable.go @@ -5,18 +5,6 @@ import ( sitter "github.com/smacker/go-tree-sitter" ) -func GetVariableDefinitionOfNode(node *sitter.Node, template string) *sitter.Node { - if node == nil { - return nil - } - if node.Type() != gotemplate.NodeTypeVariable { - return nil - } - variableName := node.Child(1).Content([]byte(template)) - - return GetVariableDefinition(variableName, node, template) -} - func GetVariableDefinition(variableName string, node *sitter.Node, template string) *sitter.Node { if node == nil { return nil diff --git a/internal/lsp/ast_variable_test.go b/internal/lsp/ast_variable_test.go index a0cc5585..7fe50412 100644 --- a/internal/lsp/ast_variable_test.go +++ b/internal/lsp/ast_variable_test.go @@ -9,20 +9,18 @@ import ( ) func TestGetVariableDefinitionDirectDecleration(t *testing.T) { - - var template = ` + template := ` {{ $variable := "text" }} {{ $variable }} ` node, err := sitter.ParseCtx(context.Background(), []byte(template), gotemplate.GetLanguage()) - if err != nil { t.Errorf("Parsing did not work") } usageNode := node.NamedChild(1) - definitionNode := GetVariableDefinitionOfNode(usageNode, template) + definitionNode := GetVariableDefinition("variable", usageNode, template) if definitionNode == nil { t.Errorf("Could not get definitionNode") @@ -32,31 +30,29 @@ func TestGetVariableDefinitionDirectDecleration(t *testing.T) { } func TestGetVariableDefinitionOtherDecleration(t *testing.T) { - var template = ` + template := ` {{ $variable := "text" }} {{ $someOther := "text" }} {{ $variable }} ` node, err := sitter.ParseCtx(context.Background(), []byte(template), gotemplate.GetLanguage()) - if err != nil { t.Errorf("Parsing did not work") } usageNode := node.NamedChild(2) - definitionNode := GetVariableDefinitionOfNode(usageNode, template) + definitionNode := GetVariableDefinition("variable", usageNode, template) if definitionNode == nil { t.Errorf("Could not get definitionNode") } else if definitionNode.Content([]byte(template)) != "$variable := \"text\"" { t.Errorf("Definition did not match but was %s", definitionNode.Content([]byte(template))) } - } func TestGetVariableDefinitionRange(t *testing.T) { - var template = `{{ range $index, $element := pipeline }}{{ $index }}{{ $element }}{{ end }}` + template := `{{ range $index, $element := pipeline }}{{ $index }}{{ $element }}{{ end }}` // (template [0, 0] - [1, 0] // (range_action [0, 0] - [0, 75] // (range_variable_definition [0, 9] - [0, 37] @@ -72,7 +68,6 @@ func TestGetVariableDefinitionRange(t *testing.T) { // name: (identifier [0, 56] - [0, 63])))) node, err := sitter.ParseCtx(context.Background(), []byte(template), gotemplate.GetLanguage()) - if err != nil { t.Errorf("Parsing did not work") } @@ -85,7 +80,7 @@ func TestGetVariableDefinitionRange(t *testing.T) { if elementUsageNode.Content([]byte(template)) != "$element" { t.Errorf("Definition did not match but was %s", elementUsageNode.Content([]byte(template))) } - definitionNode := GetVariableDefinitionOfNode(elementUsageNode, template) + definitionNode := GetVariableDefinition("element", elementUsageNode, template) if definitionNode == nil { t.Errorf("Could not get definitionNode") @@ -101,7 +96,7 @@ func TestGetVariableDefinitionRange(t *testing.T) { if indexUsageNode.Content([]byte(template)) != "$index" { t.Errorf("Definition did not match but was %s", indexUsageNode.Content([]byte(template))) } - definitionNode = GetVariableDefinitionOfNode(indexUsageNode, template) + definitionNode = GetVariableDefinition("index", indexUsageNode, template) if definitionNode == nil { t.Errorf("Could not get definitionNode") diff --git a/internal/lsp/lint.go b/internal/lsp/lint.go index a297e787..c449286a 100644 --- a/internal/lsp/lint.go +++ b/internal/lsp/lint.go @@ -2,136 +2,142 @@ package lsp import ( "fmt" + "path/filepath" "strconv" "strings" "github.com/mrjosh/helm-ls/internal/charts" "github.com/mrjosh/helm-ls/internal/log" "github.com/mrjosh/helm-ls/internal/util" - "github.com/mrjosh/helm-ls/pkg/action" - "github.com/mrjosh/helm-ls/pkg/chartutil" - "github.com/mrjosh/helm-ls/pkg/lint/support" "github.com/pkg/errors" - "github.com/mrjosh/helm-ls/pkg/lint/rules" lsp "go.lsp.dev/protocol" "go.lsp.dev/uri" + "helm.sh/helm/v3/pkg/action" + + "helm.sh/helm/v3/pkg/chartutil" + "helm.sh/helm/v3/pkg/lint/support" ) var logger = log.GetLogger() -func GetDiagnosticsNotification(chart *charts.Chart, doc *Document) *lsp.PublishDiagnosticsParams { +func GetDiagnosticsNotifications(chart *charts.Chart, doc *Document) []lsp.PublishDiagnosticsParams { vals := chart.ValuesFiles.MainValuesFile.Values if chart.ValuesFiles.OverlayValuesFile != nil { vals = chartutil.CoalesceTables(chart.ValuesFiles.OverlayValuesFile.Values, chart.ValuesFiles.MainValuesFile.Values) } - diagnostics := GetDiagnostics(doc.URI, vals) - doc.DiagnosticsCache.HelmDiagnostics = diagnostics - - return &lsp.PublishDiagnosticsParams{ - URI: doc.URI, - Diagnostics: doc.DiagnosticsCache.GetMergedDiagnostics(), + diagnostics := GetDiagnostics(chart.RootURI, vals) + + // Update the diagnostics cache only for the currently opened document + // as it will also get diagnostics from yamlls + // if currentDocDiagnostics is empty it means that all issues in that file have been fixed + // we need to send this to the client + currentDocDiagnostics := diagnostics[string(doc.URI.Filename())] + doc.DiagnosticsCache.HelmDiagnostics = currentDocDiagnostics + diagnostics[string(doc.URI.Filename())] = doc.DiagnosticsCache.GetMergedDiagnostics() + + result := []lsp.PublishDiagnosticsParams{} + + for diagnosticsURI, diagnostics := range diagnostics { + result = append(result, + lsp.PublishDiagnosticsParams{ + URI: uri.File(diagnosticsURI), + Diagnostics: diagnostics, + }, + ) } + + return result } -// GetDiagnostics will run helm linter against the currect document URI using the given values +// GetDiagnostics will run helm linter against the chart root URI using the given values // and converts the helm.support.Message to lsp.Diagnostics -func GetDiagnostics(uri uri.URI, vals chartutil.Values) []lsp.Diagnostic { - var ( - filename = uri.Filename() - paths = strings.Split(filename, "/") - dir = strings.Join(paths, "/") - diagnostics = make([]lsp.Diagnostic, 0) - ) - - pathfile := "" - - for i, p := range paths { - if p == "templates" { - dir = strings.Join(paths[0:i], "/") - pathfile = strings.Join(paths[i:], "/") - } - } +func GetDiagnostics(rootURI uri.URI, vals chartutil.Values) map[string][]lsp.Diagnostic { + diagnostics := map[string][]lsp.Diagnostic{} client := action.NewLint() - result := client.Run([]string{dir}, vals) - logger.Println(fmt.Sprintf("helm lint: result for file %s : %s", uri, result.Messages)) + result := client.Run([]string{rootURI.Filename()}, vals) for _, msg := range result.Messages { - d, filename, err := GetDiagnosticFromLinterErr(msg) - if err != nil { - continue + d, relativeFilePath, _ := GetDiagnosticFromLinterErr(msg) + absoluteFilePath := filepath.Join(rootURI.Filename(), string(relativeFilePath)) + if d != nil { + diagnostics[absoluteFilePath] = append(diagnostics[absoluteFilePath], *d) } - if filename != pathfile { - continue - } - diagnostics = append(diagnostics, *d) } + logger.Println(fmt.Sprintf("helm lint: result for chart %s : %v", rootURI.Filename(), diagnostics)) return diagnostics } func GetDiagnosticFromLinterErr(supMsg support.Message) (*lsp.Diagnostic, string, error) { - var ( - err error - msg string - line = 1 - severity lsp.DiagnosticSeverity - filename = getFilePathFromLinterErr(supMsg) - ) - - switch supMsg.Severity { - case support.ErrorSev: - - severity = lsp.DiagnosticSeverityError - - if superr, ok := supMsg.Err.(*rules.YAMLToJSONParseError); ok { - - line = superr.Line - msg = superr.Error() - - } else { - - fileLine := util.BetweenStrings(supMsg.Error(), "(", ")") - fileLineArr := strings.Split(fileLine, ":") - if len(fileLineArr) < 2 { - return nil, filename, errors.Errorf("linter Err contains no position information") - } - lineStr := fileLineArr[1] - line, err = strconv.Atoi(lineStr) - if err != nil { - return nil, filename, err - } - msgStr := util.AfterStrings(supMsg.Error(), "):") - msg = strings.TrimSpace(msgStr) + severity := parseSeverity(supMsg) + if strings.HasPrefix(supMsg.Path, "templates") { + message, err := parseTemplatesMessage(supMsg, severity) + path := getFilePathFromLinterErr(supMsg) + if err != nil { + return nil, "", err } + return &message, path, nil + } - case support.WarningSev: - - severity = lsp.DiagnosticSeverityWarning - if err, ok := supMsg.Err.(*rules.MetadataError); ok { - line = 1 - msg = err.Details().Error() - } - - case support.InfoSev: + message := string(supMsg.Err.Error()) + // NOTE: The diagnostics may not be shown correctly in the Chart.yaml file in neovim + // because the lsp is not active for that file + if supMsg.Path == "Chart.yaml" || strings.Contains(message, "chart metadata") { + return &lsp.Diagnostic{ + Severity: severity, + Source: "Helm lint", + Message: message, + }, "Chart.yaml", nil + } - severity = lsp.DiagnosticSeverityInformation - msg = supMsg.Err.Error() + return nil, "", nil +} +func parseTemplatesMessage(supMsg support.Message, severity lsp.DiagnosticSeverity) (lsp.Diagnostic, error) { + var ( + err error + line int + fileLine = util.BetweenStrings(supMsg.Error(), "(", ")") + fileLineArr = strings.Split(fileLine, ":") + ) + if len(fileLineArr) < 2 { + return lsp.Diagnostic{}, errors.Errorf("linter Err contains no position information") + } + lineStr := fileLineArr[1] + line, err = strconv.Atoi(lineStr) + if err != nil { + return lsp.Diagnostic{}, err } + msgStr := util.AfterStrings(supMsg.Error(), "):") + msg := strings.TrimSpace(msgStr) - return &lsp.Diagnostic{ + return lsp.Diagnostic{ Range: lsp.Range{ Start: lsp.Position{Line: uint32(line - 1)}, End: lsp.Position{Line: uint32(line - 1)}, }, Severity: severity, + Source: "Helm lint", Message: msg, - }, filename, nil + }, nil +} + +func parseSeverity(supMsg support.Message) lsp.DiagnosticSeverity { + var severity lsp.DiagnosticSeverity + switch supMsg.Severity { + case support.ErrorSev: + severity = lsp.DiagnosticSeverityError + case support.WarningSev: + severity = lsp.DiagnosticSeverityWarning + case support.InfoSev: + severity = lsp.DiagnosticSeverityInformation + } + return severity } func getFilePathFromLinterErr(msg support.Message) string { diff --git a/internal/lsp/lint_test.go b/internal/lsp/lint_test.go new file mode 100644 index 00000000..ba8968ff --- /dev/null +++ b/internal/lsp/lint_test.go @@ -0,0 +1,65 @@ +package lsp + +import ( + "testing" + + "github.com/mrjosh/helm-ls/internal/charts" + "github.com/stretchr/testify/assert" + "go.lsp.dev/uri" + "helm.sh/helm/v3/pkg/chartutil" +) + +func TestLint(t *testing.T) { + diagnostics := GetDiagnostics(uri.File("../../testdata/example"), chartutil.Values{}) + assert.NotEmpty(t, diagnostics) + assert.Len(t, diagnostics, 2) + assert.Len(t, diagnostics[uri.File("../../testdata/example/Chart.yaml").Filename()], 1) +} + +func TestLintNotifications(t *testing.T) { + chart := charts.Chart{ + RootURI: uri.File("../../testdata/example"), + ValuesFiles: &charts.ValuesFiles{ + MainValuesFile: &charts.ValuesFile{}, + OverlayValuesFile: &charts.ValuesFile{}, + AdditionalValuesFiles: []*charts.ValuesFile{}, + }, + } + diagnostics := GetDiagnosticsNotifications(&chart, &Document{URI: uri.File("../../testdata/example/templates/deployment-no-templates.yaml")}) + assert.NotEmpty(t, diagnostics) + assert.Len(t, diagnostics, 3) + + uris := []string{} + for _, notification := range diagnostics { + uris = append(uris, notification.URI.Filename()) + } + assert.Contains(t, uris, uri.File("../../testdata/example/templates/deployment-no-templates.yaml").Filename()) + for _, notification := range diagnostics { + if notification.URI.Filename() == uri.File("../../testdata/example/templates/deployment-no-templates.yaml").Filename() { + assert.Empty(t, notification.Diagnostics) + } + } +} + +func TestLintNotificationsIncludesEmptyDiagnosticsForFixedIssues(t *testing.T) { + chart := charts.Chart{ + RootURI: uri.File("../../testdata/example"), + ValuesFiles: &charts.ValuesFiles{ + MainValuesFile: &charts.ValuesFile{}, + OverlayValuesFile: &charts.ValuesFile{}, + AdditionalValuesFiles: []*charts.ValuesFile{}, + }, + } + diagnostics := GetDiagnosticsNotifications(&chart, &Document{URI: uri.File("../../testdata/example/templates/deployment-no-templates.yaml")}) + + uris := []string{} + for _, notification := range diagnostics { + uris = append(uris, notification.URI.Filename()) + } + assert.Contains(t, uris, uri.File("../../testdata/example/templates/deployment-no-templates.yaml").Filename()) + for _, notification := range diagnostics { + if notification.URI.Filename() == uri.File("../../testdata/example/templates/deployment-no-templates.yaml").Filename() { + assert.Empty(t, notification.Diagnostics) + } + } +} diff --git a/internal/protocol/completion.go b/internal/protocol/completion.go index 5ff66033..2ddbe4f0 100644 --- a/internal/protocol/completion.go +++ b/internal/protocol/completion.go @@ -33,12 +33,12 @@ func (c CompletionResults) WithDocs(docs []helmdocs.HelmDocumentation, kind lsp. func (c CompletionResults) WithSnippets(snippets []godocs.GoTemplateSnippet) CompletionResults { items := c.Items for _, snippet := range snippets { - items = append(items, textCompletionItem(snippet)) + items = append(items, snippetCompletionItem(snippet)) } return CompletionResults{Items: items} } -func textCompletionItem(gotemplateSnippet godocs.GoTemplateSnippet) lsp.CompletionItem { +func snippetCompletionItem(gotemplateSnippet godocs.GoTemplateSnippet) lsp.CompletionItem { return lsp.CompletionItem{ Label: gotemplateSnippet.Name, InsertText: gotemplateSnippet.Snippet, @@ -48,10 +48,3 @@ func textCompletionItem(gotemplateSnippet godocs.GoTemplateSnippet) lsp.Completi InsertTextFormat: lsp.InsertTextFormatSnippet, } } - -func GetTextCompletionItems(gotemplateSnippet []godocs.GoTemplateSnippet) (result []lsp.CompletionItem) { - for _, item := range gotemplateSnippet { - result = append(result, textCompletionItem(item)) - } - return result -} diff --git a/internal/util/values.go b/internal/util/values.go index ac4e28bd..d13496cb 100644 --- a/internal/util/values.go +++ b/internal/util/values.go @@ -5,39 +5,34 @@ import ( "reflect" "strings" - "github.com/mrjosh/helm-ls/pkg/chartutil" lsp "go.lsp.dev/protocol" "gopkg.in/yaml.v2" + "helm.sh/helm/v3/pkg/chartutil" ) -func GetTableOrValueForSelector(values chartutil.Values, selector string) (string, error) { - if len(selector) > 0 { - localValues, err := values.Table(selector) - if err != nil { - value, err := values.PathValue(selector) - return FormatToYAML(reflect.Indirect(reflect.ValueOf(value)), selector), err - } - return localValues.YAML() +func GetTableOrValueForSelector(values chartutil.Values, selector []string) (string, error) { + if len(selector) <= 0 || selector[0] == "" { + return values.YAML() } - return values.YAML() + value, err := pathLookup(values, selector) + return FormatToYAML(reflect.Indirect(reflect.ValueOf(value)), strings.Join(selector, ".")), err } func GetValueCompletion(values chartutil.Values, splittedVar []string) []lsp.CompletionItem { var ( err error - tableName = strings.Join(splittedVar, ".") localValues chartutil.Values items = make([]lsp.CompletionItem, 0) ) if len(splittedVar) > 0 { - localValues, err = values.Table(tableName) + localValues, err = valuesLookup(values, splittedVar) if err != nil { if len(splittedVar) > 1 { // the current tableName was not found, maybe because it is incomplete, we can use the previous one // e.g. gobal.im -> im was not found // but global contains the key image, so we return all keys of global - localValues, err = values.Table(strings.Join(splittedVar[:len(splittedVar)-1], ".")) + localValues, err = valuesLookup(values, splittedVar[:len(splittedVar)-1]) if err != nil { return []lsp.CompletionItem{} } @@ -55,6 +50,76 @@ func GetValueCompletion(values chartutil.Values, splittedVar []string) []lsp.Com return items } +func valuesLookup(values chartutil.Values, splittedVar []string) (chartutil.Values, error) { + result, err := pathLookup(values, splittedVar) + if err != nil { + return chartutil.Values{}, err + } + + values, ok := result.(map[string]interface{}) + if ok { + return values, nil + } + + return chartutil.Values{}, chartutil.ErrNoTable{Key: splittedVar[0]} +} + +// PathValue takes a path that traverses a YAML structure and returns the value at the end of that path. +// The path starts at the root of the YAML structure and is comprised of YAML keys separated by periods. +// Given the following YAML data the value at path "chapter.one.title" is "Loomings". The path can also +// include array indexes as in "chapters[].title" which will use the first element of the array. +// +// chapter: +// one: +// title: "Loomings" +func pathLookup(v chartutil.Values, path []string) (interface{}, error) { + if len(path) == 0 { + return v, nil + } + if strings.HasSuffix(path[0], "[]") { + return arrayLookup(v, path) + } + // if exists must be root key not table + value, ok := v[path[0]] + if !ok { + return nil, chartutil.ErrNoTable{Key: path[0]} + } + if len(path) == 1 { + return value, nil + } + if nestedValues, ok := value.(map[string]interface{}); ok { + return pathLookup(nestedValues, path[1:]) + } + return nil, chartutil.ErrNoTable{Key: path[0]} +} + +func arrayLookup(v chartutil.Values, path []string) (interface{}, error) { + v2, ok := v[path[0][:(len(path[0])-2)]] + if !ok { + return v, chartutil.ErrNoTable{Key: fmt.Sprintf("Yaml key %s does not exist", path[0])} + } + if v3, ok := v2.([]interface{}); ok { + if len(v3) == 0 { + return chartutil.Values{}, ErrEmpytArray{path[0]} + } + if len(path) == 1 { + return v3[0], nil + } + if vv, ok := v3[0].(map[string]interface{}); ok { + return pathLookup(vv, path[1:]) + } + return chartutil.Values{}, chartutil.ErrNoTable{Key: path[0]} + } + + return chartutil.Values{}, chartutil.ErrNoTable{Key: path[0]} +} + +type ErrEmpytArray struct { + Key string +} + +func (e ErrEmpytArray) Error() string { return fmt.Sprintf("%q is an empyt array", e.Key) } + func builCompletionItem(value interface{}, variable string) lsp.CompletionItem { var ( itemKind = lsp.CompletionItemKindVariable @@ -99,6 +164,8 @@ func FormatToYAML(field reflect.Value, fieldName string) string { return fmt.Sprint(GetBoolType(field)) case reflect.Float32, reflect.Float64: return fmt.Sprint(field.Float()) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return fmt.Sprint(field.Int()) default: return "" } diff --git a/internal/util/values_test.go b/internal/util/values_test.go index 90116abd..6beb1188 100644 --- a/internal/util/values_test.go +++ b/internal/util/values_test.go @@ -7,10 +7,10 @@ import ( ) func TestEmptyValues(t *testing.T) { - _, err := GetTableOrValueForSelector(make(map[string]interface{}), "global") + _, err := GetTableOrValueForSelector(make(map[string]interface{}), []string{"global"}) assert.Error(t, err) - result, err := GetTableOrValueForSelector(make(map[string]interface{}), "") + result, err := GetTableOrValueForSelector(make(map[string]interface{}), []string{""}) assert.NoError(t, err) assert.Equal(t, "{}\n", result) } @@ -19,29 +19,48 @@ func TestValues(t *testing.T) { nested := map[string]interface{}{"nested": "value"} values := map[string]interface{}{"global": nested} - result := GetValueCompletion(values, []string{"g"}) + result := GetValueCompletion(values, []string{"global", "nes"}) assert.Len(t, result, 1) - assert.Equal(t, "global", result[0].InsertText) + assert.Equal(t, "nested", result[0].InsertText) - result = GetValueCompletion(values, []string{""}) + result = GetValueCompletion(values, []string{"g"}) assert.Len(t, result, 1) assert.Equal(t, "global", result[0].InsertText) - result = GetValueCompletion(values, []string{"global", "nes"}) + result = GetValueCompletion(values, []string{""}) assert.Len(t, result, 1) - assert.Equal(t, "nested", result[0].InsertText) + assert.Equal(t, "global", result[0].InsertText) } func TestWrongValues(t *testing.T) { nested := map[string]interface{}{"nested": 1} values := map[string]interface{}{"global": nested} - _, err := GetTableOrValueForSelector(values, "some.wrong.values") + _, err := GetTableOrValueForSelector(values, []string{"some", "wrong", "values"}) assert.Error(t, err) - _, err = GetTableOrValueForSelector(values, "some.wrong") + _, err = GetTableOrValueForSelector(values, []string{"some", "wrong"}) assert.Error(t, err) - _, err = GetTableOrValueForSelector(values, "some") + _, err = GetTableOrValueForSelector(values, []string{"some"}) assert.Error(t, err) } + +func TestValuesList(t *testing.T) { + nested := []interface{}{1, 2, 3} + values := map[string]interface{}{"global": nested} + + result, err := GetTableOrValueForSelector(values, []string{"global[]"}) + assert.NoError(t, err) + assert.Equal(t, "1", result) +} + +func TestValuesListNested(t *testing.T) { + doubleNested := []interface{}{1, 2, 3} + nested := map[string]interface{}{"nested": doubleNested} + values := map[string]interface{}{"global": nested} + + result, err := GetTableOrValueForSelector(values, []string{"global", "nested[]"}) + assert.NoError(t, err) + assert.Equal(t, "1", result) +} diff --git a/internal/util/yaml.go b/internal/util/yaml.go index 6bef26f2..c2c23ed3 100644 --- a/internal/util/yaml.go +++ b/internal/util/yaml.go @@ -2,6 +2,7 @@ package util import ( "fmt" + "os" "strings" lsp "go.lsp.dev/protocol" @@ -44,3 +45,14 @@ func GetPositionOfNode(node *yamlv3.Node, query []string) (lsp.Position, error) } return lsp.Position{}, fmt.Errorf("could not find Position of %s in values.yaml. Found no match", query) } + +// ReadYamlFileToNode will parse a YAML file into a yaml Node. +func ReadYamlFileToNode(filename string) (node yamlv3.Node, err error) { + data, err := os.ReadFile(filename) + if err != nil { + return yamlv3.Node{}, err + } + + err = yamlv3.Unmarshal(data, &node) + return node, err +} diff --git a/pkg/action/lint.go b/pkg/action/lint.go deleted file mode 100644 index c14eae7b..00000000 --- a/pkg/action/lint.go +++ /dev/null @@ -1,106 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "os" - "path/filepath" - - "github.com/pkg/errors" - - "github.com/mrjosh/helm-ls/pkg/lint" - "github.com/mrjosh/helm-ls/pkg/lint/support" -) - -// Lint is the action for checking that the semantics of a chart are well-formed. -// -// It provides the implementation of 'helm lint'. -type Lint struct { - Strict bool - Namespace string - WithSubcharts bool - Quiet bool -} - -// LintResult is the result of Lint -type LintResult struct { - TotalChartsLinted int - Messages []support.Message - Errors []error -} - -// NewLint creates a new Lint object with the given configuration. -func NewLint() *Lint { - return &Lint{} -} - -// Run executes 'helm Lint' against the given chart. -func (l *Lint) Run(paths []string, vals map[string]interface{}) *LintResult { - - lowestTolerance := support.ErrorSev - if l.Strict { - lowestTolerance = support.WarningSev - } - - result := &LintResult{} - for _, path := range paths { - - linter, err := lintChart(path, vals, l.Namespace, l.Strict) - if err != nil { - result.Errors = append(result.Errors, err) - continue - } - - result.Messages = append(result.Messages, linter.Messages...) - result.TotalChartsLinted++ - for _, msg := range linter.Messages { - if msg.Severity >= lowestTolerance { - result.Errors = append(result.Errors, msg.Err) - } - } - - } - - return result -} - -// HasWaringsOrErrors checks is LintResult has any warnings or errors -func HasWarningsOrErrors(result *LintResult) bool { - for _, msg := range result.Messages { - if msg.Severity > support.InfoSev { - return true - } - } - return false -} - -func lintChart( - path string, - vals map[string]interface{}, - namespace string, - strict bool, -) (support.Linter, error) { - - linter := support.Linter{} - - // Guard: Error out if this is not a chart. - if _, err := os.Stat(filepath.Join(path, "Chart.yaml")); err != nil { - return linter, errors.Wrap(err, "unable to check Chart.yaml file in chart") - } - - return lint.All(path, vals, namespace, strict), nil -} diff --git a/pkg/chart/chart.go b/pkg/chart/chart.go deleted file mode 100644 index a3bed63a..00000000 --- a/pkg/chart/chart.go +++ /dev/null @@ -1,173 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chart - -import ( - "path/filepath" - "regexp" - "strings" -) - -// APIVersionV1 is the API version number for version 1. -const APIVersionV1 = "v1" - -// APIVersionV2 is the API version number for version 2. -const APIVersionV2 = "v2" - -// aliasNameFormat defines the characters that are legal in an alias name. -var aliasNameFormat = regexp.MustCompile("^[a-zA-Z0-9_-]+$") - -// Chart is a helm package that contains metadata, a default config, zero or more -// optionally parameterizable templates, and zero or more charts (dependencies). -type Chart struct { - // Raw contains the raw contents of the files originally contained in the chart archive. - // - // This should not be used except in special cases like `helm show values`, - // where we want to display the raw values, comments and all. - Raw []*File `json:"-"` - // Metadata is the contents of the Chartfile. - Metadata *Metadata `json:"metadata"` - // Lock is the contents of Chart.lock. - Lock *Lock `json:"lock"` - // Templates for this chart. - Templates []*File `json:"templates"` - // Values are default config for this chart. - Values map[string]interface{} `json:"values"` - // Schema is an optional JSON schema for imposing structure on Values - Schema []byte `json:"schema"` - // Files are miscellaneous files in a chart archive, - // e.g. README, LICENSE, etc. - Files []*File `json:"files"` - - parent *Chart - dependencies []*Chart -} - -type CRD struct { - // Name is the File.Name for the crd file - Name string - // Filename is the File obj Name including (sub-)chart.ChartFullPath - Filename string - // File is the File obj for the crd - File *File -} - -// SetDependencies replaces the chart dependencies. -func (ch *Chart) SetDependencies(charts ...*Chart) { - ch.dependencies = nil - ch.AddDependency(charts...) -} - -// Name returns the name of the chart. -func (ch *Chart) Name() string { - if ch.Metadata == nil { - return "" - } - return ch.Metadata.Name -} - -// AddDependency determines if the chart is a subchart. -func (ch *Chart) AddDependency(charts ...*Chart) { - for i, x := range charts { - charts[i].parent = ch - ch.dependencies = append(ch.dependencies, x) - } -} - -// Root finds the root chart. -func (ch *Chart) Root() *Chart { - if ch.IsRoot() { - return ch - } - return ch.Parent().Root() -} - -// Dependencies are the charts that this chart depends on. -func (ch *Chart) Dependencies() []*Chart { return ch.dependencies } - -// IsRoot determines if the chart is the root chart. -func (ch *Chart) IsRoot() bool { return ch.parent == nil } - -// Parent returns a subchart's parent chart. -func (ch *Chart) Parent() *Chart { return ch.parent } - -// ChartPath returns the full path to this chart in dot notation. -func (ch *Chart) ChartPath() string { - if !ch.IsRoot() { - return ch.Parent().ChartPath() + "." + ch.Name() - } - return ch.Name() -} - -// ChartFullPath returns the full path to this chart. -func (ch *Chart) ChartFullPath() string { - if !ch.IsRoot() { - return ch.Parent().ChartFullPath() + "/charts/" + ch.Name() - } - return ch.Name() -} - -// Validate validates the metadata. -func (ch *Chart) Validate() error { - return ch.Metadata.Validate() -} - -// AppVersion returns the appversion of the chart. -func (ch *Chart) AppVersion() string { - if ch.Metadata == nil { - return "" - } - return ch.Metadata.AppVersion -} - -// CRDs returns a list of File objects in the 'crds/' directory of a Helm chart. -// Deprecated: use CRDObjects() -func (ch *Chart) CRDs() []*File { - files := []*File{} - // Find all resources in the crds/ directory - for _, f := range ch.Files { - if strings.HasPrefix(f.Name, "crds/") && hasManifestExtension(f.Name) { - files = append(files, f) - } - } - // Get CRDs from dependencies, too. - for _, dep := range ch.Dependencies() { - files = append(files, dep.CRDs()...) - } - return files -} - -// CRDObjects returns a list of CRD objects in the 'crds/' directory of a Helm chart & subcharts -func (ch *Chart) CRDObjects() []CRD { - crds := []CRD{} - // Find all resources in the crds/ directory - for _, f := range ch.Files { - if strings.HasPrefix(f.Name, "crds/") && hasManifestExtension(f.Name) { - mycrd := CRD{Name: f.Name, Filename: filepath.Join(ch.ChartFullPath(), f.Name), File: f} - crds = append(crds, mycrd) - } - } - // Get CRDs from dependencies, too. - for _, dep := range ch.Dependencies() { - crds = append(crds, dep.CRDObjects()...) - } - return crds -} - -func hasManifestExtension(fname string) bool { - ext := filepath.Ext(fname) - return strings.EqualFold(ext, ".yaml") || strings.EqualFold(ext, ".yml") || strings.EqualFold(ext, ".json") -} diff --git a/pkg/chart/dependency.go b/pkg/chart/dependency.go deleted file mode 100644 index b2819f37..00000000 --- a/pkg/chart/dependency.go +++ /dev/null @@ -1,79 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chart - -import "time" - -// Dependency describes a chart upon which another chart depends. -// -// Dependencies can be used to express developer intent, or to capture the state -// of a chart. -type Dependency struct { - // Name is the name of the dependency. - // - // This must mach the name in the dependency's Chart.yaml. - Name string `json:"name"` - // Version is the version (range) of this chart. - // - // A lock file will always produce a single version, while a dependency - // may contain a semantic version range. - Version string `json:"version,omitempty"` - // The URL to the repository. - // - // Appending `index.yaml` to this string should result in a URL that can be - // used to fetch the repository index. - Repository string `json:"repository"` - // A yaml path that resolves to a boolean, used for enabling/disabling charts (e.g. subchart1.enabled ) - Condition string `json:"condition,omitempty"` - // Tags can be used to group charts for enabling/disabling together - Tags []string `json:"tags,omitempty"` - // Enabled bool determines if chart should be loaded - Enabled bool `json:"enabled,omitempty"` - // ImportValues holds the mapping of source values to parent key to be imported. Each item can be a - // string or pair of child/parent sublist items. - ImportValues []interface{} `json:"import-values,omitempty"` - // Alias usable alias to be used for the chart - Alias string `json:"alias,omitempty"` -} - -// Validate checks for common problems with the dependency datastructure in -// the chart. This check must be done at load time before the dependency's charts are -// loaded. -func (d *Dependency) Validate() error { - d.Name = sanitizeString(d.Name) - d.Version = sanitizeString(d.Version) - d.Repository = sanitizeString(d.Repository) - d.Condition = sanitizeString(d.Condition) - for i := range d.Tags { - d.Tags[i] = sanitizeString(d.Tags[i]) - } - if d.Alias != "" && !aliasNameFormat.MatchString(d.Alias) { - return ValidationErrorf("dependency %q has disallowed characters in the alias", d.Name) - } - return nil -} - -// Lock is a lock file for dependencies. -// -// It represents the state that the dependencies should be in. -type Lock struct { - // Generated is the date the lock file was last generated. - Generated time.Time `json:"generated"` - // Digest is a hash of the dependencies in Chart.yaml. - Digest string `json:"digest"` - // Dependencies is the list of dependencies that this lock file has locked. - Dependencies []*Dependency `json:"dependencies"` -} diff --git a/pkg/chart/errors.go b/pkg/chart/errors.go deleted file mode 100644 index 2fad5f37..00000000 --- a/pkg/chart/errors.go +++ /dev/null @@ -1,30 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chart - -import "fmt" - -// ValidationError represents a data validation error. -type ValidationError string - -func (v ValidationError) Error() string { - return "validation: " + string(v) -} - -// ValidationErrorf takes a message and formatting options and creates a ValidationError -func ValidationErrorf(msg string, args ...interface{}) ValidationError { - return ValidationError(fmt.Sprintf(msg, args...)) -} diff --git a/pkg/chart/file.go b/pkg/chart/file.go deleted file mode 100644 index 9dd7c08d..00000000 --- a/pkg/chart/file.go +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chart - -// File represents a file as a name/value pair. -// -// By convention, name is a relative path within the scope of the chart's -// base directory. -type File struct { - // Name is the path-like name of the template. - Name string `json:"name"` - // Data is the template as byte data. - Data []byte `json:"data"` -} diff --git a/pkg/chart/loader/archive.go b/pkg/chart/loader/archive.go deleted file mode 100644 index d9e9a52b..00000000 --- a/pkg/chart/loader/archive.go +++ /dev/null @@ -1,204 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package loader - -import ( - "archive/tar" - "bytes" - "compress/gzip" - "fmt" - "io" - "net/http" - "os" - "path" - "regexp" - "strings" - - "github.com/pkg/errors" - - "github.com/mrjosh/helm-ls/pkg/chart" -) - -var drivePathPattern = regexp.MustCompile(`^[a-zA-Z]:/`) - -const ChartFileName = "Chart.yaml" - -// FileLoader loads a chart from a file -type FileLoader string - -// Load loads a chart -func (l FileLoader) Load() (*chart.Chart, error) { - return LoadFile(string(l)) -} - -// LoadFile loads from an archive file. -func LoadFile(name string) (*chart.Chart, error) { - if fi, err := os.Stat(name); err != nil { - return nil, err - } else if fi.IsDir() { - return nil, errors.New("cannot load a directory") - } - - raw, err := os.Open(name) - if err != nil { - return nil, err - } - defer raw.Close() - - err = ensureArchive(name, raw) - if err != nil { - return nil, err - } - - c, err := LoadArchive(raw) - if err != nil { - if err == gzip.ErrHeader { - return nil, fmt.Errorf("file '%s' does not appear to be a valid chart file (details: %s)", name, err) - } - } - return c, err -} - -// ensureArchive's job is to return an informative error if the file does not appear to be a gzipped archive. -// -// Sometimes users will provide a values.yaml for an argument where a chart is expected. One common occurrence -// of this is invoking `helm template values.yaml mychart` which would otherwise produce a confusing error -// if we didn't check for this. -func ensureArchive(name string, raw *os.File) error { - - defer func() { - if _, err := raw.Seek(0, 0); err != nil { - panic(err) - } - // reset read offset to allow archive loading to proceed. - }() - - // Check the file format to give us a chance to provide the user with more actionable feedback. - buffer := make([]byte, 512) - _, err := raw.Read(buffer) - if err != nil && err != io.EOF { - return fmt.Errorf("file '%s' cannot be read: %s", name, err) - } - if contentType := http.DetectContentType(buffer); contentType != "application/x-gzip" { - // TODO: Is there a way to reliably test if a file content is YAML? ghodss/yaml accepts a wide - // variety of content (Makefile, .zshrc) as valid YAML without errors. - - // Wrong content type. Let's check if it's yaml and give an extra hint? - if strings.HasSuffix(name, ".yml") || strings.HasSuffix(name, ".yaml") { - return fmt.Errorf("file '%s' seems to be a YAML file, but expected a gzipped archive", name) - } - return fmt.Errorf("file '%s' does not appear to be a gzipped archive; got '%s'", name, contentType) - } - return nil -} - -// LoadArchiveFiles reads in files out of an archive into memory. This function -// performs important path security checks and should always be used before -// expanding a tarball -func LoadArchiveFiles(in io.Reader) ([]*BufferedFile, error) { - unzipped, err := gzip.NewReader(in) - if err != nil { - return nil, err - } - defer unzipped.Close() - - files := []*BufferedFile{} - tr := tar.NewReader(unzipped) - for { - b := bytes.NewBuffer(nil) - hd, err := tr.Next() - if err == io.EOF { - break - } - if err != nil { - return nil, err - } - - if hd.FileInfo().IsDir() { - // Use this instead of hd.Typeflag because we don't have to do any - // inference chasing. - continue - } - - switch hd.Typeflag { - // We don't want to process these extension header files. - case tar.TypeXGlobalHeader, tar.TypeXHeader: - continue - } - - // Archive could contain \ if generated on Windows - delimiter := "/" - if strings.ContainsRune(hd.Name, '\\') { - delimiter = "\\" - } - - parts := strings.Split(hd.Name, delimiter) - n := strings.Join(parts[1:], delimiter) - - // Normalize the path to the / delimiter - n = strings.ReplaceAll(n, delimiter, "/") - - if path.IsAbs(n) { - return nil, errors.New("chart illegally contains absolute paths") - } - - n = path.Clean(n) - if n == "." { - // In this case, the original path was relative when it should have been absolute. - return nil, errors.Errorf("chart illegally contains content outside the base directory: %q", hd.Name) - } - if strings.HasPrefix(n, "..") { - return nil, errors.New("chart illegally references parent directory") - } - - // In some particularly arcane acts of path creativity, it is possible to intermix - // UNIX and Windows style paths in such a way that you produce a result of the form - // c:/foo even after all the built-in absolute path checks. So we explicitly check - // for this condition. - if drivePathPattern.MatchString(n) { - return nil, errors.New("chart contains illegally named files") - } - - if parts[0] == ChartFileName { - return nil, errors.New("chart yaml not in base directory") - } - - if _, err := io.Copy(b, tr); err != nil { - return nil, err - } - - data := bytes.TrimPrefix(b.Bytes(), utf8bom) - - files = append(files, &BufferedFile{Name: n, Data: data}) - b.Reset() - } - - if len(files) == 0 { - return nil, errors.New("no files in chart archive") - } - return files, nil -} - -// LoadArchive loads from a reader containing a compressed tar archive. -func LoadArchive(in io.Reader) (*chart.Chart, error) { - files, err := LoadArchiveFiles(in) - if err != nil { - return nil, err - } - - return LoadFiles(files) -} diff --git a/pkg/chart/loader/directory.go b/pkg/chart/loader/directory.go deleted file mode 100644 index 552b28cc..00000000 --- a/pkg/chart/loader/directory.go +++ /dev/null @@ -1,119 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package loader - -import ( - "bytes" - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/pkg/errors" - "k8s.io/helm/pkg/ignore" - "k8s.io/helm/pkg/sympath" - - "github.com/mrjosh/helm-ls/pkg/chart" -) - -var utf8bom = []byte{0xEF, 0xBB, 0xBF} - -// DirLoader loads a chart from a directory -type DirLoader string - -// Load loads the chart -func (l DirLoader) Load() (*chart.Chart, error) { - return LoadDir(string(l)) -} - -// LoadDir loads from a directory. -// -// This loads charts only from directories. -func LoadDir(dir string) (*chart.Chart, error) { - topdir, err := filepath.Abs(dir) - if err != nil { - return nil, err - } - - // Just used for errors. - c := &chart.Chart{} - - rules := ignore.Empty() - ifile := filepath.Join(topdir, ignore.HelmIgnore) - if _, err := os.Stat(ifile); err == nil { - r, err := ignore.ParseFile(ifile) - if err != nil { - return c, err - } - rules = r - } - rules.AddDefaults() - - files := []*BufferedFile{} - topdir += string(filepath.Separator) - - walk := func(name string, fi os.FileInfo, err error) error { - n := strings.TrimPrefix(name, topdir) - if n == "" { - // No need to process top level. Avoid bug with helmignore .* matching - // empty names. See issue 1779. - return nil - } - - // Normalize to / since it will also work on Windows - n = filepath.ToSlash(n) - - if err != nil { - return err - } - if fi.IsDir() { - // Directory-based ignore rules should involve skipping the entire - // contents of that directory. - if rules.Ignore(n, fi) { - return filepath.SkipDir - } - return nil - } - - // If a .helmignore file matches, skip this file. - if rules.Ignore(n, fi) { - return nil - } - - // Irregular files include devices, sockets, and other uses of files that - // are not regular files. In Go they have a file mode type bit set. - // See https://golang.org/pkg/os/#FileMode for examples. - if !fi.Mode().IsRegular() { - return fmt.Errorf("cannot load irregular file %s as it has file mode type bits set", name) - } - - data, err := os.ReadFile(name) - if err != nil { - return errors.Wrapf(err, "error reading %s", n) - } - - data = bytes.TrimPrefix(data, utf8bom) - - files = append(files, &BufferedFile{Name: n, Data: data}) - return nil - } - if err = sympath.Walk(topdir, walk); err != nil { - return c, err - } - - return LoadFiles(files) -} diff --git a/pkg/chart/loader/load.go b/pkg/chart/loader/load.go deleted file mode 100644 index 7e177fcd..00000000 --- a/pkg/chart/loader/load.go +++ /dev/null @@ -1,201 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package loader - -import ( - "bytes" - "log" - "os" - "path/filepath" - "strings" - - "github.com/pkg/errors" - "sigs.k8s.io/yaml" - - "github.com/mrjosh/helm-ls/pkg/chart" -) - -// ChartLoader loads a chart. -type ChartLoader interface { - Load() (*chart.Chart, error) -} - -// Loader returns a new ChartLoader appropriate for the given chart name -func Loader(name string) (ChartLoader, error) { - fi, err := os.Stat(name) - if err != nil { - return nil, err - } - if fi.IsDir() { - return DirLoader(name), nil - } - return FileLoader(name), nil - -} - -// Load takes a string name, tries to resolve it to a file or directory, and then loads it. -// -// This is the preferred way to load a chart. It will discover the chart encoding -// and hand off to the appropriate chart reader. -// -// If a .helmignore file is present, the directory loader will skip loading any files -// matching it. But .helmignore is not evaluated when reading out of an archive. -func Load(name string) (*chart.Chart, error) { - l, err := Loader(name) - if err != nil { - return nil, err - } - return l.Load() -} - -// BufferedFile represents an archive file buffered for later processing. -type BufferedFile struct { - Name string - Data []byte -} - -// LoadFiles loads from in-memory files. -//nolint -func LoadFiles(files []*BufferedFile) (*chart.Chart, error) { - c := new(chart.Chart) - subcharts := make(map[string][]*BufferedFile) - - // do not rely on assumed ordering of files in the chart and crash - // if Chart.yaml was not coming early enough to initialize metadata - for _, f := range files { - c.Raw = append(c.Raw, &chart.File{Name: f.Name, Data: f.Data}) - if f.Name == "Chart.yaml" { - if c.Metadata == nil { - c.Metadata = new(chart.Metadata) - } - if err := yaml.Unmarshal(f.Data, c.Metadata); err != nil { - return c, errors.Wrap(err, "cannot load Chart.yaml") - } - // NOTE(bacongobbler): while the chart specification says that APIVersion must be set, - // Helm 2 accepted charts that did not provide an APIVersion in their chart metadata. - // Because of that, if APIVersion is unset, we should assume we're loading a v1 chart. - if c.Metadata.APIVersion == "" { - c.Metadata.APIVersion = chart.APIVersionV1 - } - } - } - for _, f := range files { - switch { - case f.Name == "Chart.yaml": - // already processed - continue - case f.Name == "Chart.lock": - c.Lock = new(chart.Lock) - if err := yaml.Unmarshal(f.Data, &c.Lock); err != nil { - return c, errors.Wrap(err, "cannot load Chart.lock") - } - case f.Name == "values.yaml": - c.Values = make(map[string]interface{}) - if err := yaml.Unmarshal(f.Data, &c.Values); err != nil { - return c, errors.Wrap(err, "cannot load values.yaml") - } - case f.Name == "values.schema.json": - c.Schema = f.Data - - // Deprecated: requirements.yaml is deprecated use Chart.yaml. - // We will handle it for you because we are nice people - case f.Name == "requirements.yaml": - if c.Metadata == nil { - c.Metadata = new(chart.Metadata) - } - if c.Metadata.APIVersion != chart.APIVersionV1 { - log.Printf("Warning: Dependencies are handled in Chart.yaml since apiVersion \"v2\". We recommend migrating dependencies to Chart.yaml.") - } - if err := yaml.Unmarshal(f.Data, c.Metadata); err != nil { - return c, errors.Wrap(err, "cannot load requirements.yaml") - } - if c.Metadata.APIVersion == chart.APIVersionV1 { - c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data}) - } - // Deprecated: requirements.lock is deprecated use Chart.lock. - case f.Name == "requirements.lock": - c.Lock = new(chart.Lock) - if err := yaml.Unmarshal(f.Data, &c.Lock); err != nil { - return c, errors.Wrap(err, "cannot load requirements.lock") - } - if c.Metadata == nil { - c.Metadata = new(chart.Metadata) - } - if c.Metadata.APIVersion == chart.APIVersionV1 { - c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data}) - } - - case strings.HasPrefix(f.Name, "templates/"): - c.Templates = append(c.Templates, &chart.File{Name: f.Name, Data: f.Data}) - case strings.HasPrefix(f.Name, "charts/"): - if filepath.Ext(f.Name) == ".prov" { - c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data}) - continue - } - - fname := strings.TrimPrefix(f.Name, "charts/") - cname := strings.SplitN(fname, "/", 2)[0] - subcharts[cname] = append(subcharts[cname], &BufferedFile{Name: fname, Data: f.Data}) - default: - c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data}) - } - } - - if c.Metadata == nil { - return c, errors.New("Chart.yaml file is missing") - } - - if err := c.Validate(); err != nil { - return c, err - } - - for n, files := range subcharts { - var sc *chart.Chart - var err error - switch { - case strings.IndexAny(n, "_.") == 0: - continue - case filepath.Ext(n) == ".tgz": - file := files[0] - if file.Name != n { - return c, errors.Errorf("error unpacking tar in %s: expected %s, got %s", c.Name(), n, file.Name) - } - // Untar the chart and add to c.Dependencies - sc, err = LoadArchive(bytes.NewBuffer(file.Data)) - default: - // We have to trim the prefix off of every file, and ignore any file - // that is in charts/, but isn't actually a chart. - buff := make([]*BufferedFile, 0, len(files)) - for _, f := range files { - parts := strings.SplitN(f.Name, "/", 2) - if len(parts) < 2 { - continue - } - f.Name = parts[1] - buff = append(buff, f) - } - sc, err = LoadFiles(buff) - } - - if err != nil { - return c, errors.Wrapf(err, "error unpacking %s in %s", n, c.Name()) - } - c.AddDependency(sc) - } - - return c, nil -} diff --git a/pkg/chart/metadata.go b/pkg/chart/metadata.go deleted file mode 100644 index ebf4019f..00000000 --- a/pkg/chart/metadata.go +++ /dev/null @@ -1,161 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chart - -import ( - "strings" - //nolint - "unicode" - - semver "github.com/Masterminds/semver/v3" -) - -// Maintainer describes a Chart maintainer. -type Maintainer struct { - // Name is a user name or organization name - Name string `json:"name,omitempty"` - // Email is an optional email address to contact the named maintainer - Email string `json:"email,omitempty"` - // URL is an optional URL to an address for the named maintainer - URL string `json:"url,omitempty"` -} - -// Validate checks valid data and sanitizes string characters. -func (m *Maintainer) Validate() error { - m.Name = sanitizeString(m.Name) - m.Email = sanitizeString(m.Email) - m.URL = sanitizeString(m.URL) - return nil -} - -// Metadata for a Chart file. This models the structure of a Chart.yaml file. -type Metadata struct { - // The name of the chart. Required. - Name string `json:"name,omitempty"` - // The URL to a relevant project page, git repo, or contact person - Home string `json:"home,omitempty"` - // Source is the URL to the source code of this chart - Sources []string `json:"sources,omitempty"` - // A SemVer 2 conformant version string of the chart. Required. - Version string `json:"version,omitempty"` - // A one-sentence description of the chart - Description string `json:"description,omitempty"` - // A list of string keywords - Keywords []string `json:"keywords,omitempty"` - // A list of name and URL/email address combinations for the maintainer(s) - Maintainers []*Maintainer `json:"maintainers,omitempty"` - // The URL to an icon file. - Icon string `json:"icon,omitempty"` - // The API Version of this chart. Required. - APIVersion string `json:"apiVersion,omitempty"` - // The condition to check to enable chart - Condition string `json:"condition,omitempty"` - // The tags to check to enable chart - Tags string `json:"tags,omitempty"` - // The version of the application enclosed inside of this chart. - AppVersion string `json:"appVersion,omitempty"` - // Whether or not this chart is deprecated - Deprecated bool `json:"deprecated,omitempty"` - // Annotations are additional mappings uninterpreted by Helm, - // made available for inspection by other applications. - Annotations map[string]string `json:"annotations,omitempty"` - // KubeVersion is a SemVer constraint specifying the version of Kubernetes required. - KubeVersion string `json:"kubeVersion,omitempty"` - // Dependencies are a list of dependencies for a chart. - Dependencies []*Dependency `json:"dependencies,omitempty"` - // Specifies the chart type: application or library - Type string `json:"type,omitempty"` -} - -// Validate checks the metadata for known issues and sanitizes string -// characters. -func (md *Metadata) Validate() error { - if md == nil { - return ValidationError("chart.metadata is required") - } - - md.Name = sanitizeString(md.Name) - md.Description = sanitizeString(md.Description) - md.Home = sanitizeString(md.Home) - md.Icon = sanitizeString(md.Icon) - md.Condition = sanitizeString(md.Condition) - md.Tags = sanitizeString(md.Tags) - md.AppVersion = sanitizeString(md.AppVersion) - md.KubeVersion = sanitizeString(md.KubeVersion) - for i := range md.Sources { - md.Sources[i] = sanitizeString(md.Sources[i]) - } - for i := range md.Keywords { - md.Keywords[i] = sanitizeString(md.Keywords[i]) - } - - if md.APIVersion == "" { - return ValidationError("chart.metadata.apiVersion is required") - } - if md.Name == "" { - return ValidationError("chart.metadata.name is required") - } - if md.Version == "" { - return ValidationError("chart.metadata.version is required") - } - if !isValidSemver(md.Version) { - return ValidationErrorf("chart.metadata.version %q is invalid", md.Version) - } - if !isValidChartType(md.Type) { - return ValidationError("chart.metadata.type must be application or library") - } - - for _, m := range md.Maintainers { - if err := m.Validate(); err != nil { - return err - } - } - - // Aliases need to be validated here to make sure that the alias name does - // not contain any illegal characters. - for _, dependency := range md.Dependencies { - if err := dependency.Validate(); err != nil { - return err - } - } - return nil -} - -func isValidChartType(in string) bool { - switch in { - case "", "application", "library": - return true - } - return false -} - -func isValidSemver(v string) bool { - _, err := semver.NewVersion(v) - return err == nil -} - -// sanitizeString normalize spaces and removes non-printable characters. -func sanitizeString(str string) string { - return strings.Map(func(r rune) rune { - if unicode.IsSpace(r) { - return ' ' - } - if unicode.IsPrint(r) { - return r - } - return -1 - }, str) -} diff --git a/pkg/chartutil/capabilities.go b/pkg/chartutil/capabilities.go deleted file mode 100644 index 0b37fee5..00000000 --- a/pkg/chartutil/capabilities.go +++ /dev/null @@ -1,132 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "fmt" - "strconv" - - "k8s.io/client-go/kubernetes/scheme" - - apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" - - semver "github.com/Masterminds/semver/v3" - helmversion "github.com/mrjosh/helm-ls/internal/version" -) - -var ( - // The Kubernetes version can be set by LDFLAGS. In order to do that the value - // must be a string. - k8sVersionMajor = "1" - k8sVersionMinor = "20" - - // DefaultVersionSet is the default version set, which includes only Core V1 ("v1"). - DefaultVersionSet = allKnownVersions() - - // DefaultCapabilities is the default set of capabilities. - DefaultCapabilities = &Capabilities{ - KubeVersion: KubeVersion{ - Version: fmt.Sprintf("v%s.%s.0", k8sVersionMajor, k8sVersionMinor), - Major: k8sVersionMajor, - Minor: k8sVersionMinor, - }, - APIVersions: DefaultVersionSet, - HelmVersion: helmversion.Get(), - } -) - -// Capabilities describes the capabilities of the Kubernetes cluster. -type Capabilities struct { - // KubeVersion is the Kubernetes version. - KubeVersion KubeVersion - // APIversions are supported Kubernetes API versions. - APIVersions VersionSet - // HelmVersion is the build information for this helm version - HelmVersion helmversion.BuildInfo -} - -func (capabilities *Capabilities) Copy() *Capabilities { - return &Capabilities{ - KubeVersion: capabilities.KubeVersion, - APIVersions: capabilities.APIVersions, - HelmVersion: capabilities.HelmVersion, - } -} - -// KubeVersion is the Kubernetes version. -type KubeVersion struct { - Version string // Kubernetes version - Major string // Kubernetes major version - Minor string // Kubernetes minor version -} - -// String implements fmt.Stringer -func (kv *KubeVersion) String() string { return kv.Version } - -// GitVersion returns the Kubernetes version string. -// -// Deprecated: use KubeVersion.Version. -func (kv *KubeVersion) GitVersion() string { return kv.Version } - -// ParseKubeVersion parses kubernetes version from string -func ParseKubeVersion(version string) (*KubeVersion, error) { - sv, err := semver.NewVersion(version) - if err != nil { - return nil, err - } - return &KubeVersion{ - Version: "v" + sv.String(), - Major: strconv.FormatUint(sv.Major(), 10), - Minor: strconv.FormatUint(sv.Minor(), 10), - }, nil -} - -// VersionSet is a set of Kubernetes API versions. -type VersionSet []string - -// Has returns true if the version string is in the set. -// -// vs.Has("apps/v1") -func (v VersionSet) Has(apiVersion string) bool { - for _, x := range v { - if x == apiVersion { - return true - } - } - return false -} - -func allKnownVersions() VersionSet { - - // We should register the built in extension APIs as well so CRDs are - // supported in the default version set. This has caused problems with `helm - // template` in the past, so let's be safe - if err := apiextensionsv1beta1.AddToScheme(scheme.Scheme); err != nil { - panic(err) - } - - if err := apiextensionsv1.AddToScheme(scheme.Scheme); err != nil { - panic(err) - } - - groups := scheme.Scheme.PrioritizedVersionsAllGroups() - vs := make(VersionSet, 0, len(groups)) - for _, gv := range groups { - vs = append(vs, gv.String()) - } - return vs -} diff --git a/pkg/chartutil/chartfile.go b/pkg/chartutil/chartfile.go deleted file mode 100644 index 07e59ac2..00000000 --- a/pkg/chartutil/chartfile.go +++ /dev/null @@ -1,92 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "os" - "path/filepath" - - "github.com/pkg/errors" - "sigs.k8s.io/yaml" - - "github.com/mrjosh/helm-ls/pkg/chart" -) - -// LoadChartfile loads a Chart.yaml file into a *chart.Metadata. -func LoadChartfile(filename string) (*chart.Metadata, error) { - b, err := os.ReadFile(filename) - if err != nil { - return nil, err - } - y := new(chart.Metadata) - err = yaml.Unmarshal(b, y) - return y, err -} - -// SaveChartfile saves the given metadata as a Chart.yaml file at the given path. -// -// 'filename' should be the complete path and filename ('foo/Chart.yaml') -func SaveChartfile(filename string, cf *chart.Metadata) error { - // Pull out the dependencies of a v1 Chart, since there's no way - // to tell the serializer to skip a field for just this use case - savedDependencies := cf.Dependencies - if cf.APIVersion == chart.APIVersionV1 { - cf.Dependencies = nil - } - out, err := yaml.Marshal(cf) - if cf.APIVersion == chart.APIVersionV1 { - cf.Dependencies = savedDependencies - } - if err != nil { - return err - } - return os.WriteFile(filename, out, 0644) -} - -// IsChartDir validate a chart directory. -// -// Checks for a valid Chart.yaml. -func IsChartDir(dirName string) (bool, error) { - if fi, err := os.Stat(dirName); err != nil { - return false, err - } else if !fi.IsDir() { - return false, errors.Errorf("%q is not a directory", dirName) - } - - chartYaml := filepath.Join(dirName, ChartfileName) - if _, err := os.Stat(chartYaml); os.IsNotExist(err) { - return false, errors.Errorf("no %s exists in directory %q", ChartfileName, dirName) - } - - chartYamlContent, err := os.ReadFile(chartYaml) - if err != nil { - return false, errors.Errorf("cannot read %s in directory %q", ChartfileName, dirName) - } - - chartContent := new(chart.Metadata) - if err := yaml.Unmarshal(chartYamlContent, &chartContent); err != nil { - return false, err - } - if chartContent == nil { - return false, errors.Errorf("chart metadata (%s) missing", ChartfileName) - } - if chartContent.Name == "" { - return false, errors.Errorf("invalid chart (%s): name must not be empty", ChartfileName) - } - - return true, nil -} diff --git a/pkg/chartutil/coalesce.go b/pkg/chartutil/coalesce.go deleted file mode 100644 index cbd14244..00000000 --- a/pkg/chartutil/coalesce.go +++ /dev/null @@ -1,227 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "fmt" - "log" - - "github.com/mitchellh/copystructure" - "github.com/pkg/errors" - - "github.com/mrjosh/helm-ls/pkg/chart" -) - -func concatPrefix(a, b string) string { - if a == "" { - return b - } - return fmt.Sprintf("%s.%s", a, b) -} - -// CoalesceValues coalesces all of the values in a chart (and its subcharts). -// -// Values are coalesced together using the following rules: -// -// - Values in a higher level chart always override values in a lower-level -// dependency chart -// - Scalar values and arrays are replaced, maps are merged -// - A chart has access to all of the variables for it, as well as all of -// the values destined for its dependencies. -func CoalesceValues(chrt *chart.Chart, vals map[string]interface{}) (Values, error) { - v, err := copystructure.Copy(vals) - if err != nil { - return vals, err - } - - valsCopy := v.(map[string]interface{}) - // if we have an empty map, make sure it is initialized - if valsCopy == nil { - valsCopy = make(map[string]interface{}) - } - return coalesce(log.Printf, chrt, valsCopy, "") -} - -type printFn func(format string, v ...interface{}) - -// coalesce coalesces the dest values and the chart values, giving priority to the dest values. -// -// This is a helper function for CoalesceValues. -func coalesce(printf printFn, ch *chart.Chart, dest map[string]interface{}, prefix string) (map[string]interface{}, error) { - coalesceValues(printf, ch, dest, prefix) - return coalesceDeps(printf, ch, dest, prefix) -} - -// coalesceDeps coalesces the dependencies of the given chart. -func coalesceDeps(printf printFn, chrt *chart.Chart, dest map[string]interface{}, prefix string) (map[string]interface{}, error) { - for _, subchart := range chrt.Dependencies() { - if c, ok := dest[subchart.Name()]; !ok { - // If dest doesn't already have the key, create it. - dest[subchart.Name()] = make(map[string]interface{}) - } else if !istable(c) { - return dest, errors.Errorf("type mismatch on %s: %t", subchart.Name(), c) - } - if dv, ok := dest[subchart.Name()]; ok { - dvmap := dv.(map[string]interface{}) - subPrefix := concatPrefix(prefix, chrt.Metadata.Name) - - // Get globals out of dest and merge them into dvmap. - coalesceGlobals(printf, dvmap, dest, subPrefix) - - // Now coalesce the rest of the values. - var err error - dest[subchart.Name()], err = coalesce(printf, subchart, dvmap, subPrefix) - if err != nil { - return dest, err - } - } - } - return dest, nil -} - -// coalesceGlobals copies the globals out of src and merges them into dest. -// -// For convenience, returns dest. -func coalesceGlobals(printf printFn, dest, src map[string]interface{}, prefix string) { - var dg, sg map[string]interface{} - - if destglob, ok := dest[GlobalKey]; !ok { - dg = make(map[string]interface{}) - } else if dg, ok = destglob.(map[string]interface{}); !ok { - printf("warning: skipping globals because destination %s is not a table.", GlobalKey) - return - } - - if srcglob, ok := src[GlobalKey]; !ok { - sg = make(map[string]interface{}) - } else if sg, ok = srcglob.(map[string]interface{}); !ok { - printf("warning: skipping globals because source %s is not a table.", GlobalKey) - return - } - - // EXPERIMENTAL: In the past, we have disallowed globals to test tables. This - // reverses that decision. It may somehow be possible to introduce a loop - // here, but I haven't found a way. So for the time being, let's allow - // tables in globals. - for key, val := range sg { - if istable(val) { - vv := copyMap(val.(map[string]interface{})) - if destv, ok := dg[key]; !ok { - // Here there is no merge. We're just adding. - dg[key] = vv - } else { - if destvmap, ok := destv.(map[string]interface{}); !ok { - printf("Conflict: cannot merge map onto non-map for %q. Skipping.", key) - } else { - // Basically, we reverse order of coalesce here to merge - // top-down. - subPrefix := concatPrefix(prefix, key) - coalesceTablesFullKey(printf, vv, destvmap, subPrefix) - dg[key] = vv - } - } - } else if dv, ok := dg[key]; ok && istable(dv) { - // It's not clear if this condition can actually ever trigger. - printf("key %s is table. Skipping", key) - } else { - // TODO: Do we need to do any additional checking on the value? - dg[key] = val - } - } - dest[GlobalKey] = dg -} - -func copyMap(src map[string]interface{}) map[string]interface{} { - m := make(map[string]interface{}, len(src)) - for k, v := range src { - m[k] = v - } - return m -} - -// coalesceValues builds up a values map for a particular chart. -// -// Values in v will override the values in the chart. -func coalesceValues(printf printFn, c *chart.Chart, v map[string]interface{}, prefix string) { - subPrefix := concatPrefix(prefix, c.Metadata.Name) - for key, val := range c.Values { - if value, ok := v[key]; ok { - if value == nil { - // When the YAML value is null, we remove the value's key. - // This allows Helm's various sources of values (value files or --set) to - // remove incompatible keys from any previous chart, file, or set values. - delete(v, key) - } else if dest, ok := value.(map[string]interface{}); ok { - // if v[key] is a table, merge nv's val table into v[key]. - src, ok := val.(map[string]interface{}) - if !ok { - // If the original value is nil, there is nothing to coalesce, so we don't print - // the warning - if val != nil { - printf("warning: skipped value for %s.%s: Not a table.", subPrefix, key) - } - } else { - // Because v has higher precedence than nv, dest values override src - // values. - coalesceTablesFullKey(printf, dest, src, concatPrefix(subPrefix, key)) - } - } - } else { - // If the key is not in v, copy it from nv. - v[key] = val - } - } -} - -// CoalesceTables merges a source map into a destination map. -// -// dest is considered authoritative. -func CoalesceTables(dst, src map[string]interface{}) map[string]interface{} { - return coalesceTablesFullKey(log.Printf, dst, src, "") -} - -// coalesceTablesFullKey merges a source map into a destination map. -// -// dest is considered authoritative. -func coalesceTablesFullKey(printf printFn, dst, src map[string]interface{}, prefix string) map[string]interface{} { - // When --reuse-values is set but there are no modifications yet, return new values - if src == nil { - return dst - } - if dst == nil { - return src - } - // Because dest has higher precedence than src, dest values override src - // values. - for key, val := range src { - fullkey := concatPrefix(prefix, key) - if dv, ok := dst[key]; ok && dv == nil { - delete(dst, key) - } else if !ok { - dst[key] = val - } else if istable(val) { - if istable(dv) { - coalesceTablesFullKey(printf, dv.(map[string]interface{}), val.(map[string]interface{}), fullkey) - } else { - printf("warning: cannot overwrite table with non table for %s (%v)", fullkey, val) - } - } else if istable(dv) && val != nil { - printf("warning: destination for %s is a table. Ignoring non-table value (%v)", fullkey, val) - } - } - return dst -} diff --git a/pkg/chartutil/compatible.go b/pkg/chartutil/compatible.go deleted file mode 100644 index 134df66c..00000000 --- a/pkg/chartutil/compatible.go +++ /dev/null @@ -1,36 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - semver "github.com/Masterminds/semver/v3" -) - -// IsCompatibleRange compares a version to a constraint. -// It returns true if the version matches the constraint, and false in all other cases. -func IsCompatibleRange(constraint, ver string) bool { - sv, err := semver.NewVersion(ver) - if err != nil { - return false - } - - c, err := semver.NewConstraint(constraint) - if err != nil { - return false - } - return c.Check(sv) -} diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go deleted file mode 100644 index d93a5da8..00000000 --- a/pkg/chartutil/create.go +++ /dev/null @@ -1,686 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "fmt" - "io" - "os" - "path/filepath" - "regexp" - "strings" - - "github.com/pkg/errors" - "sigs.k8s.io/yaml" - - "github.com/mrjosh/helm-ls/pkg/chart" - "github.com/mrjosh/helm-ls/pkg/chart/loader" -) - -// chartName is a regular expression for testing the supplied name of a chart. -// This regular expression is probably stricter than it needs to be. We can relax it -// somewhat. Newline characters, as well as $, quotes, +, parens, and % are known to be -// problematic. -var chartName = regexp.MustCompile("^[a-zA-Z0-9._-]+$") - -const ( - // ChartfileName is the default Chart file name. - ChartfileName = "Chart.yaml" - // ValuesfileName is the default values file name. - ValuesfileName = "values.yaml" - // SchemafileName is the default values schema file name. - SchemafileName = "values.schema.json" - // TemplatesDir is the relative directory name for templates. - TemplatesDir = "templates" - // ChartsDir is the relative directory name for charts dependencies. - ChartsDir = "charts" - // TemplatesTestsDir is the relative directory name for tests. - TemplatesTestsDir = TemplatesDir + sep + "tests" - // IgnorefileName is the name of the Helm ignore file. - IgnorefileName = ".helmignore" - // IngressFileName is the name of the example ingress file. - IngressFileName = TemplatesDir + sep + "ingress.yaml" - // DeploymentName is the name of the example deployment file. - DeploymentName = TemplatesDir + sep + "deployment.yaml" - // ServiceName is the name of the example service file. - ServiceName = TemplatesDir + sep + "service.yaml" - // ServiceAccountName is the name of the example serviceaccount file. - ServiceAccountName = TemplatesDir + sep + "serviceaccount.yaml" - // HorizontalPodAutoscalerName is the name of the example hpa file. - HorizontalPodAutoscalerName = TemplatesDir + sep + "hpa.yaml" - // NotesName is the name of the example NOTES.txt file. - NotesName = TemplatesDir + sep + "NOTES.txt" - // HelpersName is the name of the example helpers file. - HelpersName = TemplatesDir + sep + "_helpers.tpl" - // TestConnectionName is the name of the example test file. - TestConnectionName = TemplatesTestsDir + sep + "test-connection.yaml" -) - -// maxChartNameLength is lower than the limits we know of with certain file systems, -// and with certain Kubernetes fields. -const maxChartNameLength = 250 - -const sep = string(filepath.Separator) - -const defaultChartfile = `apiVersion: v2 -name: %s -description: A Helm chart for Kubernetes - -# A chart can be either an 'application' or a 'library' chart. -# -# Application charts are a collection of templates that can be packaged into versioned archives -# to be deployed. -# -# Library charts provide useful utilities or functions for the chart developer. They're included as -# a dependency of application charts to inject those utilities and functions into the rendering -# pipeline. Library charts do not define any templates and therefore cannot be deployed. -type: application - -# This is the chart version. This version number should be incremented each time you make changes -# to the chart and its templates, including the app version. -# Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.0 - -# This is the version number of the application being deployed. This version number should be -# incremented each time you make changes to the application. Versions are not expected to -# follow Semantic Versioning. They should reflect the version the application is using. -# It is recommended to use it with quotes. -appVersion: "1.16.0" -` - -const defaultValues = `# Default values for %s. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -replicaCount: 1 - -image: - repository: nginx - pullPolicy: IfNotPresent - # Overrides the image tag whose default is the chart appVersion. - tag: "" - -imagePullSecrets: [] -nameOverride: "" -fullnameOverride: "" - -serviceAccount: - # Specifies whether a service account should be created - create: true - # Annotations to add to the service account - annotations: {} - # The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template - name: "" - -podAnnotations: {} - -podSecurityContext: {} - # fsGroup: 2000 - -securityContext: {} - # capabilities: - # drop: - # - ALL - # readOnlyRootFilesystem: true - # runAsNonRoot: true - # runAsUser: 1000 - -service: - type: ClusterIP - port: 80 - -ingress: - enabled: false - className: "" - annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - hosts: - - host: chart-example.local - paths: - - path: / - pathType: ImplementationSpecific - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local - -resources: {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - -autoscaling: - enabled: false - minReplicas: 1 - maxReplicas: 100 - targetCPUUtilizationPercentage: 80 - # targetMemoryUtilizationPercentage: 80 - -nodeSelector: {} - -tolerations: [] - -affinity: {} -` - -const defaultIgnore = `# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*.orig -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ -` - -const defaultIngress = `{{- if .Values.ingress.enabled -}} -{{- $fullName := include ".fullname" . -}} -{{- $svcPort := .Values.service.port -}} -{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} - {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} - {{- end }} -{{- end }} -{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1 -{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1beta1 -{{- else -}} -apiVersion: extensions/v1beta1 -{{- end }} -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - {{- include ".labels" . | nindent 4 }} - {{- with .Values.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} - ingressClassName: {{ .Values.ingress.className }} - {{- end }} - {{- if .Values.ingress.tls }} - tls: - {{- range .Values.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} - {{- end }} - rules: - {{- range .Values.ingress.hosts }} - - host: {{ .host | quote }} - http: - paths: - {{- range .paths }} - - path: {{ .path }} - {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} - pathType: {{ .pathType }} - {{- end }} - backend: - {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} - service: - name: {{ $fullName }} - port: - number: {{ $svcPort }} - {{- else }} - serviceName: {{ $fullName }} - servicePort: {{ $svcPort }} - {{- end }} - {{- end }} - {{- end }} -{{- end }} -` - -const defaultDeployment = `apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include ".fullname" . }} - labels: - {{- include ".labels" . | nindent 4 }} -spec: - {{- if not .Values.autoscaling.enabled }} - replicas: {{ .Values.replicaCount }} - {{- end }} - selector: - matchLabels: - {{- include ".selectorLabels" . | nindent 6 }} - template: - metadata: - {{- with .Values.podAnnotations }} - annotations: - {{- toYaml . | nindent 8 }} - {{- end }} - labels: - {{- include ".selectorLabels" . | nindent 8 }} - spec: - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - serviceAccountName: {{ include ".serviceAccountName" . }} - securityContext: - {{- toYaml .Values.podSecurityContext | nindent 8 }} - containers: - - name: {{ .Chart.Name }} - securityContext: - {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - ports: - - name: http - containerPort: 80 - protocol: TCP - livenessProbe: - httpGet: - path: / - port: http - readinessProbe: - httpGet: - path: / - port: http - resources: - {{- toYaml .Values.resources | nindent 12 }} - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} -` - -const defaultService = `apiVersion: v1 -kind: Service -metadata: - name: {{ include ".fullname" . }} - labels: - {{- include ".labels" . | nindent 4 }} -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.port }} - targetPort: http - protocol: TCP - name: http - selector: - {{- include ".selectorLabels" . | nindent 4 }} -` - -const defaultServiceAccount = `{{- if .Values.serviceAccount.create -}} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include ".serviceAccountName" . }} - labels: - {{- include ".labels" . | nindent 4 }} - {{- with .Values.serviceAccount.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -{{- end }} -` - -const defaultHorizontalPodAutoscaler = `{{- if .Values.autoscaling.enabled }} -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - name: {{ include ".fullname" . }} - labels: - {{- include ".labels" . | nindent 4 }} -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: {{ include ".fullname" . }} - minReplicas: {{ .Values.autoscaling.minReplicas }} - maxReplicas: {{ .Values.autoscaling.maxReplicas }} - metrics: - {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} - - type: Resource - resource: - name: cpu - targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} - {{- end }} - {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} - - type: Resource - resource: - name: memory - targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} - {{- end }} -{{- end }} -` - -const defaultNotes = `1. Get the application URL by running these commands: -{{- if .Values.ingress.enabled }} -{{- range $host := .Values.ingress.hosts }} - {{- range .paths }} - http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} - {{- end }} -{{- end }} -{{- else if contains "NodePort" .Values.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include ".fullname" . }}) - export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - echo http://$NODE_IP:$NODE_PORT -{{- else if contains "LoadBalancer" .Values.service.type }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include ".fullname" . }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include ".fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") - echo http://$SERVICE_IP:{{ .Values.service.port }} -{{- else if contains "ClusterIP" .Values.service.type }} - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include ".name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") - echo "Visit http://127.0.0.1:8080 to use your application" - kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT -{{- end }} -` - -const defaultHelpers = `{{/* -Expand the name of the chart. -*/}} -{{- define ".name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define ".fullname" -}} -{{- if .Values.fullnameOverride }} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- $name := default .Chart.Name .Values.nameOverride }} -{{- if contains $name .Release.Name }} -{{- .Release.Name | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} -{{- end }} -{{- end }} -{{- end }} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define ".chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Common labels -*/}} -{{- define ".labels" -}} -helm.sh/chart: {{ include ".chart" . }} -{{ include ".selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} - -{{/* -Selector labels -*/}} -{{- define ".selectorLabels" -}} -app.kubernetes.io/name: {{ include ".name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end }} - -{{/* -Create the name of the service account to use -*/}} -{{- define ".serviceAccountName" -}} -{{- if .Values.serviceAccount.create }} -{{- default (include ".fullname" .) .Values.serviceAccount.name }} -{{- else }} -{{- default "default" .Values.serviceAccount.name }} -{{- end }} -{{- end }} -` - -const defaultTestConnection = `apiVersion: v1 -kind: Pod -metadata: - name: "{{ include ".fullname" . }}-test-connection" - labels: - {{- include ".labels" . | nindent 4 }} - annotations: - "helm.sh/hook": test -spec: - containers: - - name: wget - image: busybox - command: ['wget'] - args: ['{{ include ".fullname" . }}:{{ .Values.service.port }}'] - restartPolicy: Never -` - -// Stderr is an io.Writer to which error messages can be written -// -// In Helm 4, this will be replaced. It is needed in Helm 3 to preserve API backward -// compatibility. -var Stderr io.Writer = os.Stderr - -// CreateFrom creates a new chart, but scaffolds it from the src chart. -func CreateFrom(chartfile *chart.Metadata, dest, src string) error { - schart, err := loader.Load(src) - if err != nil { - return errors.Wrapf(err, "could not load %s", src) - } - - schart.Metadata = chartfile - - var updatedTemplates []*chart.File - - for _, template := range schart.Templates { - newData := transform(string(template.Data), schart.Name()) - updatedTemplates = append(updatedTemplates, &chart.File{Name: template.Name, Data: newData}) - } - - schart.Templates = updatedTemplates - b, err := yaml.Marshal(schart.Values) - if err != nil { - return errors.Wrap(err, "reading values file") - } - - var m map[string]interface{} - if err := yaml.Unmarshal(transform(string(b), schart.Name()), &m); err != nil { - return errors.Wrap(err, "transforming values file") - } - schart.Values = m - - // SaveDir looks for the file values.yaml when saving rather than the values - // key in order to preserve the comments in the YAML. The name placeholder - // needs to be replaced on that file. - for _, f := range schart.Raw { - if f.Name == ValuesfileName { - f.Data = transform(string(f.Data), schart.Name()) - } - } - - return SaveDir(schart, dest) -} - -// Create creates a new chart in a directory. -// -// Inside of dir, this will create a directory based on the name of -// chartfile.Name. It will then write the Chart.yaml into this directory and -// create the (empty) appropriate directories. -// -// The returned string will point to the newly created directory. It will be -// an absolute path, even if the provided base directory was relative. -// -// If dir does not exist, this will return an error. -// If Chart.yaml or any directories cannot be created, this will return an -// error. In such a case, this will attempt to clean up by removing the -// new chart directory. -func Create(name, dir string) (string, error) { - - // Sanity-check the name of a chart so user doesn't create one that causes problems. - if err := validateChartName(name); err != nil { - return "", err - } - - path, err := filepath.Abs(dir) - if err != nil { - return path, err - } - - if fi, err := os.Stat(path); err != nil { - return path, err - } else if !fi.IsDir() { - return path, errors.Errorf("no such directory %s", path) - } - - cdir := filepath.Join(path, name) - if fi, err := os.Stat(cdir); err == nil && !fi.IsDir() { - return cdir, errors.Errorf("file %s already exists and is not a directory", cdir) - } - - files := []struct { - path string - content []byte - }{ - { - // Chart.yaml - path: filepath.Join(cdir, ChartfileName), - content: []byte(fmt.Sprintf(defaultChartfile, name)), - }, - { - // values.yaml - path: filepath.Join(cdir, ValuesfileName), - content: []byte(fmt.Sprintf(defaultValues, name)), - }, - { - // .helmignore - path: filepath.Join(cdir, IgnorefileName), - content: []byte(defaultIgnore), - }, - { - // ingress.yaml - path: filepath.Join(cdir, IngressFileName), - content: transform(defaultIngress, name), - }, - { - // deployment.yaml - path: filepath.Join(cdir, DeploymentName), - content: transform(defaultDeployment, name), - }, - { - // service.yaml - path: filepath.Join(cdir, ServiceName), - content: transform(defaultService, name), - }, - { - // serviceaccount.yaml - path: filepath.Join(cdir, ServiceAccountName), - content: transform(defaultServiceAccount, name), - }, - { - // hpa.yaml - path: filepath.Join(cdir, HorizontalPodAutoscalerName), - content: transform(defaultHorizontalPodAutoscaler, name), - }, - { - // NOTES.txt - path: filepath.Join(cdir, NotesName), - content: transform(defaultNotes, name), - }, - { - // _helpers.tpl - path: filepath.Join(cdir, HelpersName), - content: transform(defaultHelpers, name), - }, - { - // test-connection.yaml - path: filepath.Join(cdir, TestConnectionName), - content: transform(defaultTestConnection, name), - }, - } - - for _, file := range files { - if _, err := os.Stat(file.path); err == nil { - // There is no handle to a preferred output stream here. - fmt.Fprintf(Stderr, "WARNING: File %q already exists. Overwriting.\n", file.path) - } - if err := writeFile(file.path, file.content); err != nil { - return cdir, err - } - } - // Need to add the ChartsDir explicitly as it does not contain any file OOTB - if err := os.MkdirAll(filepath.Join(cdir, ChartsDir), 0755); err != nil { - return cdir, err - } - return cdir, nil -} - -// transform performs a string replacement of the specified source for -// a given key with the replacement string -func transform(src, replacement string) []byte { - return []byte(strings.ReplaceAll(src, "", replacement)) -} - -func writeFile(name string, content []byte) error { - if err := os.MkdirAll(filepath.Dir(name), 0755); err != nil { - return err - } - return os.WriteFile(name, content, 0644) -} - -func validateChartName(name string) error { - if name == "" || len(name) > maxChartNameLength { - return fmt.Errorf("chart name must be between 1 and %d characters", maxChartNameLength) - } - if !chartName.MatchString(name) { - return fmt.Errorf("chart name must match the regular expression %q", chartName.String()) - } - return nil -} diff --git a/pkg/chartutil/dependencies.go b/pkg/chartutil/dependencies.go deleted file mode 100644 index 586d98de..00000000 --- a/pkg/chartutil/dependencies.go +++ /dev/null @@ -1,285 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "log" - "strings" - - "github.com/mrjosh/helm-ls/pkg/chart" -) - -// ProcessDependencies checks through this chart's dependencies, processing accordingly. -func ProcessDependencies(c *chart.Chart, v Values) error { - if err := processDependencyEnabled(c, v, ""); err != nil { - return err - } - return processDependencyImportValues(c) -} - -// processDependencyConditions disables charts based on condition path value in values -func processDependencyConditions(reqs []*chart.Dependency, cvals Values, cpath string) { - if reqs == nil { - return - } - for _, r := range reqs { - for _, c := range strings.Split(strings.TrimSpace(r.Condition), ",") { - if len(c) > 0 { - // retrieve value - vv, err := cvals.PathValue(cpath + c) - if err == nil { - // if not bool, warn - if bv, ok := vv.(bool); ok { - r.Enabled = bv - break - } - - log.Printf("Warning: Condition path '%s' for chart %s returned non-bool value", c, r.Name) - } else if _, ok := err.(ErrNoValue); !ok { - // this is a real error - log.Printf("Warning: PathValue returned error %v", err) - } - } - } - } -} - -// processDependencyTags disables charts based on tags in values -func processDependencyTags(reqs []*chart.Dependency, cvals Values) { - if reqs == nil { - return - } - vt, err := cvals.Table("tags") - if err != nil { - return - } - for _, r := range reqs { - var hasTrue, hasFalse bool - for _, k := range r.Tags { - if b, ok := vt[k]; ok { - // if not bool, warn - if bv, ok := b.(bool); ok { - if bv { - hasTrue = true - } else { - hasFalse = true - } - } else { - log.Printf("Warning: Tag '%s' for chart %s returned non-bool value", k, r.Name) - } - } - } - if !hasTrue && hasFalse { - r.Enabled = false - } else if hasTrue || !hasTrue && !hasFalse { - r.Enabled = true - } - } -} - -func getAliasDependency(charts []*chart.Chart, dep *chart.Dependency) *chart.Chart { - for _, c := range charts { - if c == nil { - continue - } - if c.Name() != dep.Name { - continue - } - if !IsCompatibleRange(dep.Version, c.Metadata.Version) { - continue - } - - out := *c - md := *c.Metadata - out.Metadata = &md - - if dep.Alias != "" { - md.Name = dep.Alias - } - return &out - } - return nil -} - -// processDependencyEnabled removes disabled charts from dependencies -func processDependencyEnabled(c *chart.Chart, v map[string]interface{}, path string) error { - if c.Metadata.Dependencies == nil { - return nil - } - - var chartDependencies []*chart.Chart - // If any dependency is not a part of Chart.yaml - // then this should be added to chartDependencies. - // However, if the dependency is already specified in Chart.yaml - // we should not add it, as it would be anyways processed from Chart.yaml - -Loop: - for _, existing := range c.Dependencies() { - for _, req := range c.Metadata.Dependencies { - if existing.Name() == req.Name && IsCompatibleRange(req.Version, existing.Metadata.Version) { - continue Loop - } - } - chartDependencies = append(chartDependencies, existing) - } - - for _, req := range c.Metadata.Dependencies { - if chartDependency := getAliasDependency(c.Dependencies(), req); chartDependency != nil { - chartDependencies = append(chartDependencies, chartDependency) - } - if req.Alias != "" { - req.Name = req.Alias - } - } - c.SetDependencies(chartDependencies...) - - // set all to true - for _, lr := range c.Metadata.Dependencies { - lr.Enabled = true - } - cvals, err := CoalesceValues(c, v) - if err != nil { - return err - } - // flag dependencies as enabled/disabled - processDependencyTags(c.Metadata.Dependencies, cvals) - processDependencyConditions(c.Metadata.Dependencies, cvals, path) - // make a map of charts to remove - rm := map[string]struct{}{} - for _, r := range c.Metadata.Dependencies { - if !r.Enabled { - // remove disabled chart - rm[r.Name] = struct{}{} - } - } - // don't keep disabled charts in new slice - cd := []*chart.Chart{} - copy(cd, c.Dependencies()[:0]) - for _, n := range c.Dependencies() { - if _, ok := rm[n.Metadata.Name]; !ok { - cd = append(cd, n) - } - } - // don't keep disabled charts in metadata - cdMetadata := []*chart.Dependency{} - copy(cdMetadata, c.Metadata.Dependencies[:0]) - for _, n := range c.Metadata.Dependencies { - if _, ok := rm[n.Name]; !ok { - cdMetadata = append(cdMetadata, n) - } - } - - // recursively call self to process sub dependencies - for _, t := range cd { - subpath := path + t.Metadata.Name + "." - if err := processDependencyEnabled(t, cvals, subpath); err != nil { - return err - } - } - // set the correct dependencies in metadata - c.Metadata.Dependencies = nil - c.Metadata.Dependencies = append(c.Metadata.Dependencies, cdMetadata...) - c.SetDependencies(cd...) - - return nil -} - -// pathToMap creates a nested map given a YAML path in dot notation. -func pathToMap(path string, data map[string]interface{}) map[string]interface{} { - if path == "." { - return data - } - return set(parsePath(path), data) -} - -func set(path []string, data map[string]interface{}) map[string]interface{} { - if len(path) == 0 { - return nil - } - cur := data - for i := len(path) - 1; i >= 0; i-- { - cur = map[string]interface{}{path[i]: cur} - } - return cur -} - -// processImportValues merges values from child to parent based on the chart's dependencies' ImportValues field. -func processImportValues(c *chart.Chart) error { - if c.Metadata.Dependencies == nil { - return nil - } - // combine chart values and empty config to get Values - cvals, err := CoalesceValues(c, nil) - if err != nil { - return err - } - b := make(map[string]interface{}) - // import values from each dependency if specified in import-values - for _, r := range c.Metadata.Dependencies { - var outiv []interface{} - for _, riv := range r.ImportValues { - switch iv := riv.(type) { - case map[string]interface{}: - child := iv["child"].(string) - parent := iv["parent"].(string) - - outiv = append(outiv, map[string]string{ - "child": child, - "parent": parent, - }) - - // get child table - vv, err := cvals.Table(r.Name + "." + child) - if err != nil { - log.Printf("Warning: ImportValues missing table from chart %s: %v", r.Name, err) - continue - } - // create value map from child to be merged into parent - b = CoalesceTables(cvals, pathToMap(parent, vv.AsMap())) - case string: - child := "exports." + iv - outiv = append(outiv, map[string]string{ - "child": child, - "parent": ".", - }) - vm, err := cvals.Table(r.Name + "." + child) - if err != nil { - log.Printf("Warning: ImportValues missing table: %v", err) - continue - } - b = CoalesceTables(b, vm.AsMap()) - } - } - // set our formatted import values - r.ImportValues = outiv - } - - // set the new values - c.Values = CoalesceTables(cvals, b) - - return nil -} - -// processDependencyImportValues imports specified chart values from child to parent. -func processDependencyImportValues(c *chart.Chart) error { - for _, d := range c.Dependencies() { - // recurse - if err := processDependencyImportValues(d); err != nil { - return err - } - } - return processImportValues(c) -} diff --git a/pkg/chartutil/errors.go b/pkg/chartutil/errors.go deleted file mode 100644 index e44b3a38..00000000 --- a/pkg/chartutil/errors.go +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "fmt" -) - -// ErrNoTable indicates that a chart does not have a matching table. -type ErrNoTable struct { - Key string -} - -func (e ErrNoTable) Error() string { return fmt.Sprintf("%q is not a table", e.Key) } - -type ErrEmpytArray struct { - Key string -} - -func (e ErrEmpytArray) Error() string { return fmt.Sprintf("%q is an empyt array", e.Key) } - -// ErrNoValue indicates that Values does not contain a key with a value -type ErrNoValue struct { - Key string -} - -func (e ErrNoValue) Error() string { return fmt.Sprintf("%q is not a value", e.Key) } diff --git a/pkg/chartutil/expand.go b/pkg/chartutil/expand.go deleted file mode 100644 index 2a327ccb..00000000 --- a/pkg/chartutil/expand.go +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "io" - "os" - "path/filepath" - - securejoin "github.com/cyphar/filepath-securejoin" - "github.com/pkg/errors" - "sigs.k8s.io/yaml" - - "github.com/mrjosh/helm-ls/pkg/chart" - "github.com/mrjosh/helm-ls/pkg/chart/loader" -) - -// Expand uncompresses and extracts a chart into the specified directory. -func Expand(dir string, r io.Reader) error { - files, err := loader.LoadArchiveFiles(r) - if err != nil { - return err - } - - // Get the name of the chart - var chartName string - for _, file := range files { - if file.Name == "Chart.yaml" { - ch := &chart.Metadata{} - if err := yaml.Unmarshal(file.Data, ch); err != nil { - return errors.Wrap(err, "cannot load Chart.yaml") - } - chartName = ch.Name - } - } - if chartName == "" { - return errors.New("chart name not specified") - } - - // Find the base directory - chartdir, err := securejoin.SecureJoin(dir, chartName) - if err != nil { - return err - } - - // Copy all files verbatim. We don't parse these files because parsing can remove - // comments. - for _, file := range files { - outpath, err := securejoin.SecureJoin(chartdir, file.Name) - if err != nil { - return err - } - - // Make sure the necessary subdirs get created. - basedir := filepath.Dir(outpath) - if err := os.MkdirAll(basedir, 0755); err != nil { - return err - } - - if err := os.WriteFile(outpath, file.Data, 0644); err != nil { - return err - } - } - - return nil -} - -// ExpandFile expands the src file into the dest directory. -func ExpandFile(dest, src string) error { - h, err := os.Open(src) - if err != nil { - return err - } - defer h.Close() - return Expand(dest, h) -} diff --git a/pkg/chartutil/jsonschema.go b/pkg/chartutil/jsonschema.go deleted file mode 100644 index 1a0c3976..00000000 --- a/pkg/chartutil/jsonschema.go +++ /dev/null @@ -1,87 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "bytes" - "fmt" - "strings" - - "github.com/pkg/errors" - "github.com/xeipuuv/gojsonschema" - "sigs.k8s.io/yaml" - - "github.com/mrjosh/helm-ls/pkg/chart" -) - -// ValidateAgainstSchema checks that values does not violate the structure laid out in schema -func ValidateAgainstSchema(chrt *chart.Chart, values map[string]interface{}) error { - var sb strings.Builder - if chrt.Schema != nil { - err := ValidateAgainstSingleSchema(values, chrt.Schema) - if err != nil { - sb.WriteString(fmt.Sprintf("%s:\n", chrt.Name())) - sb.WriteString(err.Error()) - } - } - - // For each dependency, recursively call this function with the coalesced values - for _, subchart := range chrt.Dependencies() { - subchartValues := values[subchart.Name()].(map[string]interface{}) - if err := ValidateAgainstSchema(subchart, subchartValues); err != nil { - sb.WriteString(err.Error()) - } - } - - if sb.Len() > 0 { - return errors.New(sb.String()) - } - - return nil -} - -// ValidateAgainstSingleSchema checks that values does not violate the structure laid out in this schema -func ValidateAgainstSingleSchema(values Values, schemaJSON []byte) error { - valuesData, err := yaml.Marshal(values) - if err != nil { - return err - } - valuesJSON, err := yaml.YAMLToJSON(valuesData) - if err != nil { - return err - } - if bytes.Equal(valuesJSON, []byte("null")) { - valuesJSON = []byte("{}") - } - schemaLoader := gojsonschema.NewBytesLoader(schemaJSON) - valuesLoader := gojsonschema.NewBytesLoader(valuesJSON) - - result, err := gojsonschema.Validate(schemaLoader, valuesLoader) - if err != nil { - return err - } - - if !result.Valid() { - var sb strings.Builder - for _, desc := range result.Errors() { - sb.WriteString(fmt.Sprintf("- %s\n", desc)) - } - return errors.New(sb.String()) - } - - return nil -} diff --git a/pkg/chartutil/save.go b/pkg/chartutil/save.go deleted file mode 100644 index e2c7daca..00000000 --- a/pkg/chartutil/save.go +++ /dev/null @@ -1,244 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "archive/tar" - "compress/gzip" - "encoding/json" - "fmt" - "os" - "path/filepath" - "time" - - "github.com/pkg/errors" - "sigs.k8s.io/yaml" - - "github.com/mrjosh/helm-ls/pkg/chart" -) - -var headerBytes = []byte("+aHR0cHM6Ly95b3V0dS5iZS96OVV6MWljandyTQo=") - -// SaveDir saves a chart as files in a directory. -// -// This takes the chart name, and creates a new subdirectory inside of the given dest -// directory, writing the chart's contents to that subdirectory. -func SaveDir(c *chart.Chart, dest string) error { - // Create the chart directory - outdir := filepath.Join(dest, c.Name()) - if fi, err := os.Stat(outdir); err == nil && !fi.IsDir() { - return errors.Errorf("file %s already exists and is not a directory", outdir) - } - if err := os.MkdirAll(outdir, 0755); err != nil { - return err - } - - // Save the chart file. - if err := SaveChartfile(filepath.Join(outdir, ChartfileName), c.Metadata); err != nil { - return err - } - - // Save values.yaml - for _, f := range c.Raw { - if f.Name == ValuesfileName { - vf := filepath.Join(outdir, ValuesfileName) - if err := writeFile(vf, f.Data); err != nil { - return err - } - } - } - - // Save values.schema.json if it exists - if c.Schema != nil { - filename := filepath.Join(outdir, SchemafileName) - if err := writeFile(filename, c.Schema); err != nil { - return err - } - } - - // Save templates and files - for _, o := range [][]*chart.File{c.Templates, c.Files} { - for _, f := range o { - n := filepath.Join(outdir, f.Name) - if err := writeFile(n, f.Data); err != nil { - return err - } - } - } - - // Save dependencies - base := filepath.Join(outdir, ChartsDir) - for _, dep := range c.Dependencies() { - // Here, we write each dependency as a tar file. - if _, err := Save(dep, base); err != nil { - return errors.Wrapf(err, "saving %s", dep.ChartFullPath()) - } - } - return nil -} - -// Save creates an archived chart to the given directory. -// -// This takes an existing chart and a destination directory. -// -// If the directory is /foo, and the chart is named bar, with version 1.0.0, this -// will generate /foo/bar-1.0.0.tgz. -// -// This returns the absolute path to the chart archive file. -func Save(c *chart.Chart, outDir string) (string, error) { - if err := c.Validate(); err != nil { - return "", errors.Wrap(err, "chart validation") - } - - filename := fmt.Sprintf("%s-%s.tgz", c.Name(), c.Metadata.Version) - filename = filepath.Join(outDir, filename) - dir := filepath.Dir(filename) - if stat, err := os.Stat(dir); err != nil { - if os.IsNotExist(err) { - if err2 := os.MkdirAll(dir, 0755); err2 != nil { - return "", err2 - } - } else { - return "", errors.Wrapf(err, "stat %s", dir) - } - } else if !stat.IsDir() { - return "", errors.Errorf("is not a directory: %s", dir) - } - - f, err := os.Create(filename) - if err != nil { - return "", err - } - - // Wrap in gzip writer - zipper := gzip.NewWriter(f) - zipper.Header.Extra = headerBytes - zipper.Header.Comment = "Helm" - - // Wrap in tar writer - twriter := tar.NewWriter(zipper) - rollback := false - defer func() { - twriter.Close() - zipper.Close() - f.Close() - if rollback { - os.Remove(filename) - } - }() - - if err := writeTarContents(twriter, c, ""); err != nil { - rollback = true - return filename, err - } - return filename, nil -} - -func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error { - base := filepath.Join(prefix, c.Name()) - - // Pull out the dependencies of a v1 Chart, since there's no way - // to tell the serializer to skip a field for just this use case - savedDependencies := c.Metadata.Dependencies - if c.Metadata.APIVersion == chart.APIVersionV1 { - c.Metadata.Dependencies = nil - } - // Save Chart.yaml - cdata, err := yaml.Marshal(c.Metadata) - if c.Metadata.APIVersion == chart.APIVersionV1 { - c.Metadata.Dependencies = savedDependencies - } - if err != nil { - return err - } - if err := writeToTar(out, filepath.Join(base, ChartfileName), cdata); err != nil { - return err - } - - // Save Chart.lock - // TODO: remove the APIVersion check when APIVersionV1 is not used anymore - if c.Metadata.APIVersion == chart.APIVersionV2 { - if c.Lock != nil { - ldata, err := yaml.Marshal(c.Lock) - if err != nil { - return err - } - if err := writeToTar(out, filepath.Join(base, "Chart.lock"), ldata); err != nil { - return err - } - } - } - - // Save values.yaml - for _, f := range c.Raw { - if f.Name == ValuesfileName { - if err := writeToTar(out, filepath.Join(base, ValuesfileName), f.Data); err != nil { - return err - } - } - } - - // Save values.schema.json if it exists - if c.Schema != nil { - if !json.Valid(c.Schema) { - return errors.New("Invalid JSON in " + SchemafileName) - } - if err := writeToTar(out, filepath.Join(base, SchemafileName), c.Schema); err != nil { - return err - } - } - - // Save templates - for _, f := range c.Templates { - n := filepath.Join(base, f.Name) - if err := writeToTar(out, n, f.Data); err != nil { - return err - } - } - - // Save files - for _, f := range c.Files { - n := filepath.Join(base, f.Name) - if err := writeToTar(out, n, f.Data); err != nil { - return err - } - } - - // Save dependencies - for _, dep := range c.Dependencies() { - if err := writeTarContents(out, dep, filepath.Join(base, ChartsDir)); err != nil { - return err - } - } - return nil -} - -// writeToTar writes a single file to a tar archive. -func writeToTar(out *tar.Writer, name string, body []byte) error { - // TODO: Do we need to create dummy parent directory names if none exist? - h := &tar.Header{ - Name: filepath.ToSlash(name), - Mode: 0644, - Size: int64(len(body)), - ModTime: time.Now(), - } - if err := out.WriteHeader(h); err != nil { - return err - } - _, err := out.Write(body) - return err -} diff --git a/pkg/chartutil/validate_name.go b/pkg/chartutil/validate_name.go deleted file mode 100644 index 05c090cb..00000000 --- a/pkg/chartutil/validate_name.go +++ /dev/null @@ -1,112 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "fmt" - "regexp" - - "github.com/pkg/errors" -) - -// validName is a regular expression for resource names. -// -// According to the Kubernetes help text, the regular expression it uses is: -// -// [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)* -// -// This follows the above regular expression (but requires a full string match, not partial). -// -// The Kubernetes documentation is here, though it is not entirely correct: -// https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names -var validName = regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`) - -var ( - // errMissingName indicates that a release (name) was not provided. - errMissingName = errors.New("no name provided") - - // errInvalidName indicates that an invalid release name was provided - errInvalidName = fmt.Errorf( - "invalid release name, must match regex %s and the length must not be longer than 53", - validName.String()) - - // errInvalidKubernetesName indicates that the name does not meet the Kubernetes - // restrictions on metadata names. - errInvalidKubernetesName = fmt.Errorf( - "invalid metadata name, must match regex %s and the length must not be longer than 253", - validName.String()) -) - -const ( - // According to the Kubernetes docs (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#rfc-1035-label-names) - // some resource names have a max length of 63 characters while others have a max - // length of 253 characters. As we cannot be sure the resources used in a chart, we - // therefore need to limit it to 63 chars and reserve 10 chars for additional part to name - // of the resource. The reason is that chart maintainers can use release name as part of - // the resource name (and some additional chars). - maxReleaseNameLen = 53 - // maxMetadataNameLen is the maximum length Kubernetes allows for any name. - maxMetadataNameLen = 253 -) - -// ValidateReleaseName performs checks for an entry for a Helm release name -// -// For Helm to allow a name, it must be below a certain character count (53) and also match -// a regular expression. -// -// According to the Kubernetes help text, the regular expression it uses is: -// -// [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)* -// -// This follows the above regular expression (but requires a full string match, not partial). -// -// The Kubernetes documentation is here, though it is not entirely correct: -// https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names -func ValidateReleaseName(name string) error { - // This case is preserved for backwards compatibility - if name == "" { - return errMissingName - - } - if len(name) > maxReleaseNameLen || !validName.MatchString(name) { - return errInvalidName - } - return nil -} - -// ValidateMetadataName validates the name field of a Kubernetes metadata object. -// -// Empty strings, strings longer than 253 chars, or strings that don't match the regexp -// will fail. -// -// According to the Kubernetes help text, the regular expression it uses is: -// -// [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)* -// -// This follows the above regular expression (but requires a full string match, not partial). -// -// The Kubernetes documentation is here, though it is not entirely correct: -// https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names -// -// Deprecated: remove in Helm 4. Name validation now uses rules defined in -// pkg/lint/rules.validateMetadataNameFunc() -func ValidateMetadataName(name string) error { - if name == "" || len(name) > maxMetadataNameLen || !validName.MatchString(name) { - return errInvalidKubernetesName - } - return nil -} diff --git a/pkg/chartutil/values.go b/pkg/chartutil/values.go deleted file mode 100644 index 1f6dd3b8..00000000 --- a/pkg/chartutil/values.go +++ /dev/null @@ -1,255 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "fmt" - "io" - "os" - "strings" - - "github.com/pkg/errors" - yamlv3 "gopkg.in/yaml.v3" - "sigs.k8s.io/yaml" - - "github.com/mrjosh/helm-ls/pkg/chart" -) - -// GlobalKey is the name of the Values key that is used for storing global vars. -const GlobalKey = "global" - -// Values represents a collection of chart values. -type Values map[string]interface{} - -// YAML encodes the Values into a YAML string. -func (v Values) YAML() (string, error) { - b, err := yaml.Marshal(v) - return string(b), err -} - -// Table gets a table (YAML subsection) from a Values object. -// -// The table is returned as a Values. -// -// Compound table names may be specified with dots: -// -// foo.bar -// -// The above will be evaluated as "The table bar inside the table -// foo". -// -// Also supports YAML arrays (only with index 0): -// foo[0].bar -// -// The above will be evaluated as "The table bar as the first -// element of the array foo". -// -// An ErrNoTable is returned if the table does not exist. -func (v Values) Table(name string) (Values, error) { - table := v - var err error - - for _, n := range parsePath(name) { - if table, err = tableLookup(table, n); err != nil { - break - } - } - return table, err -} - -// AsMap is a utility function for converting Values to a map[string]interface{}. -// -// It protects against nil map panics. -func (v Values) AsMap() map[string]interface{} { - if len(v) == 0 { - return map[string]interface{}{} - } - return v -} - -// Encode writes serialized Values information to the given io.Writer. -func (v Values) Encode(w io.Writer) error { - out, err := yaml.Marshal(v) - if err != nil { - return err - } - _, err = w.Write(out) - return err -} - -func tableLookup(v Values, simple string) (Values, error) { - if strings.HasSuffix(simple, "[]") { - return arryLookup(v, simple) - } - - v2, ok := v[simple] - if !ok { - return v, ErrNoTable{simple} - } - if vv, ok := v2.(map[string]interface{}); ok { - return vv, nil - } - - // This catches a case where a value is of type Values, but doesn't (for some - // reason) match the map[string]interface{}. This has been observed in the - // wild, and might be a result of a nil map of type Values. - if vv, ok := v2.(Values); ok { - return vv, nil - } - - return Values{}, ErrNoTable{simple} -} - -func arryLookup(v Values, simple string) (Values, error) { - v2, ok := v[simple[:(len(simple)-3)]] - if !ok { - return v, ErrNoTable{fmt.Sprintf("Yaml key %s does not exist", simple)} - } - if v3, ok := v2.([]interface{}); ok { - if len(v3) == 0 { - return Values{}, ErrEmpytArray{simple} - } - if vv, ok := v3[0].(map[string]interface{}); ok { - return vv, nil - } - if vv, ok := v3[0].(Values); ok { - return vv, nil - } - return Values{}, ErrNoTable{simple} - } - - return Values{}, ErrNoTable{simple} -} - -// ReadValues will parse YAML byte data into a Values. -func ReadValues(data []byte) (vals Values, err error) { - err = yaml.Unmarshal(data, &vals) - if len(vals) == 0 { - vals = Values{} - } - return vals, err -} - -// ReadValuesFile will parse a YAML file into a map of values. -func ReadValuesFile(filename string) (Values, error) { - data, err := os.ReadFile(filename) - if err != nil { - return map[string]interface{}{}, err - } - return ReadValues(data) -} - -// ReadYamlFileToNode will parse a YAML file into a yaml Node. -func ReadYamlFileToNode(filename string) (node yamlv3.Node, err error) { - data, err := os.ReadFile(filename) - if err != nil { - return yamlv3.Node{}, err - } - - err = yamlv3.Unmarshal(data, &node) - return node, err -} - -// ReleaseOptions represents the additional release options needed -// for the composition of the final values struct -type ReleaseOptions struct { - Name string - Namespace string - Revision int - IsUpgrade bool - IsInstall bool -} - -// ToRenderValues composes the struct from the data coming from the Releases, Charts and Values files -// -// This takes both ReleaseOptions and Capabilities to merge into the render values. -func ToRenderValues(chrt *chart.Chart, chrtVals map[string]interface{}, options ReleaseOptions, caps *Capabilities) (Values, error) { - if caps == nil { - caps = DefaultCapabilities - } - top := map[string]interface{}{ - "Chart": chrt.Metadata, - "Capabilities": caps, - "Release": map[string]interface{}{ - "Name": options.Name, - "Namespace": options.Namespace, - "IsUpgrade": options.IsUpgrade, - "IsInstall": options.IsInstall, - "Revision": options.Revision, - "Service": "Helm", - }, - } - - vals, err := CoalesceValues(chrt, chrtVals) - if err != nil { - return top, err - } - - if err := ValidateAgainstSchema(chrt, vals); err != nil { - errFmt := "values don't meet the specifications of the schema(s) in the following chart(s):\n%s" - return top, fmt.Errorf(errFmt, err.Error()) - } - - top["Values"] = vals - return top, nil -} - -// istable is a special-purpose function to see if the present thing matches the definition of a YAML table. -func istable(v interface{}) bool { - _, ok := v.(map[string]interface{}) - return ok -} - -// PathValue takes a path that traverses a YAML structure and returns the value at the end of that path. -// The path starts at the root of the YAML structure and is comprised of YAML keys separated by periods. -// Given the following YAML data the value at path "chapter.one.title" is "Loomings". -// -// chapter: -// one: -// title: "Loomings" -func (v Values) PathValue(path string) (interface{}, error) { - if path == "" { - return nil, errors.New("YAML path cannot be empty") - } - return v.pathValue(parsePath(path)) -} - -func (v Values) pathValue(path []string) (interface{}, error) { - if len(path) == 1 { - // if exists must be root key not table - if _, ok := v[path[0]]; ok && !istable(v[path[0]]) { - return v[path[0]], nil - } - return nil, ErrNoValue{path[0]} - } - - key, path := path[len(path)-1], path[:len(path)-1] - // get our table for table path - t, err := v.Table(joinPath(path...)) - if err != nil { - return nil, ErrNoValue{key} - } - // check table for key and ensure value is not a table - if k, ok := t[key]; ok && !istable(k) { - return k, nil - } - return nil, ErrNoValue{key} -} - -func parsePath(key string) []string { return strings.Split(key, ".") } - -func joinPath(path ...string) string { return strings.Join(path, ".") } diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go deleted file mode 100644 index 95855671..00000000 --- a/pkg/engine/engine.go +++ /dev/null @@ -1,416 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package engine - -import ( - "fmt" - "log" - "path" - "path/filepath" - "sort" - "strings" - "text/template" - - "github.com/pkg/errors" - "k8s.io/client-go/rest" - - "github.com/mrjosh/helm-ls/pkg/chart" - "github.com/mrjosh/helm-ls/pkg/chartutil" -) - -// Engine is an implementation of the Helm rendering implementation for templates. -type Engine struct { - // If strict is enabled, template rendering will fail if a template references - // a value that was not passed in. - Strict bool - // In LintMode, some 'required' template values may be missing, so don't fail - LintMode bool - // the rest config to connect to the kubernetes api - config *rest.Config -} - -// Render takes a chart, optional values, and value overrides, and attempts to render the Go templates. -// -// Render can be called repeatedly on the same engine. -// -// This will look in the chart's 'templates' data (e.g. the 'templates/' directory) -// and attempt to render the templates there using the values passed in. -// -// Values are scoped to their templates. A dependency template will not have -// access to the values set for its parent. If chart "foo" includes chart "bar", -// "bar" will not have access to the values for "foo". -// -// Values should be prepared with something like `chartutils.ReadValues`. -// -// Values are passed through the templates according to scope. If the top layer -// chart includes the chart foo, which includes the chart bar, the values map -// will be examined for a table called "foo". If "foo" is found in vals, -// that section of the values will be passed into the "foo" chart. And if that -// section contains a value named "bar", that value will be passed on to the -// bar chart during render time. -func (e Engine) Render(chrt *chart.Chart, values chartutil.Values) (map[string]string, error) { - tmap := allTemplates(chrt, values) - return e.render(tmap) -} - -// Render takes a chart, optional values, and value overrides, and attempts to -// render the Go templates using the default options. -func Render(chrt *chart.Chart, values chartutil.Values) (map[string]string, error) { - return new(Engine).Render(chrt, values) -} - -// RenderWithClient takes a chart, optional values, and value overrides, and attempts to -// render the Go templates using the default options. This engine is client aware and so can have template -// functions that interact with the client -func RenderWithClient(chrt *chart.Chart, values chartutil.Values, config *rest.Config) (map[string]string, error) { - return Engine{ - config: config, - }.Render(chrt, values) -} - -// renderable is an object that can be rendered. -type renderable struct { - // tpl is the current template. - tpl string - // vals are the values to be supplied to the template. - vals chartutil.Values - // namespace prefix to the templates of the current chart - basePath string -} - -const warnStartDelim = "HELM_ERR_START" -const warnEndDelim = "HELM_ERR_END" -const recursionMaxNums = 1000 - -func warnWrap(warn string) string { - return warnStartDelim + warn + warnEndDelim -} - -// initFunMap creates the Engine's FuncMap and adds context-specific functions. -func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]renderable) { - funcMap := funcMap() - includedNames := make(map[string]int) - - // Add the 'include' function here so we can close over t. - funcMap["include"] = func(name string, data interface{}) (string, error) { - var buf strings.Builder - if v, ok := includedNames[name]; ok { - if v > recursionMaxNums { - return "", errors.Wrapf(fmt.Errorf("unable to execute template"), "rendering template has a nested reference name: %s", name) - } - includedNames[name]++ - } else { - includedNames[name] = 1 - } - err := t.ExecuteTemplate(&buf, name, data) - includedNames[name]-- - return buf.String(), err - } - - // Add the 'tpl' function here - funcMap["tpl"] = func(tpl string, vals chartutil.Values) (string, error) { - basePath, err := vals.PathValue("Template.BasePath") - if err != nil { - return "", errors.Wrapf(err, "cannot retrieve Template.Basepath from values inside tpl function: %s", tpl) - } - - templateName, err := vals.PathValue("Template.Name") - if err != nil { - return "", errors.Wrapf(err, "cannot retrieve Template.Name from values inside tpl function: %s", tpl) - } - - templates := map[string]renderable{ - templateName.(string): { - tpl: tpl, - vals: vals, - basePath: basePath.(string), - }, - } - - result, err := e.renderWithReferences(templates, referenceTpls) - if err != nil { - return "", errors.Wrapf(err, "error during tpl function execution for %q", tpl) - } - return result[templateName.(string)], nil - } - - // Add the `required` function here so we can use lintMode - funcMap["required"] = func(warn string, val interface{}) (interface{}, error) { - if val == nil { - if e.LintMode { - // Don't fail on missing required values when linting - log.Printf("[INFO] Missing required value: %s", warn) - return "", nil - } - return val, errors.Errorf(warnWrap(warn)) - } else if _, ok := val.(string); ok { - if val == "" { - if e.LintMode { - // Don't fail on missing required values when linting - log.Printf("[INFO] Missing required value: %s", warn) - return "", nil - } - return val, errors.Errorf(warnWrap(warn)) - } - } - return val, nil - } - - // Override sprig fail function for linting and wrapping message - funcMap["fail"] = func(msg string) (string, error) { - if e.LintMode { - // Don't fail when linting - log.Printf("[INFO] Fail: %s", msg) - return "", nil - } - return "", errors.New(warnWrap(msg)) - } - - // If we are not linting and have a cluster connection, provide a Kubernetes-backed - // implementation. - if !e.LintMode && e.config != nil { - funcMap["lookup"] = NewLookupFunction(e.config) - } - - t.Funcs(funcMap) -} - -// render takes a map of templates/values and renders them. -func (e Engine) render(tpls map[string]renderable) (map[string]string, error) { - return e.renderWithReferences(tpls, tpls) -} - -// renderWithReferences takes a map of templates/values to render, and a map of -// templates which can be referenced within them. -func (e Engine) renderWithReferences(tpls, referenceTpls map[string]renderable) (rendered map[string]string, err error) { - - // Basically, what we do here is start with an empty parent template and then - // build up a list of templates -- one for each file. Once all of the templates - // have been parsed, we loop through again and execute every template. - // - // The idea with this process is to make it possible for more complex templates - // to share common blocks, but to make the entire thing feel like a file-based - // template engine. - defer func() { - if r := recover(); r != nil { - err = errors.Errorf("rendering template failed: %v", r) - } - }() - t := template.New("gotpl") - if e.Strict { - t.Option("missingkey=error") - } else { - // Not that zero will attempt to add default values for types it knows, - // but will still emit for others. We mitigate that later. - t.Option("missingkey=zero") - } - - e.initFunMap(t, referenceTpls) - - // We want to parse the templates in a predictable order. The order favors - // higher-level (in file system) templates over deeply nested templates. - keys := sortTemplates(tpls) - referenceKeys := sortTemplates(referenceTpls) - - errs := make([]error, 0) - for _, filename := range keys { - r := tpls[filename] - if _, err := t.New(filename).Parse(r.tpl); err != nil { - errs = append(errs, cleanupParseError(filename, err)) - } - } - if len(errs) > 0 { - return map[string]string{}, &LintError{errors: errs} - } - - // Adding the reference templates to the template context - // so they can be referenced in the tpl function - errs = make([]error, 0) - for _, filename := range referenceKeys { - if t.Lookup(filename) == nil { - r := referenceTpls[filename] - if _, err := t.New(filename).Parse(r.tpl); err != nil { - errs = append(errs, cleanupParseError(filename, err)) - } - } - } - if len(errs) > 0 { - return map[string]string{}, &LintError{errors: errs} - } - - rendered = make(map[string]string, len(keys)) - errs = make([]error, 0) - for _, filename := range keys { - // Don't render partials. We don't care out the direct output of partials. - // They are only included from other templates. - if strings.HasPrefix(path.Base(filename), "_") { - continue - } - // At render time, add information about the template that is being rendered. - vals := tpls[filename].vals - vals["Template"] = chartutil.Values{"Name": filename, "BasePath": tpls[filename].basePath} - var buf strings.Builder - if err := t.ExecuteTemplate(&buf, filename, vals); err != nil { - errs = append(errs, cleanupParseError(filename, err)) - } - - // Work around the issue where Go will emit "" even if Options(missing=zero) - // is set. Since missing=error will never get here, we do not need to handle - // the Strict case. - rendered[filename] = strings.ReplaceAll(buf.String(), "", "") - } - if len(errs) > 0 { - return map[string]string{}, &LintError{errors: errs} - } - - return rendered, nil -} - -func cleanupParseError(filename string, err error) error { - - tokens := strings.Split(err.Error(), ": ") - - if len(tokens) == 1 { - // This might happen if a non-templating error occurs - return fmt.Errorf("parse error in (%s): %s", filename, err) - } - - // The first token is "template" - // The second token is either "filename:lineno" or "filename:lineNo:columnNo" - location := tokens[1] - //// The remaining tokens make up a stacktrace-like chain, ending with the relevant error - errMsg := tokens[len(tokens)-1] - return fmt.Errorf("parse error at (%s): %s", string(location), errMsg) -} - -//func cleanupExecError(filename string, err error) error { -//if _, isExecError := err.(template.ExecError); !isExecError { -//return err -//} - -//tokens := strings.SplitN(err.Error(), ": ", 3) -//if len(tokens) != 3 { -//// This might happen if a non-templating error occurs -//return fmt.Errorf("execution error in (%s): %s", filename, err) -//} - -//// The first token is "template" -//// The second token is either "filename:lineno" or "filename:lineNo:columnNo" -//location := tokens[1] - -//warnRegex := regexp.MustCompile(warnStartDelim + `((?s).*)` + warnEndDelim) - -//parts := warnRegex.FindStringSubmatch(tokens[2]) -//if len(parts) >= 2 { -//return fmt.Errorf("execution error at (%s): %s", string(location), parts[1]) -//} - -//return err -//} - -func sortTemplates(tpls map[string]renderable) []string { - keys := make([]string, len(tpls)) - i := 0 - for key := range tpls { - keys[i] = key - i++ - } - sort.Sort(sort.Reverse(byPathLen(keys))) - return keys -} - -type byPathLen []string - -func (p byPathLen) Len() int { return len(p) } -func (p byPathLen) Swap(i, j int) { p[j], p[i] = p[i], p[j] } -func (p byPathLen) Less(i, j int) bool { - a, b := p[i], p[j] - ca, cb := strings.Count(a, "/"), strings.Count(b, "/") - if ca == cb { - return strings.Compare(a, b) == -1 - } - return ca < cb -} - -// allTemplates returns all templates for a chart and its dependencies. -// -// As it goes, it also prepares the values in a scope-sensitive manner. -func allTemplates(c *chart.Chart, vals chartutil.Values) map[string]renderable { - templates := make(map[string]renderable) - recAllTpls(c, templates, vals) - return templates -} - -// recAllTpls recurses through the templates in a chart. -// -// As it recurses, it also sets the values to be appropriate for the template -// scope. -func recAllTpls(c *chart.Chart, templates map[string]renderable, vals chartutil.Values) map[string]interface{} { - subCharts := make(map[string]interface{}) - chartMetaData := struct { - chart.Metadata - IsRoot bool - }{*c.Metadata, c.IsRoot()} - - next := map[string]interface{}{ - "Chart": chartMetaData, - "Files": newFiles(c.Files), - "Release": vals["Release"], - "Capabilities": vals["Capabilities"], - "Values": make(chartutil.Values), - "Subcharts": subCharts, - } - - // If there is a {{.Values.ThisChart}} in the parent metadata, - // copy that into the {{.Values}} for this template. - if c.IsRoot() { - next["Values"] = vals["Values"] - } else if vs, err := vals.Table("Values." + c.Name()); err == nil { - next["Values"] = vs - } - - for _, child := range c.Dependencies() { - subCharts[child.Name()] = recAllTpls(child, templates, next) - } - - newParentID := c.ChartFullPath() - for _, t := range c.Templates { - if !isTemplateValid(c, t.Name) { - continue - } - templates[path.Join(newParentID, t.Name)] = renderable{ - tpl: string(t.Data), - vals: next, - basePath: path.Join(newParentID, "templates"), - } - } - - return next -} - -// isTemplateValid returns true if the template is valid for the chart type -func isTemplateValid(ch *chart.Chart, templateName string) bool { - if isLibraryChart(ch) { - return strings.HasPrefix(filepath.Base(templateName), "_") - } - return true -} - -// isLibraryChart returns true if the chart is a library chart -func isLibraryChart(c *chart.Chart) bool { - return strings.EqualFold(c.Metadata.Type, "library") -} diff --git a/pkg/engine/error.go b/pkg/engine/error.go deleted file mode 100644 index eb87a38c..00000000 --- a/pkg/engine/error.go +++ /dev/null @@ -1,25 +0,0 @@ -package engine - -type LintError struct { - message string - errors []error -} - -func NewLintError(msg string, errs ...error) *LintError { - return &LintError{ - errors: errs, - message: msg, - } -} - -func (err *LintError) Errors() []error { - return err.errors -} - -func (err *LintError) Error() string { - message := "" - for _, e := range err.errors { - message += e.Error() + "\n" - } - return message -} diff --git a/pkg/engine/files.go b/pkg/engine/files.go deleted file mode 100644 index 48199de5..00000000 --- a/pkg/engine/files.go +++ /dev/null @@ -1,160 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package engine - -import ( - "encoding/base64" - "path" - "strings" - - "github.com/gobwas/glob" - - "github.com/mrjosh/helm-ls/pkg/chart" -) - -// files is a map of files in a chart that can be accessed from a template. -type files map[string][]byte - -// NewFiles creates a new files from chart files. -// Given an []*chart.File (the format for files in a chart.Chart), extract a map of files. -func newFiles(from []*chart.File) files { - files := make(map[string][]byte) - for _, f := range from { - files[f.Name] = f.Data - } - return files -} - -// GetBytes gets a file by path. -// -// The returned data is raw. In a template context, this is identical to calling -// {{index .Files $path}}. -// -// This is intended to be accessed from within a template, so a missed key returns -// an empty []byte. -func (f files) GetBytes(name string) []byte { - if v, ok := f[name]; ok { - return v - } - return []byte{} -} - -// Get returns a string representation of the given file. -// -// Fetch the contents of a file as a string. It is designed to be called in a -// template. -// -// {{.Files.Get "foo"}} -func (f files) Get(name string) string { - return string(f.GetBytes(name)) -} - -// Glob takes a glob pattern and returns another files object only containing -// matched files. -// -// This is designed to be called from a template. -// -// {{ range $name, $content := .Files.Glob("foo/**") }} -// {{ $name }}: | -// {{ .Files.Get($name) | indent 4 }}{{ end }} -func (f files) Glob(pattern string) files { - g, err := glob.Compile(pattern, '/') - if err != nil { - g, _ = glob.Compile("**") - } - - nf := newFiles(nil) - for name, contents := range f { - if g.Match(name) { - nf[name] = contents - } - } - - return nf -} - -// AsConfig turns a Files group and flattens it to a YAML map suitable for -// including in the 'data' section of a Kubernetes ConfigMap definition. -// Duplicate keys will be overwritten, so be aware that your file names -// (regardless of path) should be unique. -// -// This is designed to be called from a template, and will return empty string -// (via toYAML function) if it cannot be serialized to YAML, or if the Files -// object is nil. -// -// The output will not be indented, so you will want to pipe this to the -// 'indent' template function. -// -// data: -// {{ .Files.Glob("config/**").AsConfig() | indent 4 }} -func (f files) AsConfig() string { - if f == nil { - return "" - } - - m := make(map[string]string) - - // Explicitly convert to strings, and file names - for k, v := range f { - m[path.Base(k)] = string(v) - } - - return toYAML(m) -} - -// AsSecrets returns the base64-encoded value of a Files object suitable for -// including in the 'data' section of a Kubernetes Secret definition. -// Duplicate keys will be overwritten, so be aware that your file names -// (regardless of path) should be unique. -// -// This is designed to be called from a template, and will return empty string -// (via toYAML function) if it cannot be serialized to YAML, or if the Files -// object is nil. -// -// The output will not be indented, so you will want to pipe this to the -// 'indent' template function. -// -// data: -// {{ .Files.Glob("secrets/*").AsSecrets() }} -func (f files) AsSecrets() string { - if f == nil { - return "" - } - - m := make(map[string]string) - - for k, v := range f { - m[path.Base(k)] = base64.StdEncoding.EncodeToString(v) - } - - return toYAML(m) -} - -// Lines returns each line of a named file (split by "\n") as a slice, so it can -// be ranged over in your templates. -// -// This is designed to be called from a template. -// -// {{ range .Files.Lines "foo/bar.html" }} -// {{ . }}{{ end }} -func (f files) Lines(path string) []string { - if f == nil || f[path] == nil { - return []string{} - } - - return strings.Split(string(f[path]), "\n") -} diff --git a/pkg/engine/files_test.go b/pkg/engine/files_test.go deleted file mode 100644 index 4b37724f..00000000 --- a/pkg/engine/files_test.go +++ /dev/null @@ -1,98 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package engine - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -var cases = []struct { - path, data string -}{ - {"ship/captain.txt", "The Captain"}, - {"ship/stowaway.txt", "Legatt"}, - {"story/name.txt", "The Secret Sharer"}, - {"story/author.txt", "Joseph Conrad"}, - {"multiline/test.txt", "bar\nfoo"}, -} - -func getTestFiles() files { - a := make(files, len(cases)) - for _, c := range cases { - a[c.path] = []byte(c.data) - } - return a -} - -func TestNewFiles(t *testing.T) { - files := getTestFiles() - if len(files) != len(cases) { - t.Errorf("Expected len() = %d, got %d", len(cases), len(files)) - } - - for i, f := range cases { - if got := string(files.GetBytes(f.path)); got != f.data { - t.Errorf("%d: expected %q, got %q", i, f.data, got) - } - if got := files.Get(f.path); got != f.data { - t.Errorf("%d: expected %q, got %q", i, f.data, got) - } - } -} - -func TestFileGlob(t *testing.T) { - as := assert.New(t) - - f := getTestFiles() - - matched := f.Glob("story/**") - - as.Len(matched, 2, "Should be two files in glob story/**") - as.Equal("Joseph Conrad", matched.Get("story/author.txt")) -} - -func TestToConfig(t *testing.T) { - as := assert.New(t) - - f := getTestFiles() - out := f.Glob("**/captain.txt").AsConfig() - as.Equal("captain.txt: The Captain", out) - - out = f.Glob("ship/**").AsConfig() - as.Equal("captain.txt: The Captain\nstowaway.txt: Legatt", out) -} - -func TestToSecret(t *testing.T) { - as := assert.New(t) - - f := getTestFiles() - - out := f.Glob("ship/**").AsSecrets() - as.Equal("captain.txt: VGhlIENhcHRhaW4=\nstowaway.txt: TGVnYXR0", out) -} - -func TestLines(t *testing.T) { - as := assert.New(t) - - f := getTestFiles() - - out := f.Lines("multiline/test.txt") - as.Len(out, 2) - - as.Equal("bar", out[0]) -} diff --git a/pkg/engine/funcs.go b/pkg/engine/funcs.go deleted file mode 100644 index f563b5be..00000000 --- a/pkg/engine/funcs.go +++ /dev/null @@ -1,179 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package engine - -import ( - "bytes" - "encoding/json" - "strings" - "text/template" - - "github.com/BurntSushi/toml" - sprig "github.com/Masterminds/sprig/v3" - "sigs.k8s.io/yaml" -) - -const NotImplementedErrString = "not implemented" - -// funcMap returns a mapping of all of the functions that Engine has. -// -// Because some functions are late-bound (e.g. contain context-sensitive -// data), the functions may not all perform identically outside of an Engine -// as they will inside of an Engine. -// -// Known late-bound functions: -// -// - "include" -// - "tpl" -// -// These are late-bound in Engine.Render(). The -// version included in the FuncMap is a placeholder. -// -func funcMap() template.FuncMap { - f := sprig.TxtFuncMap() - delete(f, "env") - delete(f, "expandenv") - - // Add some extra functionality - extra := template.FuncMap{ - "toToml": toTOML, - "toYaml": toYAML, - "fromYaml": fromYAML, - "fromYamlArray": fromYAMLArray, - "toJson": toJSON, - "fromJson": fromJSON, - "fromJsonArray": fromJSONArray, - - // This is a placeholder for the "include" function, which is - // late-bound to a template. By declaring it here, we preserve the - // integrity of the linter. - "include": func(string, interface{}) string { return NotImplementedErrString }, - "tpl": func(string, interface{}) interface{} { return NotImplementedErrString }, - "required": func(string, interface{}) (interface{}, error) { return NotImplementedErrString, nil }, - // Provide a placeholder for the "lookup" function, which requires a kubernetes - // connection. - "lookup": func(string, string, string, string) (map[string]interface{}, error) { - return map[string]interface{}{}, nil - }, - } - - for k, v := range extra { - f[k] = v - } - - return f -} - -// toYAML takes an interface, marshals it to yaml, and returns a string. It will -// always return a string, even on marshal error (empty string). -// -// This is designed to be called from a template. -func toYAML(v interface{}) string { - data, err := yaml.Marshal(v) - if err != nil { - // Swallow errors inside of a template. - return "" - } - return strings.TrimSuffix(string(data), "\n") -} - -// fromYAML converts a YAML document into a map[string]interface{}. -// -// This is not a general-purpose YAML parser, and will not parse all valid -// YAML documents. Additionally, because its intended use is within templates -// it tolerates errors. It will insert the returned error message string into -// m["Error"] in the returned map. -func fromYAML(str string) map[string]interface{} { - m := map[string]interface{}{} - - if err := yaml.Unmarshal([]byte(str), &m); err != nil { - m["Error"] = err.Error() - } - return m -} - -// fromYAMLArray converts a YAML array into a []interface{}. -// -// This is not a general-purpose YAML parser, and will not parse all valid -// YAML documents. Additionally, because its intended use is within templates -// it tolerates errors. It will insert the returned error message string as -// the first and only item in the returned array. -func fromYAMLArray(str string) []interface{} { - a := []interface{}{} - - if err := yaml.Unmarshal([]byte(str), &a); err != nil { - a = []interface{}{err.Error()} - } - return a -} - -// toTOML takes an interface, marshals it to toml, and returns a string. It will -// always return a string, even on marshal error (empty string). -// -// This is designed to be called from a template. -func toTOML(v interface{}) string { - b := bytes.NewBuffer(nil) - e := toml.NewEncoder(b) - err := e.Encode(v) - if err != nil { - return err.Error() - } - return b.String() -} - -// toJSON takes an interface, marshals it to json, and returns a string. It will -// always return a string, even on marshal error (empty string). -// -// This is designed to be called from a template. -func toJSON(v interface{}) string { - data, err := json.Marshal(v) - if err != nil { - // Swallow errors inside of a template. - return "" - } - return string(data) -} - -// fromJSON converts a JSON document into a map[string]interface{}. -// -// This is not a general-purpose JSON parser, and will not parse all valid -// JSON documents. Additionally, because its intended use is within templates -// it tolerates errors. It will insert the returned error message string into -// m["Error"] in the returned map. -func fromJSON(str string) map[string]interface{} { - m := make(map[string]interface{}) - - if err := json.Unmarshal([]byte(str), &m); err != nil { - m["Error"] = err.Error() - } - return m -} - -// fromJSONArray converts a JSON array into a []interface{}. -// -// This is not a general-purpose JSON parser, and will not parse all valid -// JSON documents. Additionally, because its intended use is within templates -// it tolerates errors. It will insert the returned error message string as -// the first and only item in the returned array. -func fromJSONArray(str string) []interface{} { - a := []interface{}{} - - if err := json.Unmarshal([]byte(str), &a); err != nil { - a = []interface{}{err.Error()} - } - return a -} diff --git a/pkg/engine/funcs_test.go b/pkg/engine/funcs_test.go deleted file mode 100644 index 29bc121b..00000000 --- a/pkg/engine/funcs_test.go +++ /dev/null @@ -1,178 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package engine - -import ( - "strings" - "testing" - "text/template" - - "github.com/stretchr/testify/assert" -) - -func TestFuncs(t *testing.T) { - //TODO write tests for failure cases - tests := []struct { - tpl, expect string - vars interface{} - }{{ - tpl: `{{ toYaml . }}`, - expect: `foo: bar`, - vars: map[string]interface{}{"foo": "bar"}, - }, { - tpl: `{{ toToml . }}`, - expect: "foo = \"bar\"\n", - vars: map[string]interface{}{"foo": "bar"}, - }, { - tpl: `{{ toJson . }}`, - expect: `{"foo":"bar"}`, - vars: map[string]interface{}{"foo": "bar"}, - }, { - tpl: `{{ fromYaml . }}`, - expect: "map[hello:world]", - vars: `hello: world`, - }, { - tpl: `{{ fromYamlArray . }}`, - expect: "[one 2 map[name:helm]]", - vars: "- one\n- 2\n- name: helm\n", - }, { - tpl: `{{ fromYamlArray . }}`, - expect: "[one 2 map[name:helm]]", - vars: `["one", 2, { "name": "helm" }]`, - }, { - // Regression for https://github.com/helm/helm/issues/2271 - tpl: `{{ toToml . }}`, - expect: "[mast]\n sail = \"white\"\n", - vars: map[string]map[string]string{"mast": {"sail": "white"}}, - }, { - tpl: `{{ fromYaml . }}`, - expect: "map[Error:error unmarshaling JSON: while decoding JSON: json: cannot unmarshal array into Go value of type map[string]interface {}]", - vars: "- one\n- two\n", - }, { - tpl: `{{ fromJson .}}`, - expect: `map[hello:world]`, - vars: `{"hello":"world"}`, - }, { - tpl: `{{ fromJson . }}`, - expect: `map[Error:json: cannot unmarshal array into Go value of type map[string]interface {}]`, - vars: `["one", "two"]`, - }, { - tpl: `{{ fromJsonArray . }}`, - expect: `[one 2 map[name:helm]]`, - vars: `["one", 2, { "name": "helm" }]`, - }, { - tpl: `{{ fromJsonArray . }}`, - expect: `[json: cannot unmarshal object into Go value of type []interface {}]`, - vars: `{"hello": "world"}`, - }, { - tpl: `{{ merge .dict (fromYaml .yaml) }}`, - expect: `map[a:map[b:c]]`, - vars: map[string]interface{}{"dict": map[string]interface{}{"a": map[string]interface{}{"b": "c"}}, "yaml": `{"a":{"b":"d"}}`}, - }, { - tpl: `{{ merge (fromYaml .yaml) .dict }}`, - expect: `map[a:map[b:d]]`, - vars: map[string]interface{}{"dict": map[string]interface{}{"a": map[string]interface{}{"b": "c"}}, "yaml": `{"a":{"b":"d"}}`}, - }, { - tpl: `{{ fromYaml . }}`, - expect: `map[Error:error unmarshaling JSON: while decoding JSON: json: cannot unmarshal array into Go value of type map[string]interface {}]`, - vars: `["one", "two"]`, - }, { - tpl: `{{ fromYamlArray . }}`, - expect: `[error unmarshaling JSON: while decoding JSON: json: cannot unmarshal object into Go value of type []interface {}]`, - vars: `hello: world`, - }, { - // This should never result in a network lookup. Regression for #7955 - tpl: `{{ lookup "v1" "Namespace" "" "unlikelynamespace99999999" }}`, - expect: `map[]`, - vars: `["one", "two"]`, - }} - - for _, tt := range tests { - var b strings.Builder - err := template.Must(template.New("test").Funcs(funcMap()).Parse(tt.tpl)).Execute(&b, tt.vars) - assert.NoError(t, err) - assert.Equal(t, tt.expect, b.String(), tt.tpl) - } -} - -// This test to check a function provided by sprig is due to a change in a -// dependency of sprig. mergo in v0.3.9 changed the way it merges and only does -// public fields (i.e. those starting with a capital letter). This test, from -// sprig, fails in the new version. This is a behavior change for mergo that -// impacts sprig and Helm users. This test will help us to not update to a -// version of mergo (even accidentally) that causes a breaking change. See -// sprig changelog and notes for more details. -// Note, Go modules assume semver is never broken. So, there is no way to tell -// the tooling to not update to a minor or patch version. `go install` could -// be used to accidentally update mergo. This test and message should catch -// the problem and explain why it's happening. -func TestMerge(t *testing.T) { - dict := map[string]interface{}{ - "src2": map[string]interface{}{ - "h": 10, - "i": "i", - "j": "j", - }, - "src1": map[string]interface{}{ - "a": 1, - "b": 2, - "d": map[string]interface{}{ - "e": "four", - }, - "g": []int{6, 7}, - "i": "aye", - "j": "jay", - "k": map[string]interface{}{ - "l": false, - }, - }, - "dst": map[string]interface{}{ - "a": "one", - "c": 3, - "d": map[string]interface{}{ - "f": 5, - }, - "g": []int{8, 9}, - "i": "eye", - "k": map[string]interface{}{ - "l": true, - }, - }, - } - tpl := `{{merge .dst .src1 .src2}}` - var b strings.Builder - err := template.Must(template.New("test").Funcs(funcMap()).Parse(tpl)).Execute(&b, dict) - assert.NoError(t, err) - - expected := map[string]interface{}{ - "a": "one", // key overridden - "b": 2, // merged from src1 - "c": 3, // merged from dst - "d": map[string]interface{}{ // deep merge - "e": "four", - "f": 5, - }, - "g": []int{8, 9}, // overridden - arrays are not merged - "h": 10, // merged from src2 - "i": "eye", // overridden twice - "j": "jay", // overridden and merged - "k": map[string]interface{}{ - "l": true, // overridden - }, - } - assert.Equal(t, expected, dict["dst"]) -} diff --git a/pkg/engine/lookup_func.go b/pkg/engine/lookup_func.go deleted file mode 100644 index d1bf1105..00000000 --- a/pkg/engine/lookup_func.go +++ /dev/null @@ -1,124 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package engine - -import ( - "context" - "log" - "strings" - - "github.com/pkg/errors" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/discovery" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/rest" -) - -type lookupFunc = func(apiversion string, resource string, namespace string, name string) (map[string]interface{}, error) - -// NewLookupFunction returns a function for looking up objects in the cluster. -// -// If the resource does not exist, no error is raised. -// -// This function is considered deprecated, and will be renamed in Helm 4. It will no -// longer be a public function. -func NewLookupFunction(config *rest.Config) lookupFunc { - return func(apiversion string, resource string, namespace string, name string) (map[string]interface{}, error) { - var client dynamic.ResourceInterface - c, namespaced, err := getDynamicClientOnKind(apiversion, resource, config) - if err != nil { - return map[string]interface{}{}, err - } - if namespaced && namespace != "" { - client = c.Namespace(namespace) - } else { - client = c - } - if name != "" { - // this will return a single object - obj, err := client.Get(context.Background(), name, metav1.GetOptions{}) - if err != nil { - if apierrors.IsNotFound(err) { - // Just return an empty interface when the object was not found. - // That way, users can use `if not (lookup ...)` in their templates. - return map[string]interface{}{}, nil - } - return map[string]interface{}{}, err - } - return obj.UnstructuredContent(), nil - } - // this will return a list - obj, err := client.List(context.Background(), metav1.ListOptions{}) - if err != nil { - if apierrors.IsNotFound(err) { - // Just return an empty interface when the object was not found. - // That way, users can use `if not (lookup ...)` in their templates. - return map[string]interface{}{}, nil - } - return map[string]interface{}{}, err - } - return obj.UnstructuredContent(), nil - } -} - -// getDynamicClientOnUnstructured returns a dynamic client on an Unstructured type. This client can be further namespaced. -func getDynamicClientOnKind(apiversion string, kind string, config *rest.Config) (dynamic.NamespaceableResourceInterface, bool, error) { - gvk := schema.FromAPIVersionAndKind(apiversion, kind) - apiRes, err := getAPIResourceForGVK(gvk, config) - if err != nil { - log.Printf("[ERROR] unable to get apiresource from unstructured: %s , error %s", gvk.String(), err) - return nil, false, errors.Wrapf(err, "unable to get apiresource from unstructured: %s", gvk.String()) - } - gvr := schema.GroupVersionResource{ - Group: apiRes.Group, - Version: apiRes.Version, - Resource: apiRes.Name, - } - intf, err := dynamic.NewForConfig(config) - if err != nil { - log.Printf("[ERROR] unable to get dynamic client %s", err) - return nil, false, err - } - res := intf.Resource(gvr) - return res, apiRes.Namespaced, nil -} - -func getAPIResourceForGVK(gvk schema.GroupVersionKind, config *rest.Config) (metav1.APIResource, error) { - res := metav1.APIResource{} - discoveryClient, err := discovery.NewDiscoveryClientForConfig(config) - if err != nil { - log.Printf("[ERROR] unable to create discovery client %s", err) - return res, err - } - resList, err := discoveryClient.ServerResourcesForGroupVersion(gvk.GroupVersion().String()) - if err != nil { - log.Printf("[ERROR] unable to retrieve resource list for: %s , error: %s", gvk.GroupVersion().String(), err) - return res, err - } - for _, resource := range resList.APIResources { - // if a resource contains a "/" it's referencing a subresource. we don't support suberesource for now. - if resource.Kind == gvk.Kind && !strings.Contains(resource.Name, "/") { - res = resource - res.Group = gvk.Group - res.Version = gvk.Version - break - } - } - return res, nil -} diff --git a/pkg/lint/lint.go b/pkg/lint/lint.go deleted file mode 100644 index ce671c70..00000000 --- a/pkg/lint/lint.go +++ /dev/null @@ -1,37 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package lint - -import ( - "path/filepath" - - "github.com/mrjosh/helm-ls/pkg/lint/rules" - "github.com/mrjosh/helm-ls/pkg/lint/support" -) - -// All runs all of the available linters on the given base directory. -func All(basedir string, values map[string]interface{}, namespace string, strict bool) support.Linter { - // Using abs path to get directory context - chartDir, _ := filepath.Abs(basedir) - - linter := support.Linter{ChartDir: chartDir} - //rules.Chartfile(&linter) - //rules.ValuesWithOverrides(&linter, values) - rules.Templates(&linter, values, namespace, strict) - //rules.Dependencies(&linter) - return linter -} diff --git a/pkg/lint/rules/chartfile.go b/pkg/lint/rules/chartfile.go deleted file mode 100644 index 823d9889..00000000 --- a/pkg/lint/rules/chartfile.go +++ /dev/null @@ -1,209 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package rules - -import ( - "fmt" - "os" - "path/filepath" - - semver "github.com/Masterminds/semver/v3" - "github.com/asaskevich/govalidator" - "github.com/pkg/errors" - "sigs.k8s.io/yaml" - - "github.com/mrjosh/helm-ls/pkg/chart" - "github.com/mrjosh/helm-ls/pkg/chartutil" - "github.com/mrjosh/helm-ls/pkg/lint/support" -) - -// Chartfile runs a set of linter rules related to Chart.yaml file -func Chartfile(linter *support.Linter) { - chartFileName := "Chart.yaml" - chartPath := filepath.Join(linter.ChartDir, chartFileName) - - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartYamlNotDirectory(chartPath)) - - chartFile, err := chartutil.LoadChartfile(chartPath) - validChartFile := linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartYamlFormat(err)) - - // Guard clause. Following linter rules require a parsable ChartFile - if !validChartFile { - return - } - - // type check for Chart.yaml . ignoring error as any parse - // errors would already be caught in the above load function - chartFileForTypeCheck, _ := loadChartFileForTypeCheck(chartPath) - - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartName(chartFile)) - - // Chart metadata - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartAPIVersion(chartFile)) - - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartVersionType(chartFileForTypeCheck)) - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartVersion(chartFile)) - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartAppVersionType(chartFileForTypeCheck)) - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartMaintainer(chartFile)) - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartSources(chartFile)) - linter.RunLinterRule(support.InfoSev, chartFileName, validateChartIconPresence(chartFile)) - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartIconURL(chartFile)) - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartType(chartFile)) - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartDependencies(chartFile)) -} - -func validateChartVersionType(data map[string]interface{}) error { - return isStringValue(data, "version") -} - -func validateChartAppVersionType(data map[string]interface{}) error { - return isStringValue(data, "appVersion") -} - -func isStringValue(data map[string]interface{}, key string) error { - value, ok := data[key] - if !ok { - return nil - } - valueType := fmt.Sprintf("%T", value) - if valueType != "string" { - return errors.Errorf("%s should be of type string but it's of type %s", key, valueType) - } - return nil -} - -func validateChartYamlNotDirectory(chartPath string) error { - fi, err := os.Stat(chartPath) - - if err == nil && fi.IsDir() { - return errors.New("should be a file, not a directory") - } - return nil -} - -func validateChartYamlFormat(chartFileError error) error { - if chartFileError != nil { - return errors.Errorf("unable to parse YAML\n\t%s", chartFileError.Error()) - } - return nil -} - -func validateChartName(cf *chart.Metadata) error { - if cf.Name == "" { - return errors.New("name is required") - } - return nil -} - -func validateChartAPIVersion(cf *chart.Metadata) error { - if cf.APIVersion == "" { - return errors.New("apiVersion is required. The value must be either \"v1\" or \"v2\"") - } - - if cf.APIVersion != chart.APIVersionV1 && cf.APIVersion != chart.APIVersionV2 { - return fmt.Errorf("apiVersion '%s' is not valid. The value must be either \"v1\" or \"v2\"", cf.APIVersion) - } - - return nil -} - -func validateChartVersion(cf *chart.Metadata) error { - if cf.Version == "" { - return errors.New("version is required") - } - - version, err := semver.NewVersion(cf.Version) - - if err != nil { - return errors.Errorf("version '%s' is not a valid SemVer", cf.Version) - } - - c, err := semver.NewConstraint(">0.0.0-0") - if err != nil { - return err - } - valid, msg := c.Validate(version) - - if !valid && len(msg) > 0 { - return errors.Errorf("version %v", msg[0]) - } - - return nil -} - -func validateChartMaintainer(cf *chart.Metadata) error { - for _, maintainer := range cf.Maintainers { - if maintainer.Name == "" { - return errors.New("each maintainer requires a name") - } else if maintainer.Email != "" && !govalidator.IsEmail(maintainer.Email) { - return errors.Errorf("invalid email '%s' for maintainer '%s'", maintainer.Email, maintainer.Name) - } else if maintainer.URL != "" && !govalidator.IsURL(maintainer.URL) { - return errors.Errorf("invalid url '%s' for maintainer '%s'", maintainer.URL, maintainer.Name) - } - } - return nil -} - -func validateChartSources(cf *chart.Metadata) error { - for _, source := range cf.Sources { - if source == "" || !govalidator.IsRequestURL(source) { - return errors.Errorf("invalid source URL '%s'", source) - } - } - return nil -} - -func validateChartIconPresence(cf *chart.Metadata) error { - if cf.Icon == "" { - return errors.New("icon is recommended") - } - return nil -} - -func validateChartIconURL(cf *chart.Metadata) error { - if cf.Icon != "" && !govalidator.IsRequestURL(cf.Icon) { - return errors.Errorf("invalid icon URL '%s'", cf.Icon) - } - return nil -} - -func validateChartDependencies(cf *chart.Metadata) error { - if len(cf.Dependencies) > 0 && cf.APIVersion != chart.APIVersionV2 { - return fmt.Errorf("dependencies are not valid in the Chart file with apiVersion '%s'. They are valid in apiVersion '%s'", cf.APIVersion, chart.APIVersionV2) - } - return nil -} - -func validateChartType(cf *chart.Metadata) error { - if len(cf.Type) > 0 && cf.APIVersion != chart.APIVersionV2 { - return fmt.Errorf("chart type is not valid in apiVersion '%s'. It is valid in apiVersion '%s'", cf.APIVersion, chart.APIVersionV2) - } - return nil -} - -// loadChartFileForTypeCheck loads the Chart.yaml -// in a generic form of a map[string]interface{}, so that the type -// of the values can be checked -func loadChartFileForTypeCheck(filename string) (map[string]interface{}, error) { - b, err := os.ReadFile(filename) - if err != nil { - return nil, err - } - y := make(map[string]interface{}) - err = yaml.Unmarshal(b, &y) - return y, err -} diff --git a/pkg/lint/rules/dependencies.go b/pkg/lint/rules/dependencies.go deleted file mode 100644 index 1bf37f7c..00000000 --- a/pkg/lint/rules/dependencies.go +++ /dev/null @@ -1,82 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package rules - -import ( - "fmt" - "strings" - - "github.com/pkg/errors" - - "github.com/mrjosh/helm-ls/pkg/chart" - "github.com/mrjosh/helm-ls/pkg/chart/loader" - "github.com/mrjosh/helm-ls/pkg/lint/support" -) - -// Dependencies runs lints against a chart's dependencies -// -// See https://github.com/helm/helm/issues/7910 -func Dependencies(linter *support.Linter) { - c, err := loader.LoadDir(linter.ChartDir) - if !linter.RunLinterRule(support.ErrorSev, "", validateChartFormat(err)) { - return - } - - linter.RunLinterRule(support.ErrorSev, linter.ChartDir, validateDependencyInMetadata(c)) - linter.RunLinterRule(support.WarningSev, linter.ChartDir, validateDependencyInChartsDir(c)) -} - -func validateChartFormat(chartError error) error { - if chartError != nil { - return errors.Errorf("unable to load chart\n\t%s", chartError) - } - return nil -} - -func validateDependencyInChartsDir(c *chart.Chart) (err error) { - dependencies := map[string]struct{}{} - missing := []string{} - for _, dep := range c.Dependencies() { - dependencies[dep.Metadata.Name] = struct{}{} - } - for _, dep := range c.Metadata.Dependencies { - if _, ok := dependencies[dep.Name]; !ok { - missing = append(missing, dep.Name) - } - } - if len(missing) > 0 { - err = fmt.Errorf("chart directory is missing these dependencies: %s", strings.Join(missing, ",")) - } - return err -} - -func validateDependencyInMetadata(c *chart.Chart) (err error) { - dependencies := map[string]struct{}{} - missing := []string{} - for _, dep := range c.Metadata.Dependencies { - dependencies[dep.Name] = struct{}{} - } - for _, dep := range c.Dependencies() { - if _, ok := dependencies[dep.Metadata.Name]; !ok { - missing = append(missing, dep.Metadata.Name) - } - } - if len(missing) > 0 { - err = fmt.Errorf("chart metadata is missing these dependencies: %s", strings.Join(missing, ",")) - } - return err -} diff --git a/pkg/lint/rules/deprecations.go b/pkg/lint/rules/deprecations.go deleted file mode 100644 index 784e3a3d..00000000 --- a/pkg/lint/rules/deprecations.go +++ /dev/null @@ -1,98 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package rules - -import ( - "fmt" - "strconv" - - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apiserver/pkg/endpoints/deprecation" - kscheme "k8s.io/client-go/kubernetes/scheme" -) - -var ( - // This should be set in the Makefile based on the version of client-go being imported. - // These constants will be overwritten with LDFLAGS. The version components must be - // strings in order for LDFLAGS to set them. - k8sVersionMajor = "1" - k8sVersionMinor = "20" -) - -// deprecatedAPIError indicates than an API is deprecated in Kubernetes -type deprecatedAPIError struct { - Deprecated string - Message string -} - -func (e deprecatedAPIError) Error() string { - msg := e.Message - return msg -} - -func validateNoDeprecations(resource *K8sYamlStruct) error { - // if `resource` does not have an APIVersion or Kind, we cannot test it for deprecation - if resource.APIVersion == "" { - return nil - } - if resource.Kind == "" { - return nil - } - - runtimeObject, err := resourceToRuntimeObject(resource) - if err != nil { - // do not error for non-kubernetes resources - if runtime.IsNotRegisteredError(err) { - return nil - } - return err - } - maj, err := strconv.Atoi(k8sVersionMajor) - if err != nil { - return err - } - min, err := strconv.Atoi(k8sVersionMinor) - if err != nil { - return err - } - - if !deprecation.IsDeprecated(runtimeObject, maj, min) { - return nil - } - gvk := fmt.Sprintf("%s %s", resource.APIVersion, resource.Kind) - return deprecatedAPIError{ - Deprecated: gvk, - Message: deprecation.WarningMessage(runtimeObject), - } -} - -func resourceToRuntimeObject(resource *K8sYamlStruct) (runtime.Object, error) { - scheme := runtime.NewScheme() - - if err := kscheme.AddToScheme(scheme); err != nil { - return nil, err - } - - gvk := schema.FromAPIVersionAndKind(resource.APIVersion, resource.Kind) - out, err := scheme.New(gvk) - if err != nil { - return nil, err - } - out.GetObjectKind().SetGroupVersionKind(gvk) - return out, nil -} diff --git a/pkg/lint/rules/template.go b/pkg/lint/rules/template.go deleted file mode 100644 index 61ed6b9f..00000000 --- a/pkg/lint/rules/template.go +++ /dev/null @@ -1,385 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package rules - -import ( - "bufio" - "bytes" - "fmt" - "io" - "os" - "path" - "path/filepath" - "regexp" - "strconv" - "strings" - - "github.com/pkg/errors" - "k8s.io/apimachinery/pkg/api/validation" - apipath "k8s.io/apimachinery/pkg/api/validation/path" - "k8s.io/apimachinery/pkg/util/validation/field" - "k8s.io/apimachinery/pkg/util/yaml" - - "github.com/mrjosh/helm-ls/pkg/chart/loader" - "github.com/mrjosh/helm-ls/pkg/chartutil" - "github.com/mrjosh/helm-ls/pkg/engine" - "github.com/mrjosh/helm-ls/pkg/lint/support" -) - -var ( - crdHookSearch = regexp.MustCompile(`"?helm\.sh/hook"?:\s+crd-install`) - releaseTimeSearch = regexp.MustCompile(`\.Release\.Time`) -) - -// Templates lints the templates in the Linter. -func Templates(linter *support.Linter, values map[string]interface{}, namespace string, _ bool) { - fpath := "templates/" - templatesPath := filepath.Join(linter.ChartDir, fpath) - - templatesDirExist := linter.RunLinterRule(support.WarningSev, fpath, validateTemplatesDir(templatesPath)) - - // Templates directory is optional for now - if !templatesDirExist { - return - } - - // Load chart and parse templates - chart, err := loader.Load(linter.ChartDir) - - _ = linter.RunLinterRule(support.ErrorSev, fpath, err) - //if !chartLoaded { - // ignoring this error and continuing with lint - //} - - options := chartutil.ReleaseOptions{ - Name: "test-release", - Namespace: namespace, - } - - // lint ignores import-values - // See https://github.com/helm/helm/issues/9658 - if err := chartutil.ProcessDependencies(chart, values); err != nil { - return - } - - cvals, err := chartutil.CoalesceValues(chart, values) - if err != nil { - return - } - valuesToRender, err := chartutil.ToRenderValues(chart, cvals, options, nil) - if err != nil { - linter.RunLinterRule(support.ErrorSev, fpath, err) - return - } - var e engine.Engine - e.LintMode = true - renderedContentMap, err := e.Render(chart, valuesToRender) - - renderOk := linter.RunLinterRule(support.ErrorSev, fpath, err) - - if !renderOk { - return - } - - /* Iterate over all the templates to check: - - It is a .yaml file - - All the values in the template file is defined - - {{}} include | quote - - Generated content is a valid Yaml file - - Metadata.Namespace is not set - */ - for _, template := range chart.Templates { - fileName, data := template.Name, template.Data - fpath = fileName - - linter.RunLinterRule(support.ErrorSev, fpath, validateAllowedExtension(fileName)) - // These are v3 specific checks to make sure and warn people if their - // chart is not compatible with v3 - linter.RunLinterRule(support.WarningSev, fpath, validateNoCRDHooks(data)) - linter.RunLinterRule(support.ErrorSev, fpath, validateNoReleaseTime(data)) - - // We only apply the following lint rules to yaml files - if filepath.Ext(fileName) != ".yaml" || filepath.Ext(fileName) == ".yml" { - continue - } - - // NOTE: disabled for now, Refs https://github.com/helm/helm/issues/1463 - // Check that all the templates have a matching value - // linter.RunLinterRule(support.WarningSev, fpath, validateNoMissingValues(templatesPath, valuesToRender, preExecutedTemplate)) - - // NOTE: disabled for now, Refs https://github.com/helm/helm/issues/1037 - // linter.RunLinterRule(support.WarningSev, fpath, validateQuotes(string(preExecutedTemplate))) - - renderedContent := renderedContentMap[path.Join(chart.Name(), fileName)] - if strings.TrimSpace(renderedContent) != "" { - linter.RunLinterRule(support.WarningSev, fpath, validateTopIndentLevel(renderedContent)) - - decoder := yaml.NewYAMLOrJSONDecoder(strings.NewReader(renderedContent), 4096) - - // Lint all resources if the file contains multiple documents separated by --- - for { - // Even though K8sYamlStruct only defines a few fields, an error in any other - // key will be raised as well - var yamlStruct *K8sYamlStruct - - err := decoder.Decode(&yamlStruct) - if err == io.EOF { - break - } - - // If YAML linting fails, we sill progress. So we don't capture the returned state - // on this linter run. - linter.RunLinterRule(support.ErrorSev, fpath, validateYamlContent(err)) - - if yamlStruct != nil { - // NOTE: set to warnings to allow users to support out-of-date kubernetes - // Refs https://github.com/helm/helm/issues/8596 - linter.RunLinterRule(support.WarningSev, fpath, validateMetadataName(yamlStruct)) - linter.RunLinterRule(support.WarningSev, fpath, validateNoDeprecations(yamlStruct)) - - linter.RunLinterRule(support.ErrorSev, fpath, validateMatchSelector(yamlStruct, renderedContent)) - linter.RunLinterRule(support.ErrorSev, fpath, validateListAnnotations(yamlStruct, renderedContent)) - } - } - } - } -} - -// validateTopIndentLevel checks that the content does not start with an indent level > 0. -// -// This error can occur when a template accidentally inserts space. It can cause -// unpredictable errors depending on whether the text is normalized before being passed -// into the YAML parser. So we trap it here. -// -// See https://github.com/helm/helm/issues/8467 -func validateTopIndentLevel(content string) error { - // Read lines until we get to a non-empty one - scanner := bufio.NewScanner(bytes.NewBufferString(content)) - for scanner.Scan() { - line := scanner.Text() - // If line is empty, skip - if strings.TrimSpace(line) == "" { - continue - } - // If it starts with one or more spaces, this is an error - if strings.HasPrefix(line, " ") || strings.HasPrefix(line, "\t") { - return fmt.Errorf("document starts with an illegal indent: %q, which may cause parsing problems", line) - } - // Any other condition passes. - return nil - } - return scanner.Err() -} - -// Validation functions -func validateTemplatesDir(templatesPath string) error { - if fi, err := os.Stat(templatesPath); err != nil { - return errors.New("directory not found") - } else if !fi.IsDir() { - return errors.New("not a directory") - } - return nil -} - -func validateAllowedExtension(fileName string) error { - ext := filepath.Ext(fileName) - validExtensions := []string{".yaml", ".yml", ".tpl", ".txt"} - - for _, b := range validExtensions { - if b == ext { - return nil - } - } - - return errors.Errorf("file extension '%s' not valid. Valid extensions are .yaml, .yml, .tpl, or .txt", ext) -} - -type YAMLToJSONParseError struct { - message string - Line int -} - -func (err *YAMLToJSONParseError) Error() string { - return err.message -} - -func validateYamlContent(err error) error { - er, ok := err.(yaml.YAMLSyntaxError) - if ok { - - erStr := strings.ReplaceAll( - er.Error(), - "error converting YAML to JSON: yaml:", - "", - ) - - cleanStr := strings.TrimSpace(erStr) - - splittedErr := strings.Split(cleanStr, ":") - linenostr := strings.Split(splittedErr[0], " ") - - lineno, _ := strconv.Atoi(linenostr[1]) - - return &YAMLToJSONParseError{ - message: strings.TrimSpace(splittedErr[1]), - Line: lineno, - } - } - return errors.Wrap(err, "unable to parse YAML") -} - -type MetadataError struct { - message string - details error -} - -func (err *MetadataError) Error() string { - return err.message -} - -func (err *MetadataError) Details() error { - return err.details -} - -// validateMetadataName uses the correct validation function for the object -// Kind, or if not set, defaults to the standard definition of a subdomain in -// DNS (RFC 1123), used by most resources. -func validateMetadataName(obj *K8sYamlStruct) error { - fn := validateMetadataNameFunc(obj) - allErrs := field.ErrorList{} - for _, msg := range fn(obj.Metadata.Name, false) { - allErrs = append(allErrs, field.Invalid(field.NewPath("metadata").Child("name"), obj.Metadata.Name, msg)) - } - if len(allErrs) > 0 { - return &MetadataError{ - message: "object name does not conform to Kubernetes naming requirements", - details: errors.Wrapf(allErrs.ToAggregate(), "object name does not conform to Kubernetes naming requirements: %q", obj.Metadata.Name), - } - } - return nil -} - -// validateMetadataNameFunc will return a name validation function for the -// object kind, if defined below. -// -// Rules should match those set in the various api validations: -// https://github.com/kubernetes/kubernetes/blob/v1.20.0/pkg/apis/core/validation/validation.go#L205-L274 -// https://github.com/kubernetes/kubernetes/blob/v1.20.0/pkg/apis/apps/validation/validation.go#L39 -// ... -// -// Implementing here to avoid importing k/k. -// -// If no mapping is defined, returns NameIsDNSSubdomain. This is used by object -// kinds that don't have special requirements, so is the most likely to work if -// new kinds are added. -func validateMetadataNameFunc(obj *K8sYamlStruct) validation.ValidateNameFunc { - switch strings.ToLower(obj.Kind) { - case "pod", "node", "secret", "endpoints", "resourcequota", // core - "controllerrevision", "daemonset", "deployment", "replicaset", "statefulset", // apps - "autoscaler", // autoscaler - "cronjob", "job", // batch - "lease", // coordination - "endpointslice", // discovery - "networkpolicy", "ingress", // networking - "podsecuritypolicy", // policy - "priorityclass", // scheduling - "podpreset", // settings - "storageclass", "volumeattachment", "csinode": // storage - return validation.NameIsDNSSubdomain - case "service": - return validation.NameIsDNS1035Label - case "namespace": - return validation.ValidateNamespaceName - case "serviceaccount": - return validation.ValidateServiceAccountName - case "certificatesigningrequest": - // No validation. - // https://github.com/kubernetes/kubernetes/blob/v1.20.0/pkg/apis/certificates/validation/validation.go#L137-L140 - return func(name string, prefix bool) []string { return nil } - case "role", "clusterrole", "rolebinding", "clusterrolebinding": - // https://github.com/kubernetes/kubernetes/blob/v1.20.0/pkg/apis/rbac/validation/validation.go#L32-L34 - return func(name string, prefix bool) []string { - return apipath.IsValidPathSegmentName(name) - } - default: - return validation.NameIsDNSSubdomain - } -} - -func validateNoCRDHooks(manifest []byte) error { - if crdHookSearch.Match(manifest) { - return errors.New("manifest is a crd-install hook. This hook is no longer supported in v3 and all CRDs should also exist the crds/ directory at the top level of the chart") - } - return nil -} - -func validateNoReleaseTime(manifest []byte) error { - if releaseTimeSearch.Match(manifest) { - return errors.New(".Release.Time has been removed in v3, please replace with the `now` function in your templates") - } - return nil -} - -// validateMatchSelector ensures that template specs have a selector declared. -// See https://github.com/helm/helm/issues/1990 -func validateMatchSelector(yamlStruct *K8sYamlStruct, manifest string) error { - switch yamlStruct.Kind { - case "Deployment", "ReplicaSet", "DaemonSet", "StatefulSet": - // verify that matchLabels or matchExpressions is present - if !(strings.Contains(manifest, "matchLabels") || strings.Contains(manifest, "matchExpressions")) { - return fmt.Errorf("a %s must contain matchLabels or matchExpressions, and %q does not", yamlStruct.Kind, yamlStruct.Metadata.Name) - } - } - return nil -} -func validateListAnnotations(yamlStruct *K8sYamlStruct, manifest string) error { - if yamlStruct.Kind == "List" { - m := struct { - Items []struct { - Metadata struct { - Annotations map[string]string - } - } - }{} - - if err := yaml.Unmarshal([]byte(manifest), &m); err != nil { - return validateYamlContent(err) - } - - for _, i := range m.Items { - if _, ok := i.Metadata.Annotations["helm.sh/resource-policy"]; ok { - return errors.New("Annotation 'helm.sh/resource-policy' within List objects are ignored") - } - } - } - return nil -} - -// K8sYamlStruct stubs a Kubernetes YAML file. -// -// DEPRECATED: In Helm 4, this will be made a private type, as it is for use only within -// the rules package. -type K8sYamlStruct struct { - APIVersion string `json:"apiVersion"` - Kind string - Metadata k8sYamlMetadata -} - -type k8sYamlMetadata struct { - Namespace string - Name string -} diff --git a/pkg/lint/rules/values.go b/pkg/lint/rules/values.go deleted file mode 100644 index 843df159..00000000 --- a/pkg/lint/rules/values.go +++ /dev/null @@ -1,86 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package rules - -import ( - "os" - "path/filepath" - - "github.com/pkg/errors" - - "github.com/mrjosh/helm-ls/pkg/chartutil" - "github.com/mrjosh/helm-ls/pkg/lint/support" -) - -// Values lints a chart's values.yaml file. -// -// This function is deprecated and will be removed in Helm 4. -func Values(linter *support.Linter) { - ValuesWithOverrides(linter, map[string]interface{}{}) -} - -// ValuesWithOverrides tests the values.yaml file. -// -// If a schema is present in the chart, values are tested against that. Otherwise, -// they are only tested for well-formedness. -// -// If additional values are supplied, they are coalesced into the values in values.yaml. -func ValuesWithOverrides(linter *support.Linter, values map[string]interface{}) { - file := "values.yaml" - vf := filepath.Join(linter.ChartDir, file) - fileExists := linter.RunLinterRule(support.InfoSev, file, validateValuesFileExistence(vf)) - - if !fileExists { - return - } - - linter.RunLinterRule(support.ErrorSev, file, validateValuesFile(vf, values)) -} - -func validateValuesFileExistence(valuesPath string) error { - _, err := os.Stat(valuesPath) - if err != nil { - return errors.Errorf("file does not exist") - } - return nil -} - -func validateValuesFile(valuesPath string, overrides map[string]interface{}) error { - values, err := chartutil.ReadValuesFile(valuesPath) - if err != nil { - return errors.Wrap(err, "unable to parse YAML") - } - - // Helm 3.0.0 carried over the values linting from Helm 2.x, which only tests the top - // level values against the top-level expectations. Subchart values are not linted. - // We could change that. For now, though, we retain that strategy, and thus can - // coalesce tables (like reuse-values does) instead of doing the full chart - // CoalesceValues - coalescedValues := chartutil.CoalesceTables(make(map[string]interface{}, len(overrides)), overrides) - coalescedValues = chartutil.CoalesceTables(coalescedValues, values) - - ext := filepath.Ext(valuesPath) - schemaPath := valuesPath[:len(valuesPath)-len(ext)] + ".schema.json" - schema, err := os.ReadFile(schemaPath) - if len(schema) == 0 { - return nil - } - if err != nil { - return err - } - return chartutil.ValidateAgainstSingleSchema(coalescedValues, schema) -} diff --git a/pkg/lint/support/error.go b/pkg/lint/support/error.go deleted file mode 100644 index 17d5ef25..00000000 --- a/pkg/lint/support/error.go +++ /dev/null @@ -1 +0,0 @@ -package support diff --git a/pkg/lint/support/message.go b/pkg/lint/support/message.go deleted file mode 100644 index 1f22418e..00000000 --- a/pkg/lint/support/message.go +++ /dev/null @@ -1,97 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package support - -import ( - "fmt" - - "github.com/mrjosh/helm-ls/pkg/engine" -) - -// Severity indicates the severity of a Message. -const ( - // UnknownSev indicates that the severity of the error is unknown, and should not stop processing. - UnknownSev = iota - // InfoSev indicates information, for example missing values.yaml file - InfoSev - // WarningSev indicates that something does not meet code standards, but will likely function. - WarningSev - // ErrorSev indicates that something will not likely function. - ErrorSev -) - -// sev matches the *Sev states. -var sev = []string{"UNKNOWN", "INFO", "WARNING", "ERROR"} - -// Linter encapsulates a linting run of a particular chart. -type Linter struct { - Messages []Message - // The highest severity of all the failing lint rules - HighestSeverity int - ChartDir string -} - -// Message describes an error encountered while linting. -type Message struct { - // Severity is one of the *Sev constants - Severity int - Path string - Err error -} - -func (m Message) Error() string { - return fmt.Sprintf("[%s] %s: %s", sev[m.Severity], m.Path, m.Err.Error()) -} - -// NewMessage creates a new Message struct -func NewMessage(severity int, path string, err error) Message { - return Message{Severity: severity, Path: path, Err: err} -} - -// RunLinterRule returns true if the validation passed -func (l *Linter) RunLinterRule(severity int, path string, err error) bool { - // severity is out of bound - if severity < 0 || severity >= len(sev) { - return false - } - - if err != nil { - - errs, ok := err.(*engine.LintError) - if ok { - - for _, er := range errs.Errors() { - l.Messages = append(l.Messages, NewMessage(severity, path, er)) - if severity > l.HighestSeverity { - l.HighestSeverity = severity - } - } - - } else { - - l.Messages = append(l.Messages, NewMessage(severity, path, err)) - - if severity > l.HighestSeverity { - l.HighestSeverity = severity - } - } - - } - - return err == nil - -} diff --git a/testdata/example/templates/lint.yaml b/testdata/example/templates/lint.yaml new file mode 100644 index 00000000..07ce5793 --- /dev/null +++ b/testdata/example/templates/lint.yaml @@ -0,0 +1 @@ +{{ end }} From bab4b4c745a472ee517967a71d21b2892f7f0307 Mon Sep 17 00:00:00 2001 From: qvalentin <36446499+qvalentin@users.noreply.github.com> Date: Sun, 19 May 2024 15:13:40 +0200 Subject: [PATCH 31/35] fix: prevent panic on empty or unsupported template context (#83) fixes https://github.com/mrjosh/helm-ls/issues/81 --- internal/handler/completion_main_test.go | 1 + internal/handler/hover_main_test.go | 10 ++++++++++ internal/language_features/built_in_objects.go | 10 ++++------ internal/language_features/template_context.go | 7 +++++-- testdata/example/templates/deployment.yaml | 11 +++++++++++ 5 files changed, 31 insertions(+), 8 deletions(-) diff --git a/internal/handler/completion_main_test.go b/internal/handler/completion_main_test.go index 481cbab5..8f49aeec 100644 --- a/internal/handler/completion_main_test.go +++ b/internal/handler/completion_main_test.go @@ -176,6 +176,7 @@ func TestCompletionMainSingleLines(t *testing.T) { {`Test completion on {{ range .Values.ingress.hosts }} {{ .^ }} {{ end }}`, []string{"host", "paths"}, []string{}, nil}, {`Test completion on {{ range .Values.ingress.hosts }} {{ .ho^ }} {{ end }}`, []string{"host", "paths"}, []string{}, nil}, {`Test completion on {{ range .Values.ingress.hosts }} {{ range .paths }} {{ .^ }} {{ end }} {{ end }}`, []string{"pathType", "path"}, []string{}, nil}, + {`Test completion on {{ root := . }} {{ $root.test.^ }}`, []string{}, []string{}, errors.New("[$root test ] is no valid template context for helm")}, } for _, tt := range testCases { diff --git a/internal/handler/hover_main_test.go b/internal/handler/hover_main_test.go index 8eeba435..b31509da 100644 --- a/internal/handler/hover_main_test.go +++ b/internal/handler/hover_main_test.go @@ -2,6 +2,7 @@ package handler import ( "context" + "errors" "fmt" "os" "path/filepath" @@ -113,6 +114,15 @@ func TestHoverMain(t *testing.T) { expected: fmt.Sprintf("### %s\n%s\n\n", filepath.Join("..", "..", "testdata", "example", "values.yaml"), "1"), expectedError: nil, }, + { + desc: "Test hover on template context with variables", + position: lsp.Position{ + Line: 74, + Character: 50, + }, + expected: "", + expectedError: errors.New("no template context found"), + }, } for _, tt := range testCases { t.Run(tt.desc, func(t *testing.T) { diff --git a/internal/language_features/built_in_objects.go b/internal/language_features/built_in_objects.go index a80ebfca..dbeaf044 100644 --- a/internal/language_features/built_in_objects.go +++ b/internal/language_features/built_in_objects.go @@ -28,8 +28,8 @@ func (f *BuiltInObjectsFeature) AppropriateForNode() bool { allowedBuiltIns := []string{"Chart", "Values", "Files", "Template", "Release"} - templateContext, _ := f.getTemplateContext() - if len(templateContext) != 1 { + templateContext, err := f.getTemplateContext() + if err != nil || len(templateContext) != 1 { return false } for _, allowedBuiltIn := range allowedBuiltIns { @@ -41,10 +41,7 @@ func (f *BuiltInObjectsFeature) AppropriateForNode() bool { } func (f *BuiltInObjectsFeature) References() (result []lsp.Location, err error) { - templateContext, err := f.getTemplateContext() - if err != nil { - return []lsp.Location{}, err - } + templateContext, _ := f.getTemplateContext() locations := f.getReferencesFromSymbolTable(templateContext) return append(locations, f.getDefinitionLocations(templateContext)...), err @@ -68,6 +65,7 @@ func (f *BuiltInObjectsFeature) getDefinitionLocations(templateContext lsplocal. func (f *BuiltInObjectsFeature) Hover() (string, error) { templateContext, _ := f.getTemplateContext() + docs, err := f.builtInOjectDocsLookup(templateContext[0], helmdocs.BuiltInObjects) return docs.Doc, err } diff --git a/internal/language_features/template_context.go b/internal/language_features/template_context.go index 01da658e..cf088edf 100644 --- a/internal/language_features/template_context.go +++ b/internal/language_features/template_context.go @@ -35,7 +35,7 @@ func (f *TemplateContextFeature) AppropriateForNode() bool { func (f *TemplateContextFeature) References() (result []lsp.Location, err error) { templateContext, err := f.getTemplateContext() - if err != nil { + if err != nil || len(templateContext) == 0 { return []lsp.Location{}, err } @@ -45,7 +45,7 @@ func (f *TemplateContextFeature) References() (result []lsp.Location, err error) func (f *TemplateContextFeature) Definition() (result []lsp.Location, err error) { templateContext, err := f.getTemplateContext() - if err != nil { + if err != nil || len(templateContext) == 0 { return []lsp.Location{}, err } return f.getDefinitionLocations(templateContext), nil @@ -81,6 +81,9 @@ func (f *TemplateContextFeature) getDefinitionLocations(templateContext lsplocal func (f *TemplateContextFeature) Hover() (string, error) { templateContext, err := f.getTemplateContext() + if err != nil || len(templateContext) == 0 { + return "", err + } switch templateContext[0] { case "Values": diff --git a/testdata/example/templates/deployment.yaml b/testdata/example/templates/deployment.yaml index 6d2f3764..d77fd2d8 100644 --- a/testdata/example/templates/deployment.yaml +++ b/testdata/example/templates/deployment.yaml @@ -70,3 +70,14 @@ spec: {{- end }} {{- end }} hosts: {{ .Values.ingress.hosts }} + + {{- $root := . -}} + {{- range $type, $config := $root.Values.deployments }} + apiVersion: apps/v1 + kind: Deployment + metadata: + name: my-app-{{ $type }} + spec: + replicas: {{ $config.hpa.minReplicas }} + --- + {{- end }} From 59785fcb95f7782b991903c1ba1a559c37e3bf98 Mon Sep 17 00:00:00 2001 From: qvalentin <36446499+qvalentin@users.noreply.github.com> Date: Mon, 20 May 2024 12:23:37 +0200 Subject: [PATCH 32/35] feat(symbol-table): read in template context with variables (#85) * feat(symbol-table): read in template context with variables * fix: handle root variable ($) at different stage --- Makefile | 2 +- internal/handler/hover_main_test.go | 28 +++++--- internal/lsp/symbol_table.go | 9 ++- internal/lsp/symbol_table_template_context.go | 9 +-- .../lsp/symbol_table_template_context_test.go | 64 +++++++++++++++++ internal/lsp/symbol_table_test.go | 70 ++++++++++++++++--- internal/lsp/visitor.go | 8 ++- testdata/example/templates/deployment.yaml | 1 + 8 files changed, 162 insertions(+), 29 deletions(-) create mode 100644 internal/lsp/symbol_table_template_context_test.go diff --git a/Makefile b/Makefile index 9880237e..a721d6be 100644 --- a/Makefile +++ b/Makefile @@ -68,7 +68,7 @@ test: @$(GO) test ./... -v -race -tags=integration coverage: - @$(GO) test -coverprofile=.coverage -tags=integration ./internal/... && go tool cover -html=.coverage + @$(GO) test -coverprofile=.coverage -tags=integration -coverpkg=./internal/... ./internal/... && go tool cover -html=.coverage .PHONY: build-release build-release: diff --git a/internal/handler/hover_main_test.go b/internal/handler/hover_main_test.go index b31509da..2e3959fa 100644 --- a/internal/handler/hover_main_test.go +++ b/internal/handler/hover_main_test.go @@ -2,7 +2,6 @@ package handler import ( "context" - "errors" "fmt" "os" "path/filepath" @@ -24,6 +23,24 @@ func TestHoverMain(t *testing.T) { expected string expectedError error }{ + { + desc: "Test hover on template context with variables", + position: lsp.Position{ + Line: 74, + Character: 50, + }, + expected: "$root.Values.deployments", + expectedError: nil, + }, + { + desc: "Test hover on template context with variables in range loop", + position: lsp.Position{ + Line: 80, + Character: 35, + }, + expected: "$config.hpa.minReplicas", + expectedError: nil, + }, { desc: "Test hover on dot", position: lsp.Position{ @@ -114,15 +131,6 @@ func TestHoverMain(t *testing.T) { expected: fmt.Sprintf("### %s\n%s\n\n", filepath.Join("..", "..", "testdata", "example", "values.yaml"), "1"), expectedError: nil, }, - { - desc: "Test hover on template context with variables", - position: lsp.Position{ - Line: 74, - Character: 50, - }, - expected: "", - expectedError: errors.New("no template context found"), - }, } for _, tt := range testCases { t.Run(tt.desc, func(t *testing.T) { diff --git a/internal/lsp/symbol_table.go b/internal/lsp/symbol_table.go index 8588c57e..88ef5731 100644 --- a/internal/lsp/symbol_table.go +++ b/internal/lsp/symbol_table.go @@ -18,7 +18,7 @@ func (t TemplateContext) Tail() TemplateContext { } func (t TemplateContext) IsVariable() bool { - return len(t) > 0 && t[0] == "$" + return len(t) > 0 && strings.HasPrefix(t[0], "$") } func (t TemplateContext) AppendSuffix(suffix string) TemplateContext { @@ -45,7 +45,12 @@ func NewSymbolTable(ast *sitter.Tree, content []byte) *SymbolTable { } func (s *SymbolTable) AddTemplateContext(templateContext TemplateContext, pointRange sitter.Range) { - s.contexts[templateContext.Format()] = append(s.contexts[strings.Join(templateContext, ".")], pointRange) + if templateContext.IsVariable() && templateContext[0] == "$" { + // $ is a special variable that resolves to the root context + // we can just remove it from the template context + templateContext = templateContext.Tail() + } + s.contexts[templateContext.Format()] = append(s.contexts[templateContext.Format()], pointRange) sliceCopy := make(TemplateContext, len(templateContext)) copy(sliceCopy, templateContext) s.contextsReversed[pointRange] = sliceCopy diff --git a/internal/lsp/symbol_table_template_context.go b/internal/lsp/symbol_table_template_context.go index b77cdcbf..c83199b7 100644 --- a/internal/lsp/symbol_table_template_context.go +++ b/internal/lsp/symbol_table_template_context.go @@ -60,15 +60,16 @@ func (v *TemplateContextVisitor) Enter(node *sitter.Node) { v.symbolTable.AddTemplateContext(append(v.currentContext, content), GetRangeForNode(node.ChildByFieldName("name"))) case gotemplate.NodeTypeUnfinishedSelectorExpression: operandNode := node.ChildByFieldName("operand") - if operandNode.Type() == gotemplate.NodeTypeVariable && operandNode.Content(v.content) == "$" { + if operandNode.Type() == gotemplate.NodeTypeVariable { v.StashContext() } v.symbolTable.AddTemplateContext(append(getContextForSelectorExpression(operandNode, v.content), ""), GetRangeForNode(node.Child(int(node.ChildCount())-1))) case gotemplate.NodeTypeSelectorExpression: operandNode := node.ChildByFieldName("operand") - if operandNode.Type() == gotemplate.NodeTypeVariable && operandNode.Content(v.content) == "$" { + if operandNode.Type() == gotemplate.NodeTypeVariable { v.StashContext() + v.PushContext(operandNode.Content(v.content)) } } } @@ -77,7 +78,8 @@ func (v *TemplateContextVisitor) Exit(node *sitter.Node) { switch node.Type() { case gotemplate.NodeTypeSelectorExpression, gotemplate.NodeTypeUnfinishedSelectorExpression: operandNode := node.ChildByFieldName("operand") - if operandNode.Type() == gotemplate.NodeTypeVariable && operandNode.Content(v.content) == "$" { + if operandNode.Type() == gotemplate.NodeTypeVariable { + v.PopContext() v.RestoreStashedContext() } } @@ -97,7 +99,6 @@ func (v *TemplateContextVisitor) EnterContextShift(node *sitter.Node, suffix str s = s.AppendSuffix(suffix) if s.IsVariable() { v.StashContext() - s = s.Tail() } } v.PushContextMany(s) diff --git a/internal/lsp/symbol_table_template_context_test.go b/internal/lsp/symbol_table_template_context_test.go new file mode 100644 index 00000000..e9e12312 --- /dev/null +++ b/internal/lsp/symbol_table_template_context_test.go @@ -0,0 +1,64 @@ +package lsp + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetContextForSelectorExpression(t *testing.T) { + testCases := []struct { + desc string + template string + nodeContent string + expected TemplateContext + }{ + { + desc: "Selects simple selector expression correctly", + template: `{{ .Values.test }}`, + nodeContent: ".Values.test", + expected: TemplateContext{"Values", "test"}, + }, + { + desc: "Selects unfinished selector expression correctly", + template: `{{ .Values.test. }}`, + nodeContent: ".Values.test.", + expected: TemplateContext{"Values", "test"}, + }, + { + desc: "Selects selector expression with $ correctly", + template: `{{ $.Values.test }}`, + nodeContent: "$.Values.test", + expected: TemplateContext{"$", "Values", "test"}, + }, + { + desc: "Selects unfinished selector expression with $ correctly", + template: `{{ $.Values.test. }}`, + nodeContent: "$.Values.test.", + expected: TemplateContext{"$", "Values", "test"}, + }, + { + desc: "Selects selector expression with variable correctly", + template: `{{ $x.test }}`, + nodeContent: "$x.test", + expected: TemplateContext{"$x", "test"}, + }, + { + desc: "Selects unfinished selector expression with variable correctly", + template: `{{ $x.test. }}`, + nodeContent: "$x.test.", + expected: TemplateContext{"$x", "test"}, + }, + } + for _, tC := range testCases { + t.Run(tC.desc, func(t *testing.T) { + ast := ParseAst(nil, tC.template) + node := ast.RootNode().Child(1) + + assert.Equal(t, tC.nodeContent, node.Content([]byte(tC.template))) + result := getContextForSelectorExpression(node, []byte(tC.template)) + + assert.Equal(t, tC.expected, result) + }) + } +} diff --git a/internal/lsp/symbol_table_test.go b/internal/lsp/symbol_table_test.go index 530bf7e1..302ccc04 100644 --- a/internal/lsp/symbol_table_test.go +++ b/internal/lsp/symbol_table_test.go @@ -217,12 +217,55 @@ func TestSymbolTableForValuesTestFile(t *testing.T) { func TestSymbolTableForValuesSingleTests(t *testing.T) { type testCase struct { - template string - path []string - startPoint sitter.Point + template string + path []string + startPoint sitter.Point + foundContextsLen int } testCases := []testCase{ + { + template: ` + {{- $root := . -}} + {{- range $type, $config := $root.Values.deployments }} + {{- .InLoop }} + {{- end }} + {{ .Values.test }} +`, + path: []string{"$root", "Values"}, + startPoint: sitter.Point{ + Row: 2, + Column: 40, + }, + foundContextsLen: 6, + }, + { + template: `{{ $x := .Values }}{{ $x.test }}{{ .Values.test }}`, + path: []string{"$x", "test"}, + startPoint: sitter.Point{ + Row: 0, + Column: 25, + }, + foundContextsLen: 3, + }, + { + template: `{{ $x.test }}`, + path: []string{"$x", "test"}, + startPoint: sitter.Point{ + Row: 0, + Column: 6, + }, + foundContextsLen: 1, + }, + { + template: `{{ $x.test. }}`, + path: []string{"$x", "test", ""}, + startPoint: sitter.Point{ + Row: 0, + Column: 10, + }, + foundContextsLen: 2, + }, { template: `{{ if (and .Values. ) }} {{ end }} `, path: []string{"Values"}, @@ -230,6 +273,7 @@ func TestSymbolTableForValuesSingleTests(t *testing.T) { Row: 0, Column: 12, }, + foundContextsLen: 2, }, { template: `{{ if (and .Values. ) }} {{ end }} `, @@ -238,17 +282,21 @@ func TestSymbolTableForValuesSingleTests(t *testing.T) { Row: 0, Column: 18, }, + foundContextsLen: 2, }, } for _, v := range testCases { - ast := ParseAst(nil, v.template) - symbolTable := NewSymbolTable(ast, []byte(v.template)) - values := symbolTable.GetTemplateContextRanges(v.path) - points := []sitter.Point{} - for _, v := range values { - points = append(points, v.StartPoint) - } - assert.Contains(t, points, v.startPoint) + t.Run(v.template, func(t *testing.T) { + ast := ParseAst(nil, v.template) + symbolTable := NewSymbolTable(ast, []byte(v.template)) + values := symbolTable.GetTemplateContextRanges(v.path) + points := []sitter.Point{} + for _, v := range values { + points = append(points, v.StartPoint) + } + assert.Contains(t, points, v.startPoint) + assert.Len(t, symbolTable.contexts, v.foundContextsLen) + }) } } diff --git a/internal/lsp/visitor.go b/internal/lsp/visitor.go index 7c96e639..fbfaf5b9 100644 --- a/internal/lsp/visitor.go +++ b/internal/lsp/visitor.go @@ -43,7 +43,13 @@ func (v *Visitors) visitNodesRecursiveWithScopeShift(node *sitter.Node) { case gotemplate.NodeTypeRangeAction: rangeNode := node.ChildByFieldName("range") if rangeNode == nil { - break // range is optional (e.g. {{ range $index, $element := pipeline }}) + // for {{- range $type, $config := $root.Values.deployments }} the range node is in the + // range_variable_definition node an not in the range_action node + rangeNode = node.NamedChild(0).ChildByFieldName("range") + if rangeNode == nil { + logger.Error("Could not find range node") + break + } } v.visitNodesRecursiveWithScopeShift(rangeNode) for _, visitor := range v.visitors { diff --git a/testdata/example/templates/deployment.yaml b/testdata/example/templates/deployment.yaml index d77fd2d8..b8935401 100644 --- a/testdata/example/templates/deployment.yaml +++ b/testdata/example/templates/deployment.yaml @@ -79,5 +79,6 @@ spec: name: my-app-{{ $type }} spec: replicas: {{ $config.hpa.minReplicas }} + test: {{ $.Values.ingress.hosts }} --- {{- end }} From dac75a886b49470523cc74e197fd8d7432f6ee95 Mon Sep 17 00:00:00 2001 From: qvalentin <36446499+qvalentin@users.noreply.github.com> Date: Fri, 24 May 2024 19:35:07 +0200 Subject: [PATCH 33/35] feat: support values lookup for range on mapping (#86) * feat: support values lookup for range on mapping * fix: add recover to ApplyChanges * test: range over mapping test * feat: support values location lookup for range on mapping * fix: don't modify slices --- internal/charts/values_files.go | 8 +- internal/handler/completion_main_test.go | 4 +- internal/handler/hover_main_test.go | 9 ++ internal/lsp/document.go | 7 ++ internal/lsp/symbol_table.go | 7 +- internal/lsp/symbol_table_test.go | 9 ++ internal/util/values.go | 51 +++++++--- internal/util/values_test.go | 13 +++ internal/util/yaml.go | 47 +++++++-- internal/util/yaml_test.go | 107 ++++++++------------- internal/util/yaml_test_input.yaml | 6 ++ testdata/example/templates/deployment.yaml | 3 + testdata/example/values.yaml | 7 ++ 13 files changed, 188 insertions(+), 90 deletions(-) diff --git a/internal/charts/values_files.go b/internal/charts/values_files.go index ba9bf9b5..4f5ce580 100644 --- a/internal/charts/values_files.go +++ b/internal/charts/values_files.go @@ -1,6 +1,7 @@ package charts import ( + "fmt" "path/filepath" "github.com/mrjosh/helm-ls/internal/util" @@ -69,11 +70,14 @@ func (v *ValuesFiles) AllValuesFiles() []*ValuesFile { } func (v *ValuesFiles) GetPositionsForValue(query []string) []lsp.Location { + logger.Debug(fmt.Sprintf("GetPositionsForValue with query %v", query)) result := []lsp.Location{} for _, value := range v.AllValuesFiles() { - pos, err := util.GetPositionOfNode(&value.ValueNode, query) + queryCopy := append([]string{}, query...) + pos, err := util.GetPositionOfNode(&value.ValueNode, queryCopy) if err != nil { - logger.Error("Error getting position for value", value, query, err) + yaml, _ := value.Values.YAML() + logger.Error(fmt.Sprintf("Error getting position for value in yaml file %s with query %v ", yaml, query), err) continue } result = append(result, lsp.Location{URI: value.URI, Range: lsp.Range{Start: pos, End: pos}}) diff --git a/internal/handler/completion_main_test.go b/internal/handler/completion_main_test.go index 8f49aeec..ba5de282 100644 --- a/internal/handler/completion_main_test.go +++ b/internal/handler/completion_main_test.go @@ -177,6 +177,8 @@ func TestCompletionMainSingleLines(t *testing.T) { {`Test completion on {{ range .Values.ingress.hosts }} {{ .ho^ }} {{ end }}`, []string{"host", "paths"}, []string{}, nil}, {`Test completion on {{ range .Values.ingress.hosts }} {{ range .paths }} {{ .^ }} {{ end }} {{ end }}`, []string{"pathType", "path"}, []string{}, nil}, {`Test completion on {{ root := . }} {{ $root.test.^ }}`, []string{}, []string{}, errors.New("[$root test ] is no valid template context for helm")}, + {`Test completion on {{ range $type, $config := $.Values.deployments }} {{ .^ }} {{ end }}`, []string{"some"}, []string{}, nil}, + {`Test completion on {{ range $type, $config := $.Values.deployments }} {{ .s^ }} {{ end }}`, []string{"some"}, []string{}, nil}, } for _, tt := range testCases { @@ -200,10 +202,10 @@ func TestCompletionMainSingleLines(t *testing.T) { 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/hover_main_test.go b/internal/handler/hover_main_test.go index 2e3959fa..fe7f5a49 100644 --- a/internal/handler/hover_main_test.go +++ b/internal/handler/hover_main_test.go @@ -23,6 +23,15 @@ func TestHoverMain(t *testing.T) { expected string expectedError error }{ + { + desc: "Test hover on template context in range over mapping", + position: lsp.Position{ + Line: 85, + Character: 26, + }, + expected: fmt.Sprintf("### %s\n%s\n\n", filepath.Join("..", "..", "testdata", "example", "values.yaml"), "value"), + expectedError: nil, + }, { desc: "Test hover on template context with variables", position: lsp.Position{ diff --git a/internal/lsp/document.go b/internal/lsp/document.go index 99b8172c..70265950 100644 --- a/internal/lsp/document.go +++ b/internal/lsp/document.go @@ -2,6 +2,7 @@ package lsp import ( "bytes" + "fmt" "strings" "github.com/mrjosh/helm-ls/internal/util" @@ -24,6 +25,12 @@ type Document struct { // ApplyChanges updates the content of the document from LSP textDocument/didChange events. func (d *Document) ApplyChanges(changes []lsp.TextDocumentContentChangeEvent) { + defer func() { + if r := recover(); r != nil { + logger.Error(fmt.Sprintf("Recovered in ApplyChanges for %s, the document may be corrupted ", d.URI), r) + } + }() + content := []byte(d.Content) for _, change := range changes { start, end := util.PositionToIndex(change.Range.Start, content), util.PositionToIndex(change.Range.End, content) diff --git a/internal/lsp/symbol_table.go b/internal/lsp/symbol_table.go index 88ef5731..83ea3340 100644 --- a/internal/lsp/symbol_table.go +++ b/internal/lsp/symbol_table.go @@ -13,6 +13,10 @@ func (t TemplateContext) Format() string { return strings.Join(t, ".") } +func (t TemplateContext) Copy() TemplateContext { + return append(TemplateContext{}, t...) +} + func (t TemplateContext) Tail() TemplateContext { return t[1:] } @@ -65,7 +69,8 @@ func (s *SymbolTable) GetTemplateContext(pointRange sitter.Range) (TemplateConte if !ok { return result, fmt.Errorf("no template context found") } - return result, nil + // return a copy to never modify the original + return result.Copy(), nil } func (s *SymbolTable) AddIncludeDefinition(symbol string, pointRange sitter.Range) { diff --git a/internal/lsp/symbol_table_test.go b/internal/lsp/symbol_table_test.go index 302ccc04..c81a0867 100644 --- a/internal/lsp/symbol_table_test.go +++ b/internal/lsp/symbol_table_test.go @@ -284,6 +284,15 @@ func TestSymbolTableForValuesSingleTests(t *testing.T) { }, foundContextsLen: 2, }, + { + template: `{{- range $type, $config := .Values.deployments }} {{ .test }} {{ end }} `, + path: []string{"Values", "deployments[]", "test"}, + startPoint: sitter.Point{ + Row: 0, + Column: 55, + }, + foundContextsLen: 3, + }, } for _, v := range testCases { diff --git a/internal/util/values.go b/internal/util/values.go index d13496cb..b446b1a6 100644 --- a/internal/util/values.go +++ b/internal/util/values.go @@ -64,7 +64,7 @@ func valuesLookup(values chartutil.Values, splittedVar []string) (chartutil.Valu return chartutil.Values{}, chartutil.ErrNoTable{Key: splittedVar[0]} } -// PathValue takes a path that traverses a YAML structure and returns the value at the end of that path. +// pathLookup takes a path that traverses a YAML structure and returns the value at the end of that path. // The path starts at the root of the YAML structure and is comprised of YAML keys separated by periods. // Given the following YAML data the value at path "chapter.one.title" is "Loomings". The path can also // include array indexes as in "chapters[].title" which will use the first element of the array. @@ -77,7 +77,7 @@ func pathLookup(v chartutil.Values, path []string) (interface{}, error) { return v, nil } if strings.HasSuffix(path[0], "[]") { - return arrayLookup(v, path) + return rangePathLookup(v, path) } // if exists must be root key not table value, ok := v[path[0]] @@ -93,32 +93,59 @@ func pathLookup(v chartutil.Values, path []string) (interface{}, error) { return nil, chartutil.ErrNoTable{Key: path[0]} } -func arrayLookup(v chartutil.Values, path []string) (interface{}, error) { +func rangePathLookup(v chartutil.Values, path []string) (interface{}, error) { v2, ok := v[path[0][:(len(path[0])-2)]] if !ok { return v, chartutil.ErrNoTable{Key: fmt.Sprintf("Yaml key %s does not exist", path[0])} } if v3, ok := v2.([]interface{}); ok { - if len(v3) == 0 { - return chartutil.Values{}, ErrEmpytArray{path[0]} - } + return rangeArrayLookup(v3, path) + } + if nestedValues, ok := v2.(map[string]interface{}); ok { + return rangeMappingLookup(nestedValues, path) + } + + return chartutil.Values{}, chartutil.ErrNoTable{Key: path[0]} +} + +func rangeArrayLookup(v3 []interface{}, path []string) (interface{}, error) { + if len(v3) == 0 { + return chartutil.Values{}, ErrEmpytArray{path[0]} + } + if len(path) == 1 { + return v3[0], nil + } + if vv, ok := v3[0].(map[string]interface{}); ok { + return pathLookup(vv, path[1:]) + } + return chartutil.Values{}, chartutil.ErrNoTable{Key: path[0]} +} + +func rangeMappingLookup(nestedValues map[string]interface{}, path []string) (interface{}, error) { + if len(nestedValues) == 0 { + return chartutil.Values{}, ErrEmpytMapping{path[0]} + } + + for k := range nestedValues { if len(path) == 1 { - return v3[0], nil + return nestedValues[k], nil } - if vv, ok := v3[0].(map[string]interface{}); ok { - return pathLookup(vv, path[1:]) + if nestedValues, ok := (nestedValues[k]).(map[string]interface{}); ok { + return pathLookup(nestedValues, path[1:]) } - return chartutil.Values{}, chartutil.ErrNoTable{Key: path[0]} } - return chartutil.Values{}, chartutil.ErrNoTable{Key: path[0]} } type ErrEmpytArray struct { Key string } +type ErrEmpytMapping struct { + Key string +} -func (e ErrEmpytArray) Error() string { return fmt.Sprintf("%q is an empyt array", e.Key) } +func (e ErrEmpytArray) Error() string { return fmt.Sprintf("%q is an empyt array", e.Key) } +func (e ErrEmpytMapping) Error() string { return fmt.Sprintf("%q is an empyt mapping", e.Key) } func builCompletionItem(value interface{}, variable string) lsp.CompletionItem { var ( diff --git a/internal/util/values_test.go b/internal/util/values_test.go index 6beb1188..3672307e 100644 --- a/internal/util/values_test.go +++ b/internal/util/values_test.go @@ -64,3 +64,16 @@ func TestValuesListNested(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "1", result) } + +func TestValuesRangeLookupOnMapping(t *testing.T) { + doubleNested := map[string]interface{}{"a": 1} + nested := map[string]interface{}{"nested": doubleNested, "other": doubleNested} + values := map[string]interface{}{"global": nested} + + input := []string{"global[]"} + inputCopy := append([]string{}, input...) + result, err := GetTableOrValueForSelector(values, input) + assert.NoError(t, err) + assert.Equal(t, "a: 1\n", result) + assert.Equal(t, inputCopy, input) +} diff --git a/internal/util/yaml.go b/internal/util/yaml.go index c2c23ed3..ec2b9fea 100644 --- a/internal/util/yaml.go +++ b/internal/util/yaml.go @@ -14,36 +14,65 @@ func GetPositionOfNode(node *yamlv3.Node, query []string) (lsp.Position, error) return lsp.Position{}, fmt.Errorf("could not find Position of %s in values.yaml. Node was zero", query) } + if node.Kind == yamlv3.DocumentNode { + if len(node.Content) < 1 { + return lsp.Position{}, fmt.Errorf("could not find Position of %s in values.yaml. Document is empty", query) + } + return GetPositionOfNode(node.Content[0], query) + } + if len(query) == 0 { return lsp.Position{Line: uint32(node.Line) - 1, Character: uint32(node.Column) - 1}, nil } - query[0] = strings.TrimSuffix(query[0], "[]") + isRange := false - switch node.Kind { - case yamlv3.DocumentNode: - if len(node.Content) < 1 { - return lsp.Position{}, fmt.Errorf("could not find Position of %s in values.yaml. Document is empty", query) - } - return GetPositionOfNode(node.Content[0], query) + if strings.HasSuffix(query[0], "[]") { + query = append([]string{}, query...) + query[0] = strings.TrimSuffix(query[0], "[]") + isRange = true + } + + kind := node.Kind + switch kind { case yamlv3.SequenceNode: if len(node.Content) > 0 { return GetPositionOfNode(node.Content[0], query) } } + checkNested := []string{} for index, nestedNode := range node.Content { + checkNested = append(checkNested, nestedNode.Value) if nestedNode.Value == query[0] { if len(query) == 1 { return GetPositionOfNode(nestedNode, query[1:]) } if len(node.Content) < index+1 { - return lsp.Position{}, fmt.Errorf("could not find Position of %s in values.yaml", query) + return lsp.Position{}, fmt.Errorf("could not find Position of %s in values", query) + } + if isRange { + return getPositionOfNodeAfterRange(node.Content[index+1], query[1:]) } return GetPositionOfNode(node.Content[index+1], query[1:]) } } - return lsp.Position{}, fmt.Errorf("could not find Position of %s in values.yaml. Found no match", query) + return lsp.Position{}, fmt.Errorf("could not find Position of %s in values.yaml. Found no match. Possible values %v. Kind is %d", query, checkNested, kind) +} + +func getPositionOfNodeAfterRange(node *yamlv3.Node, query []string) (lsp.Position, error) { + switch node.Kind { + case yamlv3.SequenceNode: + if len(node.Content) > 0 { + return GetPositionOfNode(node.Content[0], query) + } + case yamlv3.MappingNode: + if len(node.Content) > 1 { + return GetPositionOfNode(node.Content[1], query) + } + } + + return lsp.Position{}, fmt.Errorf("could not find Position of %s in values. Found no match", query) } // ReadYamlFileToNode will parse a YAML file into a yaml Node. diff --git a/internal/util/yaml_test.go b/internal/util/yaml_test.go index e54c4373..210c32d7 100644 --- a/internal/util/yaml_test.go +++ b/internal/util/yaml_test.go @@ -11,97 +11,74 @@ import ( "gopkg.in/yaml.v3" ) -func TestGetPositionOfNode(t *testing.T) { - data, err := os.ReadFile("./yaml_test_input.yaml") - if err != nil { - print(fmt.Sprint(err)) - t.Errorf("error yml parsing") - } - +func TestGetPositionOfNodeInEmptyDocument(t *testing.T) { var node yaml.Node - err = yaml.Unmarshal(data, &node) + err := yaml.Unmarshal([]byte(""), &node) if err != nil { print(fmt.Sprint(err)) t.Errorf("error yml parsing") } - result, err := GetPositionOfNode(&node, []string{"replicaCount"}) - expected := lsp.Position{Line: 5, Character: 0} - assert.NoError(t, err) - assert.Equal(t, expected, result) - - result, err = GetPositionOfNode(&node, []string{"image", "repository"}) - expected = lsp.Position{Line: 8, Character: 2} - assert.NoError(t, err) - assert.Equal(t, expected, result) - - result, err = GetPositionOfNode(&node, []string{"service", "test", "nested", "value"}) - expected = lsp.Position{Line: 30, Character: 6} - assert.NoError(t, err) - assert.Equal(t, expected, result) + result, err := GetPositionOfNode(&node, []string{"list[]"}) + expected := lsp.Position{} - result, err = GetPositionOfNode(&node, []string{"service", "test", "wrong", "value"}) - expected = lsp.Position{} assert.Error(t, err) assert.Equal(t, expected, result) -} -func TestGetPositionOfNodeWithList(t *testing.T) { - data, err := os.ReadFile("./yaml_test_input.yaml") - if err != nil { - print(fmt.Sprint(err)) - t.Errorf("error yml parsing") - } - - var node yaml.Node - err = yaml.Unmarshal(data, &node) + err = yaml.Unmarshal([]byte(" "), &node) if err != nil { print(fmt.Sprint(err)) t.Errorf("error yml parsing") } - result, err := GetPositionOfNode(&node, []string{"list[]"}) - expected := lsp.Position{Line: 32, Character: 0} - - assert.NoError(t, err) - assert.Equal(t, expected, result) - - result, err = GetPositionOfNode(&node, []string{"list[]", "first"}) - expected = lsp.Position{Line: 33, Character: 4} - - assert.NoError(t, err) - assert.Equal(t, expected, result) - - result, err = GetPositionOfNode(&node, []string{"notExistingList[]", "first"}) + result, err = GetPositionOfNode(&node, []string{"list[]"}) expected = lsp.Position{} assert.Error(t, err) assert.Equal(t, expected, result) } -func TestGetPositionOfNodeInEmptyDocument(t *testing.T) { - var node yaml.Node - err := yaml.Unmarshal([]byte(""), &node) - if err != nil { - print(fmt.Sprint(err)) - t.Errorf("error yml parsing") +func TestGetPositionOfNodeTable(t *testing.T) { + tests := []struct { + name string + query []string + expected lsp.Position + expectErr bool + }{ + {"replicaCount", []string{"replicaCount"}, lsp.Position{Line: 5, Character: 0}, false}, + {"image, repository", []string{"image", "repository"}, lsp.Position{Line: 8, Character: 2}, false}, + {"service, test, nested, value", []string{"service", "test", "nested", "value"}, lsp.Position{Line: 30, Character: 6}, false}, + {"service, test, wrong, value", []string{"service", "test", "wrong", "value"}, lsp.Position{}, true}, + {"list[]", []string{"list[]"}, lsp.Position{Line: 32, Character: 0}, false}, + {"list[], first", []string{"list[]", "first"}, lsp.Position{Line: 33, Character: 4}, false}, + {"notExistingList[], first", []string{"notExistingList[]", "first"}, lsp.Position{}, true}, + {"mapping[], something", []string{"mapping[]", "something"}, lsp.Position{Line: 40, Character: 4}, false}, } - result, err := GetPositionOfNode(&node, []string{"list[]"}) - expected := lsp.Position{} - - assert.Error(t, err) - assert.Equal(t, expected, result) + data, err := os.ReadFile("./yaml_test_input.yaml") + if err != nil { + t.Fatalf("error reading test input file: %v", err) + } - err = yaml.Unmarshal([]byte(" "), &node) + var node yaml.Node + err = yaml.Unmarshal(data, &node) if err != nil { - print(fmt.Sprint(err)) - t.Errorf("error yml parsing") + t.Fatalf("error parsing YAML: %v", err) } - result, err = GetPositionOfNode(&node, []string{"list[]"}) - expected = lsp.Position{} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + queryCopy := append([]string{}, tt.query...) - assert.Error(t, err) - assert.Equal(t, expected, result) + result, err := GetPositionOfNode(&node, tt.query) + + if tt.expectErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expected, result) + } + assert.Equal(t, queryCopy, tt.query) + }) + } } diff --git a/internal/util/yaml_test_input.yaml b/internal/util/yaml_test_input.yaml index decd4ef8..65ac07bc 100644 --- a/internal/util/yaml_test_input.yaml +++ b/internal/util/yaml_test_input.yaml @@ -35,3 +35,9 @@ list: nested: 1 - second: nested: 2 + +mapping: + a: + something: value + b: + something: value diff --git a/testdata/example/templates/deployment.yaml b/testdata/example/templates/deployment.yaml index b8935401..50d3c002 100644 --- a/testdata/example/templates/deployment.yaml +++ b/testdata/example/templates/deployment.yaml @@ -82,3 +82,6 @@ spec: test: {{ $.Values.ingress.hosts }} --- {{- end }} + {{- range $type, $config := $.Values.deployments }} + test: {{ toYaml .some | nindent 4}} + {{- end }} diff --git a/testdata/example/values.yaml b/testdata/example/values.yaml index 7e405f4a..515e40d7 100644 --- a/testdata/example/values.yaml +++ b/testdata/example/values.yaml @@ -105,3 +105,10 @@ nodeSelector: {} tolerations: [] affinity: {} + + +deployments: + first: + some: value + second: + some: value From bce3daf2222524f393a7a24d7c3281438ed9a959 Mon Sep 17 00:00:00 2001 From: Cristian Betivu Date: Sun, 26 May 2024 16:19:41 +0300 Subject: [PATCH 34/35] Support list of glob patterns --- internal/charts/values_files.go | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/internal/charts/values_files.go b/internal/charts/values_files.go index 4f5ce580..a893ed68 100644 --- a/internal/charts/values_files.go +++ b/internal/charts/values_files.go @@ -3,6 +3,7 @@ package charts import ( "fmt" "path/filepath" + "strings" "github.com/mrjosh/helm-ls/internal/util" lsp "go.lsp.dev/protocol" @@ -49,18 +50,21 @@ func getLintOverlayValuesFile(lintOverlayValuesFile string, additionalValuesFile func getAdditionalValuesFiles(additionalValuesFilesGlob string, rootURI uri.URI, mainValuesFileName string) []*ValuesFile { additionalValuesFiles := []*ValuesFile{} if additionalValuesFilesGlob != "" { - - matches, err := filepath.Glob(filepath.Join(rootURI.Filename(), additionalValuesFilesGlob)) - if err != nil { - logger.Error("Error loading additional values files with glob pattern", additionalValuesFilesGlob, err) - } else { - for _, match := range matches { - if match == filepath.Join(rootURI.Filename(), mainValuesFileName) { - continue + globPatterns := strings.Split(additionalValuesFilesGlob, ",") + for _, pattern := range globPatterns { + matches, err := filepath.Glob(filepath.Join(rootURI.Filename(), pattern)) + if err != nil { + logger.Error("Error loading additional values files with glob pattern", pattern, err) + } else { + for _, match := range matches { + if match == filepath.Join(rootURI.Filename(), mainValuesFileName) { + continue + } + additionalValuesFiles = append(additionalValuesFiles, NewValuesFile(match)) } - additionalValuesFiles = append(additionalValuesFiles, NewValuesFile(match)) } } + } return additionalValuesFiles } From 154adf859e4f04b220f35fe7270efa768d533f4b Mon Sep 17 00:00:00 2001 From: Cristian Betivu Date: Sun, 26 May 2024 16:20:33 +0300 Subject: [PATCH 35/35] Build only linux amd64 --- .goreleaser.yaml | 64 +++--------------------------------------------- 1 file changed, 3 insertions(+), 61 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 79616442..08b94053 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -1,32 +1,4 @@ builds: - - id: darwin-amd64 - main: ./ - binary: helm-ls - goos: - - darwin - goarch: - - amd64 - env: - - CC=o64-clang - - CXX=o64-clang++ - flags: - - -mod=readonly - ldflags: - - -s -w -X main.Version={{ .Env.VERSION }} -X main.GitCommit={{ .Env.GIT_COMMIT }} -X main.CompiledBy={{ .Env.COMPILED_BY }} -X main.Branch={{ .Env.BRANCH_NAME }} -X main.BuildTime={{ .Env.BUILD_TIME }} - - id: darwin-arm64 - binary: helm-ls - main: ./ - goarch: - - arm64 - goos: - - darwin - env: - - CC=oa64-clang - - CXX=oa64-clang++ - flags: - - -trimpath - ldflags: - - -X main.Version={{ .Env.VERSION }} -X main.GitCommit={{ .Env.GIT_COMMIT }} -X main.CompiledBy={{ .Env.COMPILED_BY }} -X main.Branch={{ .Env.BRANCH_NAME }} -X main.BuildTime={{ .Env.BUILD_TIME }} - id: linux-amd64 main: ./ binary: helm-ls @@ -38,43 +10,13 @@ builds: - -mod=readonly ldflags: - -s -w -X main.Version={{ .Env.VERSION }} -X main.GitCommit={{ .Env.GIT_COMMIT }} -X main.CompiledBy={{ .Env.COMPILED_BY }} -X main.Branch={{ .Env.BRANCH_NAME }} -X main.BuildTime={{ .Env.BUILD_TIME }} - - id: linux-arm64 - main: ./ - binary: helm-ls - goos: - - linux - goarch: - - arm64 - env: - - CC=aarch64-linux-gnu-gcc - - CXX=aarch64-linux-gnu-g++ - flags: - - -trimpath - ldflags: - - -s -w -X main.Version={{ .Env.VERSION }} -X main.GitCommit={{ .Env.GIT_COMMIT }} -X main.CompiledBy={{ .Env.COMPILED_BY }} -X main.Branch={{ .Env.BRANCH_NAME }} -X main.BuildTime={{ .Env.BUILD_TIME }} - - id: windows-amd64 - main: ./ - binary: helm-ls - goarch: - - amd64 - goos: - - windows - env: - - CC=x86_64-w64-mingw32-gcc - - CXX=x86_64-w64-mingw32-g++ - flags: - - -trimpath - - -buildmode=exe - ldflags: - - -X main.Version={{ .Env.VERSION }} -X main.GitCommit={{ .Env.GIT_COMMIT }} -X main.CompiledBy={{ .Env.COMPILED_BY }} -X main.Branch={{ .Env.BRANCH_NAME }} -X main.BuildTime={{ .Env.BUILD_TIME }} checksum: - name_template: 'checksums.txt' + name_template: "checksums.txt" snapshot: name_template: "{{ .Tag }}" changelog: sort: asc filters: exclude: - - '^docs:' - - '^test:' - + - "^docs:" + - "^test:"