From 6513548230d6e1c84ba70249d3a2bf91486bae6f Mon Sep 17 00:00:00 2001 From: qvalentin Date: Sun, 3 Nov 2024 12:54:14 +0100 Subject: [PATCH 01/11] wip: generate json schema for completion --- internal/adapter/yamlls/custom_new_client.go | 24 ++++ internal/adapter/yamlls/document_sync.go | 107 ++------------- .../adapter/yamlls/document_sync_template.go | 123 ++++++++++++++++++ internal/adapter/yamlls/initization.go | 5 + .../adapter/yamlls/integration_test_utils.go | 4 +- internal/adapter/yamlls/yamlls.go | 19 ++- .../handler/template_handler/configure.go | 3 +- .../handler/template_handler/text_document.go | 8 +- internal/handler/yaml_handler/completion.go | 12 ++ internal/handler/yaml_handler/configure.go | 41 ++++++ .../handler/yaml_handler/text_document.go | 4 + internal/handler/yaml_handler/yaml_handler.go | 57 ++++++-- internal/json_schema/generator.go | 51 ++++++++ internal/json_schema/json_schema.go | 59 +++++++++ internal/json_schema/json_schema_test.go | 69 ++++++++++ 15 files changed, 466 insertions(+), 120 deletions(-) create mode 100644 internal/adapter/yamlls/custom_new_client.go create mode 100644 internal/adapter/yamlls/document_sync_template.go create mode 100644 internal/handler/yaml_handler/completion.go create mode 100644 internal/handler/yaml_handler/configure.go create mode 100644 internal/json_schema/generator.go create mode 100644 internal/json_schema/json_schema.go create mode 100644 internal/json_schema/json_schema_test.go diff --git a/internal/adapter/yamlls/custom_new_client.go b/internal/adapter/yamlls/custom_new_client.go new file mode 100644 index 0000000..e0b9d8d --- /dev/null +++ b/internal/adapter/yamlls/custom_new_client.go @@ -0,0 +1,24 @@ +package yamlls + +import ( + "context" + + "go.lsp.dev/jsonrpc2" + "go.lsp.dev/protocol" + "go.uber.org/zap" +) + +// CustomNewClient returns the context in which Client is embedded, jsonrpc2.Conn, and the Server. +func (yamllsConnector Connector) CustomNewClient(ctx context.Context, client protocol.Client, stream jsonrpc2.Stream, logger *zap.Logger) (context.Context, jsonrpc2.Conn, protocol.Server) { + ctx = protocol.WithClient(ctx, client) + + conn := jsonrpc2.NewConn(stream) + conn.Go(ctx, + protocol.Handlers( + protocol.ClientHandler(client, yamllsConnector.customHandler), + ), + ) + server := protocol.ServerDispatcher(conn, logger.Named("server")) + + return ctx, conn, server +} diff --git a/internal/adapter/yamlls/document_sync.go b/internal/adapter/yamlls/document_sync.go index a6d1583..1cd98d2 100644 --- a/internal/adapter/yamlls/document_sync.go +++ b/internal/adapter/yamlls/document_sync.go @@ -3,121 +3,32 @@ package yamlls import ( "context" - lsplocal "github.com/mrjosh/helm-ls/internal/lsp" - "github.com/mrjosh/helm-ls/internal/lsp/document" - "github.com/mrjosh/helm-ls/internal/util" - sitter "github.com/smacker/go-tree-sitter" lsp "go.lsp.dev/protocol" ) -func (yamllsConnector Connector) InitiallySyncOpenDocuments(docs []*document.TemplateDocument) { - if yamllsConnector.server == nil { - return - } - - for _, doc := range docs { - if !doc.IsOpen { - continue - } - - doc.IsYaml = yamllsConnector.IsYamllsEnabled(doc.URI) - - if !yamllsConnector.isRelevantFile(doc.URI) { - continue - } +// func (yamllsConnector Connector) InitiallySyncOpenDocuments() { +// // TODO +// } - yamllsConnector.DocumentDidOpen(doc.Ast, lsp.DidOpenTextDocumentParams{ - TextDocument: lsp.TextDocumentItem{ - URI: doc.URI, - Text: string(doc.Content), - }, - }) - } -} - -func (yamllsConnector Connector) DocumentDidOpen(ast *sitter.Tree, params lsp.DidOpenTextDocumentParams) { +func (yamllsConnector Connector) DocumentDidOpen(params *lsp.DidOpenTextDocumentParams) { logger.Debug("YamllsConnector DocumentDidOpen", params.TextDocument.URI) - - if !yamllsConnector.shouldRun(params.TextDocument.URI) { - return - } - params.TextDocument.Text = lsplocal.TrimTemplate(ast, []byte(params.TextDocument.Text)) - - err := yamllsConnector.server.DidOpen(context.Background(), ¶ms) + err := yamllsConnector.server.DidOpen(context.Background(), params) if err != nil { logger.Error("Error calling yamlls for didOpen", err) } } -func (yamllsConnector Connector) DocumentDidSave(doc *document.TemplateDocument, params lsp.DidSaveTextDocumentParams) { - if !yamllsConnector.shouldRun(doc.URI) { - return - } - - params.Text = lsplocal.TrimTemplate(doc.Ast, doc.Content) - - err := yamllsConnector.server.DidSave(context.Background(), ¶ms) +func (yamllsConnector Connector) DocumentDidSave(params *lsp.DidSaveTextDocumentParams) { + err := yamllsConnector.server.DidSave(context.Background(), params) if err != nil { logger.Error("Error calling yamlls for didSave", err) } - - yamllsConnector.DocumentDidChangeFullSync(doc, lsp.DidChangeTextDocumentParams{ - TextDocument: lsp.VersionedTextDocumentIdentifier{ - TextDocumentIdentifier: params.TextDocument, - }, - }) } -func (yamllsConnector Connector) DocumentDidChange(doc *document.TemplateDocument, params lsp.DidChangeTextDocumentParams) { - if !yamllsConnector.shouldRun(doc.URI) { - return - } - trimmedText := lsplocal.TrimTemplate(doc.Ast, doc.Content) - +func (yamllsConnector Connector) DocumentDidChange(params *lsp.DidChangeTextDocumentParams) { logger.Debug("Sending DocumentDidChange previous", params) - for i, change := range params.ContentChanges { - var ( - start = util.PositionToIndex(change.Range.Start, []byte(doc.Content)) - end = start + len([]byte(change.Text)) - ) - - if end >= len(trimmedText) { - end = len(trimmedText) - 1 - } - - logger.Debug("Start end", start, end) - logger.Debug("Setting change text to ", trimmedText[start:end]) - params.ContentChanges[i].Text = trimmedText[start:end] - } - - logger.Debug("Sending DocumentDidChange", params) - err := yamllsConnector.server.DidChange(context.Background(), ¶ms) - if err != nil { - logger.Println("Error calling yamlls for didChange", err) - } -} - -func (yamllsConnector Connector) DocumentDidChangeFullSync(doc *document.TemplateDocument, params lsp.DidChangeTextDocumentParams) { - if !yamllsConnector.shouldRun(doc.URI) { - return - } - - logger.Debug("Sending DocumentDidChange with full sync, current content:", string(doc.Content)) - trimmedText := lsplocal.TrimTemplate(doc.Ast.Copy(), doc.Content) - - params.ContentChanges = []lsp.TextDocumentContentChangeEvent{ - { - Text: trimmedText, - }, - } - - logger.Println("Sending DocumentDidChange with full sync", params) - err := yamllsConnector.server.DidChange(context.Background(), ¶ms) + err := yamllsConnector.server.DidChange(context.Background(), params) if err != nil { logger.Println("Error calling yamlls for didChange", err) } } - -func (yamllsConnector Connector) IsYamllsEnabled(uri lsp.URI) bool { - return yamllsConnector.EnabledForFilesGlobObject.Match(uri.Filename()) -} diff --git a/internal/adapter/yamlls/document_sync_template.go b/internal/adapter/yamlls/document_sync_template.go new file mode 100644 index 0000000..004a997 --- /dev/null +++ b/internal/adapter/yamlls/document_sync_template.go @@ -0,0 +1,123 @@ +package yamlls + +import ( + "context" + + lsplocal "github.com/mrjosh/helm-ls/internal/lsp" + "github.com/mrjosh/helm-ls/internal/lsp/document" + "github.com/mrjosh/helm-ls/internal/util" + sitter "github.com/smacker/go-tree-sitter" + lsp "go.lsp.dev/protocol" +) + +func (yamllsConnector Connector) InitiallySyncOpenDocuments(docs []*document.TemplateDocument) { + if yamllsConnector.server == nil { + return + } + + for _, doc := range docs { + if !doc.IsOpen { + continue + } + + doc.IsYaml = yamllsConnector.IsYamllsEnabled(doc.URI) + + if !yamllsConnector.isRelevantFile(doc.URI) { + continue + } + + yamllsConnector.DocumentDidOpenTemplate(doc.Ast, lsp.DidOpenTextDocumentParams{ + TextDocument: lsp.TextDocumentItem{ + URI: doc.URI, + Text: string(doc.Content), + }, + }) + } +} + +func (yamllsConnector Connector) DocumentDidOpenTemplate(ast *sitter.Tree, params lsp.DidOpenTextDocumentParams) { + logger.Debug("YamllsConnector DocumentDidOpen", params.TextDocument.URI) + + if !yamllsConnector.shouldRun(params.TextDocument.URI) { + return + } + params.TextDocument.Text = lsplocal.TrimTemplate(ast, []byte(params.TextDocument.Text)) + + err := yamllsConnector.server.DidOpen(context.Background(), ¶ms) + if err != nil { + logger.Error("Error calling yamlls for didOpen", err) + } +} + +func (yamllsConnector Connector) DocumentDidSaveTemplate(doc *document.TemplateDocument, params lsp.DidSaveTextDocumentParams) { + if !yamllsConnector.shouldRun(doc.URI) { + return + } + + params.Text = lsplocal.TrimTemplate(doc.Ast, doc.Content) + + err := yamllsConnector.server.DidSave(context.Background(), ¶ms) + if err != nil { + logger.Error("Error calling yamlls for didSave", err) + } + + yamllsConnector.DocumentDidChangeFullSyncTemplate(doc, lsp.DidChangeTextDocumentParams{ + TextDocument: lsp.VersionedTextDocumentIdentifier{ + TextDocumentIdentifier: params.TextDocument, + }, + }) +} + +func (yamllsConnector Connector) DocumentDidChangeTemplate(doc *document.TemplateDocument, params lsp.DidChangeTextDocumentParams) { + if !yamllsConnector.shouldRun(doc.URI) { + return + } + trimmedText := lsplocal.TrimTemplate(doc.Ast, doc.Content) + + logger.Debug("Sending DocumentDidChange previous", params) + for i, change := range params.ContentChanges { + var ( + start = util.PositionToIndex(change.Range.Start, []byte(doc.Content)) + end = start + len([]byte(change.Text)) + ) + + if end >= len(trimmedText) { + end = len(trimmedText) - 1 + } + + logger.Debug("Start end", start, end) + logger.Debug("Setting change text to ", trimmedText[start:end]) + params.ContentChanges[i].Text = trimmedText[start:end] + } + + logger.Debug("Sending DocumentDidChange", params) + err := yamllsConnector.server.DidChange(context.Background(), ¶ms) + if err != nil { + logger.Println("Error calling yamlls for didChange", err) + } +} + +func (yamllsConnector Connector) DocumentDidChangeFullSyncTemplate(doc *document.TemplateDocument, params lsp.DidChangeTextDocumentParams) { + if !yamllsConnector.shouldRun(doc.URI) { + return + } + + logger.Debug("Sending DocumentDidChange with full sync, current content:", string(doc.Content)) + trimmedText := lsplocal.TrimTemplate(doc.Ast.Copy(), doc.Content) + + params.ContentChanges = []lsp.TextDocumentContentChangeEvent{ + { + Text: trimmedText, + }, + } + + logger.Println("Sending DocumentDidChange with full sync", params) + err := yamllsConnector.server.DidChange(context.Background(), ¶ms) + if err != nil { + logger.Println("Error calling yamlls for didChange", err) + } +} + +func (yamllsConnector Connector) IsYamllsEnabled(uri lsp.URI) bool { + return yamllsConnector.EnabledForFilesGlobObject.Match(uri.Filename()) +} diff --git a/internal/adapter/yamlls/initization.go b/internal/adapter/yamlls/initization.go index ebf3ddb..7305886 100644 --- a/internal/adapter/yamlls/initization.go +++ b/internal/adapter/yamlls/initization.go @@ -36,5 +36,10 @@ func (yamllsConnector Connector) CallInitialize(ctx context.Context, workspaceUR if err != nil { return err } + + defer func() { + yamllsConnector.conn.Notify(ctx, "yaml/registerCustomSchemaRequest", nil) + }() + 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 index 9bca680..a7ebd73 100644 --- a/internal/adapter/yamlls/integration_test_utils.go +++ b/internal/adapter/yamlls/integration_test_utils.go @@ -55,7 +55,7 @@ func getYamlLsConnector(t *testing.T, config util.YamllsConfiguration) (*Connect zapLogger, _ := zap.NewProduction() client := protocol.ClientDispatcher(con, zapLogger) - yamllsConnector := NewConnector(context.Background(), config, client, documents) + yamllsConnector := NewConnector(context.Background(), config, client, documents, jsonrpc2.MethodNotFoundHandler) if yamllsConnector.server == nil { t.Fatal("Could not connect to yaml-language-server") @@ -82,5 +82,5 @@ func openFile(t *testing.T, documents *document.DocumentStore, path string, yaml }, } doc, err := documents.DidOpenTemplateDocument(&d, util.DefaultConfig) - yamllsConnector.DocumentDidOpen(doc.Ast, d) + yamllsConnector.DocumentDidOpenTemplate(doc.Ast, d) } diff --git a/internal/adapter/yamlls/yamlls.go b/internal/adapter/yamlls/yamlls.go index 3b90c43..726973a 100644 --- a/internal/adapter/yamlls/yamlls.go +++ b/internal/adapter/yamlls/yamlls.go @@ -21,12 +21,19 @@ var logger = log.GetLogger() type Connector struct { config util.YamllsConfiguration server protocol.Server + conn jsonrpc2.Conn documents *document.DocumentStore client protocol.Client + customHandler jsonrpc2.Handler EnabledForFilesGlobObject glob.Glob } -func NewConnector(ctx context.Context, yamllsConfiguration util.YamllsConfiguration, client protocol.Client, documents *document.DocumentStore) *Connector { +func NewConnector(ctx context.Context, + yamllsConfiguration util.YamllsConfiguration, + client protocol.Client, + documents *document.DocumentStore, + customHandler jsonrpc2.Handler, +) *Connector { yamllsCmd := exec.Command(yamllsConfiguration.Path, "--stdio") stdin, err := yamllsCmd.StdinPipe() @@ -71,15 +78,17 @@ func NewConnector(ctx context.Context, yamllsConfiguration util.YamllsConfigurat }() yamllsConnector := Connector{ - config: yamllsConfiguration, - documents: documents, - client: client, + config: yamllsConfiguration, + documents: documents, + client: client, + customHandler: customHandler, } zapLogger, _ := zap.NewProduction() - _, _, server := protocol.NewClient(ctx, yamllsConnector, jsonrpc2.NewStream(readWriteCloser), zapLogger) + _, conn, server := yamllsConnector.CustomNewClient(ctx, yamllsConnector, jsonrpc2.NewStream(readWriteCloser), zapLogger) yamllsConnector.server = server + yamllsConnector.conn = conn return &yamllsConnector } diff --git a/internal/handler/template_handler/configure.go b/internal/handler/template_handler/configure.go index 31e951e..4c89f01 100644 --- a/internal/handler/template_handler/configure.go +++ b/internal/handler/template_handler/configure.go @@ -6,6 +6,7 @@ import ( "github.com/gobwas/glob" "github.com/mrjosh/helm-ls/internal/adapter/yamlls" "github.com/mrjosh/helm-ls/internal/util" + "go.lsp.dev/jsonrpc2" ) func (h *TemplateHandler) Configure(ctx context.Context, helmlsConfig util.HelmlsConfiguration) { @@ -25,7 +26,7 @@ func (h *TemplateHandler) configureYamlls(ctx context.Context, helmlsConfig util config := helmlsConfig if config.YamllsConfiguration.Enabled { h.configureYamlsEnabledGlob(helmlsConfig) - h.setYamllsConnector(yamlls.NewConnector(ctx, config.YamllsConfiguration, h.client, h.documents)) + h.setYamllsConnector(yamlls.NewConnector(ctx, config.YamllsConfiguration, h.client, h.documents, jsonrpc2.MethodNotFoundHandler)) err := h.yamllsConnector.CallInitialize(ctx, h.chartStore.RootURI) if err != nil { logger.Error("Error initializing yamlls", err) diff --git a/internal/handler/template_handler/text_document.go b/internal/handler/template_handler/text_document.go index a823c39..5967c11 100644 --- a/internal/handler/template_handler/text_document.go +++ b/internal/handler/template_handler/text_document.go @@ -16,7 +16,7 @@ func (h *TemplateHandler) DidOpen(ctx context.Context, params *lsp.DidOpenTextDo return err } - h.yamllsConnector.DocumentDidOpen(doc.Ast, *params) + h.yamllsConnector.DocumentDidOpenTemplate(doc.Ast, *params) return nil } @@ -27,7 +27,7 @@ func (h *TemplateHandler) DidSave(ctx context.Context, params *lsp.DidSaveTextDo return errors.New("Could not get document: " + params.TextDocument.URI.Filename()) } - h.yamllsConnector.DocumentDidSave(doc, *params) + h.yamllsConnector.DocumentDidSaveTemplate(doc, *params) return nil } @@ -47,9 +47,9 @@ func (h *TemplateHandler) PostDidChange(ctx context.Context, params *lsp.DidChan } } if shouldSendFullUpdateToYamlls { - h.yamllsConnector.DocumentDidChangeFullSync(doc, *params) + h.yamllsConnector.DocumentDidChangeFullSyncTemplate(doc, *params) } else { - h.yamllsConnector.DocumentDidChange(doc, *params) + h.yamllsConnector.DocumentDidChangeTemplate(doc, *params) } return nil diff --git a/internal/handler/yaml_handler/completion.go b/internal/handler/yaml_handler/completion.go new file mode 100644 index 0000000..4ceaac6 --- /dev/null +++ b/internal/handler/yaml_handler/completion.go @@ -0,0 +1,12 @@ +package yamlhandler + +import ( + "context" + + "go.lsp.dev/protocol" +) + +// Completion implements handler.LangHandler. +func (h *YamlHandler) Completion(ctx context.Context, params *protocol.CompletionParams) (result *protocol.CompletionList, err error) { + return h.yamllsConnector.CallCompletion(ctx, params) +} diff --git a/internal/handler/yaml_handler/configure.go b/internal/handler/yaml_handler/configure.go new file mode 100644 index 0000000..9d18a43 --- /dev/null +++ b/internal/handler/yaml_handler/configure.go @@ -0,0 +1,41 @@ +package yamlhandler + +import ( + "context" + + "github.com/gobwas/glob" + "github.com/mrjosh/helm-ls/internal/adapter/yamlls" + "github.com/mrjosh/helm-ls/internal/util" +) + +func (h *YamlHandler) Configure(ctx context.Context, helmlsConfig util.HelmlsConfiguration) { + h.configureYamlls(ctx, helmlsConfig) +} + +func (h *YamlHandler) configureYamlsEnabledGlob(helmlsConfig util.HelmlsConfiguration) { + globObject, err := glob.Compile(helmlsConfig.YamllsConfiguration.EnabledForFilesGlob) + if err != nil { + logger.Error("Error compiling glob for yamlls EnabledForFilesGlob", err) + globObject = util.DefaultConfig.YamllsConfiguration.EnabledForFilesGlobObject + } + h.yamllsConnector.EnabledForFilesGlobObject = globObject +} + +func (h *YamlHandler) configureYamlls(ctx context.Context, helmlsConfig util.HelmlsConfiguration) { + config := helmlsConfig + if config.YamllsConfiguration.Enabled { + h.configureYamlsEnabledGlob(helmlsConfig) + h.setYamllsConnector( + yamlls.NewConnector(ctx, + config.YamllsConfiguration, + h.client, + h.documents, + h.CustomHandler)) + err := h.yamllsConnector.CallInitialize(ctx, h.chartStore.RootURI) + if err != nil { + logger.Error("Error initializing yamlls", err) + } + + h.yamllsConnector.InitiallySyncOpenDocuments(h.documents.GetAllTemplateDocs()) + } +} diff --git a/internal/handler/yaml_handler/text_document.go b/internal/handler/yaml_handler/text_document.go index 8e60f03..ef4bd8d 100644 --- a/internal/handler/yaml_handler/text_document.go +++ b/internal/handler/yaml_handler/text_document.go @@ -9,20 +9,24 @@ import ( // DidOpen implements handler.LangHandler. func (h *YamlHandler) DidOpen(ctx context.Context, params *protocol.DidOpenTextDocumentParams, helmlsConfig util.HelmlsConfiguration) (err error) { + h.yamllsConnector.DocumentDidOpen(params) _, err = h.documents.DidOpenYamlDocument(params, helmlsConfig) if err != nil { logger.Error(err) return err } + return nil } // DidSave implements handler.LangHandler. func (h *YamlHandler) DidSave(ctx context.Context, params *protocol.DidSaveTextDocumentParams) (err error) { + h.yamllsConnector.DocumentDidSave(params) return nil } // DidChange implements handler.LangHandler. func (h *YamlHandler) PostDidChange(ctx context.Context, params *protocol.DidChangeTextDocumentParams) (err error) { + h.yamllsConnector.DocumentDidChange(params) return nil } diff --git a/internal/handler/yaml_handler/yaml_handler.go b/internal/handler/yaml_handler/yaml_handler.go index f877da5..0d09961 100644 --- a/internal/handler/yaml_handler/yaml_handler.go +++ b/internal/handler/yaml_handler/yaml_handler.go @@ -2,13 +2,16 @@ package yamlhandler import ( "context" + "encoding/json" "github.com/mrjosh/helm-ls/internal/adapter/yamlls" "github.com/mrjosh/helm-ls/internal/charts" + jsonschema "github.com/mrjosh/helm-ls/internal/json_schema" "github.com/mrjosh/helm-ls/internal/log" "github.com/mrjosh/helm-ls/internal/lsp/document" - "github.com/mrjosh/helm-ls/internal/util" + "go.lsp.dev/jsonrpc2" "go.lsp.dev/protocol" + "go.lsp.dev/uri" ) var logger = log.GetLogger() @@ -16,18 +19,10 @@ var logger = log.GetLogger() type YamlHandler struct { documents *document.DocumentStore chartStore *charts.ChartStore + client protocol.Client yamllsConnector *yamlls.Connector } -// Completion implements handler.LangHandler. -func (h *YamlHandler) Completion(ctx context.Context, params *protocol.CompletionParams) (result *protocol.CompletionList, err error) { - // TODO - return nil, nil -} - -// Configure implements handler.LangHandler. -func (h *YamlHandler) Configure(ctx context.Context, helmlsConfig util.HelmlsConfiguration) {} - // Definition implements handler.LangHandler. func (h *YamlHandler) Definition(ctx context.Context, params *protocol.DefinitionParams) (result []protocol.Location, err error) { panic("unimplemented") @@ -52,3 +47,45 @@ func NewYamlHandler(client protocol.Client, documents *document.DocumentStore, c func (h *YamlHandler) SetChartStore(chartStore *charts.ChartStore) { h.chartStore = chartStore } + +func (h *YamlHandler) setYamllsConnector(yamllsConnector *yamlls.Connector) { + h.yamllsConnector = yamllsConnector +} + +func (h *YamlHandler) CustomHandler(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error { + switch req.Method() { + case "custom/schema/request": + + params := []string{} + jsonBytes, err := req.Params().MarshalJSON() + if err != nil { + logger.Error(err) + return reply(ctx, nil, nil) + } + + err = json.Unmarshal(jsonBytes, ¶ms) + if err != nil { + logger.Error(err) + return reply(ctx, nil, nil) + } + + logger.Println("YamlHandler: custom/schema/request", req.Params()) + + if len(params) == 0 { + return reply(ctx, nil, nil) + } + chart, err := h.chartStore.GetChartForDoc(uri.New(params[0])) + if err != nil { + logger.Error(err) + } + schemaFilePath, err := jsonschema.CreateJsonSchemaForChart(chart) + if err != nil { + logger.Error(err) + return reply(ctx, nil, nil) + } + + return reply(ctx, uri.New(schemaFilePath), nil) + } + + return jsonrpc2.MethodNotFoundHandler(ctx, reply, req) +} diff --git a/internal/json_schema/generator.go b/internal/json_schema/generator.go new file mode 100644 index 0000000..4a7adad --- /dev/null +++ b/internal/json_schema/generator.go @@ -0,0 +1,51 @@ +package jsonschema + +// GenerateJSONSchema generates a JSON schema from a map[string]interface{} instance +func GenerateJSONSchema(data map[string]interface{}) (map[string]interface{}, error) { + schema := map[string]interface{}{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": generateProperties(data), + } + + return schema, nil +} + +// generateProperties recursively inspects data and generates schema properties +func generateProperties(data map[string]interface{}) map[string]interface{} { + properties := make(map[string]interface{}) + + for key, value := range data { + properties[key] = generateSchemaType(value) + } + + return properties +} + +// generateSchemaType infers the JSON schema type based on the value's Go type +func generateSchemaType(value interface{}) map[string]interface{} { + schema := make(map[string]interface{}) + + switch v := value.(type) { + case string: + schema["type"] = "string" + case int, int32, int64, float32, float64: + schema["type"] = "number" + case bool: + schema["type"] = "boolean" + case map[string]interface{}: + schema["type"] = "object" + schema["properties"] = generateProperties(v) + case []interface{}: + schema["type"] = "array" + if len(v) > 0 { + schema["items"] = generateSchemaType(v[0]) + } else { + schema["items"] = map[string]interface{}{"type": "null"} // Default for empty arrays + } + default: + schema["type"] = "null" // Fallback to null for unknown types + } + + return schema +} diff --git a/internal/json_schema/json_schema.go b/internal/json_schema/json_schema.go new file mode 100644 index 0000000..ab1ec98 --- /dev/null +++ b/internal/json_schema/json_schema.go @@ -0,0 +1,59 @@ +package jsonschema + +import ( + "encoding/base64" + "encoding/json" + "os" + + // "github.com/invopop/jsonschema" + "github.com/mrjosh/helm-ls/internal/charts" + "github.com/mrjosh/helm-ls/internal/log" +) + +var logger = log.GetLogger() + +func CreateJsonSchemaForChart(chart *charts.Chart) (string, error) { + // reflector := jsonschema.Reflector{ + // ExpandedStruct: true, + // AllowAdditionalProperties: true, + // } + + schema, err := GenerateJSONSchema(chart.ValuesFiles.MainValuesFile.Values) + + bytes, err := json.Marshal(schema) + if err != nil { + logger.Error(err) + return "", err + } + + // create a tmp file and write the schema + + file, err := os.CreateTemp("", base64.StdEncoding.EncodeToString([]byte(chart.RootURI.Filename()))) + if err != nil { + logger.Error(err) + return "", err + } + + _, err = file.Write(bytes) + if err != nil { + logger.Error(err) + return "", err + } + + return file.Name(), nil +} + +// func GenerateSchemaFromData(data interface{}) error { +// jsonBytes, err := json.Marshal(data) +// if err != nil { +// return nil, err +// } +// +// documentLoader := gojsonschema.NewStringLoader(string(jsonBytes)) +// schema, err := gojsonschema.NewSchema(documentLoader) +// if err != nil { +// return nil, err +// } +// +// return schema.Root(), nil +// } diff --git a/internal/json_schema/json_schema_test.go b/internal/json_schema/json_schema_test.go new file mode 100644 index 0000000..172dda0 --- /dev/null +++ b/internal/json_schema/json_schema_test.go @@ -0,0 +1,69 @@ +package jsonschema + +import ( + "encoding/json" + "testing" +) + +func TestGenerateJSONSchema(t *testing.T) { + // Define a sample input map to generate a schema from + input := map[string]interface{}{ + "name": "example", + "age": 30, + "address": map[string]interface{}{"city": "ExampleCity", "zip": "12345"}, + "tags": []interface{}{"go", "json", "schema"}, + } + + // Generate the schema + schema, err := GenerateJSONSchema(input) + if err != nil { + t.Fatalf("Failed to generate schema: %v", err) + } + + // Marshal the schema to JSON + schemaJSON, err := json.MarshalIndent(schema, "", " ") + if err != nil { + t.Fatalf("Failed to marshal schema to JSON: %v", err) + } + + // Print the JSON schema for visual verification + t.Logf("Generated JSON Schema:\n%s", schemaJSON) + + // Expected JSON schema structure + expected := map[string]interface{}{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": map[string]interface{}{ + "name": map[string]interface{}{ + "type": "string", + }, + "age": map[string]interface{}{ + "type": "number", + }, + "address": map[string]interface{}{ + "type": "object", + "properties": map[string]interface{}{ + "city": map[string]interface{}{"type": "string"}, + "zip": map[string]interface{}{"type": "string"}, + }, + }, + "tags": map[string]interface{}{ + "type": "array", + "items": map[string]interface{}{ + "type": "string", + }, + }, + }, + } + + // Convert the expected schema to JSON for comparison + expectedJSON, err := json.MarshalIndent(expected, "", " ") + if err != nil { + t.Fatalf("Failed to marshal expected schema to JSON: %v", err) + } + + // Check if generated schema matches the expected schema + if string(schemaJSON) != string(expectedJSON) { + t.Errorf("Generated schema does not match expected schema.\nExpected:\n%s\nGot:\n%s", expectedJSON, schemaJSON) + } +} From 20c6b414f5b1100be3770f4f9ea9744e7af09c89 Mon Sep 17 00:00:00 2001 From: qvalentin Date: Wed, 13 Nov 2024 20:20:46 +0100 Subject: [PATCH 02/11] fix: only notify registerCustomSchemaRequest on values files --- internal/adapter/yamlls/custom_new_client.go | 13 +++++- internal/adapter/yamlls/document_sync.go | 22 +++++++-- .../adapter/yamlls/document_sync_template.go | 2 +- internal/adapter/yamlls/initization.go | 4 +- .../adapter/yamlls/integration_test_utils.go | 2 +- internal/adapter/yamlls/yamlls.go | 4 +- .../handler/template_handler/configure.go | 5 +-- internal/handler/template_handler/hover.go | 2 + internal/handler/yaml_handler/configure.go | 45 +++++++++---------- internal/json_schema/json_schema_test.go | 24 +++------- internal/lsp/document/document_store.go | 13 ++++++ 11 files changed, 82 insertions(+), 54 deletions(-) diff --git a/internal/adapter/yamlls/custom_new_client.go b/internal/adapter/yamlls/custom_new_client.go index e0b9d8d..093257d 100644 --- a/internal/adapter/yamlls/custom_new_client.go +++ b/internal/adapter/yamlls/custom_new_client.go @@ -8,6 +8,17 @@ import ( "go.uber.org/zap" ) +type CustomHandler struct { + Handler jsonrpc2.Handler + PostInitialize SetupCustomHandler +} + +type SetupCustomHandler func(ctx context.Context, conn jsonrpc2.Conn) error + +var DefaultCustomHandler = CustomHandler{ + jsonrpc2.MethodNotFoundHandler, func(ctx context.Context, conn jsonrpc2.Conn) error { return nil }, +} + // CustomNewClient returns the context in which Client is embedded, jsonrpc2.Conn, and the Server. func (yamllsConnector Connector) CustomNewClient(ctx context.Context, client protocol.Client, stream jsonrpc2.Stream, logger *zap.Logger) (context.Context, jsonrpc2.Conn, protocol.Server) { ctx = protocol.WithClient(ctx, client) @@ -15,7 +26,7 @@ func (yamllsConnector Connector) CustomNewClient(ctx context.Context, client pro conn := jsonrpc2.NewConn(stream) conn.Go(ctx, protocol.Handlers( - protocol.ClientHandler(client, yamllsConnector.customHandler), + protocol.ClientHandler(client, yamllsConnector.customHandler.Handler), ), ) server := protocol.ServerDispatcher(conn, logger.Named("server")) diff --git a/internal/adapter/yamlls/document_sync.go b/internal/adapter/yamlls/document_sync.go index 1cd98d2..17cf1ef 100644 --- a/internal/adapter/yamlls/document_sync.go +++ b/internal/adapter/yamlls/document_sync.go @@ -3,12 +3,28 @@ package yamlls import ( "context" + "github.com/mrjosh/helm-ls/internal/lsp/document" lsp "go.lsp.dev/protocol" ) -// func (yamllsConnector Connector) InitiallySyncOpenDocuments() { -// // TODO -// } +func (yamllsConnector Connector) InitiallySyncOpenYamlDocuments(docs []*document.YamlDocument) { + if yamllsConnector.server == nil { + return + } + + for _, doc := range docs { + if !doc.IsOpen { + continue + } + + yamllsConnector.DocumentDidOpen(&lsp.DidOpenTextDocumentParams{ + TextDocument: lsp.TextDocumentItem{ + URI: doc.URI, + Text: string(doc.Content), + }, + }) + } +} func (yamllsConnector Connector) DocumentDidOpen(params *lsp.DidOpenTextDocumentParams) { logger.Debug("YamllsConnector DocumentDidOpen", params.TextDocument.URI) diff --git a/internal/adapter/yamlls/document_sync_template.go b/internal/adapter/yamlls/document_sync_template.go index 004a997..db6dd37 100644 --- a/internal/adapter/yamlls/document_sync_template.go +++ b/internal/adapter/yamlls/document_sync_template.go @@ -10,7 +10,7 @@ import ( lsp "go.lsp.dev/protocol" ) -func (yamllsConnector Connector) InitiallySyncOpenDocuments(docs []*document.TemplateDocument) { +func (yamllsConnector Connector) InitiallySyncOpenTemplateDocuments(docs []*document.TemplateDocument) { if yamllsConnector.server == nil { return } diff --git a/internal/adapter/yamlls/initization.go b/internal/adapter/yamlls/initization.go index 7305886..23c1e8c 100644 --- a/internal/adapter/yamlls/initization.go +++ b/internal/adapter/yamlls/initization.go @@ -37,8 +37,10 @@ func (yamllsConnector Connector) CallInitialize(ctx context.Context, workspaceUR return err } + yamllsConnector.customHandler.PostInitialize(ctx, yamllsConnector.conn) + defer func() { - yamllsConnector.conn.Notify(ctx, "yaml/registerCustomSchemaRequest", nil) + yamllsConnector.customHandler.PostInitialize(ctx, yamllsConnector.conn) }() 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 index a7ebd73..fe67746 100644 --- a/internal/adapter/yamlls/integration_test_utils.go +++ b/internal/adapter/yamlls/integration_test_utils.go @@ -55,7 +55,7 @@ func getYamlLsConnector(t *testing.T, config util.YamllsConfiguration) (*Connect zapLogger, _ := zap.NewProduction() client := protocol.ClientDispatcher(con, zapLogger) - yamllsConnector := NewConnector(context.Background(), config, client, documents, jsonrpc2.MethodNotFoundHandler) + yamllsConnector := NewConnector(context.Background(), config, client, documents, DefaultCustomHandler) if yamllsConnector.server == nil { t.Fatal("Could not connect to yaml-language-server") diff --git a/internal/adapter/yamlls/yamlls.go b/internal/adapter/yamlls/yamlls.go index 726973a..ff92042 100644 --- a/internal/adapter/yamlls/yamlls.go +++ b/internal/adapter/yamlls/yamlls.go @@ -24,7 +24,7 @@ type Connector struct { conn jsonrpc2.Conn documents *document.DocumentStore client protocol.Client - customHandler jsonrpc2.Handler + customHandler CustomHandler EnabledForFilesGlobObject glob.Glob } @@ -32,7 +32,7 @@ func NewConnector(ctx context.Context, yamllsConfiguration util.YamllsConfiguration, client protocol.Client, documents *document.DocumentStore, - customHandler jsonrpc2.Handler, + customHandler CustomHandler, ) *Connector { yamllsCmd := exec.Command(yamllsConfiguration.Path, "--stdio") diff --git a/internal/handler/template_handler/configure.go b/internal/handler/template_handler/configure.go index 4c89f01..665e000 100644 --- a/internal/handler/template_handler/configure.go +++ b/internal/handler/template_handler/configure.go @@ -6,7 +6,6 @@ import ( "github.com/gobwas/glob" "github.com/mrjosh/helm-ls/internal/adapter/yamlls" "github.com/mrjosh/helm-ls/internal/util" - "go.lsp.dev/jsonrpc2" ) func (h *TemplateHandler) Configure(ctx context.Context, helmlsConfig util.HelmlsConfiguration) { @@ -26,12 +25,12 @@ func (h *TemplateHandler) configureYamlls(ctx context.Context, helmlsConfig util config := helmlsConfig if config.YamllsConfiguration.Enabled { h.configureYamlsEnabledGlob(helmlsConfig) - h.setYamllsConnector(yamlls.NewConnector(ctx, config.YamllsConfiguration, h.client, h.documents, jsonrpc2.MethodNotFoundHandler)) + h.setYamllsConnector(yamlls.NewConnector(ctx, config.YamllsConfiguration, h.client, h.documents, yamlls.DefaultCustomHandler)) err := h.yamllsConnector.CallInitialize(ctx, h.chartStore.RootURI) if err != nil { logger.Error("Error initializing yamlls", err) } - h.yamllsConnector.InitiallySyncOpenDocuments(h.documents.GetAllTemplateDocs()) + h.yamllsConnector.InitiallySyncOpenTemplateDocuments(h.documents.GetAllTemplateDocs()) } } diff --git a/internal/handler/template_handler/hover.go b/internal/handler/template_handler/hover.go index 8a33a87..2da0cc1 100644 --- a/internal/handler/template_handler/hover.go +++ b/internal/handler/template_handler/hover.go @@ -34,6 +34,8 @@ func (h *TemplateHandler) Hover(ctx context.Context, params *lsp.HoverParams) (r } if genericDocumentUseCase.NodeType == gotemplate.NodeTypeText { + // Do not use the TextUsecase, as we don't want to map the hover response + // from yamlls to string and then back word := genericDocumentUseCase.Document.WordAt(params.Position) response, err := h.yamllsConnector.CallHover(ctx, *params, word) return response, err diff --git a/internal/handler/yaml_handler/configure.go b/internal/handler/yaml_handler/configure.go index 9d18a43..9cdc1ec 100644 --- a/internal/handler/yaml_handler/configure.go +++ b/internal/handler/yaml_handler/configure.go @@ -3,39 +3,36 @@ package yamlhandler import ( "context" - "github.com/gobwas/glob" "github.com/mrjosh/helm-ls/internal/adapter/yamlls" "github.com/mrjosh/helm-ls/internal/util" + "go.lsp.dev/jsonrpc2" ) func (h *YamlHandler) Configure(ctx context.Context, helmlsConfig util.HelmlsConfiguration) { h.configureYamlls(ctx, helmlsConfig) } -func (h *YamlHandler) configureYamlsEnabledGlob(helmlsConfig util.HelmlsConfiguration) { - globObject, err := glob.Compile(helmlsConfig.YamllsConfiguration.EnabledForFilesGlob) - if err != nil { - logger.Error("Error compiling glob for yamlls EnabledForFilesGlob", err) - globObject = util.DefaultConfig.YamllsConfiguration.EnabledForFilesGlobObject - } - h.yamllsConnector.EnabledForFilesGlobObject = globObject -} - func (h *YamlHandler) configureYamlls(ctx context.Context, helmlsConfig util.HelmlsConfiguration) { config := helmlsConfig - if config.YamllsConfiguration.Enabled { - h.configureYamlsEnabledGlob(helmlsConfig) - h.setYamllsConnector( - yamlls.NewConnector(ctx, - config.YamllsConfiguration, - h.client, - h.documents, - h.CustomHandler)) - err := h.yamllsConnector.CallInitialize(ctx, h.chartStore.RootURI) - if err != nil { - logger.Error("Error initializing yamlls", err) - } - - h.yamllsConnector.InitiallySyncOpenDocuments(h.documents.GetAllTemplateDocs()) + + customHandler := yamlls.CustomHandler{ + Handler: h.CustomHandler, + PostInitialize: func(ctx context.Context, conn jsonrpc2.Conn) error { + return conn.Notify(ctx, "yaml/registerCustomSchemaRequest", nil) + }, } + + connector := yamlls.NewConnector(ctx, + config.YamllsConfiguration, + h.client, + h.documents, + customHandler) + h.setYamllsConnector(connector) + + err := h.yamllsConnector.CallInitialize(ctx, h.chartStore.RootURI) + if err != nil { + logger.Error("Error initializing yamlls", err) + } + + h.yamllsConnector.InitiallySyncOpenYamlDocuments(h.documents.GetAllYamlDocs()) } diff --git a/internal/json_schema/json_schema_test.go b/internal/json_schema/json_schema_test.go index 172dda0..cea7cf9 100644 --- a/internal/json_schema/json_schema_test.go +++ b/internal/json_schema/json_schema_test.go @@ -3,10 +3,11 @@ package jsonschema import ( "encoding/json" "testing" + + "github.com/stretchr/testify/assert" ) func TestGenerateJSONSchema(t *testing.T) { - // Define a sample input map to generate a schema from input := map[string]interface{}{ "name": "example", "age": 30, @@ -14,20 +15,11 @@ func TestGenerateJSONSchema(t *testing.T) { "tags": []interface{}{"go", "json", "schema"}, } - // Generate the schema schema, err := GenerateJSONSchema(input) - if err != nil { - t.Fatalf("Failed to generate schema: %v", err) - } + assert.NoError(t, err) - // Marshal the schema to JSON schemaJSON, err := json.MarshalIndent(schema, "", " ") - if err != nil { - t.Fatalf("Failed to marshal schema to JSON: %v", err) - } - - // Print the JSON schema for visual verification - t.Logf("Generated JSON Schema:\n%s", schemaJSON) + assert.NoError(t, err) // Expected JSON schema structure expected := map[string]interface{}{ @@ -56,14 +48,10 @@ func TestGenerateJSONSchema(t *testing.T) { }, } - // Convert the expected schema to JSON for comparison expectedJSON, err := json.MarshalIndent(expected, "", " ") + assert.NoError(t, err) + assert.Equal(t, string(expectedJSON), string(schemaJSON)) if err != nil { t.Fatalf("Failed to marshal expected schema to JSON: %v", err) } - - // Check if generated schema matches the expected schema - if string(schemaJSON) != string(expectedJSON) { - t.Errorf("Generated schema does not match expected schema.\nExpected:\n%s\nGot:\n%s", expectedJSON, schemaJSON) - } } diff --git a/internal/lsp/document/document_store.go b/internal/lsp/document/document_store.go index 8ca1701..fabbde3 100644 --- a/internal/lsp/document/document_store.go +++ b/internal/lsp/document/document_store.go @@ -90,6 +90,19 @@ func (s *DocumentStore) GetAllTemplateDocs() []*TemplateDocument { return docs } +func (s *DocumentStore) GetAllYamlDocs() []*YamlDocument { + var docs []*YamlDocument + s.documents.Range(func(_, v interface{}) bool { + doc, ok := v.(*YamlDocument) + if !ok { + return true + } + docs = append(docs, doc) + return true + }) + return docs +} + func (s *DocumentStore) LoadDocsOnNewChart(chart *charts.Chart, helmlsConfig util.HelmlsConfiguration) { if chart.HelmChart == nil { return From 4f7a813259825e9d3ebd7ee4244826f8d887248b Mon Sep 17 00:00:00 2001 From: qvalentin Date: Wed, 13 Nov 2024 21:26:46 +0100 Subject: [PATCH 03/11] fix(yamlls): remove duplicate code in document_sync --- .../adapter/yamlls/document_sync_template.go | 26 ++++--------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/internal/adapter/yamlls/document_sync_template.go b/internal/adapter/yamlls/document_sync_template.go index db6dd37..83caa58 100644 --- a/internal/adapter/yamlls/document_sync_template.go +++ b/internal/adapter/yamlls/document_sync_template.go @@ -1,8 +1,6 @@ package yamlls import ( - "context" - lsplocal "github.com/mrjosh/helm-ls/internal/lsp" "github.com/mrjosh/helm-ls/internal/lsp/document" "github.com/mrjosh/helm-ls/internal/util" @@ -43,10 +41,7 @@ func (yamllsConnector Connector) DocumentDidOpenTemplate(ast *sitter.Tree, param } params.TextDocument.Text = lsplocal.TrimTemplate(ast, []byte(params.TextDocument.Text)) - err := yamllsConnector.server.DidOpen(context.Background(), ¶ms) - if err != nil { - logger.Error("Error calling yamlls for didOpen", err) - } + yamllsConnector.DocumentDidOpen(¶ms) } func (yamllsConnector Connector) DocumentDidSaveTemplate(doc *document.TemplateDocument, params lsp.DidSaveTextDocumentParams) { @@ -54,13 +49,9 @@ func (yamllsConnector Connector) DocumentDidSaveTemplate(doc *document.TemplateD return } - params.Text = lsplocal.TrimTemplate(doc.Ast, doc.Content) - - err := yamllsConnector.server.DidSave(context.Background(), ¶ms) - if err != nil { - logger.Error("Error calling yamlls for didSave", err) - } + yamllsConnector.DocumentDidSave(¶ms) + // this is required as params.Text has no effect since the default of includeText is false yamllsConnector.DocumentDidChangeFullSyncTemplate(doc, lsp.DidChangeTextDocumentParams{ TextDocument: lsp.VersionedTextDocumentIdentifier{ TextDocumentIdentifier: params.TextDocument, @@ -90,11 +81,7 @@ func (yamllsConnector Connector) DocumentDidChangeTemplate(doc *document.Templat params.ContentChanges[i].Text = trimmedText[start:end] } - logger.Debug("Sending DocumentDidChange", params) - err := yamllsConnector.server.DidChange(context.Background(), ¶ms) - if err != nil { - logger.Println("Error calling yamlls for didChange", err) - } + yamllsConnector.DocumentDidChange(¶ms) } func (yamllsConnector Connector) DocumentDidChangeFullSyncTemplate(doc *document.TemplateDocument, params lsp.DidChangeTextDocumentParams) { @@ -112,10 +99,7 @@ func (yamllsConnector Connector) DocumentDidChangeFullSyncTemplate(doc *document } logger.Println("Sending DocumentDidChange with full sync", params) - err := yamllsConnector.server.DidChange(context.Background(), ¶ms) - if err != nil { - logger.Println("Error calling yamlls for didChange", err) - } + yamllsConnector.DocumentDidChange(¶ms) } func (yamllsConnector Connector) IsYamllsEnabled(uri lsp.URI) bool { From 3f37bfa9eaaef2d710e83d1c888a4a4099281553 Mon Sep 17 00:00:00 2001 From: qvalentin Date: Thu, 14 Nov 2024 19:35:43 +0100 Subject: [PATCH 04/11] fix: add CustomSchemaProvider --- internal/adapter/yamlls/client.go | 2 + internal/adapter/yamlls/custom_new_client.go | 49 +++++++++++++++++++ internal/handler/yaml_handler/configure.go | 14 ++---- internal/handler/yaml_handler/yaml_handler.go | 13 +++++ 4 files changed, 69 insertions(+), 9 deletions(-) diff --git a/internal/adapter/yamlls/client.go b/internal/adapter/yamlls/client.go index 529ca7a..c36289a 100644 --- a/internal/adapter/yamlls/client.go +++ b/internal/adapter/yamlls/client.go @@ -2,6 +2,7 @@ package yamlls import ( "context" + "fmt" "go.lsp.dev/protocol" ) @@ -13,6 +14,7 @@ func (y Connector) ApplyEdit(ctx context.Context, params *protocol.ApplyWorkspac // LogMessage implements protocol.Client. func (y Connector) LogMessage(ctx context.Context, params *protocol.LogMessageParams) (err error) { + logger.Debug(fmt.Sprintf("LogMessage from yamlls: %s %s", params.Type, params.Message)) return nil } diff --git a/internal/adapter/yamlls/custom_new_client.go b/internal/adapter/yamlls/custom_new_client.go index 093257d..97146c0 100644 --- a/internal/adapter/yamlls/custom_new_client.go +++ b/internal/adapter/yamlls/custom_new_client.go @@ -2,9 +2,11 @@ package yamlls import ( "context" + "encoding/json" "go.lsp.dev/jsonrpc2" "go.lsp.dev/protocol" + "go.lsp.dev/uri" "go.uber.org/zap" ) @@ -19,6 +21,53 @@ var DefaultCustomHandler = CustomHandler{ jsonrpc2.MethodNotFoundHandler, func(ctx context.Context, conn jsonrpc2.Conn) error { return nil }, } +func NewCustomSchemaHandler(handler jsonrpc2.Handler) *CustomHandler { + customHandler := &CustomHandler{ + Handler: handler, + PostInitialize: func(ctx context.Context, conn jsonrpc2.Conn) error { + return conn.Notify(ctx, "yaml/registerCustomSchemaRequest", nil) + }, + } + + return customHandler +} + +type CustomSchemaProvider func(ctx context.Context, uri uri.URI) (uri.URI, error) + +func NewCustomSchemaProviderHandler(provider CustomSchemaProvider) jsonrpc2.Handler { + return func(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error { + switch req.Method() { + case "custom/schema/request": + + params := []string{} + jsonBytes, err := req.Params().MarshalJSON() + if err != nil { + logger.Error(err) + return reply(ctx, nil, nil) + } + + err = json.Unmarshal(jsonBytes, ¶ms) + if err != nil { + logger.Error(err) + return reply(ctx, nil, nil) + } + + logger.Println("YamlHandler: custom/schema/request", req.Params()) + + if len(params) == 0 { + return reply(ctx, nil, nil) + } + + schemaURI, err := provider(ctx, uri.New(params[0])) + if err != nil { + return reply(ctx, nil, err) + } + return reply(ctx, schemaURI, nil) + } + return jsonrpc2.MethodNotFoundHandler(ctx, reply, req) + } +} + // CustomNewClient returns the context in which Client is embedded, jsonrpc2.Conn, and the Server. func (yamllsConnector Connector) CustomNewClient(ctx context.Context, client protocol.Client, stream jsonrpc2.Stream, logger *zap.Logger) (context.Context, jsonrpc2.Conn, protocol.Server) { ctx = protocol.WithClient(ctx, client) diff --git a/internal/handler/yaml_handler/configure.go b/internal/handler/yaml_handler/configure.go index 9cdc1ec..67dec0a 100644 --- a/internal/handler/yaml_handler/configure.go +++ b/internal/handler/yaml_handler/configure.go @@ -5,7 +5,6 @@ import ( "github.com/mrjosh/helm-ls/internal/adapter/yamlls" "github.com/mrjosh/helm-ls/internal/util" - "go.lsp.dev/jsonrpc2" ) func (h *YamlHandler) Configure(ctx context.Context, helmlsConfig util.HelmlsConfiguration) { @@ -15,18 +14,15 @@ func (h *YamlHandler) Configure(ctx context.Context, helmlsConfig util.HelmlsCon func (h *YamlHandler) configureYamlls(ctx context.Context, helmlsConfig util.HelmlsConfiguration) { config := helmlsConfig - customHandler := yamlls.CustomHandler{ - Handler: h.CustomHandler, - PostInitialize: func(ctx context.Context, conn jsonrpc2.Conn) error { - return conn.Notify(ctx, "yaml/registerCustomSchemaRequest", nil) - }, - } - connector := yamlls.NewConnector(ctx, config.YamllsConfiguration, h.client, h.documents, - customHandler) + *yamlls.NewCustomSchemaHandler( + yamlls.NewCustomSchemaProviderHandler(h.CustomSchemaProvider), + ), + ) + h.setYamllsConnector(connector) err := h.yamllsConnector.CallInitialize(ctx, h.chartStore.RootURI) diff --git a/internal/handler/yaml_handler/yaml_handler.go b/internal/handler/yaml_handler/yaml_handler.go index 0d09961..a0bb2df 100644 --- a/internal/handler/yaml_handler/yaml_handler.go +++ b/internal/handler/yaml_handler/yaml_handler.go @@ -52,6 +52,19 @@ func (h *YamlHandler) setYamllsConnector(yamllsConnector *yamlls.Connector) { h.yamllsConnector = yamllsConnector } +func (h *YamlHandler) CustomSchemaProvider(ctx context.Context, URI uri.URI) (uri.URI, error) { + chart, err := h.chartStore.GetChartForDoc(URI) + if err != nil { + logger.Error(err) + } + schemaFilePath, err := jsonschema.CreateJsonSchemaForChart(chart) + if err != nil { + logger.Error(err) + return uri.New(""), err + } + return uri.File(schemaFilePath), nil +} + func (h *YamlHandler) CustomHandler(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error { switch req.Method() { case "custom/schema/request": From 0a444cef9568bace1db2597e5fab0b700ae43991 Mon Sep 17 00:00:00 2001 From: qvalentin Date: Tue, 19 Nov 2024 09:06:08 +0100 Subject: [PATCH 05/11] wip: integra test --- .../yamlls/completion_integration_test.go | 2 +- ...ew_client.go => custom_schema_provider.go} | 2 +- ...custom_schema_provider_integration_test.go | 79 +++++++++++++++++++ .../yamlls/diagnostics_integration_test.go | 4 +- .../adapter/yamlls/hover_integration_test.go | 2 +- internal/adapter/yamlls/initization.go | 2 - .../adapter/yamlls/integration_test_utils.go | 4 +- .../adapter/yamlls/symbol_integration_test.go | 2 +- internal/adapter/yamlls/yamlls.go | 4 +- internal/charts/chart_for_document.go | 19 ++++- internal/charts/chart_for_document_test.go | 30 +++++++ .../handler/template_handler/configure.go | 2 +- internal/handler/yaml_handler/configure.go | 2 +- internal/handler/yaml_handler/yaml_handler.go | 39 +-------- internal/json_schema/json_schema.go | 22 ------ 15 files changed, 140 insertions(+), 75 deletions(-) rename internal/adapter/yamlls/{custom_new_client.go => custom_schema_provider.go} (96%) create mode 100644 internal/adapter/yamlls/custom_schema_provider_integration_test.go diff --git a/internal/adapter/yamlls/completion_integration_test.go b/internal/adapter/yamlls/completion_integration_test.go index 3e3687b..6db8b42 100644 --- a/internal/adapter/yamlls/completion_integration_test.go +++ b/internal/adapter/yamlls/completion_integration_test.go @@ -68,7 +68,7 @@ func TestYamllsCompletionIntegration(t *testing.T) { tt := tt1 t.Run(tt.desc, func(t *testing.T) { t.Parallel() - yamllsConnector, documents, _ := getYamlLsConnector(t, config) + yamllsConnector, documents, _ := getYamlLsConnector(t, config, &DefaultCustomHandler) openFile(t, documents, tt.file, yamllsConnector) assert.EventuallyWithT(t, func(c *assert.CollectT) { diff --git a/internal/adapter/yamlls/custom_new_client.go b/internal/adapter/yamlls/custom_schema_provider.go similarity index 96% rename from internal/adapter/yamlls/custom_new_client.go rename to internal/adapter/yamlls/custom_schema_provider.go index 97146c0..b4aaad2 100644 --- a/internal/adapter/yamlls/custom_new_client.go +++ b/internal/adapter/yamlls/custom_schema_provider.go @@ -52,7 +52,7 @@ func NewCustomSchemaProviderHandler(provider CustomSchemaProvider) jsonrpc2.Hand return reply(ctx, nil, nil) } - logger.Println("YamlHandler: custom/schema/request", req.Params()) + logger.Println("YamlHandler: custom/schema/request", string(req.Params())) if len(params) == 0 { return reply(ctx, nil, nil) diff --git a/internal/adapter/yamlls/custom_schema_provider_integration_test.go b/internal/adapter/yamlls/custom_schema_provider_integration_test.go new file mode 100644 index 0000000..fa1361b --- /dev/null +++ b/internal/adapter/yamlls/custom_schema_provider_integration_test.go @@ -0,0 +1,79 @@ +//go:build integration + +package yamlls + +import ( + "context" + "os" + "path" + "testing" + "time" + + "github.com/mrjosh/helm-ls/internal/util" + "github.com/stretchr/testify/assert" + lsp "go.lsp.dev/protocol" + "go.lsp.dev/uri" +) + +var TEST_JSON_SCHEMA = ` +{ + "$id": "https://example.com/address.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "An address similar to http://microformats.org/wiki/h-card", + "type": "object", + "properties": { + "postOfficeBox": { + "type": "string" + }, + "countryName": { + "type": "string" + } + } +} +` + +func TestYamllsCustomSchemaProviderIntegration(t *testing.T) { + config := util.DefaultConfig.YamllsConfiguration + config.Path = "yamlls-debug.sh" + + // tempDir := t.TempDir() + tempDir := "/data/data/com.termux/files/usr/tmp/" + schemaFile := path.Join(tempDir, "schema.json") + // write schema + err := os.WriteFile(schemaFile, []byte(TEST_JSON_SCHEMA), 0o644) + assert.NoError(t, err) + + testFile := path.Join(tempDir, "test.yaml") + err = os.WriteFile(testFile, []byte("c"), 0o644) + assert.NoError(t, err) + + customHandler := NewCustomSchemaHandler( + NewCustomSchemaProviderHandler( + func(ctx context.Context, URI uri.URI) (uri.URI, error) { + t.Log("Calling Schema provider") + return "http://localhost:8000/schema.json", nil + })) + + yamllsConnector, documents, _ := getYamlLsConnector(t, config, customHandler) + openFile(t, documents, testFile, yamllsConnector) + + assert.EventuallyWithT(t, func(c *assert.CollectT) { + result, _ := yamllsConnector.CallCompletion(context.Background(), &lsp.CompletionParams{ + TextDocumentPositionParams: lsp.TextDocumentPositionParams{ + TextDocument: lsp.TextDocumentIdentifier{ + URI: uri.File(testFile), + }, + Position: lsp.Position{ + Line: 0, + Character: 1, + }, + }, + }) + assert.NotNil(c, result) + if result == nil { + t.Log("result is nil") + return + } + t.Log("reuslt is", result) + }, time.Second*20, time.Second*2) +} diff --git a/internal/adapter/yamlls/diagnostics_integration_test.go b/internal/adapter/yamlls/diagnostics_integration_test.go index 65237db..8e9cc3c 100644 --- a/internal/adapter/yamlls/diagnostics_integration_test.go +++ b/internal/adapter/yamlls/diagnostics_integration_test.go @@ -79,7 +79,7 @@ func TestYamllsDiagnosticsIntegration(t *testing.T) { Enable: false, } config.YamllsSettings = yamllsSettings - yamllsConnector, documents, diagnosticsChan := getYamlLsConnector(t, config) + yamllsConnector, documents, diagnosticsChan := getYamlLsConnector(t, config, &DefaultCustomHandler) didOpenChan := make(chan string) go readTestFiles(TEST_DATA_DIR, didOpenChan, doneReadingFilesChan) @@ -119,7 +119,7 @@ func TestYamllsDiagnosticsIntegrationWithSchema(t *testing.T) { diagnosticsChan := make(chan lsp.PublishDiagnosticsParams) config := util.DefaultConfig.YamllsConfiguration - yamllsConnector, documents, diagnosticsChan := getYamlLsConnector(t, config) + yamllsConnector, documents, diagnosticsChan := getYamlLsConnector(t, config, &DefaultCustomHandler) file := filepath.Join("..", "..", "..", "testdata", "example", "templates", "service.yaml") openFile(t, documents, file, yamllsConnector) diff --git a/internal/adapter/yamlls/hover_integration_test.go b/internal/adapter/yamlls/hover_integration_test.go index 2cc474e..2ae0442 100644 --- a/internal/adapter/yamlls/hover_integration_test.go +++ b/internal/adapter/yamlls/hover_integration_test.go @@ -60,7 +60,7 @@ func TestYamllsHoverIntegration(t *testing.T) { tt := tt1 t.Run(tt.desc, func(t *testing.T) { t.Parallel() - yamllsConnector, documents, _ := getYamlLsConnector(t, config) + yamllsConnector, documents, _ := getYamlLsConnector(t, config, &DefaultCustomHandler) openFile(t, documents, tt.file, yamllsConnector) assert.Eventually(t, func() bool { diff --git a/internal/adapter/yamlls/initization.go b/internal/adapter/yamlls/initization.go index 23c1e8c..db1afc7 100644 --- a/internal/adapter/yamlls/initization.go +++ b/internal/adapter/yamlls/initization.go @@ -37,8 +37,6 @@ func (yamllsConnector Connector) CallInitialize(ctx context.Context, workspaceUR return err } - yamllsConnector.customHandler.PostInitialize(ctx, yamllsConnector.conn) - defer func() { yamllsConnector.customHandler.PostInitialize(ctx, yamllsConnector.conn) }() diff --git a/internal/adapter/yamlls/integration_test_utils.go b/internal/adapter/yamlls/integration_test_utils.go index fe67746..56c6ff8 100644 --- a/internal/adapter/yamlls/integration_test_utils.go +++ b/internal/adapter/yamlls/integration_test_utils.go @@ -47,7 +47,7 @@ func (proc readWriteCloseMock) Close() error { return nil } -func getYamlLsConnector(t *testing.T, config util.YamllsConfiguration) (*Connector, *document.DocumentStore, chan lsp.PublishDiagnosticsParams) { +func getYamlLsConnector(t *testing.T, config util.YamllsConfiguration, customHandler *CustomHandler) (*Connector, *document.DocumentStore, chan lsp.PublishDiagnosticsParams) { dir := t.TempDir() documents := document.NewDocumentStore() diagnosticsChan := make(chan lsp.PublishDiagnosticsParams) @@ -55,7 +55,7 @@ func getYamlLsConnector(t *testing.T, config util.YamllsConfiguration) (*Connect zapLogger, _ := zap.NewProduction() client := protocol.ClientDispatcher(con, zapLogger) - yamllsConnector := NewConnector(context.Background(), config, client, documents, DefaultCustomHandler) + yamllsConnector := NewConnector(context.Background(), config, client, documents, customHandler) if yamllsConnector.server == nil { t.Fatal("Could not connect to yaml-language-server") diff --git a/internal/adapter/yamlls/symbol_integration_test.go b/internal/adapter/yamlls/symbol_integration_test.go index ded8cd4..a6ee4a9 100644 --- a/internal/adapter/yamlls/symbol_integration_test.go +++ b/internal/adapter/yamlls/symbol_integration_test.go @@ -37,7 +37,7 @@ func TestYamllsDocumentSymoblIntegration(t *testing.T) { tt := tt1 t.Run(tt.file, func(t *testing.T) { t.Parallel() - yamllsConnector, documents, _ := getYamlLsConnector(t, config) + yamllsConnector, documents, _ := getYamlLsConnector(t, config, &DefaultCustomHandler) openFile(t, documents, tt.file, yamllsConnector) assert.EventuallyWithT(t, func(c *assert.CollectT) { diff --git a/internal/adapter/yamlls/yamlls.go b/internal/adapter/yamlls/yamlls.go index ff92042..9fc21bf 100644 --- a/internal/adapter/yamlls/yamlls.go +++ b/internal/adapter/yamlls/yamlls.go @@ -24,7 +24,7 @@ type Connector struct { conn jsonrpc2.Conn documents *document.DocumentStore client protocol.Client - customHandler CustomHandler + customHandler *CustomHandler EnabledForFilesGlobObject glob.Glob } @@ -32,7 +32,7 @@ func NewConnector(ctx context.Context, yamllsConfiguration util.YamllsConfiguration, client protocol.Client, documents *document.DocumentStore, - customHandler CustomHandler, + customHandler *CustomHandler, ) *Connector { yamllsCmd := exec.Command(yamllsConfiguration.Path, "--stdio") diff --git a/internal/charts/chart_for_document.go b/internal/charts/chart_for_document.go index 207ea6f..544d544 100644 --- a/internal/charts/chart_for_document.go +++ b/internal/charts/chart_for_document.go @@ -3,6 +3,7 @@ package charts import ( "fmt" "os" + "path" "path/filepath" "strings" @@ -17,7 +18,10 @@ func (s *ChartStore) GetChartForDoc(uri lsp.DocumentURI) (*Chart, error) { return chart, nil } - chart, err := s.getChartFromFilesystemForTemplates(uri.Filename()) + chart, err := s.getChartFromFilesystemForNonTemplates(uri.Filename()) + if err != nil { + chart, err = s.getChartFromFilesystemForTemplates(uri.Filename()) + } if err != nil { return chart, ErrChartNotFound{ URI: uri, @@ -46,13 +50,26 @@ func (s *ChartStore) GetChartOrParentForDoc(uri lsp.DocumentURI) (*Chart, error) func (s *ChartStore) getChartFromCache(uri lsp.DocumentURI) *Chart { for chartURI, chart := range s.Charts { + // template files if strings.HasPrefix(uri.Filename(), filepath.Join(chartURI.Filename(), "template")) { return chart } + // values.yaml files etc. + if path.Dir(uri.Filename()) == chartURI.Filename() { + return chart + } } return nil } +func (s *ChartStore) getChartFromFilesystemForNonTemplates(path string) (*Chart, error) { + directory := filepath.Dir(path) + if isChartDirectory(directory) { + return s.newChart(uri.File(directory), s.valuesFilesConfig), nil + } + return nil, ErrChartNotFound{} +} + func (s *ChartStore) getChartFromFilesystemForTemplates(path string) (*Chart, error) { directory := filepath.Dir(path) if filepath.Base(directory) == "templates" { diff --git a/internal/charts/chart_for_document_test.go b/internal/charts/chart_for_document_test.go index 0aa6f29..953713c 100644 --- a/internal/charts/chart_for_document_test.go +++ b/internal/charts/chart_for_document_test.go @@ -155,3 +155,33 @@ func TestGetChartForDocumentWorksForChartWithDependencies(t *testing.T) { assert.NotNil(t, chartStore.Charts[uri.File(filepath.Join(rootDir, "charts", "subchartexample"))]) assert.NotNil(t, chartStore.Charts[uri.File(filepath.Join(rootDir, "charts", charts.DependencyCacheFolder, "common"))]) } + +func TestGetChartForDocumentWorksForValuesFile(t *testing.T) { + var ( + rootDir = "../../testdata/dependenciesExample/" + chartStore = charts.NewChartStore(uri.File(rootDir), charts.NewChart, addChartCallback) + ) + + result1, error := chartStore.GetChartForDoc(uri.File(filepath.Join(rootDir, "values.yaml"))) + assert.NoError(t, error) + + assert.Len(t, result1.HelmChart.Dependencies(), 2) + assert.Len(t, chartStore.Charts, 3) + + assert.NotNil(t, chartStore.Charts[uri.File(rootDir)]) +} + +func TestGetChartForDocumentWorksForValuesFileWithCache(t *testing.T) { + var ( + rootDir = "../../testdata/dependenciesExample/" + chartStore = charts.NewChartStore(uri.File(rootDir), charts.NewChart, addChartCallback) + ) + + result1, error := chartStore.GetChartForDoc(uri.File(filepath.Join(rootDir, "values.yaml"))) + assert.NoError(t, error) + assert.NotNil(t, chartStore.Charts[uri.File(rootDir)]) + + result2, error := chartStore.GetChartForDoc(uri.File(filepath.Join(rootDir, "values.yaml"))) + + assert.Same(t, result1, result2) +} diff --git a/internal/handler/template_handler/configure.go b/internal/handler/template_handler/configure.go index 665e000..53aeb78 100644 --- a/internal/handler/template_handler/configure.go +++ b/internal/handler/template_handler/configure.go @@ -25,7 +25,7 @@ func (h *TemplateHandler) configureYamlls(ctx context.Context, helmlsConfig util config := helmlsConfig if config.YamllsConfiguration.Enabled { h.configureYamlsEnabledGlob(helmlsConfig) - h.setYamllsConnector(yamlls.NewConnector(ctx, config.YamllsConfiguration, h.client, h.documents, yamlls.DefaultCustomHandler)) + h.setYamllsConnector(yamlls.NewConnector(ctx, config.YamllsConfiguration, h.client, h.documents, &yamlls.DefaultCustomHandler)) err := h.yamllsConnector.CallInitialize(ctx, h.chartStore.RootURI) if err != nil { logger.Error("Error initializing yamlls", err) diff --git a/internal/handler/yaml_handler/configure.go b/internal/handler/yaml_handler/configure.go index 67dec0a..4b3be8b 100644 --- a/internal/handler/yaml_handler/configure.go +++ b/internal/handler/yaml_handler/configure.go @@ -18,7 +18,7 @@ func (h *YamlHandler) configureYamlls(ctx context.Context, helmlsConfig util.Hel config.YamllsConfiguration, h.client, h.documents, - *yamlls.NewCustomSchemaHandler( + yamlls.NewCustomSchemaHandler( yamlls.NewCustomSchemaProviderHandler(h.CustomSchemaProvider), ), ) diff --git a/internal/handler/yaml_handler/yaml_handler.go b/internal/handler/yaml_handler/yaml_handler.go index a0bb2df..cad6130 100644 --- a/internal/handler/yaml_handler/yaml_handler.go +++ b/internal/handler/yaml_handler/yaml_handler.go @@ -56,6 +56,7 @@ func (h *YamlHandler) CustomSchemaProvider(ctx context.Context, URI uri.URI) (ur chart, err := h.chartStore.GetChartForDoc(URI) if err != nil { logger.Error(err) + // we can ignore the error, providing a wrong schema is still useful } schemaFilePath, err := jsonschema.CreateJsonSchemaForChart(chart) if err != nil { @@ -64,41 +65,3 @@ func (h *YamlHandler) CustomSchemaProvider(ctx context.Context, URI uri.URI) (ur } return uri.File(schemaFilePath), nil } - -func (h *YamlHandler) CustomHandler(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error { - switch req.Method() { - case "custom/schema/request": - - params := []string{} - jsonBytes, err := req.Params().MarshalJSON() - if err != nil { - logger.Error(err) - return reply(ctx, nil, nil) - } - - err = json.Unmarshal(jsonBytes, ¶ms) - if err != nil { - logger.Error(err) - return reply(ctx, nil, nil) - } - - logger.Println("YamlHandler: custom/schema/request", req.Params()) - - if len(params) == 0 { - return reply(ctx, nil, nil) - } - chart, err := h.chartStore.GetChartForDoc(uri.New(params[0])) - if err != nil { - logger.Error(err) - } - schemaFilePath, err := jsonschema.CreateJsonSchemaForChart(chart) - if err != nil { - logger.Error(err) - return reply(ctx, nil, nil) - } - - return reply(ctx, uri.New(schemaFilePath), nil) - } - - return jsonrpc2.MethodNotFoundHandler(ctx, reply, req) -} diff --git a/internal/json_schema/json_schema.go b/internal/json_schema/json_schema.go index ab1ec98..6113eda 100644 --- a/internal/json_schema/json_schema.go +++ b/internal/json_schema/json_schema.go @@ -13,11 +13,6 @@ import ( var logger = log.GetLogger() func CreateJsonSchemaForChart(chart *charts.Chart) (string, error) { - // reflector := jsonschema.Reflector{ - // ExpandedStruct: true, - // AllowAdditionalProperties: true, - // } - schema, err := GenerateJSONSchema(chart.ValuesFiles.MainValuesFile.Values) bytes, err := json.Marshal(schema) @@ -26,8 +21,6 @@ func CreateJsonSchemaForChart(chart *charts.Chart) (string, error) { return "", err } - // create a tmp file and write the schema - file, err := os.CreateTemp("", base64.StdEncoding.EncodeToString([]byte(chart.RootURI.Filename()))) if err != nil { logger.Error(err) @@ -42,18 +35,3 @@ func CreateJsonSchemaForChart(chart *charts.Chart) (string, error) { return file.Name(), nil } - -// func GenerateSchemaFromData(data interface{}) error { -// jsonBytes, err := json.Marshal(data) -// if err != nil { -// return nil, err -// } -// -// documentLoader := gojsonschema.NewStringLoader(string(jsonBytes)) -// schema, err := gojsonschema.NewSchema(documentLoader) -// if err != nil { -// return nil, err -// } -// -// return schema.Root(), nil -// } From 7f15ac877328a8c2120d568d6d4d48fe49f8ea01 Mon Sep 17 00:00:00 2001 From: qvalentin Date: Fri, 22 Nov 2024 17:53:05 +0100 Subject: [PATCH 06/11] wip --- .../yamlls/custom_schema_provider_integration_test.go | 11 ++++++++--- internal/handler/yaml_handler/yaml_handler.go | 2 -- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/internal/adapter/yamlls/custom_schema_provider_integration_test.go b/internal/adapter/yamlls/custom_schema_provider_integration_test.go index fa1361b..6dad296 100644 --- a/internal/adapter/yamlls/custom_schema_provider_integration_test.go +++ b/internal/adapter/yamlls/custom_schema_provider_integration_test.go @@ -37,7 +37,8 @@ func TestYamllsCustomSchemaProviderIntegration(t *testing.T) { config.Path = "yamlls-debug.sh" // tempDir := t.TempDir() - tempDir := "/data/data/com.termux/files/usr/tmp/" + // tempDir := "/data/data/com.termux/files/usr/tmp/" + tempDir := "/tmp/" schemaFile := path.Join(tempDir, "schema.json") // write schema err := os.WriteFile(schemaFile, []byte(TEST_JSON_SCHEMA), 0o644) @@ -51,13 +52,14 @@ func TestYamllsCustomSchemaProviderIntegration(t *testing.T) { NewCustomSchemaProviderHandler( func(ctx context.Context, URI uri.URI) (uri.URI, error) { t.Log("Calling Schema provider") - return "http://localhost:8000/schema.json", nil + return uri.File(schemaFile), nil })) yamllsConnector, documents, _ := getYamlLsConnector(t, config, customHandler) openFile(t, documents, testFile, yamllsConnector) assert.EventuallyWithT(t, func(c *assert.CollectT) { + logger.Println("Calling completion") result, _ := yamllsConnector.CallCompletion(context.Background(), &lsp.CompletionParams{ TextDocumentPositionParams: lsp.TextDocumentPositionParams{ TextDocument: lsp.TextDocumentIdentifier{ @@ -69,11 +71,14 @@ func TestYamllsCustomSchemaProviderIntegration(t *testing.T) { }, }, }) + logger.Println("Called completion") + assert.NotNil(c, result) if result == nil { + logger.Println("result is nil") t.Log("result is nil") return } - t.Log("reuslt is", result) + t.Log("result is", result) }, time.Second*20, time.Second*2) } diff --git a/internal/handler/yaml_handler/yaml_handler.go b/internal/handler/yaml_handler/yaml_handler.go index cad6130..dca1b3a 100644 --- a/internal/handler/yaml_handler/yaml_handler.go +++ b/internal/handler/yaml_handler/yaml_handler.go @@ -2,14 +2,12 @@ package yamlhandler import ( "context" - "encoding/json" "github.com/mrjosh/helm-ls/internal/adapter/yamlls" "github.com/mrjosh/helm-ls/internal/charts" jsonschema "github.com/mrjosh/helm-ls/internal/json_schema" "github.com/mrjosh/helm-ls/internal/log" "github.com/mrjosh/helm-ls/internal/lsp/document" - "go.lsp.dev/jsonrpc2" "go.lsp.dev/protocol" "go.lsp.dev/uri" ) From 9e2e2d0a866b0cacb0f0d99d686e3808969efb78 Mon Sep 17 00:00:00 2001 From: qvalentin Date: Sat, 23 Nov 2024 21:12:59 +0100 Subject: [PATCH 07/11] feat(completion): add variable completion --- .../handler/template_handler/completion.go | 1 + .../template_handler/completion_main_test.go | 2 ++ internal/language_features/variables.go | 7 ++++- .../symbol_table/symbol_table_variables.go | 10 +++++++ internal/protocol/completion.go | 27 +++++++++++++++++++ 5 files changed, 46 insertions(+), 1 deletion(-) diff --git a/internal/handler/template_handler/completion.go b/internal/handler/template_handler/completion.go index 64c3ab8..b7fe27d 100644 --- a/internal/handler/template_handler/completion.go +++ b/internal/handler/template_handler/completion.go @@ -24,6 +24,7 @@ func (h *TemplateHandler) Completion(ctx context.Context, params *lsp.Completion languagefeatures.NewFunctionCallFeature(genericDocumentUseCase), languagefeatures.NewTextFeature(ctx, genericDocumentUseCase, h.yamllsConnector, ¶ms.TextDocumentPositionParams), languagefeatures.NewIncludesCallFeature(genericDocumentUseCase), + languagefeatures.NewVariablesFeature(genericDocumentUseCase), } for _, usecase := range usecases { diff --git a/internal/handler/template_handler/completion_main_test.go b/internal/handler/template_handler/completion_main_test.go index 5ab94e5..8ba4c3c 100644 --- a/internal/handler/template_handler/completion_main_test.go +++ b/internal/handler/template_handler/completion_main_test.go @@ -164,6 +164,8 @@ func TestCompletionMainSingleLines(t *testing.T) { notExpectedInsertTexts []string err error }{ + {`Test completion on {{ $variable := "hello" }} {{ $v^ }}`, []string{"$variable"}, []string{".Values", "include"}, nil}, + {`Test completion on {{ $variable := "hello" }} {{ $^ }}`, []string{"$variable"}, []string{".Values", "include"}, nil}, {"Test completion on {{.Bad.^}}", []string{}, []string{}, errors.New("[Bad ] is no valid template context for helm")}, {"Test completion on {{ .Bad.^ }}", []string{}, []string{}, errors.New("[Bad ] is no valid template context for helm")}, {"Test completion on {{ n^ }}", []string{"not"}, []string{}, nil}, diff --git a/internal/language_features/variables.go b/internal/language_features/variables.go index a98162a..ddacd27 100644 --- a/internal/language_features/variables.go +++ b/internal/language_features/variables.go @@ -1,6 +1,7 @@ package languagefeatures import ( + "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" @@ -17,7 +18,7 @@ func NewVariablesFeature(genericDocumentUseCase *GenericDocumentUseCase) *Variab } func (f *VariablesFeature) AppropriateForNode() bool { - return f.NodeType == gotemplate.NodeTypeIdentifier && + return (f.NodeType == gotemplate.NodeTypeIdentifier || f.NodeType == gotemplate.NodeTypeDollar) && f.ParentNodeType == gotemplate.NodeTypeVariable } @@ -41,3 +42,7 @@ func (f *VariablesFeature) References() (result []lsp.Location, err error) { } return result, nil } + +func (f *VariablesFeature) Completion() (result *lsp.CompletionList, err error) { + return protocol.CompletionResults{}.WithVariableDefinitions(f.Document.SymbolTable.GetAllVariableDefinitions()).ToList(), nil +} diff --git a/internal/lsp/symbol_table/symbol_table_variables.go b/internal/lsp/symbol_table/symbol_table_variables.go index e0045d7..391cfa2 100644 --- a/internal/lsp/symbol_table/symbol_table_variables.go +++ b/internal/lsp/symbol_table/symbol_table_variables.go @@ -45,6 +45,16 @@ func (s *SymbolTable) getVariableDefinition(name string, accessRange sitter.Rang return definition, nil } +func (s *SymbolTable) GetAllVariableDefinitions() (result map[string][]VariableDefinition) { + result = map[string][]VariableDefinition{} + for name, definitions := range s.variableDefinitions { + result[name] = append( + []VariableDefinition{}, + definitions...) + } + return result +} + func (s *SymbolTable) GetVariableDefinitionForNode(node *sitter.Node, content []byte) (VariableDefinition, error) { name, err := getVariableName(node, content) if err != nil { diff --git a/internal/protocol/completion.go b/internal/protocol/completion.go index f59a57a..e4931cd 100644 --- a/internal/protocol/completion.go +++ b/internal/protocol/completion.go @@ -3,6 +3,7 @@ package protocol import ( "github.com/mrjosh/helm-ls/internal/documentation/godocs" helmdocs "github.com/mrjosh/helm-ls/internal/documentation/helm" + symboltable "github.com/mrjosh/helm-ls/internal/lsp/symbol_table" lsp "go.lsp.dev/protocol" ) @@ -48,3 +49,29 @@ func snippetCompletionItem(gotemplateSnippet godocs.GoTemplateSnippet) lsp.Compl InsertTextFormat: lsp.InsertTextFormatSnippet, } } + +func (c CompletionResults) WithVariableDefinitions(variableDefinitions map[string][]symboltable.VariableDefinition) CompletionResults { + items := c.Items + for variableName, definitions := range variableDefinitions { + + if len(definitions) == 0 { + continue + } + definition := definitions[0] + + items = append(items, + variableCompletionItem(variableName, definition), + ) + } + return CompletionResults{Items: items} +} + +func variableCompletionItem(variableName string, definition symboltable.VariableDefinition) lsp.CompletionItem { + return lsp.CompletionItem{ + Label: variableName, + Detail: definition.Value, + InsertText: variableName, + InsertTextFormat: lsp.InsertTextFormatPlainText, + Kind: lsp.CompletionItemKindVariable, + } +} From 3a4cd25447493e6c6ebee57e43bd5d786a4a7f8b Mon Sep 17 00:00:00 2001 From: qvalentin Date: Sun, 15 Dec 2024 20:17:26 +0100 Subject: [PATCH 08/11] fix: use node.Range instead of custom function --- internal/language_features/generic_template_context.go | 2 +- internal/lsp/symbol_table/symbol_table_includes.go | 4 ++-- .../lsp/symbol_table/symbol_table_template_context.go | 9 ++++----- internal/lsp/symbol_table/symbol_table_variables.go | 2 +- internal/lsp/symbol_table/variables_visitor.go | 3 +-- internal/lsp/yaml_ast.go | 3 +-- internal/util/points.go | 9 --------- 7 files changed, 10 insertions(+), 22 deletions(-) diff --git a/internal/language_features/generic_template_context.go b/internal/language_features/generic_template_context.go index 10d73d0..95ea543 100644 --- a/internal/language_features/generic_template_context.go +++ b/internal/language_features/generic_template_context.go @@ -14,7 +14,7 @@ type GenericTemplateContextFeature struct { } func (f *GenericTemplateContextFeature) getTemplateContext() (symboltable.TemplateContext, error) { - return f.GenericDocumentUseCase.Document.SymbolTable.GetTemplateContext(util.GetRangeForNode(f.Node)) + return f.GenericDocumentUseCase.Document.SymbolTable.GetTemplateContext(f.Node.Range()) } func (f *GenericTemplateContextFeature) getReferencesFromSymbolTable(templateContext symboltable.TemplateContext) []lsp.Location { diff --git a/internal/lsp/symbol_table/symbol_table_includes.go b/internal/lsp/symbol_table/symbol_table_includes.go index 44e40e3..241d459 100644 --- a/internal/lsp/symbol_table/symbol_table_includes.go +++ b/internal/lsp/symbol_table/symbol_table_includes.go @@ -23,7 +23,7 @@ func NewIncludeDefinitionsVisitor(symbolTable *SymbolTable, content []byte) *Inc 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), util.GetRangeForNode(node)) + v.symbolTable.AddIncludeDefinition(util.RemoveQuotes(content), node.Range()) } if node.Type() == gotemplate.NodeTypeFunctionCall { @@ -37,7 +37,7 @@ func (v *IncludeDefinitionsVisitor) enterFunctionCall(node *sitter.Node) { return } - v.symbolTable.AddIncludeReference(includeName, util.GetRangeForNode(node)) + v.symbolTable.AddIncludeReference(includeName, node.Range()) } func ParseIncludeFunctionCall(node *sitter.Node, content []byte) (string, error) { diff --git a/internal/lsp/symbol_table/symbol_table_template_context.go b/internal/lsp/symbol_table/symbol_table_template_context.go index 0ee0138..69d3c52 100644 --- a/internal/lsp/symbol_table/symbol_table_template_context.go +++ b/internal/lsp/symbol_table/symbol_table_template_context.go @@ -2,7 +2,6 @@ package symboltable import ( "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" - "github.com/mrjosh/helm-ls/internal/util" sitter "github.com/smacker/go-tree-sitter" ) @@ -55,13 +54,13 @@ func (v *TemplateContextVisitor) Enter(node *sitter.Node) { nodeType := node.Type() switch nodeType { case gotemplate.NodeTypeDot: - v.symbolTable.AddTemplateContext(v.currentContext, util.GetRangeForNode(node)) + v.symbolTable.AddTemplateContext(v.currentContext, node.Range()) case gotemplate.NodeTypeFieldIdentifier: content := node.Content(v.content) - v.symbolTable.AddTemplateContext(append(v.currentContext, content), util.GetRangeForNode(node)) + v.symbolTable.AddTemplateContext(append(v.currentContext, content), node.Range()) case gotemplate.NodeTypeField: content := node.ChildByFieldName("name").Content(v.content) - v.symbolTable.AddTemplateContext(append(v.currentContext, content), util.GetRangeForNode(node.ChildByFieldName("name"))) + v.symbolTable.AddTemplateContext(append(v.currentContext, content), node.ChildByFieldName("name").Range()) case gotemplate.NodeTypeUnfinishedSelectorExpression: operandNode := node.ChildByFieldName("operand") content := getContextForSelectorExpression(operandNode, v.content) @@ -69,7 +68,7 @@ func (v *TemplateContextVisitor) Enter(node *sitter.Node) { content = append(v.currentContext, content...) } v.symbolTable.AddTemplateContext(append(content, ""), - util.GetRangeForNode(node.Child(int(node.ChildCount())-1))) + node.Child(int(node.ChildCount())-1).Range()) case gotemplate.NodeTypeSelectorExpression: operandNode := node.ChildByFieldName("operand") if operandNode.Type() == gotemplate.NodeTypeVariable { diff --git a/internal/lsp/symbol_table/symbol_table_variables.go b/internal/lsp/symbol_table/symbol_table_variables.go index 391cfa2..040bfcd 100644 --- a/internal/lsp/symbol_table/symbol_table_variables.go +++ b/internal/lsp/symbol_table/symbol_table_variables.go @@ -60,7 +60,7 @@ func (s *SymbolTable) GetVariableDefinitionForNode(node *sitter.Node, content [] if err != nil { return VariableDefinition{}, err } - return s.getVariableDefinition(name, util.GetRangeForNode(node)) + return s.getVariableDefinition(name, node.Range()) } func (s *SymbolTable) GetVariableReferencesForNode(node *sitter.Node, content []byte) (ranges []sitter.Range, err error) { diff --git a/internal/lsp/symbol_table/variables_visitor.go b/internal/lsp/symbol_table/variables_visitor.go index bf761a3..6af8a6b 100644 --- a/internal/lsp/symbol_table/variables_visitor.go +++ b/internal/lsp/symbol_table/variables_visitor.go @@ -2,7 +2,6 @@ package symboltable import ( "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" - "github.com/mrjosh/helm-ls/internal/util" sitter "github.com/smacker/go-tree-sitter" ) @@ -115,7 +114,7 @@ func (v *VariablesVisitor) addVariableDefinition(variableType VariableType, defi } func (v *VariablesVisitor) addVariableUsage(node *sitter.Node) { - v.symbolTable.AddVariableUsage(node.Content(v.content), util.GetRangeForNode(node)) + v.symbolTable.AddVariableUsage(node.Content(v.content), node.Range()) } func (v *VariablesVisitor) EnterContextShift(_ *sitter.Node, _ string) {} diff --git a/internal/lsp/yaml_ast.go b/internal/lsp/yaml_ast.go index 2076fea..4bddac4 100644 --- a/internal/lsp/yaml_ast.go +++ b/internal/lsp/yaml_ast.go @@ -2,7 +2,6 @@ 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" ) @@ -12,7 +11,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, util.GetRangeForNode(child)) + textNodes = append(textNodes, child.Range()) } else { textNodes = append(textNodes, getTextNodeRanges(child)...) } diff --git a/internal/util/points.go b/internal/util/points.go index 3e11c9c..cdec6cc 100644 --- a/internal/util/points.go +++ b/internal/util/points.go @@ -23,12 +23,3 @@ func RangeToLocation(URI uri.URI, range_ sitter.Range) lsp.Location { }, } } - -func GetRangeForNode(node *sitter.Node) sitter.Range { - return sitter.Range{ - StartPoint: node.StartPoint(), - EndPoint: node.EndPoint(), - StartByte: node.StartByte(), - EndByte: node.EndByte(), - } -} From 7bf68f5b0c1db2d49ffff79b9fe85d2f1191e00e Mon Sep 17 00:00:00 2001 From: qvalentin Date: Sun, 15 Dec 2024 20:38:42 +0100 Subject: [PATCH 09/11] chore: rename variables --- internal/language_features/includes.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/language_features/includes.go b/internal/language_features/includes.go index 22fbac8..4df3270 100644 --- a/internal/language_features/includes.go +++ b/internal/language_features/includes.go @@ -102,9 +102,9 @@ func (f *IncludesFeature) getReferenceLocations(includeName string) []lsp.Locati func (f *IncludesFeature) getDefinitionLocations(includeName string) []lsp.Location { locations := []lsp.Location{} for _, doc := range f.GenericDocumentUseCase.DocumentStore.GetAllTemplateDocs() { - referenceRanges := doc.SymbolTable.GetIncludeDefinitions(includeName) - for _, referenceRange := range referenceRanges { - locations = append(locations, util.RangeToLocation(doc.URI, referenceRange)) + definitionRanges := doc.SymbolTable.GetIncludeDefinitions(includeName) + for _, definitionRange := range definitionRanges { + locations = append(locations, util.RangeToLocation(doc.URI, definitionRange)) } if len(locations) > 0 { charts.SyncToDisk(doc) @@ -117,9 +117,9 @@ func (f *IncludesFeature) getDefinitionLocations(includeName string) []lsp.Locat func (f *IncludesFeature) getDefinitionsHover(includeName string) protocol.HoverResultsWithFiles { result := protocol.HoverResultsWithFiles{} for _, doc := range f.GenericDocumentUseCase.DocumentStore.GetAllTemplateDocs() { - referenceRanges := doc.SymbolTable.GetIncludeDefinitions(includeName) - for _, referenceRange := range referenceRanges { - node := doc.Ast.RootNode().NamedDescendantForPointRange(referenceRange.StartPoint, referenceRange.EndPoint) + definitionRanges := doc.SymbolTable.GetIncludeDefinitions(includeName) + for _, definitionRange := range definitionRanges { + node := doc.Ast.RootNode().NamedDescendantForPointRange(definitionRange.StartPoint, definitionRange.EndPoint) if node != nil { result = append(result, protocol.HoverResultWithFile{ Value: node.Content([]byte(doc.Content)), From b51041525b9bac80f1136148a2cc2d02d5036448 Mon Sep 17 00:00:00 2001 From: qvalentin Date: Sun, 15 Dec 2024 20:57:42 +0100 Subject: [PATCH 10/11] chore: remove duplicates --- internal/language_features/generic_template_context.go | 4 +--- internal/language_features/includes.go | 8 ++------ internal/language_features/template_context.go | 4 +--- internal/language_features/variables.go | 4 +--- internal/util/points.go | 7 +++++++ internal/util/points_test.go | 0 6 files changed, 12 insertions(+), 15 deletions(-) create mode 100644 internal/util/points_test.go diff --git a/internal/language_features/generic_template_context.go b/internal/language_features/generic_template_context.go index 95ea543..66707ad 100644 --- a/internal/language_features/generic_template_context.go +++ b/internal/language_features/generic_template_context.go @@ -22,9 +22,7 @@ func (f *GenericTemplateContextFeature) getReferencesFromSymbolTable(templateCon for _, doc := range f.GenericDocumentUseCase.DocumentStore.GetAllTemplateDocs() { referenceRanges := doc.SymbolTable.GetTemplateContextRanges(templateContext) - for _, referenceRange := range referenceRanges { - locations = append(locations, util.RangeToLocation(doc.URI, referenceRange)) - } + locations = append(locations, util.RangesToLocations(doc.URI, referenceRanges)...) } return locations diff --git a/internal/language_features/includes.go b/internal/language_features/includes.go index 4df3270..afa0aab 100644 --- a/internal/language_features/includes.go +++ b/internal/language_features/includes.go @@ -88,9 +88,7 @@ func (f *IncludesFeature) getReferenceLocations(includeName string) []lsp.Locati locations := []lsp.Location{} for _, doc := range f.GenericDocumentUseCase.DocumentStore.GetAllTemplateDocs() { referenceRanges := doc.SymbolTable.GetIncludeReference(includeName) - for _, referenceRange := range referenceRanges { - locations = append(locations, util.RangeToLocation(doc.URI, referenceRange)) - } + locations = append(locations, util.RangesToLocations(doc.URI, referenceRanges)...) if len(locations) > 0 { charts.SyncToDisk(doc) } @@ -103,9 +101,7 @@ func (f *IncludesFeature) getDefinitionLocations(includeName string) []lsp.Locat locations := []lsp.Location{} for _, doc := range f.GenericDocumentUseCase.DocumentStore.GetAllTemplateDocs() { definitionRanges := doc.SymbolTable.GetIncludeDefinitions(includeName) - for _, definitionRange := range definitionRanges { - locations = append(locations, util.RangeToLocation(doc.URI, definitionRange)) - } + locations = append(locations, util.RangesToLocations(doc.URI, definitionRanges)...) if len(locations) > 0 { charts.SyncToDisk(doc) } diff --git a/internal/language_features/template_context.go b/internal/language_features/template_context.go index 78eddec..caff198 100644 --- a/internal/language_features/template_context.go +++ b/internal/language_features/template_context.go @@ -57,9 +57,7 @@ func (f *TemplateContextFeature) getReferenceLocations(templateContext symboltab locations := []lsp.Location{} for _, doc := range f.GenericDocumentUseCase.DocumentStore.GetAllTemplateDocs() { referenceRanges := doc.SymbolTable.GetTemplateContextRanges(templateContext) - for _, referenceRange := range referenceRanges { - locations = append(locations, util.RangeToLocation(doc.URI, referenceRange)) - } + locations = append(locations, util.RangesToLocations(doc.URI, referenceRanges)...) } return append(locations, f.getDefinitionLocations(templateContext)...) diff --git a/internal/language_features/variables.go b/internal/language_features/variables.go index ddacd27..f4b173e 100644 --- a/internal/language_features/variables.go +++ b/internal/language_features/variables.go @@ -37,9 +37,7 @@ func (f *VariablesFeature) References() (result []lsp.Location, err error) { return []lsp.Location{}, err } - for _, reference := range variableReferences { - result = append(result, util.RangeToLocation(f.Document.URI, reference)) - } + result = append(result, util.RangesToLocations(f.Document.URI, variableReferences)...) return result, nil } diff --git a/internal/util/points.go b/internal/util/points.go index cdec6cc..7d8e261 100644 --- a/internal/util/points.go +++ b/internal/util/points.go @@ -23,3 +23,10 @@ func RangeToLocation(URI uri.URI, range_ sitter.Range) lsp.Location { }, } } + +func RangesToLocations(URI uri.URI, ranges []sitter.Range) (locations []lsp.Location) { + for _, definitionRange := range ranges { + locations = append(locations, RangeToLocation(URI, definitionRange)) + } + return locations +} diff --git a/internal/util/points_test.go b/internal/util/points_test.go new file mode 100644 index 0000000..e69de29 From f41935036b3f0f8352b84fcd295fc2c24409f003 Mon Sep 17 00:00:00 2001 From: qvalentin Date: Sun, 15 Dec 2024 21:01:22 +0100 Subject: [PATCH 11/11] chore: remove duplicate function --- internal/language_features/template_context.go | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/internal/language_features/template_context.go b/internal/language_features/template_context.go index caff198..5708ccc 100644 --- a/internal/language_features/template_context.go +++ b/internal/language_features/template_context.go @@ -41,7 +41,7 @@ func (f *TemplateContextFeature) References() (result []lsp.Location, err error) return []lsp.Location{}, err } - locations := f.getReferenceLocations(templateContext) + locations := f.getReferencesFromSymbolTable(templateContext) return append(locations, f.getDefinitionLocations(templateContext)...), nil } @@ -53,16 +53,6 @@ func (f *TemplateContextFeature) Definition() (result []lsp.Location, err error) return f.getDefinitionLocations(templateContext), nil } -func (f *TemplateContextFeature) getReferenceLocations(templateContext symboltable.TemplateContext) []lsp.Location { - locations := []lsp.Location{} - for _, doc := range f.GenericDocumentUseCase.DocumentStore.GetAllTemplateDocs() { - referenceRanges := doc.SymbolTable.GetTemplateContextRanges(templateContext) - locations = append(locations, util.RangesToLocations(doc.URI, referenceRanges)...) - } - - return append(locations, f.getDefinitionLocations(templateContext)...) -} - func (f *TemplateContextFeature) getDefinitionLocations(templateContext symboltable.TemplateContext) []lsp.Location { locations := []lsp.Location{}