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, &params); 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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			if err := client.ShowMessage(ctx, &params); err != nil {
-				log.Errorf(ctx, "%v", err)
-			}
-		case "window/logMessage": // notif
-			var params LogMessageParams
-			if err := json.Unmarshal(*r.Params, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			if err := client.LogMessage(ctx, &params); err != nil {
-				log.Errorf(ctx, "%v", err)
-			}
-		case "telemetry/event": // notif
-			var params interface{}
-			if err := json.Unmarshal(*r.Params, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			if err := client.Event(ctx, &params); err != nil {
-				log.Errorf(ctx, "%v", err)
-			}
-		case "textDocument/publishDiagnostics": // notif
-			var params PublishDiagnosticsParams
-			if err := json.Unmarshal(*r.Params, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			if err := client.PublishDiagnostics(ctx, &params); 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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := client.Configuration(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			err := client.RegisterCapability(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			err := client.UnregisterCapability(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := client.ShowMessageRequest(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := client.ApplyEdit(ctx, &params)
-			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, &params); 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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		if err := h.client.ShowMessage(ctx, &params); err != nil {
+			h.log.Errorf(ctx, "%v", err)
+		}
+		return true
+	case "window/logMessage": // notif
+		var params LogMessageParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		if err := h.client.LogMessage(ctx, &params); err != nil {
+			h.log.Errorf(ctx, "%v", err)
+		}
+		return true
+	case "telemetry/event": // notif
+		var params interface{}
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		if err := h.client.Event(ctx, &params); err != nil {
+			h.log.Errorf(ctx, "%v", err)
+		}
+		return true
+	case "textDocument/publishDiagnostics": // notif
+		var params PublishDiagnosticsParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		if err := h.client.PublishDiagnostics(ctx, &params); 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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.client.Configuration(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		err := h.client.RegisterCapability(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		err := h.client.UnregisterCapability(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.client.ShowMessageRequest(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.client.ApplyEdit(ctx, &params)
+		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, &params); 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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			if err := server.DidChangeWorkspaceFolders(ctx, &params); err != nil {
-				log.Errorf(ctx, "%v", err)
-			}
-		case "initialized": // notif
-			var params InitializedParams
-			if err := json.Unmarshal(*r.Params, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			if err := server.Initialized(ctx, &params); 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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			if err := server.DidChangeConfiguration(ctx, &params); err != nil {
-				log.Errorf(ctx, "%v", err)
-			}
-		case "textDocument/didOpen": // notif
-			var params DidOpenTextDocumentParams
-			if err := json.Unmarshal(*r.Params, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			if err := server.DidOpen(ctx, &params); err != nil {
-				log.Errorf(ctx, "%v", err)
-			}
-		case "textDocument/didChange": // notif
-			var params DidChangeTextDocumentParams
-			if err := json.Unmarshal(*r.Params, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			if err := server.DidChange(ctx, &params); err != nil {
-				log.Errorf(ctx, "%v", err)
-			}
-		case "textDocument/didClose": // notif
-			var params DidCloseTextDocumentParams
-			if err := json.Unmarshal(*r.Params, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			if err := server.DidClose(ctx, &params); err != nil {
-				log.Errorf(ctx, "%v", err)
-			}
-		case "textDocument/didSave": // notif
-			var params DidSaveTextDocumentParams
-			if err := json.Unmarshal(*r.Params, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			if err := server.DidSave(ctx, &params); err != nil {
-				log.Errorf(ctx, "%v", err)
-			}
-		case "textDocument/willSave": // notif
-			var params WillSaveTextDocumentParams
-			if err := json.Unmarshal(*r.Params, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			if err := server.WillSave(ctx, &params); err != nil {
-				log.Errorf(ctx, "%v", err)
-			}
-		case "workspace/didChangeWatchedFiles": // notif
-			var params DidChangeWatchedFilesParams
-			if err := json.Unmarshal(*r.Params, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			if err := server.DidChangeWatchedFiles(ctx, &params); err != nil {
-				log.Errorf(ctx, "%v", err)
-			}
-		case "$/setTraceNotification": // notif
-			var params SetTraceParams
-			if err := json.Unmarshal(*r.Params, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			if err := server.SetTraceNotification(ctx, &params); err != nil {
-				log.Errorf(ctx, "%v", err)
-			}
-		case "$/logTraceNotification": // notif
-			var params LogTraceParams
-			if err := json.Unmarshal(*r.Params, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			if err := server.LogTraceNotification(ctx, &params); err != nil {
-				log.Errorf(ctx, "%v", err)
-			}
-		case "textDocument/implementation": // req
-			var params TextDocumentPositionParams
-			if err := json.Unmarshal(*r.Params, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.Implementation(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.TypeDefinition(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.DocumentColor(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.ColorPresentation(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.FoldingRange(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.Declaration(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.SelectionRange(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.Initialize(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.WillSaveWaitUntil(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.Completion(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.Resolve(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.Hover(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.SignatureHelp(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.Definition(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.References(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.DocumentHighlight(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.DocumentSymbol(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.Symbol(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.CodeAction(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.CodeLens(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.ResolveCodeLens(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.Formatting(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.RangeFormatting(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.OnTypeFormatting(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.Rename(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.PrepareRename(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.DocumentLink(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.ResolveDocumentLink(ctx, &params)
-			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, &params); err != nil {
-				sendParseError(ctx, log, r, err)
-				return
-			}
-			resp, err := server.ExecuteCommand(ctx, &params)
-			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, &params); 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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		if err := h.server.DidChangeWorkspaceFolders(ctx, &params); err != nil {
+			h.log.Errorf(ctx, "%v", err)
+		}
+		return true
+	case "initialized": // notif
+		var params InitializedParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		if err := h.server.Initialized(ctx, &params); 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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		if err := h.server.DidChangeConfiguration(ctx, &params); err != nil {
+			h.log.Errorf(ctx, "%v", err)
+		}
+		return true
+	case "textDocument/didOpen": // notif
+		var params DidOpenTextDocumentParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		if err := h.server.DidOpen(ctx, &params); err != nil {
+			h.log.Errorf(ctx, "%v", err)
+		}
+		return true
+	case "textDocument/didChange": // notif
+		var params DidChangeTextDocumentParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		if err := h.server.DidChange(ctx, &params); err != nil {
+			h.log.Errorf(ctx, "%v", err)
+		}
+		return true
+	case "textDocument/didClose": // notif
+		var params DidCloseTextDocumentParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		if err := h.server.DidClose(ctx, &params); err != nil {
+			h.log.Errorf(ctx, "%v", err)
+		}
+		return true
+	case "textDocument/didSave": // notif
+		var params DidSaveTextDocumentParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		if err := h.server.DidSave(ctx, &params); err != nil {
+			h.log.Errorf(ctx, "%v", err)
+		}
+		return true
+	case "textDocument/willSave": // notif
+		var params WillSaveTextDocumentParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		if err := h.server.WillSave(ctx, &params); err != nil {
+			h.log.Errorf(ctx, "%v", err)
+		}
+		return true
+	case "workspace/didChangeWatchedFiles": // notif
+		var params DidChangeWatchedFilesParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		if err := h.server.DidChangeWatchedFiles(ctx, &params); err != nil {
+			h.log.Errorf(ctx, "%v", err)
+		}
+		return true
+	case "$/setTraceNotification": // notif
+		var params SetTraceParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		if err := h.server.SetTraceNotification(ctx, &params); err != nil {
+			h.log.Errorf(ctx, "%v", err)
+		}
+		return true
+	case "$/logTraceNotification": // notif
+		var params LogTraceParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		if err := h.server.LogTraceNotification(ctx, &params); err != nil {
+			h.log.Errorf(ctx, "%v", err)
+		}
+		return true
+	case "textDocument/implementation": // req
+		var params TextDocumentPositionParams
+		if err := json.Unmarshal(*r.Params, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.Implementation(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.TypeDefinition(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.DocumentColor(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.ColorPresentation(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.FoldingRange(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.Declaration(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.SelectionRange(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.Initialize(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.WillSaveWaitUntil(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.Completion(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.Resolve(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.Hover(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.SignatureHelp(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.Definition(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.References(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.DocumentHighlight(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.DocumentSymbol(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.Symbol(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.CodeAction(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.CodeLens(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.ResolveCodeLens(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.Formatting(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.RangeFormatting(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.OnTypeFormatting(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.Rename(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.PrepareRename(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.DocumentLink(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.ResolveDocumentLink(ctx, &params)
+		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, &params); err != nil {
+			sendParseError(ctx, h.log, r, err)
+			return true
+		}
+		resp, err := h.server.ExecuteCommand(ctx, &params)
+		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, &params); err != nil {
-      sendParseError(ctx, log, r, err)
-      return
+      sendParseError(ctx, h.log, r, err)
+      return true
     }
-    if err := ${side.name}.${nm}(ctx, &params); err != nil {
-      log.Errorf(ctx, "%v", err)
-    }`;
+    if err := h.${side.name}.${nm}(ctx, &params); 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, &params); err != nil {
-      sendParseError(ctx, log, r, err)
-      return
+      sendParseError(ctx, h.log, r, err)
+      return true
     }`;
   }
   const arg2 = a == '' ? '' : ', &params';
-  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, &params); 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 {