From 0eb7a04d0e707fee0e6ede59df6b7dfe3671cb46 Mon Sep 17 00:00:00 2001 From: qvalentin Date: Sun, 10 Mar 2024 17:27:47 +0100 Subject: [PATCH] refactor(lsp): use lsp server and client interfaces --- cmds/serve.go | 11 +- internal/adapter/yamlls/client.go | 57 +++ internal/adapter/yamlls/completion.go | 16 +- internal/adapter/yamlls/configuration.go | 22 +- internal/adapter/yamlls/diagnostics.go | 16 +- .../yamlls/diagnostics_integration_test.go | 8 +- internal/adapter/yamlls/handler.go | 25 -- internal/adapter/yamlls/hover.go | 31 +- internal/adapter/yamlls/initization.go | 20 +- internal/adapter/yamlls/yamlls.go | 25 +- internal/handler/completion.go | 39 +- internal/handler/configuration.go | 20 +- internal/handler/definition.go | 21 +- internal/handler/handler.go | 332 +++++++++++++----- internal/handler/hover.go | 31 +- internal/handler/initialization.go | 29 +- internal/handler/text.go | 48 --- internal/handler/text_document.go | 102 ++++++ internal/handler/watched_files.go | 14 +- internal/lsp/document.go | 3 +- internal/lsp/document_test.go | 2 +- internal/lsp/lint.go | 14 +- internal/util/lsp.go | 4 +- main.go | 4 +- 24 files changed, 528 insertions(+), 366 deletions(-) create mode 100644 internal/adapter/yamlls/client.go create mode 100644 internal/handler/text_document.go diff --git a/cmds/serve.go b/cmds/serve.go index 3a756d6e..3629af0c 100644 --- a/cmds/serve.go +++ b/cmds/serve.go @@ -1,25 +1,18 @@ package cmds import ( - "context" "os" "github.com/mrjosh/helm-ls/internal/handler" "github.com/spf13/cobra" - "go.lsp.dev/jsonrpc2" ) func newServeCmd() *cobra.Command { cmd := &cobra.Command{ Use: "serve", Short: "Start helm lint language server", - RunE: func(cmd *cobra.Command, args []string) error { - - conn := jsonrpc2.NewConn(jsonrpc2.NewStream(stdrwc{})) - handler := handler.NewHandler(conn) - handlerSrv := jsonrpc2.HandlerServer(handler) - - return handlerSrv.ServeStream(context.Background(), conn) + Run: func(cmd *cobra.Command, args []string) { + handler.StartHandler(stdrwc{}) }, } diff --git a/internal/adapter/yamlls/client.go b/internal/adapter/yamlls/client.go new file mode 100644 index 00000000..529ca7ad --- /dev/null +++ b/internal/adapter/yamlls/client.go @@ -0,0 +1,57 @@ +package yamlls + +import ( + "context" + + "go.lsp.dev/protocol" +) + +// ApplyEdit implements protocol.Client. +func (y Connector) ApplyEdit(ctx context.Context, params *protocol.ApplyWorkspaceEditParams) (result bool, err error) { + return true, nil +} + +// LogMessage implements protocol.Client. +func (y Connector) LogMessage(ctx context.Context, params *protocol.LogMessageParams) (err error) { + return nil +} + +// Progress implements protocol.Client. +func (y Connector) Progress(ctx context.Context, params *protocol.ProgressParams) (err error) { + return nil +} + +// RegisterCapability implements protocol.Client. +func (y Connector) RegisterCapability(ctx context.Context, params *protocol.RegistrationParams) (err error) { + return nil +} + +// ShowMessage implements protocol.Client. +func (y Connector) ShowMessage(ctx context.Context, params *protocol.ShowMessageParams) (err error) { + return y.client.ShowMessage(ctx, params) +} + +// ShowMessageRequest implements protocol.Client. +func (y Connector) ShowMessageRequest(ctx context.Context, params *protocol.ShowMessageRequestParams) (result *protocol.MessageActionItem, err error) { + return nil, nil +} + +// Telemetry implements protocol.Client. +func (y Connector) Telemetry(ctx context.Context, params interface{}) (err error) { + return nil +} + +// UnregisterCapability implements protocol.Client. +func (y Connector) UnregisterCapability(ctx context.Context, params *protocol.UnregistrationParams) (err error) { + return nil +} + +// WorkDoneProgressCreate implements protocol.Client. +func (y Connector) WorkDoneProgressCreate(ctx context.Context, params *protocol.WorkDoneProgressCreateParams) (err error) { + return nil +} + +// WorkspaceFolders implements protocol.Client. +func (y Connector) WorkspaceFolders(ctx context.Context) (result []protocol.WorkspaceFolder, err error) { + return nil, nil +} diff --git a/internal/adapter/yamlls/completion.go b/internal/adapter/yamlls/completion.go index 8e73eb9a..71f9d063 100644 --- a/internal/adapter/yamlls/completion.go +++ b/internal/adapter/yamlls/completion.go @@ -2,24 +2,14 @@ package yamlls import ( "context" - "reflect" lsp "go.lsp.dev/protocol" ) -func (yamllsConnector Connector) CallCompletion(ctx context.Context, params lsp.CompletionParams) *lsp.CompletionList { +func (yamllsConnector Connector) CallCompletion(ctx context.Context, params *lsp.CompletionParams) (*lsp.CompletionList, error) { if yamllsConnector.Conn == nil { - return &lsp.CompletionList{} + return &lsp.CompletionList{}, nil } - logger.Println("Calling yamlls for completions") - var response = reflect.New(reflect.TypeOf(lsp.CompletionList{})).Interface() - _, err := (*yamllsConnector.Conn).Call(ctx, lsp.MethodTextDocumentCompletion, params, response) - if err != nil { - logger.Error("Error Calling yamlls for completions", err) - return &lsp.CompletionList{} - } - - logger.Debug("Got completions from yamlls", response) - return response.(*lsp.CompletionList) + return yamllsConnector.server.Completion(ctx, params) } diff --git a/internal/adapter/yamlls/configuration.go b/internal/adapter/yamlls/configuration.go index 94e4f4b9..607bc7ed 100644 --- a/internal/adapter/yamlls/configuration.go +++ b/internal/adapter/yamlls/configuration.go @@ -1,18 +1,18 @@ package yamlls import ( - "encoding/json" + "context" - "go.lsp.dev/jsonrpc2" - lsp "go.lsp.dev/protocol" + "go.lsp.dev/protocol" ) -func (yamllsConnector Connector) handleConfiguration(req jsonrpc2.Request) []interface{} { - var params lsp.ConfigurationParams - if err := json.Unmarshal(req.Params(), ¶ms); err != nil { - logger.Error("Error parsing configuration request from yamlls", err) - } - logger.Debug("Yamlls ConfigurationParams", params) - settings := []interface{}{yamllsConnector.config.YamllsSettings} - return settings +// Configuration implements protocol.Client. +func (y Connector) Configuration(ctx context.Context, params *protocol.ConfigurationParams) (result []interface{}, err error) { + settings := []interface{}{y.config.YamllsSettings} + return settings, nil +} + +func (y Connector) DidChangeConfiguration() (err error) { + ctx := context.Background() + return y.server.DidChangeConfiguration(ctx, &protocol.DidChangeConfigurationParams{}) } diff --git a/internal/adapter/yamlls/diagnostics.go b/internal/adapter/yamlls/diagnostics.go index 01bb695e..7b92f6f6 100644 --- a/internal/adapter/yamlls/diagnostics.go +++ b/internal/adapter/yamlls/diagnostics.go @@ -2,21 +2,15 @@ package yamlls import ( "context" - "encoding/json" lsplocal "github.com/mrjosh/helm-ls/internal/lsp" sitter "github.com/smacker/go-tree-sitter" - "go.lsp.dev/jsonrpc2" + "go.lsp.dev/protocol" lsp "go.lsp.dev/protocol" ) -func (yamllsConnector *Connector) handleDiagnostics(req jsonrpc2.Request, clientConn jsonrpc2.Conn, documents *lsplocal.DocumentStore) { - var params lsp.PublishDiagnosticsParams - if err := json.Unmarshal(req.Params(), ¶ms); err != nil { - logger.Println("Error handling diagnostic", err) - } - - doc, ok := documents.Get(params.URI) +func (c Connector) PublishDiagnostics(ctx context.Context, params *protocol.PublishDiagnosticsParams) (err error) { + doc, ok := c.documents.Get(params.URI) if !ok { logger.Println("Error handling diagnostic. Could not get document: " + params.URI.Filename()) } @@ -25,11 +19,13 @@ func (yamllsConnector *Connector) handleDiagnostics(req jsonrpc2.Request, client if doc.DiagnosticsCache.ShouldShowDiagnosticsOnNewYamlDiagnostics() { logger.Debug("Publishing yamlls diagnostics") params.Diagnostics = doc.DiagnosticsCache.GetMergedDiagnostics() - err := clientConn.Notify(context.Background(), lsp.MethodTextDocumentPublishDiagnostics, ¶ms) + err := c.client.PublishDiagnostics(ctx, params) if err != nil { logger.Println("Error calling yamlls for diagnostics", err) } } + + return nil } func filterDiagnostics(diagnostics []lsp.Diagnostic, ast *sitter.Tree, content string) (filtered []lsp.Diagnostic) { diff --git a/internal/adapter/yamlls/diagnostics_integration_test.go b/internal/adapter/yamlls/diagnostics_integration_test.go index 3426d82e..6a6a6310 100644 --- a/internal/adapter/yamlls/diagnostics_integration_test.go +++ b/internal/adapter/yamlls/diagnostics_integration_test.go @@ -15,8 +15,10 @@ import ( "github.com/mrjosh/helm-ls/internal/util" "github.com/stretchr/testify/assert" "go.lsp.dev/jsonrpc2" + "go.lsp.dev/protocol" lsp "go.lsp.dev/protocol" "go.lsp.dev/uri" + "go.uber.org/zap" ) // must be relative to this file @@ -90,7 +92,7 @@ func sendTestFilesToYamlls(documents *lsplocal.DocumentStore, yamllsConnector *C for { select { case d := <-filesChan: - documents.DidOpen(d, util.DefaultConfig) + documents.DidOpen(&d, util.DefaultConfig) tree := lsplocal.ParseAst(nil, d.TextDocument.Text) yamllsConnector.DocumentDidOpen(tree, d) ownCount++ @@ -112,6 +114,8 @@ func TestYamllsDiagnosticsIntegration(t *testing.T) { dir := t.TempDir() documents := lsplocal.NewDocumentStore() con := jsonrpc2.NewConn(jsonrpc2.NewStream(readWriteCloseMock{diagnosticsChan})) + zapLogger, _ := zap.NewProduction() + client := protocol.ClientDispatcher(con, zapLogger) config := util.DefaultConfig.YamllsConfiguration yamllsSettings := util.DefaultYamllsSettings @@ -122,7 +126,7 @@ func TestYamllsDiagnosticsIntegration(t *testing.T) { Enable: false, } config.YamllsSettings = yamllsSettings - yamllsConnector := NewConnector(config, con, documents) + yamllsConnector := NewConnector(config, client, documents) if yamllsConnector.Conn == nil { t.Fatal("Could not connect to yaml-language-server") diff --git a/internal/adapter/yamlls/handler.go b/internal/adapter/yamlls/handler.go index 05416f5c..64de88e5 100644 --- a/internal/adapter/yamlls/handler.go +++ b/internal/adapter/yamlls/handler.go @@ -1,26 +1 @@ package yamlls - -import ( - "context" - - lsplocal "github.com/mrjosh/helm-ls/internal/lsp" - "go.lsp.dev/jsonrpc2" - lsp "go.lsp.dev/protocol" -) - -func (yamllsConnector *Connector) yamllsHandler(clientConn jsonrpc2.Conn, documents *lsplocal.DocumentStore) jsonrpc2.Handler { - return func(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error { - - switch req.Method() { - case lsp.MethodTextDocumentPublishDiagnostics: - yamllsConnector.handleDiagnostics(req, clientConn, documents) - case lsp.MethodWorkspaceConfiguration: - settings := yamllsConnector.handleConfiguration(req) - return reply(ctx, settings, nil) - default: - logger.Debug("Method not handled by yamlls handler: ", req.Method()) - } - - return reply(ctx, true, nil) - } -} diff --git a/internal/adapter/yamlls/hover.go b/internal/adapter/yamlls/hover.go index 41627990..1dc3520d 100644 --- a/internal/adapter/yamlls/hover.go +++ b/internal/adapter/yamlls/hover.go @@ -2,7 +2,6 @@ package yamlls import ( "context" - "reflect" "github.com/mrjosh/helm-ls/internal/util" lsp "go.lsp.dev/protocol" @@ -15,45 +14,33 @@ func (yamllsConnector Connector) CallHover(ctx context.Context, params lsp.Hover return &lsp.Hover{}, nil } - hoverResponse, err := (yamllsConnector).getHoverFromHover(ctx, params) + hoverResponse, err := yamllsConnector.server.Hover(ctx, ¶ms) if err != nil { return hoverResponse, err } - if hoverResponse.Contents.Value != "" { + if hoverResponse != nil && hoverResponse.Contents.Value != "" { return hoverResponse, nil } return (yamllsConnector).getHoverFromCompletion(ctx, params, word) } -func (yamllsConnector Connector) getHoverFromHover(ctx context.Context, params lsp.HoverParams) (*lsp.Hover, error) { - - var hoverResponse = reflect.New(reflect.TypeOf(lsp.Hover{})).Interface() - _, err := (*yamllsConnector.Conn).Call(ctx, lsp.MethodTextDocumentHover, params, hoverResponse) - if err != nil { - logger.Error("Error calling yamlls for hover", err) - return &lsp.Hover{}, err - } - logger.Debug("Got hover from yamlls", hoverResponse.(*lsp.Hover).Contents.Value) - return hoverResponse.(*lsp.Hover), nil -} - func (yamllsConnector Connector) getHoverFromCompletion(ctx context.Context, params lsp.HoverParams, word string) (*lsp.Hover, error) { var ( - err error - documentation string - completionResponse = reflect.New(reflect.TypeOf(lsp.CompletionList{})).Interface() - completionParams = lsp.CompletionParams{ + err error + documentation string + completionParams = lsp.CompletionParams{ TextDocumentPositionParams: params.TextDocumentPositionParams, } ) - _, err = (*yamllsConnector.Conn).Call(ctx, lsp.MethodTextDocumentCompletion, completionParams, completionResponse) + + completionList, err := yamllsConnector.server.Completion(ctx, &completionParams) if err != nil { logger.Error("Error calling yamlls for Completion", err) return &lsp.Hover{}, err } - for _, completionItem := range completionResponse.(*lsp.CompletionList).Items { + for _, completionItem := range completionList.Items { if completionItem.InsertText == word { documentation = completionItem.Documentation.(string) break @@ -61,5 +48,5 @@ func (yamllsConnector Connector) getHoverFromCompletion(ctx context.Context, par } response := util.BuildHoverResponse(documentation, lsp.Range{}) - return &response, nil + return response, nil } diff --git a/internal/adapter/yamlls/initization.go b/internal/adapter/yamlls/initization.go index 7d1a3ac6..ff45772f 100644 --- a/internal/adapter/yamlls/initization.go +++ b/internal/adapter/yamlls/initization.go @@ -8,7 +8,7 @@ import ( "go.lsp.dev/uri" ) -func (yamllsConnector Connector) CallInitialize(workspaceURI uri.URI) { +func (yamllsConnector Connector) CallInitialize(workspaceURI uri.URI) (result *lsp.InitializeResult, err error) { if yamllsConnector.Conn == nil { return } @@ -21,21 +21,5 @@ func (yamllsConnector Connector) CallInitialize(workspaceURI uri.URI) { }, } - var response interface{} - _, err := (*yamllsConnector.Conn).Call(context.Background(), lsp.MethodInitialize, params, response) - if err != nil { - logger.Error("Error calling yamlls for initialize", err) - return - } - err = (*yamllsConnector.Conn).Notify(context.Background(), lsp.MethodInitialized, params) - - if err != nil { - logger.Error("Error calling yamlls for initialized", err) - } - - changeConfigurationParams := lsp.DidChangeConfigurationParams{} - err = (*yamllsConnector.Conn).Notify(context.Background(), lsp.MethodWorkspaceDidChangeConfiguration, changeConfigurationParams) - if err != nil { - logger.Error("Error calling yamlls for didChangeConfiguration", err) - } + return yamllsConnector.server.Initialize(context.Background(), ¶ms) } diff --git a/internal/adapter/yamlls/yamlls.go b/internal/adapter/yamlls/yamlls.go index 3b6a6ed5..8025ae2d 100644 --- a/internal/adapter/yamlls/yamlls.go +++ b/internal/adapter/yamlls/yamlls.go @@ -8,17 +8,22 @@ import ( lsplocal "github.com/mrjosh/helm-ls/internal/lsp" "github.com/mrjosh/helm-ls/internal/util" "go.lsp.dev/jsonrpc2" + "go.lsp.dev/protocol" + "go.uber.org/zap" ) var logger = log.GetLogger() type Connector struct { - Conn *jsonrpc2.Conn - config util.YamllsConfiguration + Conn *jsonrpc2.Conn + config util.YamllsConfiguration + server protocol.Server + documents *lsplocal.DocumentStore + client protocol.Client } -func NewConnector(yamllsConfiguration util.YamllsConfiguration, clientConn jsonrpc2.Conn, documents *lsplocal.DocumentStore) *Connector { - yamllsCmd := exec.Command(yamllsConfiguration.Path, "--stdio") +func NewConnector(yamllsConfiguration util.YamllsConfiguration, client protocol.Client, documents *lsplocal.DocumentStore) *Connector { + yamllsCmd := exec.Command("yamlls-debug.sh", "--stdio") stdin, err := yamllsCmd.StdinPipe() if err != nil { @@ -50,10 +55,14 @@ func NewConnector(yamllsConfiguration util.YamllsConfiguration, clientConn jsonr return &Connector{} } } - var yamllsConnector = Connector{} - conn := jsonrpc2.NewConn(jsonrpc2.NewStream(readWriteCloser)) - yamllsConnector.config = yamllsConfiguration - conn.Go(context.Background(), yamllsConnector.yamllsHandler(clientConn, documents)) + yamllsConnector := Connector{documents: documents, config: yamllsConfiguration, client: client} + + ctx := context.Background() + zapLogger, _ := zap.NewProduction() + ctx, conn, server := protocol.NewClient(ctx, yamllsConnector, jsonrpc2.NewStream(readWriteCloser), zapLogger) + + // conn.Go(context.Background(), yamllsConnector.yamllsHandler(clientConn, documents)) yamllsConnector.Conn = &conn + yamllsConnector.server = server return &yamllsConnector } diff --git a/internal/handler/completion.go b/internal/handler/completion.go index 4fccc320..2838a32e 100644 --- a/internal/handler/completion.go +++ b/internal/handler/completion.go @@ -2,7 +2,6 @@ package handler import ( "context" - "encoding/json" "errors" "fmt" "reflect" @@ -13,7 +12,7 @@ import ( gotemplate "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" "github.com/mrjosh/helm-ls/pkg/chartutil" sitter "github.com/smacker/go-tree-sitter" - "go.lsp.dev/jsonrpc2" + "go.lsp.dev/protocol" lsp "go.lsp.dev/protocol" yaml "gopkg.in/yaml.v2" @@ -33,19 +32,10 @@ func init() { textCompletionsItems = append(textCompletionsItems, getTextCompletionItems(godocs.TextSnippets)...) } -func (h *langHandler) handleTextDocumentCompletion(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) (err error) { - if req.Params() == nil { - return &jsonrpc2.Error{Code: jsonrpc2.InvalidParams} - } - - var params lsp.CompletionParams - if err := json.Unmarshal(req.Params(), ¶ms); err != nil { - return err - } - +func (h *langHandler) Completion(ctx context.Context, params *lsp.CompletionParams) (result *lsp.CompletionList, err error) { doc, ok := h.documents.Get(params.TextDocument.URI) if !ok { - return errors.New("Could not get document: " + params.TextDocument.URI.Filename()) + return nil, errors.New("Could not get document: " + params.TextDocument.URI.Filename()) } chart, err := h.chartStore.GetChartForDoc(params.TextDocument.URI) if err != nil { @@ -58,7 +48,7 @@ func (h *langHandler) handleTextDocumentCompletion(ctx context.Context, reply js result := make([]lsp.CompletionItem, 0) result = append(result, textCompletionsItems...) result = append(result, yamllsCompletions(ctx, h, params)...) - return reply(ctx, result, err) + return &protocol.CompletionList{IsIncomplete: false, Items: result}, err } var ( @@ -78,8 +68,17 @@ func (h *langHandler) handleTextDocumentCompletion(ctx context.Context, reply js logger.Println(fmt.Sprintf("Word found for completions is < %s >", word)) + items = make([]lsp.CompletionItem, 0) + for _, v := range basicItems { + items = append(items, lsp.CompletionItem{ + Label: v.Name, + InsertText: v.Name, + Detail: v.Detail, + Documentation: v.Doc, + }) + } if len(variableSplitted) == 0 { - return reply(ctx, basicItems, err) + return &lsp.CompletionList{IsIncomplete: false, Items: items}, err } // $ always points to the root context so we can safely remove it @@ -104,11 +103,15 @@ func (h *langHandler) handleTextDocumentCompletion(ctx context.Context, reply js items = append(items, functionsCompletionItems...) } - return reply(ctx, items, err) + return &lsp.CompletionList{IsIncomplete: false, Items: items}, err } -func yamllsCompletions(ctx context.Context, h *langHandler, params lsp.CompletionParams) []lsp.CompletionItem { - response := *h.yamllsConnector.CallCompletion(ctx, params) +func yamllsCompletions(ctx context.Context, h *langHandler, params *lsp.CompletionParams) []lsp.CompletionItem { + response, err := h.yamllsConnector.CallCompletion(ctx, params) + if err != nil { + logger.Error("Error getting yamlls completions", err) + return []lsp.CompletionItem{} + } logger.Debug("Got completions from yamlls", response) return response.Items } diff --git a/internal/handler/configuration.go b/internal/handler/configuration.go index d5b088eb..599576bf 100644 --- a/internal/handler/configuration.go +++ b/internal/handler/configuration.go @@ -5,28 +5,29 @@ import ( // "reflect" "github.com/mrjosh/helm-ls/internal/util" - "go.lsp.dev/jsonrpc2" lsp "go.lsp.dev/protocol" ) -func (h *langHandler) handleWorkspaceDidChangeConfiguration(ctx context.Context, reply jsonrpc2.Replier, _ jsonrpc2.Request) (err error) { +func (h *langHandler) DidChangeConfiguration(ctx context.Context, params *lsp.DidChangeConfigurationParams) (err error) { // go h.retrieveWorkspaceConfiguration(ctx) logger.Println("Changing workspace config is not implemented") - return reply(ctx, nil, nil) + return nil } -func (h *langHandler) retrieveWorkspaceConfiguration(ctx context.Context) { +func (h *langHandler) RetrieveWorkspaceConfiguration(ctx context.Context) { logger.Println("Calling workspace/configuration") result := []util.HelmlsConfiguration{util.DefaultConfig} - _, err := h.connPool.Call(ctx, lsp.MethodWorkspaceConfiguration, lsp.ConfigurationParams{ + configurationParams := lsp.ConfigurationParams{ Items: []lsp.ConfigurationItem{{Section: "helm-ls"}}, - }, &result) + } + + rawResult, err := h.client.Configuration(ctx, &configurationParams) if err != nil { logger.Println("Error calling workspace/configuration", err) } else { - logger.Println("Workspace configuration:", result) + logger.Println("Workspace configuration:", rawResult) } if len(result) == 0 { @@ -34,6 +35,9 @@ func (h *langHandler) retrieveWorkspaceConfiguration(ctx context.Context) { return } - h.helmlsConfig = result[0] + // TODO: use retrieved config + h.helmlsConfig = util.DefaultConfig + + logger.Println("Workspace configuration:", h.helmlsConfig) h.initializationWithConfig() } diff --git a/internal/handler/definition.go b/internal/handler/definition.go index 00c42901..408b26af 100644 --- a/internal/handler/definition.go +++ b/internal/handler/definition.go @@ -2,7 +2,6 @@ package handler import ( "context" - "encoding/json" "errors" "fmt" "strings" @@ -12,39 +11,29 @@ import ( gotemplate "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" "github.com/mrjosh/helm-ls/internal/util" sitter "github.com/smacker/go-tree-sitter" - "go.lsp.dev/jsonrpc2" lsp "go.lsp.dev/protocol" "gopkg.in/yaml.v3" ) -func (h *langHandler) handleDefinition(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) (err error) { - if req.Params() == nil { - return &jsonrpc2.Error{Code: jsonrpc2.InvalidParams} - } - - var params lsp.DefinitionParams - if err := json.Unmarshal(req.Params(), ¶ms); err != nil { - return err - } - +func (h *langHandler) Definition(ctx context.Context, params *lsp.DefinitionParams) (result []lsp.Location, err error) { doc, ok := h.documents.Get(params.TextDocument.URI) if !ok { - return errors.New("Could not get document: " + params.TextDocument.URI.Filename()) + return nil, errors.New("Could not get document: " + params.TextDocument.URI.Filename()) } chart, err := h.chartStore.GetChartForDoc(params.TextDocument.URI) if err != nil { logger.Error("Error getting chart info for file", params.TextDocument.URI, err) } - result, err := h.definitionAstParsing(chart, doc, params.Position) + result, err = h.definitionAstParsing(chart, doc, params.Position) if err != nil { // suppress errors for clients // otherwise using go-to-definition on words that have no definition // will result in an error logger.Println("Error getting definitions", err) - return reply(ctx, nil, nil) + return nil, nil } - return reply(ctx, result, err) + return result, nil } func (h *langHandler) definitionAstParsing(chart *charts.Chart, doc *lsplocal.Document, position lsp.Position) ([]lsp.Location, error) { diff --git a/internal/handler/handler.go b/internal/handler/handler.go index 983b5a12..4898aa97 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -2,15 +2,16 @@ package handler import ( "context" - "encoding/json" - "errors" + "io" "github.com/mrjosh/helm-ls/internal/adapter/yamlls" "github.com/mrjosh/helm-ls/internal/charts" lsplocal "github.com/mrjosh/helm-ls/internal/lsp" "github.com/mrjosh/helm-ls/internal/util" "go.lsp.dev/jsonrpc2" + "go.lsp.dev/protocol" lsp "go.lsp.dev/protocol" + "go.uber.org/zap" "github.com/mrjosh/helm-ls/internal/log" ) @@ -18,6 +19,7 @@ import ( var logger = log.GetLogger() type langHandler struct { + client protocol.Client connPool jsonrpc2.Conn linterName string documents *lsplocal.DocumentStore @@ -26,9 +28,25 @@ type langHandler struct { helmlsConfig util.HelmlsConfiguration } -func NewHandler(connPool jsonrpc2.Conn) jsonrpc2.Handler { +func StartHandler(stream io.ReadWriteCloser) { + logger, _ := zap.NewProduction() + + server := newHandler(nil, nil) + _, conn, client := protocol.NewServer(context.Background(), + server, + jsonrpc2.NewStream(stream), + logger, + ) + server.connPool = conn + server.client = client + + <-conn.Done() +} + +func newHandler(connPool jsonrpc2.Conn, client protocol.Client) *langHandler { documents := lsplocal.NewDocumentStore() handler := &langHandler{ + client: client, linterName: "helm-lint", connPool: connPool, documents: documents, @@ -36,108 +54,236 @@ func NewHandler(connPool jsonrpc2.Conn) jsonrpc2.Handler { yamllsConnector: &yamlls.Connector{}, } logger.Printf("helm-lint-langserver: connections opened") - return jsonrpc2.ReplyHandler(handler.handle) -} - -func (h *langHandler) handle(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error { - logger.Debug("helm-lint-langserver: request method:", req.Method()) - - switch req.Method() { - case lsp.MethodInitialize: - return h.handleInitialize(ctx, reply, req) - case lsp.MethodInitialized: - go h.retrieveWorkspaceConfiguration(ctx) - return reply(ctx, nil, nil) - case lsp.MethodShutdown: - return h.handleShutdown(ctx, reply, req) - case lsp.MethodTextDocumentDidOpen: - return h.handleTextDocumentDidOpen(ctx, reply, req) - case lsp.MethodTextDocumentDidClose: - return h.handleTextDocumentDidClose(ctx, reply, req) - case lsp.MethodTextDocumentDidChange: - return h.handleTextDocumentDidChange(ctx, reply, req) - case lsp.MethodTextDocumentDidSave: - return h.handleTextDocumentDidSave(ctx, reply, req) - case lsp.MethodTextDocumentCompletion: - return h.handleTextDocumentCompletion(ctx, reply, req) - case lsp.MethodTextDocumentDefinition: - return h.handleDefinition(ctx, reply, req) - case lsp.MethodTextDocumentHover: - return h.handleHover(ctx, reply, req) - case lsp.MethodWorkspaceDidChangeConfiguration: - return h.handleWorkspaceDidChangeConfiguration(ctx, reply, req) - case lsp.MethodWorkspaceDidChangeWatchedFiles: - return h.handleDidChangeWatchedFiles(ctx, reply, req) - default: - logger.Debug("Unsupported method", req.Method()) - } - return jsonrpc2.MethodNotFoundHandler(ctx, reply, req) + return handler +} + +// CodeAction implements protocol.Server. +func (h *langHandler) CodeAction(ctx context.Context, params *lsp.CodeActionParams) (result []lsp.CodeAction, err error) { + panic("unimplemented") +} + +// CodeLens implements protocol.Server. +func (h *langHandler) CodeLens(ctx context.Context, params *lsp.CodeLensParams) (result []lsp.CodeLens, err error) { + panic("unimplemented") +} + +// CodeLensRefresh implements protocol.Server. +func (h *langHandler) CodeLensRefresh(ctx context.Context) (err error) { + panic("unimplemented") +} + +// CodeLensResolve implements protocol.Server. +func (h *langHandler) CodeLensResolve(ctx context.Context, params *lsp.CodeLens) (result *lsp.CodeLens, err error) { + panic("unimplemented") +} + +// ColorPresentation implements protocol.Server. +func (h *langHandler) ColorPresentation(ctx context.Context, params *lsp.ColorPresentationParams) (result []lsp.ColorPresentation, err error) { + panic("unimplemented") +} + +// CompletionResolve implements protocol.Server. +func (h *langHandler) CompletionResolve(ctx context.Context, params *lsp.CompletionItem) (result *lsp.CompletionItem, err error) { + panic("unimplemented") +} + +// Declaration implements protocol.Server. +func (h *langHandler) Declaration(ctx context.Context, params *lsp.DeclarationParams) (result []lsp.Location, err error) { + panic("unimplemented") +} + +// DidChangeWorkspaceFolders implements protocol.Server. +func (h *langHandler) DidChangeWorkspaceFolders(ctx context.Context, params *lsp.DidChangeWorkspaceFoldersParams) (err error) { + panic("unimplemented") +} + +// DocumentColor implements protocol.Server. +func (h *langHandler) DocumentColor(ctx context.Context, params *lsp.DocumentColorParams) (result []lsp.ColorInformation, err error) { + panic("unimplemented") +} + +// DocumentHighlight implements protocol.Server. +func (h *langHandler) DocumentHighlight(ctx context.Context, params *lsp.DocumentHighlightParams) (result []lsp.DocumentHighlight, err error) { + panic("unimplemented") +} + +// DocumentLink implements protocol.Server. +func (h *langHandler) DocumentLink(ctx context.Context, params *lsp.DocumentLinkParams) (result []lsp.DocumentLink, err error) { + panic("unimplemented") +} + +// DocumentLinkResolve implements protocol.Server. +func (h *langHandler) DocumentLinkResolve(ctx context.Context, params *lsp.DocumentLink) (result *lsp.DocumentLink, err error) { + panic("unimplemented") +} + +// DocumentSymbol implements protocol.Server. +func (h *langHandler) DocumentSymbol(ctx context.Context, params *lsp.DocumentSymbolParams) (result []interface{}, err error) { + panic("unimplemented") +} + +// ExecuteCommand implements protocol.Server. +func (h *langHandler) ExecuteCommand(ctx context.Context, params *lsp.ExecuteCommandParams) (result interface{}, err error) { + panic("unimplemented") +} + +// Exit implements protocol.Server. +func (h *langHandler) Exit(ctx context.Context) (err error) { + panic("unimplemented") +} + +// FoldingRanges implements protocol.Server. +func (h *langHandler) FoldingRanges(ctx context.Context, params *lsp.FoldingRangeParams) (result []lsp.FoldingRange, err error) { + panic("unimplemented") +} + +// Formatting implements protocol.Server. +func (h *langHandler) Formatting(ctx context.Context, params *lsp.DocumentFormattingParams) (result []lsp.TextEdit, err error) { + panic("unimplemented") +} + +// Implementation implements protocol.Server. +func (h *langHandler) Implementation(ctx context.Context, params *lsp.ImplementationParams) (result []lsp.Location, err error) { + panic("unimplemented") +} + +// IncomingCalls implements protocol.Server. +func (h *langHandler) IncomingCalls(ctx context.Context, params *lsp.CallHierarchyIncomingCallsParams) (result []lsp.CallHierarchyIncomingCall, err error) { + panic("unimplemented") +} + +// LinkedEditingRange implements protocol.Server. +func (h *langHandler) LinkedEditingRange(ctx context.Context, params *lsp.LinkedEditingRangeParams) (result *lsp.LinkedEditingRanges, err error) { + panic("unimplemented") +} + +// LogTrace implements protocol.Server. +func (h *langHandler) LogTrace(ctx context.Context, params *lsp.LogTraceParams) (err error) { + panic("unimplemented") } -func (h *langHandler) handleShutdown(_ context.Context, _ jsonrpc2.Replier, _ jsonrpc2.Request) (err error) { +// Moniker implements protocol.Server. +func (h *langHandler) Moniker(ctx context.Context, params *lsp.MonikerParams) (result []lsp.Moniker, err error) { + panic("unimplemented") +} + +// OnTypeFormatting implements protocol.Server. +func (h *langHandler) OnTypeFormatting(ctx context.Context, params *lsp.DocumentOnTypeFormattingParams) (result []lsp.TextEdit, err error) { + panic("unimplemented") +} + +// OutgoingCalls implements protocol.Server. +func (h *langHandler) OutgoingCalls(ctx context.Context, params *lsp.CallHierarchyOutgoingCallsParams) (result []lsp.CallHierarchyOutgoingCall, err error) { + panic("unimplemented") +} + +// PrepareCallHierarchy implements protocol.Server. +func (h *langHandler) PrepareCallHierarchy(ctx context.Context, params *lsp.CallHierarchyPrepareParams) (result []lsp.CallHierarchyItem, err error) { + panic("unimplemented") +} + +// PrepareRename implements protocol.Server. +func (h *langHandler) PrepareRename(ctx context.Context, params *lsp.PrepareRenameParams) (result *lsp.Range, err error) { + panic("unimplemented") +} + +// RangeFormatting implements protocol.Server. +func (h *langHandler) RangeFormatting(ctx context.Context, params *lsp.DocumentRangeFormattingParams) (result []lsp.TextEdit, err error) { + panic("unimplemented") +} + +// References implements protocol.Server. +func (h *langHandler) References(ctx context.Context, params *lsp.ReferenceParams) (result []lsp.Location, err error) { + panic("unimplemented") +} + +// Rename implements protocol.Server. +func (h *langHandler) Rename(ctx context.Context, params *lsp.RenameParams) (result *lsp.WorkspaceEdit, err error) { + panic("unimplemented") +} + +// Request implements protocol.Server. +func (h *langHandler) Request(ctx context.Context, method string, params interface{}) (result interface{}, err error) { + panic("unimplemented") +} + +// SemanticTokensFull implements protocol.Server. +func (h *langHandler) SemanticTokensFull(ctx context.Context, params *lsp.SemanticTokensParams) (result *lsp.SemanticTokens, err error) { + panic("unimplemented") +} + +// SemanticTokensFullDelta implements protocol.Server. +func (h *langHandler) SemanticTokensFullDelta(ctx context.Context, params *lsp.SemanticTokensDeltaParams) (result interface{}, err error) { + panic("unimplemented") +} + +// SemanticTokensRange implements protocol.Server. +func (h *langHandler) SemanticTokensRange(ctx context.Context, params *lsp.SemanticTokensRangeParams) (result *lsp.SemanticTokens, err error) { + panic("unimplemented") +} + +// SemanticTokensRefresh implements protocol.Server. +func (h *langHandler) SemanticTokensRefresh(ctx context.Context) (err error) { + panic("unimplemented") +} + +// SetTrace implements protocol.Server. +func (h *langHandler) SetTrace(ctx context.Context, params *lsp.SetTraceParams) (err error) { + panic("unimplemented") +} + +// ShowDocument implements protocol.Server. +func (h *langHandler) ShowDocument(ctx context.Context, params *lsp.ShowDocumentParams) (result *lsp.ShowDocumentResult, err error) { + panic("unimplemented") +} + +// Shutdown implements protocol.Server. +func (h *langHandler) Shutdown(ctx context.Context) (err error) { return h.connPool.Close() } -func (h *langHandler) handleTextDocumentDidOpen(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) (err error) { - var params lsp.DidOpenTextDocumentParams - if err := json.Unmarshal(req.Params(), ¶ms); err != nil { - return reply(ctx, nil, err) - } +// SignatureHelp implements protocol.Server. +func (h *langHandler) SignatureHelp(ctx context.Context, params *lsp.SignatureHelpParams) (result *lsp.SignatureHelp, err error) { + panic("unimplemented") +} - doc, err := h.documents.DidOpen(params, h.helmlsConfig) - if err != nil { - logger.Println(err) - return reply(ctx, nil, err) - } +// Symbols implements protocol.Server. +func (h *langHandler) Symbols(ctx context.Context, params *lsp.WorkspaceSymbolParams) (result []lsp.SymbolInformation, err error) { + panic("unimplemented") +} - h.yamllsConnector.DocumentDidOpen(doc.Ast, params) +// TypeDefinition implements protocol.Server. +func (h *langHandler) TypeDefinition(ctx context.Context, params *lsp.TypeDefinitionParams) (result []lsp.Location, err error) { + panic("unimplemented") +} - _, err = h.chartStore.GetChartForDoc(doc.URI) - if err != nil { - logger.Error("Error getting chart info for file", doc.URI, err) - } +// WillCreateFiles implements protocol.Server. +func (h *langHandler) WillCreateFiles(ctx context.Context, params *lsp.CreateFilesParams) (result *lsp.WorkspaceEdit, err error) { + panic("unimplemented") +} - doc, ok := h.documents.Get(params.TextDocument.URI) - if !ok { - return errors.New("Could not get document: " + params.TextDocument.URI.Filename()) - } - chart, err := h.chartStore.GetChartForDoc(doc.URI) - if err != nil { - logger.Error("Error getting chart info for file", doc.URI, err) - } - notification, err := lsplocal.NotificationFromLint(ctx, h.connPool, chart, doc) - return reply(ctx, notification, err) -} - -func (h *langHandler) handleTextDocumentDidClose(ctx context.Context, reply jsonrpc2.Replier, _ jsonrpc2.Request) (err error) { - return reply( - ctx, - h.connPool.Notify( - ctx, - lsp.MethodTextDocumentDidClose, - nil, - ), - nil, - ) +// WillDeleteFiles implements protocol.Server. +func (h *langHandler) WillDeleteFiles(ctx context.Context, params *lsp.DeleteFilesParams) (result *lsp.WorkspaceEdit, err error) { + panic("unimplemented") } -func (h *langHandler) handleTextDocumentDidSave(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) (err error) { - var params lsp.DidSaveTextDocumentParams - if err := json.Unmarshal(req.Params(), ¶ms); err != nil { - return err - } +// WillRenameFiles implements protocol.Server. +func (h *langHandler) WillRenameFiles(ctx context.Context, params *lsp.RenameFilesParams) (result *lsp.WorkspaceEdit, err error) { + panic("unimplemented") +} - doc, ok := h.documents.Get(params.TextDocument.URI) - if !ok { - return errors.New("Could not get document: " + params.TextDocument.URI.Filename()) - } - chart, err := h.chartStore.GetChartForDoc(doc.URI) - if err != nil { - logger.Error("Error getting chart info for file", doc.URI, err) - } +// WillSave implements protocol.Server. +func (h *langHandler) WillSave(ctx context.Context, params *lsp.WillSaveTextDocumentParams) (err error) { + panic("unimplemented") +} + +// WillSaveWaitUntil implements protocol.Server. +func (h *langHandler) WillSaveWaitUntil(ctx context.Context, params *lsp.WillSaveTextDocumentParams) (result []lsp.TextEdit, err error) { + panic("unimplemented") +} - h.yamllsConnector.DocumentDidSave(doc, params) - notification, err := lsplocal.NotificationFromLint(ctx, h.connPool, chart, doc) - return reply(ctx, notification, err) +// WorkDoneProgressCancel implements protocol.Server. +func (h *langHandler) WorkDoneProgressCancel(ctx context.Context, params *lsp.WorkDoneProgressCancelParams) (err error) { + panic("unimplemented") } diff --git a/internal/handler/hover.go b/internal/handler/hover.go index c9b2cb2f..3ae3553f 100644 --- a/internal/handler/hover.go +++ b/internal/handler/hover.go @@ -2,7 +2,6 @@ package handler import ( "context" - "encoding/json" "errors" "fmt" "path/filepath" @@ -16,24 +15,14 @@ import ( "github.com/mrjosh/helm-ls/internal/util" "github.com/mrjosh/helm-ls/pkg/chart" "github.com/mrjosh/helm-ls/pkg/chartutil" - "go.lsp.dev/jsonrpc2" lsp "go.lsp.dev/protocol" "go.lsp.dev/uri" ) -func (h *langHandler) handleHover(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) (err error) { - if req.Params() == nil { - return &jsonrpc2.Error{Code: jsonrpc2.InvalidParams} - } - - var params lsp.HoverParams - if err := json.Unmarshal(req.Params(), ¶ms); err != nil { - return err - } - +func (h *langHandler) Hover(ctx context.Context, params *lsp.HoverParams) (result *lsp.Hover, err error) { doc, ok := h.documents.Get(params.TextDocument.URI) if !ok { - return errors.New("Could not get document: " + params.TextDocument.URI.Filename()) + return nil, errors.New("Could not get document: " + params.TextDocument.URI.Filename()) } chart, err := h.chartStore.GetChartForDoc(params.TextDocument.URI) if err != nil { @@ -49,7 +38,7 @@ func (h *langHandler) handleHover(ctx context.Context, reply jsonrpc2.Replier, r if parent == nil { err = errors.New("Could not parse ast correctly") - return reply(ctx, nil, err) + return nil, err } pt := parent.Type() @@ -59,8 +48,8 @@ func (h *langHandler) handleHover(ctx context.Context, reply jsonrpc2.Replier, r if len(word) > 2 && string(word[len(word)-1]) == ":" { word = word[0 : len(word)-1] } - response, err := h.yamllsConnector.CallHover(ctx, params, word) - return reply(ctx, response, err) + response, err := h.yamllsConnector.CallHover(ctx, *params, word) + return response, err } if pt == "function_call" && ct == "identifier" { word = currentNode.Content([]byte(doc.Content)) @@ -79,7 +68,7 @@ func (h *langHandler) handleHover(ctx context.Context, reply jsonrpc2.Replier, r ) if word == "" { - return reply(ctx, nil, err) + return nil, err } for _, s := range splitted { @@ -114,9 +103,9 @@ func (h *langHandler) handleHover(ctx context.Context, reply jsonrpc2.Replier, r value = "\"\"" } result := util.BuildHoverResponse(value, wordRange) - return reply(ctx, result, err) + return result, err } - return reply(ctx, nil, err) + return nil, err } searchWord := variableSplitted[0] @@ -132,10 +121,10 @@ func (h *langHandler) handleHover(ctx context.Context, reply jsonrpc2.Replier, r for _, completionItem := range toSearch { if searchWord == completionItem.Name { result := util.BuildHoverResponse(fmt.Sprint(completionItem.Doc), wordRange) - return reply(ctx, result, err) + return result, err } } - return reply(ctx, lsp.Hover{}, err) + return nil, err } func (h *langHandler) getChartMetadataHover(metadata *chart.Metadata, key string) (string, error) { diff --git a/internal/handler/initialization.go b/internal/handler/initialization.go index bc38c36e..56cbe69d 100644 --- a/internal/handler/initialization.go +++ b/internal/handler/initialization.go @@ -2,34 +2,24 @@ package handler import ( "context" - "encoding/json" "os" "github.com/mrjosh/helm-ls/internal/adapter/yamlls" "github.com/mrjosh/helm-ls/internal/charts" "github.com/mrjosh/helm-ls/internal/util" "github.com/sirupsen/logrus" - "go.lsp.dev/jsonrpc2" lsp "go.lsp.dev/protocol" "go.lsp.dev/uri" ) -func (h *langHandler) handleInitialize(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error { - var ( - params lsp.InitializeParams - workspaceURI uri.URI - err error - ) - if err := json.Unmarshal(req.Params(), ¶ms); err != nil { - return err - } +func (h *langHandler) Initialize(ctx context.Context, params *lsp.InitializeParams) (result *lsp.InitializeResult, err error) { + var workspaceURI uri.URI - logger.Debug("handleInitialize with params ", req.Params()) if len(params.WorkspaceFolders) != 0 { workspaceURI, err = uri.Parse(params.WorkspaceFolders[0].URI) if err != nil { logger.Error("Error parsing workspace URI", err) - return err + return nil, err } } else { logger.Error("length WorkspaceFolders is 0, falling back to current working directory") @@ -43,7 +33,7 @@ func (h *langHandler) handleInitialize(ctx context.Context, reply jsonrpc2.Repli h.chartStore = charts.NewChartStore(workspaceURI, h.NewChartWithWatchedFiles) logger.Debug("Initializing done") - return reply(ctx, lsp.InitializeResult{ + return &lsp.InitializeResult{ Capabilities: lsp.ServerCapabilities{ TextDocumentSync: lsp.TextDocumentSyncOptions{ Change: lsp.TextDocumentSyncKindIncremental, @@ -59,10 +49,16 @@ func (h *langHandler) handleInitialize(ctx context.Context, reply jsonrpc2.Repli HoverProvider: true, DefinitionProvider: true, }, - }, nil) + }, nil +} + +func (h *langHandler) Initialized(ctx context.Context, params *lsp.InitializedParams) (err error) { + go h.RetrieveWorkspaceConfiguration(ctx) + return nil } func (h *langHandler) initializationWithConfig() { + logger.Println("initializationWithConfig") configureLogLevel(h.helmlsConfig) h.chartStore.SetValuesFilesConfig(h.helmlsConfig.ValuesFilesConfig) configureYamlls(h) @@ -71,8 +67,9 @@ func (h *langHandler) initializationWithConfig() { func configureYamlls(h *langHandler) { config := h.helmlsConfig if config.YamllsConfiguration.Enabled { - h.yamllsConnector = yamlls.NewConnector(config.YamllsConfiguration, h.connPool, h.documents) + h.yamllsConnector = yamlls.NewConnector(config.YamllsConfiguration, h.client, h.documents) h.yamllsConnector.CallInitialize(h.chartStore.RootURI) + h.yamllsConnector.DidChangeConfiguration() h.yamllsConnector.InitiallySyncOpenDocuments(h.documents.GetAllDocs()) } } diff --git a/internal/handler/text.go b/internal/handler/text.go index eeeebad4..abeebd16 100644 --- a/internal/handler/text.go +++ b/internal/handler/text.go @@ -1,49 +1 @@ package handler - -import ( - "context" - "encoding/json" - "errors" - - lspinternal "github.com/mrjosh/helm-ls/internal/lsp" - "go.lsp.dev/jsonrpc2" - lsp "go.lsp.dev/protocol" -) - -func (h *langHandler) handleTextDocumentDidChange(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) (err error) { - - if req.Params() == nil { - return &jsonrpc2.Error{Code: jsonrpc2.InvalidParams} - } - - var params lsp.DidChangeTextDocumentParams - if err := json.Unmarshal(req.Params(), ¶ms); err != nil { - return err - } - - doc, ok := h.documents.Get(params.TextDocument.URI) - if !ok { - return errors.New("Could not get document: " + params.TextDocument.URI.Filename()) - } - - var shouldSendFullUpdateToYamlls = false - - // Synchronise changes into the doc's ContentChanges - doc.ApplyChanges(params.ContentChanges) - - for _, change := range params.ContentChanges { - var node = lspinternal.NodeAtPosition(doc.Ast, change.Range.Start) - if node.Type() != "text" { - shouldSendFullUpdateToYamlls = true - break - } - } - - if shouldSendFullUpdateToYamlls { - h.yamllsConnector.DocumentDidChangeFullSync(doc, params) - } else { - h.yamllsConnector.DocumentDidChange(doc, params) - } - - return reply(ctx, nil, nil) -} diff --git a/internal/handler/text_document.go b/internal/handler/text_document.go new file mode 100644 index 00000000..bc9e1283 --- /dev/null +++ b/internal/handler/text_document.go @@ -0,0 +1,102 @@ +package handler + +import ( + "context" + "errors" + + lspinternal "github.com/mrjosh/helm-ls/internal/lsp" + lsplocal "github.com/mrjosh/helm-ls/internal/lsp" + lsp "go.lsp.dev/protocol" +) + +func (h *langHandler) DidOpen(ctx context.Context, params *lsp.DidOpenTextDocumentParams) (err error) { + doc, err := h.documents.DidOpen(params, h.helmlsConfig) + if err != nil { + logger.Error(err) + return err + } + + h.yamllsConnector.DocumentDidOpen(doc.Ast, *params) + + _, err = h.chartStore.GetChartForDoc(doc.URI) + if err != nil { + logger.Error("Error getting chart info for file", doc.URI, err) + } + + doc, ok := h.documents.Get(params.TextDocument.URI) + if !ok { + return errors.New("Could not get document: " + params.TextDocument.URI.Filename()) + } + chart, err := h.chartStore.GetChartForDoc(doc.URI) + if err != nil { + logger.Error("Error getting chart info for file", doc.URI, err) + } + notification, err := lsplocal.NotificationFromLint(ctx, h.connPool, chart, doc) + + h.client.PublishDiagnostics(ctx, notification) + + return err +} + +func (h *langHandler) DidClose(ctx context.Context, params *lsp.DidCloseTextDocumentParams) (err error) { + return nil +} + +func (h *langHandler) DidSave(ctx context.Context, params *lsp.DidSaveTextDocumentParams) (err error) { + doc, ok := h.documents.Get(params.TextDocument.URI) + if !ok { + return errors.New("Could not get document: " + params.TextDocument.URI.Filename()) + } + chart, err := h.chartStore.GetChartForDoc(doc.URI) + if err != nil { + logger.Error("Error getting chart info for file", doc.URI, err) + } + + h.yamllsConnector.DocumentDidSave(doc, *params) + notification, err := lsplocal.NotificationFromLint(ctx, h.connPool, chart, doc) + + h.client.PublishDiagnostics(ctx, notification) + return nil +} + +func (h *langHandler) DidChange(ctx context.Context, params *lsp.DidChangeTextDocumentParams) (err error) { + doc, ok := h.documents.Get(params.TextDocument.URI) + if !ok { + return errors.New("Could not get document: " + params.TextDocument.URI.Filename()) + } + + shouldSendFullUpdateToYamlls := false + + // Synchronise changes into the doc's ContentChanges + doc.ApplyChanges(params.ContentChanges) + + for _, change := range params.ContentChanges { + node := lspinternal.NodeAtPosition(doc.Ast, change.Range.Start) + if node.Type() != "text" { + shouldSendFullUpdateToYamlls = true + break + } + } + + if shouldSendFullUpdateToYamlls { + h.yamllsConnector.DocumentDidChangeFullSync(doc, *params) + } else { + h.yamllsConnector.DocumentDidChange(doc, *params) + } + + return nil +} + +func (h *langHandler) DidCreateFiles(ctx context.Context, params *lsp.CreateFilesParams) (err error) { + panic("unimplemented") +} + +// DidDeleteFiles implements protocol.Server. +func (h *langHandler) DidDeleteFiles(ctx context.Context, params *lsp.DeleteFilesParams) (err error) { + panic("unimplemented") +} + +// DidRenameFiles implements protocol.Server. +func (h *langHandler) DidRenameFiles(ctx context.Context, params *lsp.RenameFilesParams) (err error) { + panic("unimplemented") +} diff --git a/internal/handler/watched_files.go b/internal/handler/watched_files.go index 906ad594..74e7eb05 100644 --- a/internal/handler/watched_files.go +++ b/internal/handler/watched_files.go @@ -2,7 +2,6 @@ package handler import ( "context" - "encoding/json" "github.com/mrjosh/helm-ls/internal/charts" "github.com/mrjosh/helm-ls/internal/util" @@ -50,19 +49,10 @@ func (h *langHandler) RegisterWatchedFiles(ctx context.Context, conn jsonrpc2.Co } } -func (h *langHandler) handleDidChangeWatchedFiles(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) (err error) { - if req.Params() == nil { - return &jsonrpc2.Error{Code: jsonrpc2.InvalidParams} - } - - var params lsp.DidChangeWatchedFilesParams - if err := json.Unmarshal(req.Params(), ¶ms); err != nil { - return err - } - +func (h *langHandler) DidChangeWatchedFiles(ctx context.Context, params *lsp.DidChangeWatchedFilesParams) (err error) { for _, change := range params.Changes { h.chartStore.ReloadValuesFile(change.URI) } - return reply(ctx, nil, nil) + return nil } diff --git a/internal/lsp/document.go b/internal/lsp/document.go index 0d9c1a7b..8c263e93 100644 --- a/internal/lsp/document.go +++ b/internal/lsp/document.go @@ -32,7 +32,7 @@ func (s *DocumentStore) GetAllDocs() []*Document { return docs } -func (s *DocumentStore) DidOpen(params lsp.DidOpenTextDocumentParams, helmlsConfig util.HelmlsConfiguration) (*Document, error) { +func (s *DocumentStore) DidOpen(params *lsp.DidOpenTextDocumentParams, helmlsConfig util.HelmlsConfiguration) (*Document, error) { logger.Debug(fmt.Sprintf("Opening document %s with langID %s", params.TextDocument.URI, params.TextDocument.LanguageID)) uri := params.TextDocument.URI @@ -44,6 +44,7 @@ func (s *DocumentStore) DidOpen(params lsp.DidOpenTextDocumentParams, helmlsConf Ast: ParseAst(nil, params.TextDocument.Text), DiagnosticsCache: NewDiagnosticsCache(helmlsConfig), } + // logger.Println("Storing doc ", path, s.documents) s.documents.Store(path, doc) return doc, nil } diff --git a/internal/lsp/document_test.go b/internal/lsp/document_test.go index ea0d84db..c09d0ca3 100644 --- a/internal/lsp/document_test.go +++ b/internal/lsp/document_test.go @@ -20,7 +20,7 @@ func TestDocumentStore(t *testing.T) { assert.Nil(doc) assert.False(ok) - sut.DidOpen(protocol.DidOpenTextDocumentParams{ + sut.DidOpen(&protocol.DidOpenTextDocumentParams{ TextDocument: protocol.TextDocumentItem{ URI: uri.File("test.yaml"), LanguageID: "helm", diff --git a/internal/lsp/lint.go b/internal/lsp/lint.go index 7cb6378d..5950a1d2 100644 --- a/internal/lsp/lint.go +++ b/internal/lsp/lint.go @@ -23,7 +23,7 @@ import ( var logger = log.GetLogger() -func NotificationFromLint(ctx context.Context, conn jsonrpc2.Conn, chart *charts.Chart, doc *Document) (*jsonrpc2.Notification, error) { +func NotificationFromLint(ctx context.Context, conn jsonrpc2.Conn, chart *charts.Chart, doc *Document) (*lsp.PublishDiagnosticsParams, error) { vals := chart.ValuesFiles.MainValuesFile.Values if chart.ValuesFiles.OverlayValuesFile != nil { vals = chartutil.CoalesceTables(chart.ValuesFiles.OverlayValuesFile.Values, chart.ValuesFiles.MainValuesFile.Values) @@ -35,14 +35,10 @@ func NotificationFromLint(ctx context.Context, conn jsonrpc2.Conn, chart *charts } doc.DiagnosticsCache.HelmDiagnostics = diagnostics - return nil, conn.Notify( - ctx, - lsp.MethodTextDocumentPublishDiagnostics, - &lsp.PublishDiagnosticsParams{ - URI: doc.URI, - Diagnostics: doc.DiagnosticsCache.GetMergedDiagnostics(), - }, - ) + return &lsp.PublishDiagnosticsParams{ + URI: doc.URI, + Diagnostics: doc.DiagnosticsCache.GetMergedDiagnostics(), + }, nil } // GetDiagnostics will run helm linter against the currect document URI using the given values diff --git a/internal/util/lsp.go b/internal/util/lsp.go index ca6e694e..e1fd5932 100644 --- a/internal/util/lsp.go +++ b/internal/util/lsp.go @@ -2,7 +2,7 @@ package util import lsp "go.lsp.dev/protocol" -func BuildHoverResponse(value string, wordRange lsp.Range) lsp.Hover { +func BuildHoverResponse(value string, wordRange lsp.Range) *lsp.Hover { content := lsp.MarkupContent{ Kind: lsp.Markdown, Value: value, @@ -11,5 +11,5 @@ func BuildHoverResponse(value string, wordRange lsp.Range) lsp.Hover { Contents: content, Range: &wordRange, } - return result + return &result } diff --git a/main.go b/main.go index 6af9da2d..1c4abe80 100644 --- a/main.go +++ b/main.go @@ -19,7 +19,6 @@ var ( ) func main() { - rootCmd := &cobra.Command{ Use: "helm_ls", Long: ` @@ -27,7 +26,7 @@ func main() { / /_/ / _ \ | '_ ' _ \ / / / __| / __ / __/ | | | | | / /__\__ \ \/ /_/ \___|_|_| |_| |_\____/___/`, - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(cmd *cobra.Command, _ []string) error { return cmd.Help() }, } @@ -47,5 +46,4 @@ func main() { fmt.Fprintln(os.Stderr, err) os.Exit(1) } - }