Skip to content

Commit

Permalink
move more template context implementions to usecases
Browse files Browse the repository at this point in the history
  • Loading branch information
qvalentin committed Apr 14, 2024
1 parent 2c253fc commit e6fac55
Show file tree
Hide file tree
Showing 12 changed files with 191 additions and 154 deletions.
19 changes: 19 additions & 0 deletions internal/charts/chart.go
Original file line number Diff line number Diff line change
@@ -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"
)

Expand Down Expand Up @@ -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
}
118 changes: 13 additions & 105 deletions internal/handler/definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package handler
import (
"context"
"fmt"
"strings"

"github.com/mrjosh/helm-ls/internal/charts"
languagefeatures "github.com/mrjosh/helm-ls/internal/language_features"
Expand All @@ -12,7 +11,6 @@ import (
"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) {
Expand Down Expand Up @@ -45,25 +43,26 @@ func (h *langHandler) definitionAstParsing(genericDocumentUseCase *languagefeatu
parentType = parentNode.Type()
}

usecases := []languagefeatures.DefinitionUseCase{
languagefeatures.NewBuiltInObjectsFeature(genericDocumentUseCase), // has to be before template context
languagefeatures.NewTemplateContextFeature(genericDocumentUseCase),
languagefeatures.NewIncludesCallFeature(genericDocumentUseCase),
}

for _, usecase := range usecases {
if usecase.AppropriateForNode(genericDocumentUseCase.NodeType, genericDocumentUseCase.ParentNodeType, genericDocumentUseCase.Node) {
result, err := usecase.Definition()
return result, err
}
}

nodeType := relevantChildNode.Type()
switch nodeType {
case gotemplate.NodeTypeIdentifier:
logger.Println("Parent type", 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)
}

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())
Expand All @@ -77,94 +76,3 @@ func (h *langHandler) getDefinitionForVariable(node *sitter.Node, doc *lsplocal.
}
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)
}
12 changes: 6 additions & 6 deletions internal/handler/definition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
},
Expand Down Expand Up @@ -289,23 +289,23 @@ 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,
},
},
}, {
URI: testOtherValuesURI,
Range: lsp.Range{
Start: lsp.Position{
Line: 1,
Line: 0,
Character: 0,
},
End: lsp.Position{
Line: 1,
Line: 0,
Character: 0,
},
},
Expand Down
3 changes: 2 additions & 1 deletion internal/handler/hover.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ func (h *langHandler) Hover(ctx context.Context, params *lsp.HoverParams) (resul
}

usecases := []languagefeatures.HoverUseCase{
languagefeatures.NewValuesFeature(genericDocumentUseCase),
languagefeatures.NewBuiltInObjectsFeature(genericDocumentUseCase), // has to be before template context
languagefeatures.NewTemplateContextFeature(genericDocumentUseCase),
}

for _, usecase := range usecases {
Expand Down
2 changes: 1 addition & 1 deletion internal/handler/references.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ 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 {
Expand Down
78 changes: 78 additions & 0 deletions internal/language_features/built_in_objects.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
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"
sitter "github.com/smacker/go-tree-sitter"
lsp "go.lsp.dev/protocol"
)

type BuiltInObjectsFeature struct {
*GenericTemplateContextFeature
}

func NewBuiltInObjectsFeature(genericDocumentUseCase *GenericDocumentUseCase) *BuiltInObjectsFeature {
return &BuiltInObjectsFeature{
GenericTemplateContextFeature: &GenericTemplateContextFeature{genericDocumentUseCase},
}
}

func (f *BuiltInObjectsFeature) AppropriateForNode(currentNodeType string, parentNodeType string, node *sitter.Node) bool {
if !(parentNodeType == gotemplate.NodeTypeField && currentNodeType == gotemplate.NodeTypeIdentifier) && currentNodeType != gotemplate.NodeTypeDot && currentNodeType != gotemplate.NodeTypeDotSymbol {
return false
}

allowedBuiltIns := []string{"Chart", "Values"}

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":

Check failure on line 54 in internal/language_features/built_in_objects.go

View workflow job for this annotation

GitHub Actions / lint (1.21.5, ubuntu-latest)

string `Values` has 3 occurrences, make it a constant (goconst)
for _, valueFile := range f.Chart.ValuesFiles.AllValuesFiles() {
locations = append(locations, lsp.Location{URI: valueFile.URI})
}
return locations
case "Chart":

Check failure on line 59 in internal/language_features/built_in_objects.go

View workflow job for this annotation

GitHub Actions / lint (1.21.5, ubuntu-latest)

string `Chart` has 3 occurrences, make it a constant (goconst)
return []lsp.Location{{URI: f.Chart.ChartMetadata.URI}}
}

return []lsp.Location{}
}

func (f *BuiltInObjectsFeature) Hover() (string, error) {
templateContext, _ := f.getTemplateContext()
docs, error := f.builtInOjectDocsLookup(templateContext[0], helmdocs.BuiltInObjects)

Check failure on line 68 in internal/language_features/built_in_objects.go

View workflow job for this annotation

GitHub Actions / lint (1.21.5, ubuntu-latest)

redefines-builtin-id: redefinition of the built-in type error (revive)
return docs.Doc, error
}

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
}
43 changes: 43 additions & 0 deletions internal/language_features/generic_template_context.go
Original file line number Diff line number Diff line change
@@ -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)
}
Loading

0 comments on commit e6fac55

Please sign in to comment.