Skip to content

Commit

Permalink
Merge pull request #72 from mrjosh/feature/symbol-tables
Browse files Browse the repository at this point in the history
feat: add symboltable
  • Loading branch information
qvalentin authored May 9, 2024
2 parents 24d50b5 + 88706f0 commit aab553b
Show file tree
Hide file tree
Showing 63 changed files with 7,241 additions and 5,302 deletions.
2 changes: 1 addition & 1 deletion internal/adapter/yamlls/completion_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
7 changes: 7 additions & 0 deletions internal/adapter/yamlls/diagnostics.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}
}
1 change: 1 addition & 0 deletions internal/adapter/yamlls/diagnostics_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,5 @@ func TestYamllsDiagnosticsIntegrationWithSchema(t *testing.T) {
}

assert.Contains(t, diagnostic, expected)
assert.Len(t, diagnostic, 1)
}
3 changes: 3 additions & 0 deletions internal/adapter/yamlls/documentSync.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
22 changes: 17 additions & 5 deletions internal/adapter/yamlls/hover.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ package yamlls
import (
"context"

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

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

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

word = removeTrailingColon(word)

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

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

func removeTrailingColon(word string) string {
if len(word) > 2 && string(word[len(word)-1]) == ":" {
word = word[0 : len(word)-1]
}
return word
}
2 changes: 1 addition & 1 deletion internal/adapter/yamlls/hover_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
}
}
20 changes: 20 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,20 @@ 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, 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 {
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
}
127 changes: 62 additions & 65 deletions internal/documentation/godocs/gotemplate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}",
},
}
Loading

0 comments on commit aab553b

Please sign in to comment.