internal/jsonrpc2: cleanup the jsonrpc2 callbacks
We merge them into a single interface and support multiple of them rather than
just one.
This will allow us to stack handlers with different responsabilities and extract
some core logic (like tracing) out to a handler where it belongs.
Change-Id: I6aab92138550c5062fcb1bed86171e0850d1eb38
Reviewed-on: https://go-review.googlesource.com/c/tools/+/185879
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/jsonrpc2/handler.go b/internal/jsonrpc2/handler.go
new file mode 100644
index 0000000..a1175bf
--- /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 8311aaf..7b765d2 100644
--- a/internal/jsonrpc2/jsonrpc2.go
+++ b/internal/jsonrpc2/jsonrpc2.go
@@ -23,18 +23,14 @@
// 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 @@
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 @@
// 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 @@
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 @@
}()
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 c052f8c..731f588 100644
--- a/internal/jsonrpc2/jsonrpc2_test.go
+++ b/internal/jsonrpc2/jsonrpc2_test.go
@@ -10,9 +10,11 @@
"flag"
"fmt"
"io"
+ "log"
"path"
"reflect"
"testing"
+ "time"
"golang.org/x/tools/internal/jsonrpc2"
)
@@ -106,10 +108,7 @@
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 @@
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 f0e8c7f..0000000
--- 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 4a07bf7..db7cffb 100644
--- a/internal/lsp/cmd/serve.go
+++ b/internal/lsp/cmd/serve.go
@@ -80,7 +80,7 @@
// 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 @@
}
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 @@
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 b5a0093..a4146a4 100644
--- a/internal/lsp/protocol/protocol.go
+++ b/internal/lsp/protocol/protocol.go
@@ -6,6 +6,8 @@
import (
"context"
+ "encoding/json"
+ "time"
"golang.org/x/tools/internal/jsonrpc2"
"golang.org/x/tools/internal/lsp/telemetry/trace"
@@ -15,23 +17,38 @@
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 @@
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 ccfa852..6d096a8 100644
--- a/internal/lsp/protocol/tsclient.go
+++ b/internal/lsp/protocol/tsclient.go
@@ -7,7 +7,6 @@
"encoding/json"
"golang.org/x/tools/internal/jsonrpc2"
- "golang.org/x/tools/internal/lsp/xlog"
)
type Client interface {
@@ -23,117 +22,127 @@
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 e8c3850..fb5d2d4c 100644
--- a/internal/lsp/protocol/tsserver.go
+++ b/internal/lsp/protocol/tsserver.go
@@ -7,7 +7,6 @@
"encoding/json"
"golang.org/x/tools/internal/jsonrpc2"
- "golang.org/x/tools/internal/lsp/xlog"
)
type Server interface {
@@ -55,424 +54,466 @@
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 9110d9a..3390290 100644
--- a/internal/lsp/protocol/typescript/requests.ts
+++ b/internal/lsp/protocol/typescript/requests.ts
@@ -59,7 +59,7 @@
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 @@
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 @@
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 @@
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 @@
"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 {