Skip to content

Commit

Permalink
more implementions for includes
Browse files Browse the repository at this point in the history
  • Loading branch information
qvalentin committed Apr 7, 2024
1 parent 260f1bf commit f75ec3e
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 70 deletions.
38 changes: 18 additions & 20 deletions internal/handler/definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand All @@ -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)
}
Expand All @@ -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
Expand Down Expand Up @@ -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{
Expand Down
1 change: 1 addition & 0 deletions internal/handler/definition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"testing"

"github.com/mrjosh/helm-ls/internal/charts"
languagefeatures "github.com/mrjosh/helm-ls/internal/language_features"

Check failure on line 10 in internal/handler/definition_test.go

View workflow job for this annotation

GitHub Actions / tests (1.21.5, ubuntu-latest)

"github.com/mrjosh/helm-ls/internal/language_features" imported as languagefeatures and not used

Check failure on line 10 in internal/handler/definition_test.go

View workflow job for this annotation

GitHub Actions / lint (1.21.5, ubuntu-latest)

"github.com/mrjosh/helm-ls/internal/language_features" imported as languagefeatures and not used
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"
Expand Down
46 changes: 14 additions & 32 deletions internal/handler/hover.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,27 @@ 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"

"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"
"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)
Expand Down Expand Up @@ -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, ".")
Expand Down Expand Up @@ -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) {
Expand Down
6 changes: 3 additions & 3 deletions internal/handler/references_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func TestRefercesIncludes(t *testing.T) {

expected := []lsp.Location{
{
URI: "file:///tmp/testfile.yaml",
URI: uri.File("/tmp/testfile.yaml"),
Range: protocol.Range{
Start: protocol.Position{
Line: 0x1, Character: 0x3,
Expand All @@ -34,7 +34,7 @@ func TestRefercesIncludes(t *testing.T) {
},
},
protocol.Location{
URI: "file:///tmp/testfile.yaml",
URI: uri.File("/tmp/testfile.yaml"),
Range: protocol.Range{
Start: protocol.Position{
Line: 0x2,
Expand All @@ -47,7 +47,7 @@ func TestRefercesIncludes(t *testing.T) {
},
},
protocol.Location{
URI: "file:///tmp/testfile.yaml",
URI: uri.File("/tmp/testfile.yaml"),
Range: protocol.Range{
Start: protocol.Position{
Line: 0x0,
Expand Down
61 changes: 57 additions & 4 deletions internal/language_features/includes.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ type IncludesFeature struct {
GenericDocumentUseCase
}

// {{ include "name" . }}
// should be called on {{ include "name" . }}
type IncludesCallFeature struct {
IncludesFeature
}

// {{ define "name" }}
// should be called on {{ define "name" }}
type IncludesDefinitionFeature struct {
IncludesFeature
}
Expand All @@ -34,8 +34,7 @@ func NewIncludesDefinitionFeature(genericDocumentUseCase GenericDocumentUseCase)
}

func (f *IncludesCallFeature) References() (result []lsp.Location, err error) {
functionCallNode := f.Node.Parent().Parent()
includeName, err := lsplocal.ParseIncludeFunctionCall(functionCallNode, []byte(f.GenericDocumentUseCase.Document.Content))
includeName, err := f.getIncludeName()
if err != nil {
return []lsp.Location{}, err
}
Expand All @@ -44,6 +43,12 @@ func (f *IncludesCallFeature) References() (result []lsp.Location, err error) {
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())

Expand All @@ -62,3 +67,51 @@ func (f *IncludesFeature) getReferenceLocations(includeName string) []lsp.Locati

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
}
16 changes: 6 additions & 10 deletions internal/lsp/symbol_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ import (
type SymbolTable struct {
values map[string][]sitter.Range
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),
includeDefinitions: make(map[string][]sitter.Range),
includeReferences: make(map[string][]sitter.Range),
includeUseages: make(map[string][]sitter.Range),
}
s.parseTree(ast, content)
return s
Expand All @@ -35,19 +35,15 @@ 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 {
result := s.includeReferences[symbol]
result := s.includeUseages[symbol]
definitions := s.includeDefinitions[symbol]
return append(result, definitions...)
}
Expand Down
39 changes: 38 additions & 1 deletion internal/util/lsp.go
Original file line number Diff line number Diff line change
@@ -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{
Expand Down

0 comments on commit f75ec3e

Please sign in to comment.