diff --git a/internal/jsonrpc2/handler.go b/internal/jsonrpc2/handler.go new file mode 100644 index 00000000..a1175bf0 --- /dev/null +++ b/internal/jsonrpc2/handler.go @@ -0,0 +1,89 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jsonrpc2 + +import ( + "context" + "encoding/json" + "time" +) + +// Handler is the interface used to hook into the mesage handling of an rpc +// connection. +type Handler interface { + // Deliver is invoked to handle incoming requests. + // If the request returns false from IsNotify then the Handler must eventually + // call Reply on the Conn with the supplied request. + // Handlers are called synchronously, they should pass the work off to a go + // routine if they are going to take a long time. + // If Deliver returns true all subsequent handlers will be invoked with + // delivered set to true, and should not attempt to deliver the message. + Deliver(ctx context.Context, r *Request, delivered bool) bool + + // Cancel is invoked for cancelled outgoing requests. + // It is okay to use the connection to send notifications, but the context will + // be in the cancelled state, so you must do it with the background context + // instead. + // If Cancel returns true all subsequent handlers will be invoked with + // cancelled set to true, and should not attempt to cancel the message. + Cancel(ctx context.Context, conn *Conn, id ID, cancelled bool) bool + + // Log is invoked for all messages flowing through a Conn. + // direction indicates if the message being received or sent + // id is the message id, if not set it was a notification + // elapsed is the time between a call being seen and the response, and is + // negative for anything that is not a response. + // method is the method name specified in the message + // payload is the parameters for a call or notification, and the result for a + // response + Log(direction Direction, id *ID, elapsed time.Duration, method string, payload *json.RawMessage, err *Error) +} + +// Direction is used to indicate to a logger whether the logged message was being +// sent or received. +type Direction bool + +const ( + // Send indicates the message is outgoing. + Send = Direction(true) + // Receive indicates the message is incoming. + Receive = Direction(false) +) + +func (d Direction) String() string { + switch d { + case Send: + return "send" + case Receive: + return "receive" + default: + panic("unreachable") + } +} + +type EmptyHandler struct{} + +func (EmptyHandler) Deliver(ctx context.Context, r *Request, delivered bool) bool { + return false +} + +func (EmptyHandler) Cancel(ctx context.Context, conn *Conn, id ID, cancelled bool) bool { + return false +} + +func (EmptyHandler) Log(direction Direction, id *ID, elapsed time.Duration, method string, payload *json.RawMessage, err *Error) { +} + +type defaultHandler struct{ EmptyHandler } + +func (defaultHandler) Deliver(ctx context.Context, r *Request, delivered bool) bool { + if delivered { + return false + } + if !r.IsNotify() { + r.Reply(ctx, nil, NewErrorf(CodeMethodNotFound, "method %q not found", r.Method)) + } + return true +} diff --git a/internal/jsonrpc2/jsonrpc2.go b/internal/jsonrpc2/jsonrpc2.go index 8311aafb..7b765d23 100644 --- a/internal/jsonrpc2/jsonrpc2.go +++ b/internal/jsonrpc2/jsonrpc2.go @@ -23,18 +23,14 @@ import ( // Conn is a JSON RPC 2 client server connection. // Conn is bidirectional; it does not have a designated server or client end. type Conn struct { - seq int64 // must only be accessed using atomic operations - Handler Handler - Canceler Canceler - Logger Logger - Capacity int - RejectIfOverloaded bool - stream Stream - err error - pendingMu sync.Mutex // protects the pending map - pending map[ID]chan *wireResponse - handlingMu sync.Mutex // protects the handling map - handling map[ID]*Request + seq int64 // must only be accessed using atomic operations + handlers []Handler + stream Stream + err error + pendingMu sync.Mutex // protects the pending map + pending map[ID]chan *wireResponse + handlingMu sync.Mutex // protects the handling map + handling map[ID]*Request } type requestState int @@ -65,20 +61,6 @@ type Request struct { ID *ID } -// Handler is an option you can pass to NewConn to handle incoming requests. -// If the request returns false from IsNotify then the Handler must eventually -// call Reply on the Conn with the supplied request. -// Handlers are called synchronously, they should pass the work off to a go -// routine if they are going to take a long time. -type Handler func(context.Context, *Request) - -// Canceler is an option you can pass to NewConn which is invoked for -// cancelled outgoing requests. -// It is okay to use the connection to send notifications, but the context will -// be in the cancelled state, so you must do it with the background context -// instead. -type Canceler func(context.Context, *Conn, ID) - type rpcStats struct { server bool method string @@ -133,23 +115,23 @@ func NewErrorf(code int64, format string, args ...interface{}) *Error { // You must call Run for the connection to be active. func NewConn(s Stream) *Conn { conn := &Conn{ + handlers: []Handler{defaultHandler{}}, stream: s, pending: make(map[ID]chan *wireResponse), handling: make(map[ID]*Request), } - // the default handler reports a method error - conn.Handler = func(ctx context.Context, r *Request) { - if !r.IsNotify() { - r.Reply(ctx, nil, NewErrorf(CodeMethodNotFound, "method %q not found", r.Method)) - } - } - // the default canceler does nothing - conn.Canceler = func(context.Context, *Conn, ID) {} - // the default logger does nothing - conn.Logger = func(Direction, *ID, time.Duration, string, *json.RawMessage, *Error) {} return conn } +// AddHandler adds a new handler to the set the connection will invoke. +// Handlers are invoked in the reverse order of how they were added, this +// allows the most recent addition to be the first one to attempt to handle a +// message. +func (c *Conn) AddHandler(handler Handler) { + // prepend the new handlers so we use them first + c.handlers = append([]Handler{handler}, c.handlers...) +} + // Cancel cancels a pending Call on the server side. // The call is identified by its id. // JSON RPC 2 does not specify a cancel message, so cancellation support is not @@ -183,7 +165,9 @@ func (c *Conn) Notify(ctx context.Context, method string, params interface{}) (e if err != nil { return fmt.Errorf("marshalling notify request: %v", err) } - c.Logger(Send, nil, -1, request.Method, request.Params, nil) + for _, h := range c.handlers { + h.Log(Send, nil, -1, request.Method, request.Params, nil) + } n, err := c.stream.Write(ctx, data) telemetry.SentBytes.Record(ctx, n) return err @@ -225,7 +209,9 @@ func (c *Conn) Call(ctx context.Context, method string, params, result interface }() // now we are ready to send before := time.Now() - c.Logger(Send, request.ID, -1, request.Method, request.Params, nil) + for _, h := range c.handlers { + h.Log(Send, request.ID, -1, request.Method, request.Params, nil) + } n, err := c.stream.Write(ctx, data) telemetry.SentBytes.Record(ctx, n) if err != nil { @@ -236,7 +222,9 @@ func (c *Conn) Call(ctx context.Context, method string, params, result interface select { case response := <-rchan: elapsed := time.Since(before) - c.Logger(Receive, response.ID, elapsed, request.Method, response.Result, response.Error) + for _, h := range c.handlers { + h.Log(Receive, response.ID, elapsed, request.Method, response.Result, response.Error) + } // is it an error response? if response.Error != nil { return response.Error @@ -250,7 +238,12 @@ func (c *Conn) Call(ctx context.Context, method string, params, result interface return nil case <-ctx.Done(): // allow the handler to propagate the cancel - c.Canceler(ctx, c, id) + cancelled := false + for _, h := range c.handlers { + if h.Cancel(ctx, c, id, cancelled) { + cancelled = true + } + } return ctx.Err() } } @@ -320,7 +313,9 @@ func (r *Request) Reply(ctx context.Context, result interface{}, err error) erro if err != nil { return err } - r.conn.Logger(Send, response.ID, elapsed, r.Method, response.Result, response.Error) + for _, h := range r.conn.handlers { + h.Log(Send, response.ID, elapsed, r.Method, response.Result, response.Error) + } n, err := r.conn.stream.Write(ctx, data) telemetry.SentBytes.Record(ctx, n) @@ -378,7 +373,9 @@ func (c *Conn) Run(ctx context.Context) error { if err := json.Unmarshal(data, msg); err != nil { // a badly formed message arrived, log it and continue // we trust the stream to have isolated the error to just this message - c.Logger(Receive, nil, -1, "", nil, NewErrorf(0, "unmarshal failed: %v", err)) + for _, h := range c.handlers { + h.Log(Receive, nil, -1, "", nil, NewErrorf(0, "unmarshal failed: %v", err)) + } continue } // work out which kind of message we have @@ -412,8 +409,13 @@ func (c *Conn) Run(ctx context.Context) error { rpcStats.end(reqCtx, nil) cancelReq() }() - c.Logger(Receive, req.ID, -1, req.Method, req.Params, nil) - c.Handler(reqCtx, req) + delivered := false + for _, h := range c.handlers { + h.Log(Receive, req.ID, -1, req.Method, req.Params, nil) + if h.Deliver(reqCtx, req, delivered) { + delivered = true + } + } }() case msg.ID != nil: // we have a response, get the pending entry from the map @@ -432,7 +434,9 @@ func (c *Conn) Run(ctx context.Context) error { rchan <- response close(rchan) default: - c.Logger(Receive, nil, -1, "", nil, NewErrorf(0, "message not a call, notify or response, ignoring")) + for _, h := range c.handlers { + h.Log(Receive, nil, -1, "", nil, NewErrorf(0, "message not a call, notify or response, ignoring")) + } } } } diff --git a/internal/jsonrpc2/jsonrpc2_test.go b/internal/jsonrpc2/jsonrpc2_test.go index c052f8c0..731f5883 100644 --- a/internal/jsonrpc2/jsonrpc2_test.go +++ b/internal/jsonrpc2/jsonrpc2_test.go @@ -10,9 +10,11 @@ import ( "flag" "fmt" "io" + "log" "path" "reflect" "testing" + "time" "golang.org/x/tools/internal/jsonrpc2" ) @@ -106,10 +108,7 @@ func run(ctx context.Context, t *testing.T, withHeaders bool, r io.ReadCloser, w stream = jsonrpc2.NewStream(r, w) } conn := jsonrpc2.NewConn(stream) - conn.Handler = handle - if *logRPC { - conn.Logger = jsonrpc2.Log - } + conn.AddHandler(handle{}) go func() { defer func() { r.Close() @@ -122,36 +121,55 @@ func run(ctx context.Context, t *testing.T, withHeaders bool, r io.ReadCloser, w return conn } -func handle(ctx context.Context, r *jsonrpc2.Request) { +type handle struct{ jsonrpc2.EmptyHandler } + +func (handle) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool { switch r.Method { case "no_args": if r.Params != nil { r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeInvalidParams, "Expected no params")) - return + return true } r.Reply(ctx, true, nil) case "one_string": var v string if err := json.Unmarshal(*r.Params, &v); err != nil { r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeParseError, "%v", err.Error())) - return + return true } r.Reply(ctx, "got:"+v, nil) case "one_number": var v int if err := json.Unmarshal(*r.Params, &v); err != nil { r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeParseError, "%v", err.Error())) - return + return true } r.Reply(ctx, fmt.Sprintf("got:%d", v), nil) case "join": var v []string if err := json.Unmarshal(*r.Params, &v); err != nil { r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeParseError, "%v", err.Error())) - return + return true } r.Reply(ctx, path.Join(v...), nil) default: r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeMethodNotFound, "method %q not found", r.Method)) } + return true +} + +func (handle) Log(direction jsonrpc2.Direction, id *jsonrpc2.ID, elapsed time.Duration, method string, payload *json.RawMessage, err *jsonrpc2.Error) { + if !*logRPC { + return + } + switch { + case err != nil: + log.Printf("%v failure [%v] %s %v", direction, id, method, err) + case id == nil: + log.Printf("%v notification %s %s", direction, method, *payload) + case elapsed >= 0: + log.Printf("%v response in %v [%v] %s %s", direction, elapsed, id, method, *payload) + default: + log.Printf("%v call [%v] %s %s", direction, id, method, *payload) + } } diff --git a/internal/jsonrpc2/log.go b/internal/jsonrpc2/log.go deleted file mode 100644 index f0e8c7f2..00000000 --- a/internal/jsonrpc2/log.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package jsonrpc2 - -import ( - "encoding/json" - "log" - "time" -) - -// Logger is an option you can pass to NewConn which is invoked for -// all messages flowing through a Conn. -// direction indicates if the message being recieved or sent -// id is the message id, if not set it was a notification -// elapsed is the time between a call being seen and the response, and is -// negative for anything that is not a response. -// method is the method name specified in the message -// payload is the parameters for a call or notification, and the result for a -// response -type Logger = func(direction Direction, id *ID, elapsed time.Duration, method string, payload *json.RawMessage, err *Error) - -// Direction is used to indicate to a logger whether the logged message was being -// sent or received. -type Direction bool - -const ( - // Send indicates the message is outgoing. - Send = Direction(true) - // Receive indicates the message is incoming. - Receive = Direction(false) -) - -func (d Direction) String() string { - switch d { - case Send: - return "send" - case Receive: - return "receive" - default: - panic("unreachable") - } -} - -// Log is an implementation of Logger that outputs using log.Print -// It is not used by default, but is provided for easy logging in users code. -func Log(direction Direction, id *ID, elapsed time.Duration, method string, payload *json.RawMessage, err *Error) { - switch { - case err != nil: - log.Printf("%v failure [%v] %s %v", direction, id, method, err) - case id == nil: - log.Printf("%v notification %s %s", direction, method, *payload) - case elapsed >= 0: - log.Printf("%v response in %v [%v] %s %s", direction, elapsed, id, method, *payload) - default: - log.Printf("%v call [%v] %s %s", direction, id, method, *payload) - } -} diff --git a/internal/lsp/cmd/serve.go b/internal/lsp/cmd/serve.go index 4a07bf7d..db7cffbf 100644 --- a/internal/lsp/cmd/serve.go +++ b/internal/lsp/cmd/serve.go @@ -80,7 +80,7 @@ func (s *Serve) Run(ctx context.Context, args ...string) error { // For debugging purposes only. run := func(srv *lsp.Server) { - srv.Conn.Logger = logger(s.Trace, out) + srv.Conn.AddHandler(&handler{trace: s.Trace, out: out}) go srv.Run(ctx) } if s.Address != "" { @@ -91,7 +91,7 @@ func (s *Serve) Run(ctx context.Context, args ...string) error { } stream := jsonrpc2.NewHeaderStream(os.Stdin, os.Stdout) srv := lsp.NewServer(s.app.cache, stream) - srv.Conn.Logger = logger(s.Trace, out) + srv.Conn.AddHandler(&handler{trace: s.Trace, out: out}) return srv.Run(ctx) } @@ -115,55 +115,66 @@ func (s *Serve) forward() error { return <-errc } -func logger(trace bool, out io.Writer) jsonrpc2.Logger { - return func(direction jsonrpc2.Direction, id *jsonrpc2.ID, elapsed time.Duration, method string, payload *json.RawMessage, err *jsonrpc2.Error) { - if !trace { - return - } - const eol = "\r\n\r\n\r\n" - if err != nil { - fmt.Fprintf(out, "[Error - %v] %s %s%s %v%s", time.Now().Format("3:04:05 PM"), - direction, method, id, err, eol) - return - } - outx := new(strings.Builder) - fmt.Fprintf(outx, "[Trace - %v] ", time.Now().Format("3:04:05 PM")) - switch direction { - case jsonrpc2.Send: - fmt.Fprint(outx, "Received ") - case jsonrpc2.Receive: - fmt.Fprint(outx, "Sending ") - } - switch { - case id == nil: - fmt.Fprint(outx, "notification ") - case elapsed >= 0: - fmt.Fprint(outx, "response ") - default: - fmt.Fprint(outx, "request ") - } - fmt.Fprintf(outx, "'%s", method) - switch { - case id == nil: - // do nothing - case id.Name != "": - fmt.Fprintf(outx, " - (%s)", id.Name) - default: - fmt.Fprintf(outx, " - (%d)", id.Number) - } - fmt.Fprint(outx, "'") - if elapsed >= 0 { - msec := int(elapsed.Round(time.Millisecond) / time.Millisecond) - fmt.Fprintf(outx, " in %dms", msec) - } - params := "null" - if payload != nil { - params = string(*payload) - } - if params == "null" { - params = "{}" - } - fmt.Fprintf(outx, ".\r\nParams: %s%s", params, eol) - fmt.Fprintf(out, "%s", outx.String()) - } +type handler struct { + trace bool + out io.Writer +} + +func (h *handler) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool { + return false +} + +func (h *handler) Cancel(ctx context.Context, conn *jsonrpc2.Conn, id jsonrpc2.ID, cancelled bool) bool { + return false +} + +func (h *handler) Log(direction jsonrpc2.Direction, id *jsonrpc2.ID, elapsed time.Duration, method string, payload *json.RawMessage, err *jsonrpc2.Error) { + if !h.trace { + return + } + const eol = "\r\n\r\n\r\n" + if err != nil { + fmt.Fprintf(h.out, "[Error - %v] %s %s%s %v%s", time.Now().Format("3:04:05 PM"), + direction, method, id, err, eol) + return + } + outx := new(strings.Builder) + fmt.Fprintf(outx, "[Trace - %v] ", time.Now().Format("3:04:05 PM")) + switch direction { + case jsonrpc2.Send: + fmt.Fprint(outx, "Received ") + case jsonrpc2.Receive: + fmt.Fprint(outx, "Sending ") + } + switch { + case id == nil: + fmt.Fprint(outx, "notification ") + case elapsed >= 0: + fmt.Fprint(outx, "response ") + default: + fmt.Fprint(outx, "request ") + } + fmt.Fprintf(outx, "'%s", method) + switch { + case id == nil: + // do nothing + case id.Name != "": + fmt.Fprintf(outx, " - (%s)", id.Name) + default: + fmt.Fprintf(outx, " - (%d)", id.Number) + } + fmt.Fprint(outx, "'") + if elapsed >= 0 { + msec := int(elapsed.Round(time.Millisecond) / time.Millisecond) + fmt.Fprintf(outx, " in %dms", msec) + } + params := "null" + if payload != nil { + params = string(*payload) + } + if params == "null" { + params = "{}" + } + fmt.Fprintf(outx, ".\r\nParams: %s%s", params, eol) + fmt.Fprintf(h.out, "%s", outx.String()) } diff --git a/internal/lsp/protocol/protocol.go b/internal/lsp/protocol/protocol.go index b5a00932..a4146a42 100644 --- a/internal/lsp/protocol/protocol.go +++ b/internal/lsp/protocol/protocol.go @@ -6,6 +6,8 @@ package protocol import ( "context" + "encoding/json" + "time" "golang.org/x/tools/internal/jsonrpc2" "golang.org/x/tools/internal/lsp/telemetry/trace" @@ -15,23 +17,38 @@ import ( type DocumentUri = string -const defaultMessageBufferSize = 20 -const defaultRejectIfOverloaded = false +type canceller struct{} -func canceller(ctx context.Context, conn *jsonrpc2.Conn, id jsonrpc2.ID) { +type clientHandler struct { + canceller + log xlog.Logger + client Client +} + +type serverHandler struct { + canceller + log xlog.Logger + server Server +} + +func (canceller) Cancel(ctx context.Context, conn *jsonrpc2.Conn, id jsonrpc2.ID, cancelled bool) bool { + if cancelled { + return false + } ctx = xcontext.Detach(ctx) ctx, done := trace.StartSpan(ctx, "protocol.canceller") defer done() conn.Notify(ctx, "$/cancelRequest", &CancelParams{ID: id}) + return true +} + +func (canceller) Log(direction jsonrpc2.Direction, id *jsonrpc2.ID, elapsed time.Duration, method string, payload *json.RawMessage, err *jsonrpc2.Error) { } func NewClient(stream jsonrpc2.Stream, client Client) (*jsonrpc2.Conn, Server, xlog.Logger) { log := xlog.New(NewLogger(client)) conn := jsonrpc2.NewConn(stream) - conn.Capacity = defaultMessageBufferSize - conn.RejectIfOverloaded = defaultRejectIfOverloaded - conn.Handler = clientHandler(log, client) - conn.Canceler = jsonrpc2.Canceler(canceller) + conn.AddHandler(&clientHandler{log: log, client: client}) return conn, &serverDispatcher{Conn: conn}, log } @@ -39,10 +56,7 @@ func NewServer(stream jsonrpc2.Stream, server Server) (*jsonrpc2.Conn, Client, x conn := jsonrpc2.NewConn(stream) client := &clientDispatcher{Conn: conn} log := xlog.New(NewLogger(client)) - conn.Capacity = defaultMessageBufferSize - conn.RejectIfOverloaded = defaultRejectIfOverloaded - conn.Handler = serverHandler(log, server) - conn.Canceler = jsonrpc2.Canceler(canceller) + conn.AddHandler(&serverHandler{log: log, server: server}) return conn, client, log } diff --git a/internal/lsp/protocol/tsclient.go b/internal/lsp/protocol/tsclient.go index ccfa852f..6d096a81 100644 --- a/internal/lsp/protocol/tsclient.go +++ b/internal/lsp/protocol/tsclient.go @@ -7,7 +7,6 @@ import ( "encoding/json" "golang.org/x/tools/internal/jsonrpc2" - "golang.org/x/tools/internal/lsp/xlog" ) type Client interface { @@ -23,117 +22,127 @@ type Client interface { ApplyEdit(context.Context, *ApplyWorkspaceEditParams) (*ApplyWorkspaceEditResponse, error) } -func clientHandler(log xlog.Logger, client Client) jsonrpc2.Handler { - return func(ctx context.Context, r *jsonrpc2.Request) { - switch r.Method { - case "$/cancelRequest": - var params CancelParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - r.Conn().Cancel(params.ID) - case "window/showMessage": // notif - var params ShowMessageParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - if err := client.ShowMessage(ctx, ¶ms); err != nil { - log.Errorf(ctx, "%v", err) - } - case "window/logMessage": // notif - var params LogMessageParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - if err := client.LogMessage(ctx, ¶ms); err != nil { - log.Errorf(ctx, "%v", err) - } - case "telemetry/event": // notif - var params interface{} - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - if err := client.Event(ctx, ¶ms); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/publishDiagnostics": // notif - var params PublishDiagnosticsParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - if err := client.PublishDiagnostics(ctx, ¶ms); err != nil { - log.Errorf(ctx, "%v", err) - } - case "workspace/workspaceFolders": // req - if r.Params != nil { - r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeInvalidParams, "Expected no params")) - return - } - resp, err := client.WorkspaceFolders(ctx) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "workspace/configuration": // req - var params ConfigurationParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := client.Configuration(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "client/registerCapability": // req - var params RegistrationParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - err := client.RegisterCapability(ctx, ¶ms) - if err := r.Reply(ctx, nil, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "client/unregisterCapability": // req - var params UnregistrationParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - err := client.UnregisterCapability(ctx, ¶ms) - if err := r.Reply(ctx, nil, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "window/showMessageRequest": // req - var params ShowMessageRequestParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := client.ShowMessageRequest(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "workspace/applyEdit": // req - var params ApplyWorkspaceEditParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := client.ApplyEdit(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - - default: - if r.IsNotify() { - r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeMethodNotFound, "method %q not found", r.Method)) - } +func (h clientHandler) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool { + if delivered { + return false + } + switch r.Method { + case "$/cancelRequest": + var params CancelParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true } + r.Conn().Cancel(params.ID) + return true + case "window/showMessage": // notif + var params ShowMessageParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + if err := h.client.ShowMessage(ctx, ¶ms); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "window/logMessage": // notif + var params LogMessageParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + if err := h.client.LogMessage(ctx, ¶ms); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "telemetry/event": // notif + var params interface{} + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + if err := h.client.Event(ctx, ¶ms); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/publishDiagnostics": // notif + var params PublishDiagnosticsParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + if err := h.client.PublishDiagnostics(ctx, ¶ms); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "workspace/workspaceFolders": // req + if r.Params != nil { + r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeInvalidParams, "Expected no params")) + return true + } + resp, err := h.client.WorkspaceFolders(ctx) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "workspace/configuration": // req + var params ConfigurationParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.client.Configuration(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "client/registerCapability": // req + var params RegistrationParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + err := h.client.RegisterCapability(ctx, ¶ms) + if err := r.Reply(ctx, nil, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "client/unregisterCapability": // req + var params UnregistrationParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + err := h.client.UnregisterCapability(ctx, ¶ms) + if err := r.Reply(ctx, nil, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "window/showMessageRequest": // req + var params ShowMessageRequestParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.client.ShowMessageRequest(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "workspace/applyEdit": // req + var params ApplyWorkspaceEditParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.client.ApplyEdit(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + + default: + return false } } diff --git a/internal/lsp/protocol/tsserver.go b/internal/lsp/protocol/tsserver.go index e8c38508..fb5d2d4c 100644 --- a/internal/lsp/protocol/tsserver.go +++ b/internal/lsp/protocol/tsserver.go @@ -7,7 +7,6 @@ import ( "encoding/json" "golang.org/x/tools/internal/jsonrpc2" - "golang.org/x/tools/internal/lsp/xlog" ) type Server interface { @@ -55,424 +54,466 @@ type Server interface { ExecuteCommand(context.Context, *ExecuteCommandParams) (interface{}, error) } -func serverHandler(log xlog.Logger, server Server) jsonrpc2.Handler { - return func(ctx context.Context, r *jsonrpc2.Request) { - switch r.Method { - case "$/cancelRequest": - var params CancelParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - r.Conn().Cancel(params.ID) - case "workspace/didChangeWorkspaceFolders": // notif - var params DidChangeWorkspaceFoldersParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - if err := server.DidChangeWorkspaceFolders(ctx, ¶ms); err != nil { - log.Errorf(ctx, "%v", err) - } - case "initialized": // notif - var params InitializedParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - if err := server.Initialized(ctx, ¶ms); err != nil { - log.Errorf(ctx, "%v", err) - } - case "exit": // notif - if err := server.Exit(ctx); err != nil { - log.Errorf(ctx, "%v", err) - } - case "workspace/didChangeConfiguration": // notif - var params DidChangeConfigurationParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - if err := server.DidChangeConfiguration(ctx, ¶ms); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/didOpen": // notif - var params DidOpenTextDocumentParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - if err := server.DidOpen(ctx, ¶ms); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/didChange": // notif - var params DidChangeTextDocumentParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - if err := server.DidChange(ctx, ¶ms); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/didClose": // notif - var params DidCloseTextDocumentParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - if err := server.DidClose(ctx, ¶ms); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/didSave": // notif - var params DidSaveTextDocumentParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - if err := server.DidSave(ctx, ¶ms); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/willSave": // notif - var params WillSaveTextDocumentParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - if err := server.WillSave(ctx, ¶ms); err != nil { - log.Errorf(ctx, "%v", err) - } - case "workspace/didChangeWatchedFiles": // notif - var params DidChangeWatchedFilesParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - if err := server.DidChangeWatchedFiles(ctx, ¶ms); err != nil { - log.Errorf(ctx, "%v", err) - } - case "$/setTraceNotification": // notif - var params SetTraceParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - if err := server.SetTraceNotification(ctx, ¶ms); err != nil { - log.Errorf(ctx, "%v", err) - } - case "$/logTraceNotification": // notif - var params LogTraceParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - if err := server.LogTraceNotification(ctx, ¶ms); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/implementation": // req - var params TextDocumentPositionParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.Implementation(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/typeDefinition": // req - var params TextDocumentPositionParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.TypeDefinition(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/documentColor": // req - var params DocumentColorParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.DocumentColor(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/colorPresentation": // req - var params ColorPresentationParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.ColorPresentation(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/foldingRange": // req - var params FoldingRangeParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.FoldingRange(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/declaration": // req - var params TextDocumentPositionParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.Declaration(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/selectionRange": // req - var params SelectionRangeParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.SelectionRange(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "initialize": // req - var params InitializeParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.Initialize(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "shutdown": // req - if r.Params != nil { - r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeInvalidParams, "Expected no params")) - return - } - err := server.Shutdown(ctx) - if err := r.Reply(ctx, nil, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/willSaveWaitUntil": // req - var params WillSaveTextDocumentParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.WillSaveWaitUntil(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/completion": // req - var params CompletionParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.Completion(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "completionItem/resolve": // req - var params CompletionItem - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.Resolve(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/hover": // req - var params TextDocumentPositionParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.Hover(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/signatureHelp": // req - var params TextDocumentPositionParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.SignatureHelp(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/definition": // req - var params TextDocumentPositionParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.Definition(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/references": // req - var params ReferenceParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.References(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/documentHighlight": // req - var params TextDocumentPositionParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.DocumentHighlight(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/documentSymbol": // req - var params DocumentSymbolParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.DocumentSymbol(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "workspace/symbol": // req - var params WorkspaceSymbolParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.Symbol(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/codeAction": // req - var params CodeActionParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.CodeAction(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/codeLens": // req - var params CodeLensParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.CodeLens(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "codeLens/resolve": // req - var params CodeLens - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.ResolveCodeLens(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/formatting": // req - var params DocumentFormattingParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.Formatting(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/rangeFormatting": // req - var params DocumentRangeFormattingParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.RangeFormatting(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/onTypeFormatting": // req - var params DocumentOnTypeFormattingParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.OnTypeFormatting(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/rename": // req - var params RenameParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.Rename(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/prepareRename": // req - var params TextDocumentPositionParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.PrepareRename(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "textDocument/documentLink": // req - var params DocumentLinkParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.DocumentLink(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "documentLink/resolve": // req - var params DocumentLink - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.ResolveDocumentLink(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - case "workspace/executeCommand": // req - var params ExecuteCommandParams - if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return - } - resp, err := server.ExecuteCommand(ctx, ¶ms) - if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - } - - default: - if r.IsNotify() { - r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeMethodNotFound, "method %q not found", r.Method)) - } +func (h serverHandler) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool { + if delivered { + return false + } + switch r.Method { + case "$/cancelRequest": + var params CancelParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true } + r.Conn().Cancel(params.ID) + return true + case "workspace/didChangeWorkspaceFolders": // notif + var params DidChangeWorkspaceFoldersParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + if err := h.server.DidChangeWorkspaceFolders(ctx, ¶ms); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "initialized": // notif + var params InitializedParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + if err := h.server.Initialized(ctx, ¶ms); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "exit": // notif + if err := h.server.Exit(ctx); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "workspace/didChangeConfiguration": // notif + var params DidChangeConfigurationParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + if err := h.server.DidChangeConfiguration(ctx, ¶ms); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/didOpen": // notif + var params DidOpenTextDocumentParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + if err := h.server.DidOpen(ctx, ¶ms); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/didChange": // notif + var params DidChangeTextDocumentParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + if err := h.server.DidChange(ctx, ¶ms); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/didClose": // notif + var params DidCloseTextDocumentParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + if err := h.server.DidClose(ctx, ¶ms); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/didSave": // notif + var params DidSaveTextDocumentParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + if err := h.server.DidSave(ctx, ¶ms); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/willSave": // notif + var params WillSaveTextDocumentParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + if err := h.server.WillSave(ctx, ¶ms); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "workspace/didChangeWatchedFiles": // notif + var params DidChangeWatchedFilesParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + if err := h.server.DidChangeWatchedFiles(ctx, ¶ms); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "$/setTraceNotification": // notif + var params SetTraceParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + if err := h.server.SetTraceNotification(ctx, ¶ms); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "$/logTraceNotification": // notif + var params LogTraceParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + if err := h.server.LogTraceNotification(ctx, ¶ms); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/implementation": // req + var params TextDocumentPositionParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.Implementation(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/typeDefinition": // req + var params TextDocumentPositionParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.TypeDefinition(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/documentColor": // req + var params DocumentColorParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.DocumentColor(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/colorPresentation": // req + var params ColorPresentationParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.ColorPresentation(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/foldingRange": // req + var params FoldingRangeParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.FoldingRange(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/declaration": // req + var params TextDocumentPositionParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.Declaration(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/selectionRange": // req + var params SelectionRangeParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.SelectionRange(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "initialize": // req + var params InitializeParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.Initialize(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "shutdown": // req + if r.Params != nil { + r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeInvalidParams, "Expected no params")) + return true + } + err := h.server.Shutdown(ctx) + if err := r.Reply(ctx, nil, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/willSaveWaitUntil": // req + var params WillSaveTextDocumentParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.WillSaveWaitUntil(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/completion": // req + var params CompletionParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.Completion(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "completionItem/resolve": // req + var params CompletionItem + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.Resolve(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/hover": // req + var params TextDocumentPositionParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.Hover(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/signatureHelp": // req + var params TextDocumentPositionParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.SignatureHelp(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/definition": // req + var params TextDocumentPositionParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.Definition(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/references": // req + var params ReferenceParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.References(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/documentHighlight": // req + var params TextDocumentPositionParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.DocumentHighlight(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/documentSymbol": // req + var params DocumentSymbolParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.DocumentSymbol(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "workspace/symbol": // req + var params WorkspaceSymbolParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.Symbol(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/codeAction": // req + var params CodeActionParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.CodeAction(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/codeLens": // req + var params CodeLensParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.CodeLens(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "codeLens/resolve": // req + var params CodeLens + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.ResolveCodeLens(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/formatting": // req + var params DocumentFormattingParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.Formatting(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/rangeFormatting": // req + var params DocumentRangeFormattingParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.RangeFormatting(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/onTypeFormatting": // req + var params DocumentOnTypeFormattingParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.OnTypeFormatting(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/rename": // req + var params RenameParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.Rename(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/prepareRename": // req + var params TextDocumentPositionParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.PrepareRename(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "textDocument/documentLink": // req + var params DocumentLinkParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.DocumentLink(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "documentLink/resolve": // req + var params DocumentLink + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.ResolveDocumentLink(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + case "workspace/executeCommand": // req + var params ExecuteCommandParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, h.log, r, err) + return true + } + resp, err := h.server.ExecuteCommand(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true + + default: + return false } } diff --git a/internal/lsp/protocol/typescript/requests.ts b/internal/lsp/protocol/typescript/requests.ts index 9110d9a7..33902901 100644 --- a/internal/lsp/protocol/typescript/requests.ts +++ b/internal/lsp/protocol/typescript/requests.ts @@ -59,7 +59,7 @@ function generate(files: string[], options: ts.CompilerOptions): void { setReceives(); // distinguish client and server // for each of Client and Server there are 3 parts to the output: // 1. type X interface {methods} - // 2. serverHandler(...) { return func(...) { switch r.method}} + // 2. func (h *serverHandler) Deliver(...) { switch r.method } // 3. func (x *xDispatcher) Method(ctx, parm) not.forEach( (v, k) => { @@ -99,7 +99,7 @@ function sig(nm: string, a: string, b: string, names?: boolean): string { const notNil = `if r.Params != nil { r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeInvalidParams, "Expected no params")) - return + return true }`; // Go code for notifications. Side is client or server, m is the request method function goNot(side: side, m: string) { @@ -113,16 +113,18 @@ function goNot(side: side, m: string) { if (a != '') { case1 = `var params ${a} if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return + sendParseError(ctx, h.log, r, err) + return true } - if err := ${side.name}.${nm}(ctx, ¶ms); err != nil { - log.Errorf(ctx, "%v", err) - }`; + if err := h.${side.name}.${nm}(ctx, ¶ms); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true`; } else { - case1 = `if err := ${side.name}.${nm}(ctx); err != nil { - log.Errorf(ctx, "%v", err) - }`; + case1 = `if err := h.${side.name}.${nm}(ctx); err != nil { + h.log.Errorf(ctx, "%v", err) + } + return true`; } side.cases.push(`${caseHdr}\n${case1}`); @@ -152,24 +154,26 @@ function goReq(side: side, m: string) { if (a != '') { case1 = `var params ${a} if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return + sendParseError(ctx, h.log, r, err) + return true }`; } const arg2 = a == '' ? '' : ', ¶ms'; - let case2 = `if err := ${side.name}.${nm}(ctx${arg2}); err != nil { - log.Errorf(ctx, "%v", err) + let case2 = `if err := h.${side.name}.${nm}(ctx${arg2}); err != nil { + h.log.Errorf(ctx, "%v", err) }`; if (b != '') { - case2 = `resp, err := ${side.name}.${nm}(ctx${arg2}) + case2 = `resp, err := h.${side.name}.${nm}(ctx${arg2}) if err := r.Reply(ctx, resp, err); err != nil { - log.Errorf(ctx, "%v", err) - }`; + h.log.Errorf(ctx, "%v", err) + } + return true`; } else { // response is nil - case2 = `err := ${side.name}.${nm}(ctx${arg2}) + case2 = `err := h.${side.name}.${nm}(ctx${arg2}) if err := r.Reply(ctx, nil, err); err != nil { - log.Errorf(ctx, "%v", err) - }` + h.log.Errorf(ctx, "%v", err) + } + return true` } side.cases.push(`${caseHdr}\n${case1}\n${case2}`); @@ -222,32 +226,30 @@ function output(side: side) { "encoding/json" "golang.org/x/tools/internal/jsonrpc2" - "golang.org/x/tools/internal/lsp/xlog" ) `); const a = side.name[0].toUpperCase() + side.name.substring(1) f(`type ${a} interface {`); side.methods.forEach((v) => { f(v) }); f('}\n'); - f(`func ${side.name}Handler(log xlog.Logger, ${side.name} ${ - side.goName}) jsonrpc2.Handler { - return func(ctx context.Context, r *jsonrpc2.Request) { + f(`func (h ${side.name}Handler) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool { + if delivered { + return false + } switch r.Method { case "$/cancelRequest": var params CancelParams if err := json.Unmarshal(*r.Params, ¶ms); err != nil { - sendParseError(ctx, log, r, err) - return + sendParseError(ctx, h.log, r, err) + return true } - r.Conn().Cancel(params.ID)`); + r.Conn().Cancel(params.ID) + return true`); side.cases.forEach((v) => { f(v) }); f(` default: - if r.IsNotify() { - r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeMethodNotFound, "method %q not found", r.Method)) - } + return false } -} }`); f(` type ${side.name}Dispatcher struct {