Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Symbol tables #72

Merged
merged 8 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading