all: merge master into gopls-release-branch.0.4

Change-Id: Icf1ae4d1f86ca3b24481d4755d511e82c5f9e609
diff --git a/internal/jsonrpc2/handler.go b/internal/jsonrpc2/handler.go
index 8351579..1382b5e 100644
--- a/internal/jsonrpc2/handler.go
+++ b/internal/jsonrpc2/handler.go
@@ -6,31 +6,17 @@
 
 import (
 	"context"
+	"fmt"
+	"sync"
+
+	"golang.org/x/tools/internal/telemetry/event"
 )
 
-// Handler is the interface used to hook into the message 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
-
-	// Request is called near the start of processing any request.
-	Request(ctx context.Context, conn *Conn, direction Direction, r *WireRequest) context.Context
-}
+// Handler 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.
+// The handler should return ErrNotHandled if it could not handle the request.
+type Handler func(context.Context, *Request) error
 
 // Direction is used to indicate to a logger whether the logged message was being
 // sent or received.
@@ -54,32 +40,77 @@
 	}
 }
 
-type EmptyHandler struct{}
-
-func (EmptyHandler) Deliver(ctx context.Context, r *Request, delivered bool) bool {
-	return false
+// MethodNotFound is a Handler that replies to all call requests with the
+// standard method not found response.
+// This should normally be the final handler in a chain.
+func MethodNotFound(ctx context.Context, r *Request) error {
+	return r.Reply(ctx, nil, NewErrorf(CodeMethodNotFound, "method %q not found", r.Method))
 }
 
-func (EmptyHandler) Cancel(ctx context.Context, conn *Conn, id ID, cancelled bool) bool {
-	return false
-}
-
-func (EmptyHandler) Request(ctx context.Context, conn *Conn, direction Direction, r *WireRequest) context.Context {
-	return ctx
-}
-
-func (EmptyHandler) Response(ctx context.Context, conn *Conn, direction Direction, r *WireResponse) context.Context {
-	return ctx
-}
-
-type defaultHandler struct{ EmptyHandler }
-
-func (defaultHandler) Deliver(ctx context.Context, r *Request, delivered bool) bool {
-	if delivered {
-		return false
+// MustReply creates a Handler that panics if the wrapped handler does
+// not call Reply for every request that it is passed.
+func MustReply(handler Handler) Handler {
+	return func(ctx context.Context, req *Request) error {
+		err := handler(ctx, req)
+		if req.done != nil {
+			panic(fmt.Errorf("request %q was never replied to", req.Method))
+		}
+		return err
 	}
-	if !r.IsNotify() {
-		r.Reply(ctx, nil, NewErrorf(CodeMethodNotFound, "method %q not found", r.Method))
+}
+
+// CancelHandler returns a handler that supports cancellation, and a canceller
+// that can be used to trigger canceling in progress requests.
+func CancelHandler(handler Handler) (Handler, Canceller) {
+	var mu sync.Mutex
+	handling := make(map[ID]context.CancelFunc)
+	wrapped := func(ctx context.Context, req *Request) error {
+		if req.ID != nil {
+			cancelCtx, cancel := context.WithCancel(ctx)
+			ctx = cancelCtx
+			mu.Lock()
+			handling[*req.ID] = cancel
+			mu.Unlock()
+			req.OnReply(func() {
+				mu.Lock()
+				delete(handling, *req.ID)
+				mu.Unlock()
+			})
+		}
+		return handler(ctx, req)
 	}
-	return true
+	return wrapped, func(id ID) {
+		mu.Lock()
+		cancel, found := handling[id]
+		mu.Unlock()
+		if found {
+			cancel()
+		}
+	}
+}
+
+// AsyncHandler returns a handler that processes each request goes in its own
+// goroutine.
+// The handler returns immediately, without the request being processed.
+// Each request then waits for the previous request to finish before it starts.
+// This allows the stream to unblock at the cost of unbounded goroutines
+// all stalled on the previous one.
+func AsyncHandler(handler Handler) Handler {
+	nextRequest := make(chan struct{})
+	close(nextRequest)
+	return func(ctx context.Context, req *Request) error {
+		waitForPrevious := nextRequest
+		nextRequest = make(chan struct{})
+		unlockNext := nextRequest
+		req.OnReply(func() { close(unlockNext) })
+		_, queueDone := event.StartSpan(ctx, "queued")
+		go func() {
+			<-waitForPrevious
+			queueDone()
+			if err := handler(ctx, req); err != nil {
+				event.Error(ctx, "jsonrpc2 async message delivery failed", err)
+			}
+		}()
+		return nil
+	}
 }
diff --git a/internal/jsonrpc2/jsonrpc2.go b/internal/jsonrpc2/jsonrpc2.go
index c7724ad..1f2fd4c 100644
--- a/internal/jsonrpc2/jsonrpc2.go
+++ b/internal/jsonrpc2/jsonrpc2.go
@@ -18,39 +18,46 @@
 	"golang.org/x/tools/internal/telemetry/event"
 )
 
+const (
+	// ErrIdleTimeout is returned when serving timed out waiting for new connections.
+	ErrIdleTimeout = constError("timed out waiting for new connections")
+
+	// ErrDisconnected signals that the stream or connection exited normally.
+	ErrDisconnected = constError("disconnected")
+)
+
 // 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
-	handlers   []Handler
-	stream     Stream
-	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
+	stream      Stream
+	pendingMu   sync.Mutex // protects the pending map
+	pending     map[ID]chan *WireResponse
+	onCancelled CallCanceller
 }
 
-type requestState int
-
-const (
-	requestWaiting = requestState(iota)
-	requestSerial
-	requestParallel
-	requestReplied
-	requestDone
-)
-
 // Request is sent to a server to represent a Call or Notify operaton.
 type Request struct {
-	conn        *Conn
-	cancel      context.CancelFunc
-	state       requestState
-	nextRequest chan struct{}
+	conn *Conn
+	// done holds set of callbacks added by OnReply, and is set back to nil if
+	// Reply has been called.
+	done []func()
 
 	// The Wire values of the request.
 	WireRequest
 }
 
+// Canceller is the type for a function that can cancel an in progress request.
+type Canceller func(id ID)
+
+// CallCanceller is the type for a callback when an outgoing request is
+// has it's context cancelled.
+type CallCanceller func(context.Context, *Conn, ID)
+
+type constError string
+
+func (e constError) Error() string { return string(e) }
+
 // NewErrorf builds a Error struct for the supplied message and code.
 // If args is not empty, message and args will be passed to Sprintf.
 func NewErrorf(code int64, format string, args ...interface{}) *Error {
@@ -64,35 +71,17 @@
 // 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),
+		stream:  s,
+		pending: make(map[ID]chan *WireResponse),
 	}
 	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
-// directly wired in. This method allows a higher level protocol to choose how
-// to propagate the cancel.
-func (c *Conn) Cancel(id ID) {
-	c.handlingMu.Lock()
-	handling, found := c.handling[id]
-	c.handlingMu.Unlock()
-	if found {
-		handling.cancel()
-	}
+// OnCancelled sets the callback used when an outgoing call request has
+// it's context cancelled when still in progress.
+// Only the last callback registered is used.
+func (c *Conn) OnCancelled(cancelled CallCanceller) {
+	c.onCancelled = cancelled
 }
 
 // Notify is called to send a notification request over the connection.
@@ -111,9 +100,6 @@
 	if err != nil {
 		return fmt.Errorf("marshalling notify request: %v", err)
 	}
-	for _, h := range c.handlers {
-		ctx = h.Request(ctx, c, Send, request)
-	}
 	ctx, done := event.StartSpan(ctx, request.Method,
 		tag.Method.Of(request.Method),
 		tag.RPCDirection.Of(tag.Outbound),
@@ -126,7 +112,7 @@
 
 	event.Record(ctx, tag.Started.Of(1))
 	n, err := c.stream.Write(ctx, data)
-	event.Record(ctx, tag.ReceivedBytes.Of(n))
+	event.Record(ctx, tag.SentBytes.Of(n))
 	return err
 }
 
@@ -150,9 +136,6 @@
 	if err != nil {
 		return fmt.Errorf("marshalling call request: %v", err)
 	}
-	for _, h := range c.handlers {
-		ctx = h.Request(ctx, c, Send, request)
-	}
 	ctx, done := event.StartSpan(ctx, request.Method,
 		tag.Method.Of(request.Method),
 		tag.RPCDirection.Of(tag.Outbound),
@@ -178,7 +161,7 @@
 	}()
 	// now we are ready to send
 	n, err := c.stream.Write(ctx, data)
-	event.Record(ctx, tag.ReceivedBytes.Of(n))
+	event.Record(ctx, tag.SentBytes.Of(n))
 	if err != nil {
 		// sending failed, we will never get a response, so don't leave it pending
 		return err
@@ -199,11 +182,8 @@
 		return nil
 	case <-ctx.Done():
 		// Allow the handler to propagate the cancel.
-		cancelled := false
-		for _, h := range c.handlers {
-			if h.Cancel(ctx, c, id, cancelled) {
-				cancelled = true
-			}
+		if c.onCancelled != nil {
+			c.onCancelled(ctx, c, id)
 		}
 		return ctx.Err()
 	}
@@ -217,39 +197,27 @@
 	return r.ID == nil
 }
 
-// Parallel indicates that the system is now allowed to process other requests
-// in parallel with this one.
-// It is safe to call any number of times, but must only be called from the
-// request handling go routine.
-// It is implied by both reply and by the handler returning.
-func (r *Request) Parallel() {
-	if r.state >= requestParallel {
-		return
-	}
-	r.state = requestParallel
-	close(r.nextRequest)
-}
-
 // Reply sends a reply to the given request.
-// It is an error to call this if request was not a call.
 // You must call this exactly once for any given request.
-// It should only be called from the handler go routine.
 // If err is set then result will be ignored.
-// If the request has not yet dropped into parallel mode
-// it will be before this function returns.
+// This will mark the request as done, triggering any done
+// handlers
 func (r *Request) Reply(ctx context.Context, result interface{}, err error) error {
-	if r.state >= requestReplied {
+	if r.done == nil {
 		return fmt.Errorf("reply invoked more than once")
 	}
+
+	defer func() {
+		recordStatus(ctx, err)
+		for i := len(r.done); i > 0; i-- {
+			r.done[i-1]()
+		}
+		r.done = nil
+	}()
+
 	if r.IsNotify() {
-		return fmt.Errorf("reply not invoked with a valid call: %v, %v", r.Method, r.Params)
+		return nil
 	}
-	// reply ends the handling phase of a call, so if we are not yet
-	// parallel we should be now. The go routine is allowed to continue
-	// to do work after replying, which is why it is important to unlock
-	// the rpc system at this point.
-	r.Parallel()
-	r.state = requestReplied
 
 	var raw *json.RawMessage
 	if err == nil {
@@ -271,7 +239,7 @@
 		return err
 	}
 	n, err := r.conn.stream.Write(ctx, data)
-	event.Record(ctx, tag.ReceivedBytes.Of(n))
+	event.Record(ctx, tag.SentBytes.Of(n))
 
 	if err != nil {
 		// TODO(iancottrell): if a stream write fails, we really need to shut down
@@ -281,17 +249,15 @@
 	return nil
 }
 
-func (c *Conn) setHandling(r *Request, active bool) {
-	if r.ID == nil {
-		return
-	}
-	r.conn.handlingMu.Lock()
-	defer r.conn.handlingMu.Unlock()
-	if active {
-		r.conn.handling[*r.ID] = r
-	} else {
-		delete(r.conn.handling, *r.ID)
-	}
+// OnReply adds a done callback to the request.
+// All added callbacks are invoked during the one required call to Reply, and
+// then dropped.
+// It is an error to call this after Reply.
+// This call is not safe for concurrent use, but should only be invoked by
+// handlers and in general only one handler should be working on a request
+// at any time.
+func (r *Request) OnReply(do func()) {
+	r.done = append(r.done, do)
 }
 
 // combined has all the fields of both Request and Response.
@@ -309,12 +275,7 @@
 // caused the termination.
 // It must be called exactly once for each Conn.
 // It returns only when the reader is closed or there is an error in the stream.
-func (c *Conn) Run(runCtx context.Context) error {
-	// we need to make the next request "lock" in an unlocked state to allow
-	// the first incoming request to proceed. All later requests are unlocked
-	// by the preceding request going to parallel mode.
-	nextRequest := make(chan struct{})
-	close(nextRequest)
+func (c *Conn) Run(runCtx context.Context, handler Handler) error {
 	for {
 		// get the data for a message
 		data, n, err := c.stream.Read(runCtx)
@@ -334,13 +295,17 @@
 		switch {
 		case msg.Method != "":
 			// If method is set it must be a request.
-			reqCtx, cancelReq := context.WithCancel(runCtx)
-			thisRequest := nextRequest
-			nextRequest = make(chan struct{})
+			reqCtx, spanDone := event.StartSpan(runCtx, msg.Method,
+				tag.Method.Of(msg.Method),
+				tag.RPCDirection.Of(tag.Inbound),
+				tag.RPCID.Of(msg.ID.String()),
+			)
+			event.Record(reqCtx,
+				tag.Started.Of(1),
+				tag.ReceivedBytes.Of(n))
+
 			req := &Request{
-				conn:        c,
-				cancel:      cancelReq,
-				nextRequest: nextRequest,
+				conn: c,
 				WireRequest: WireRequest{
 					VersionTag: msg.VersionTag,
 					Method:     msg.Method,
@@ -348,40 +313,12 @@
 					ID:         msg.ID,
 				},
 			}
-			for _, h := range c.handlers {
-				reqCtx = h.Request(reqCtx, c, Receive, &req.WireRequest)
+			req.OnReply(func() { spanDone() })
+
+			if err := handler(reqCtx, req); err != nil {
+				// delivery failed, not much we can do
+				event.Error(reqCtx, "jsonrpc2 message delivery failed", err)
 			}
-			reqCtx, done := event.StartSpan(reqCtx, req.WireRequest.Method,
-				tag.Method.Of(req.WireRequest.Method),
-				tag.RPCDirection.Of(tag.Inbound),
-				tag.RPCID.Of(req.WireRequest.ID.String()),
-			)
-			event.Record(reqCtx,
-				tag.Started.Of(1),
-				tag.SentBytes.Of(n))
-			c.setHandling(req, true)
-			_, queueDone := event.StartSpan(reqCtx, "queued")
-			go func() {
-				<-thisRequest
-				queueDone()
-				req.state = requestSerial
-				defer func() {
-					c.setHandling(req, false)
-					if !req.IsNotify() && req.state < requestReplied {
-						req.Reply(reqCtx, nil, NewErrorf(CodeInternalError, "method %q did not reply", req.Method))
-					}
-					req.Parallel()
-					recordStatus(reqCtx, nil)
-					done()
-					cancelReq()
-				}()
-				delivered := false
-				for _, h := range c.handlers {
-					if h.Deliver(reqCtx, req, delivered) {
-						delivered = true
-					}
-				}
-			}()
 		case msg.ID != nil:
 			// If method is not set, this should be a response, in which case we must
 			// have an id to send the response back to the caller.
diff --git a/internal/jsonrpc2/jsonrpc2_test.go b/internal/jsonrpc2/jsonrpc2_test.go
index 4fcca31..a458f44 100644
--- a/internal/jsonrpc2/jsonrpc2_test.go
+++ b/internal/jsonrpc2/jsonrpc2_test.go
@@ -10,11 +10,9 @@
 	"flag"
 	"fmt"
 	"io"
-	"log"
 	"path"
 	"reflect"
 	"testing"
-	"time"
 
 	"golang.org/x/tools/internal/jsonrpc2"
 )
@@ -108,95 +106,46 @@
 		stream = jsonrpc2.NewStream(r, w)
 	}
 	conn := jsonrpc2.NewConn(stream)
-	conn.AddHandler(&handle{log: *logRPC})
 	go func() {
 		defer func() {
 			r.Close()
 			w.Close()
 		}()
-		if err := conn.Run(ctx); err != nil {
+		if err := conn.Run(ctx, testHandler(*logRPC)); err != nil {
 			t.Errorf("Stream failed: %v", err)
 		}
 	}()
 	return conn
 }
 
-type handle struct {
-	log bool
-}
-
-func (h *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 true
+func testHandler(log bool) jsonrpc2.Handler {
+	return func(ctx context.Context, r *jsonrpc2.Request) error {
+		switch r.Method {
+		case "no_args":
+			if r.Params != nil {
+				return r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeInvalidParams, "Expected no params"))
+			}
+			return r.Reply(ctx, true, nil)
+		case "one_string":
+			var v string
+			if err := json.Unmarshal(*r.Params, &v); err != nil {
+				return r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeParseError, "%v", err.Error()))
+			}
+			return r.Reply(ctx, "got:"+v, nil)
+		case "one_number":
+			var v int
+			if err := json.Unmarshal(*r.Params, &v); err != nil {
+				return r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeParseError, "%v", err.Error()))
+			}
+			return r.Reply(ctx, fmt.Sprintf("got:%d", v), nil)
+		case "join":
+			var v []string
+			if err := json.Unmarshal(*r.Params, &v); err != nil {
+				return r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeParseError, "%v", err.Error()))
+			}
+			return r.Reply(ctx, path.Join(v...), nil)
+		default:
+			return jsonrpc2.MethodNotFound(ctx, r)
 		}
-		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 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 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 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 (h *handle) Cancel(ctx context.Context, conn *jsonrpc2.Conn, id jsonrpc2.ID, cancelled bool) bool {
-	return false
-}
-
-func (h *handle) Request(ctx context.Context, conn *jsonrpc2.Conn, direction jsonrpc2.Direction, r *jsonrpc2.WireRequest) context.Context {
-	if h.log {
-		if r.ID != nil {
-			log.Printf("%v call [%v] %s %v", direction, r.ID, r.Method, r.Params)
-		} else {
-			log.Printf("%v notification %s %v", direction, r.Method, r.Params)
-		}
-		ctx = context.WithValue(ctx, "method", r.Method)
-		ctx = context.WithValue(ctx, "start", time.Now())
-	}
-	return ctx
-}
-
-func (h *handle) Response(ctx context.Context, conn *jsonrpc2.Conn, direction jsonrpc2.Direction, r *jsonrpc2.WireResponse) context.Context {
-	if h.log {
-		method := ctx.Value("method")
-		elapsed := time.Since(ctx.Value("start").(time.Time))
-		log.Printf("%v response in %v [%v] %s %v", direction, elapsed, r.ID, method, r.Result)
-	}
-	return ctx
-}
-
-func (h *handle) Done(ctx context.Context, err error) {
-}
-
-func (h *handle) Read(ctx context.Context, bytes int64) context.Context {
-	return ctx
-}
-
-func (h *handle) Wrote(ctx context.Context, bytes int64) context.Context {
-	return ctx
-}
-
-func (h *handle) Error(ctx context.Context, err error) {
-	log.Printf("%v", err)
 }
diff --git a/internal/jsonrpc2/serve.go b/internal/jsonrpc2/serve.go
index c2cfa60..fe1899e 100644
--- a/internal/jsonrpc2/serve.go
+++ b/internal/jsonrpc2/serve.go
@@ -6,7 +6,6 @@
 
 import (
 	"context"
-	"errors"
 	"fmt"
 	"log"
 	"net"
@@ -39,8 +38,7 @@
 func HandlerServer(h Handler) StreamServer {
 	return ServerFunc(func(ctx context.Context, s Stream) error {
 		conn := NewConn(s)
-		conn.AddHandler(h)
-		return conn.Run(ctx)
+		return conn.Run(ctx, h)
 	})
 }
 
@@ -59,9 +57,6 @@
 	return Serve(ctx, ln, server, idleTimeout)
 }
 
-// ErrIdleTimeout is returned when serving timed out waiting for new connections.
-var ErrIdleTimeout = errors.New("timed out waiting for new connections")
-
 // Serve accepts incoming connections from the network, and handles them using
 // the provided server. If idleTimeout is non-zero, ListenAndServe exits after
 // there are no clients for this duration, otherwise it exits only on error.
diff --git a/internal/jsonrpc2/serve_test.go b/internal/jsonrpc2/serve_test.go
index ec6e1e1..2cbfe8f 100644
--- a/internal/jsonrpc2/serve_test.go
+++ b/internal/jsonrpc2/serve_test.go
@@ -30,7 +30,7 @@
 		return conn
 	}
 
-	server := HandlerServer(EmptyHandler{})
+	server := HandlerServer(MethodNotFound)
 	// connTimer := &fakeTimer{c: make(chan time.Time, 1)}
 	var (
 		runErr error
diff --git a/internal/jsonrpc2/servertest/servertest.go b/internal/jsonrpc2/servertest/servertest.go
index 1217591..13815e0 100644
--- a/internal/jsonrpc2/servertest/servertest.go
+++ b/internal/jsonrpc2/servertest/servertest.go
@@ -53,9 +53,7 @@
 	s.cls.add(func() {
 		netConn.Close()
 	})
-	conn := jsonrpc2.NewConn(jsonrpc2.NewHeaderStream(netConn, netConn))
-	go conn.Run(ctx)
-	return conn
+	return jsonrpc2.NewConn(jsonrpc2.NewHeaderStream(netConn, netConn))
 }
 
 // Close closes all connected pipes.
@@ -92,9 +90,7 @@
 	go s.server.ServeStream(ctx, serverStream)
 
 	clientStream := jsonrpc2.NewStream(cReader, sWriter)
-	clientConn := jsonrpc2.NewConn(clientStream)
-	go clientConn.Run(ctx)
-	return clientConn
+	return jsonrpc2.NewConn(clientStream)
 }
 
 // Close closes all connected pipes.
diff --git a/internal/jsonrpc2/servertest/servertest_test.go b/internal/jsonrpc2/servertest/servertest_test.go
index 21b540a..c0905d1 100644
--- a/internal/jsonrpc2/servertest/servertest_test.go
+++ b/internal/jsonrpc2/servertest/servertest_test.go
@@ -12,25 +12,18 @@
 	"golang.org/x/tools/internal/jsonrpc2"
 )
 
-type fakeHandler struct {
-	jsonrpc2.EmptyHandler
-}
-
 type msg struct {
 	Msg string
 }
 
-func (fakeHandler) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool {
-	if err := r.Reply(ctx, &msg{"pong"}, nil); err != nil {
-		panic(err)
-	}
-	return true
+func fakeHandler(ctx context.Context, r *jsonrpc2.Request) error {
+	return r.Reply(ctx, &msg{"pong"}, nil)
 }
 
 func TestTestServer(t *testing.T) {
 	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
 	defer cancel()
-	server := jsonrpc2.HandlerServer(fakeHandler{})
+	server := jsonrpc2.HandlerServer(fakeHandler)
 	tcpTS := NewTCPServer(ctx, server)
 	defer tcpTS.Close()
 	pipeTS := NewPipeServer(ctx, server)
@@ -47,6 +40,7 @@
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
 			conn := test.connector.Connect(ctx)
+			go conn.Run(ctx, jsonrpc2.MethodNotFound)
 			var got msg
 			if err := conn.Call(ctx, "ping", &msg{"ping"}, &got); err != nil {
 				t.Fatal(err)
diff --git a/internal/jsonrpc2/stream.go b/internal/jsonrpc2/stream.go
index 2d1e7c4..3276c1a 100644
--- a/internal/jsonrpc2/stream.go
+++ b/internal/jsonrpc2/stream.go
@@ -8,7 +8,6 @@
 	"bufio"
 	"context"
 	"encoding/json"
-	"errors"
 	"fmt"
 	"io"
 	"strconv"
@@ -29,9 +28,6 @@
 	Write(context.Context, []byte) (int64, error)
 }
 
-// ErrDisconnected signals that the stream or connection exited normally.
-var ErrDisconnected = errors.New("disconnected")
-
 // NewStream returns a Stream built on top of an io.Reader and io.Writer
 // The messages are sent with no wrapping, and rely on json decode consistency
 // to determine message boundaries.
diff --git a/internal/lsp/cmd/cmd.go b/internal/lsp/cmd/cmd.go
index f667c46..e05d2b5 100644
--- a/internal/lsp/cmd/cmd.go
+++ b/internal/lsp/cmd/cmd.go
@@ -233,10 +233,11 @@
 	stream := jsonrpc2.NewHeaderStream(conn, conn)
 	cc := jsonrpc2.NewConn(stream)
 	connection.Server = protocol.ServerDispatcher(cc)
-	cc.AddHandler(protocol.ClientHandler(connection.Client))
-	cc.AddHandler(protocol.Canceller{})
 	ctx = protocol.WithClient(ctx, connection.Client)
-	go cc.Run(ctx)
+	go cc.Run(ctx,
+		protocol.Handlers(
+			protocol.ClientHandler(connection.Client,
+				jsonrpc2.MethodNotFound)))
 	return connection, connection.initialize(ctx, app.options)
 }
 
diff --git a/internal/lsp/debug/rpc.go b/internal/lsp/debug/rpc.go
index 823ee9d..e1c93e2 100644
--- a/internal/lsp/debug/rpc.go
+++ b/internal/lsp/debug/rpc.go
@@ -16,7 +16,6 @@
 	"golang.org/x/tools/internal/lsp/debug/tag"
 	"golang.org/x/tools/internal/telemetry/event"
 	"golang.org/x/tools/internal/telemetry/export"
-	"golang.org/x/tools/internal/telemetry/export/metric"
 )
 
 var rpcTmpl = template.Must(template.Must(baseTemplate.Clone()).Parse(`
@@ -32,10 +31,10 @@
 		<b>{{.Method}}</b> {{.Started}} <a href="/trace/{{.Method}}">traces</a> ({{.InProgress}} in progress)
 		<br>
 		<i>Latency</i> {{with .Latency}}{{.Mean}} ({{.Min}}<{{.Max}}){{end}}
-		<i>By bucket</i> 0s {{range .Latency.Values}}<b>{{.Count}}</b> {{.Limit}} {{end}}
+		<i>By bucket</i> 0s {{range .Latency.Values}}{{if gt .Count 0}}<b>{{.Count}}</b> {{.Limit}} {{end}}{{end}}
 		<br>
-		<i>Received</i> {{with .Received}}{{.Mean}} ({{.Min}}<{{.Max}}){{end}}
-		<i>Sent</i> {{with .Sent}}{{.Mean}} ({{.Min}}<{{.Max}}){{end}}
+		<i>Received</i> {{.Received}} (avg. {{.ReceivedMean}})
+		<i>Sent</i> {{.Sent}} (avg. {{.SentMean}})
 		<br>
 		<i>Result codes</i> {{range .Codes}}{{.Key}}={{.Count}} {{end}}
 		</P>
@@ -45,25 +44,24 @@
 
 type rpcs struct {
 	mu       sync.Mutex
-	Inbound  []*rpcStats
-	Outbound []*rpcStats
+	Inbound  []*rpcStats // stats for incoming lsp rpcs sorted by method name
+	Outbound []*rpcStats // stats for outgoing lsp rpcs sorted by method name
 }
 
 type rpcStats struct {
-	Method     string
-	Started    int64
-	Completed  int64
-	InProgress int64
-	Latency    rpcTimeHistogram
-	Received   rpcBytesHistogram
-	Sent       rpcBytesHistogram
-	Codes      []*rpcCodeBucket
+	Method    string
+	Started   int64
+	Completed int64
+
+	Latency  rpcTimeHistogram
+	Received byteUnits
+	Sent     byteUnits
+	Codes    []*rpcCodeBucket
 }
 
 type rpcTimeHistogram struct {
 	Sum    timeUnits
 	Count  int64
-	Mean   timeUnits
 	Min    timeUnits
 	Max    timeUnits
 	Values []rpcTimeBucket
@@ -74,140 +72,141 @@
 	Count int64
 }
 
-type rpcBytesHistogram struct {
-	Sum    byteUnits
-	Count  int64
-	Mean   byteUnits
-	Min    byteUnits
-	Max    byteUnits
-	Values []rpcBytesBucket
-}
-
-type rpcBytesBucket struct {
-	Limit byteUnits
-	Count int64
-}
-
 type rpcCodeBucket struct {
 	Key   string
 	Count int64
 }
 
 func (r *rpcs) ProcessEvent(ctx context.Context, ev event.Event, tagMap event.TagMap) context.Context {
-	switch {
-	case ev.IsEndSpan():
-		// calculate latency if this was an rpc span
-		span := export.GetSpan(ctx)
-		if span == nil {
-			return ctx
-		}
-		// is this a finished rpc span, if so it will have a status code record
-		for _, ev := range span.Events() {
-			code := tag.StatusCode.Get(ev.Map())
-			if code != "" {
-				elapsedTime := span.Finish().At.Sub(span.Start().At)
-				latencyMillis := float64(elapsedTime) / float64(time.Millisecond)
-				statsCtx := event.Label1(ctx, tag.StatusCode.Of(code))
-				event.Record1(statsCtx, tag.Latency.Of(latencyMillis))
-			}
-		}
-		return ctx
-	case ev.IsRecord():
-		// fall through to the metrics handling logic
-	default:
-		// ignore all other event types
-		return ctx
-	}
 	r.mu.Lock()
 	defer r.mu.Unlock()
-	//TODO(38168): we should just deal with the events here and not use metrics
-	metrics := metric.Entries.Get(tagMap).([]metric.Data)
-	for _, data := range metrics {
-		for i, group := range data.Groups() {
-			set := &r.Inbound
-			groupTags := event.NewTagMap(group...)
-			if tag.RPCDirection.Get(groupTags) == tag.Outbound {
-				set = &r.Outbound
-			}
-			method := tag.Method.Get(groupTags)
-			index := sort.Search(len(*set), func(i int) bool {
-				return (*set)[i].Method >= method
-			})
-			if index >= len(*set) || (*set)[index].Method != method {
-				old := *set
-				*set = make([]*rpcStats, len(old)+1)
-				copy(*set, old[:index])
-				copy((*set)[index+1:], old[index:])
-				(*set)[index] = &rpcStats{Method: method}
-			}
-			stats := (*set)[index]
-			switch data.Handle() {
-			case started.Name:
-				stats.Started = data.(*metric.Int64Data).Rows[i]
-			case completed.Name:
-				status := tag.StatusCode.Get(groupTags)
-				var b *rpcCodeBucket
-				for c, entry := range stats.Codes {
-					if entry.Key == status {
-						b = stats.Codes[c]
-						break
-					}
-				}
-				if b == nil {
-					b = &rpcCodeBucket{Key: status}
-					stats.Codes = append(stats.Codes, b)
-					sort.Slice(stats.Codes, func(i int, j int) bool {
-						return stats.Codes[i].Key < stats.Codes[j].Key
-					})
-				}
-				b.Count = data.(*metric.Int64Data).Rows[i]
-			case latency.Name:
-				data := data.(*metric.HistogramFloat64Data)
-				row := data.Rows[i]
-				stats.Latency.Count = row.Count
-				stats.Latency.Sum = timeUnits(row.Sum)
-				stats.Latency.Min = timeUnits(row.Min)
-				stats.Latency.Max = timeUnits(row.Max)
-				stats.Latency.Mean = timeUnits(row.Sum) / timeUnits(row.Count)
-				stats.Latency.Values = make([]rpcTimeBucket, len(data.Info.Buckets))
-				last := int64(0)
-				for i, b := range data.Info.Buckets {
-					stats.Latency.Values[i].Limit = timeUnits(b)
-					stats.Latency.Values[i].Count = row.Values[i] - last
-					last = row.Values[i]
-				}
-			case sentBytes.Name:
-				data := data.(*metric.HistogramInt64Data)
-				row := data.Rows[i]
-				stats.Sent.Count = row.Count
-				stats.Sent.Sum = byteUnits(row.Sum)
-				stats.Sent.Min = byteUnits(row.Min)
-				stats.Sent.Max = byteUnits(row.Max)
-				stats.Sent.Mean = byteUnits(row.Sum) / byteUnits(row.Count)
-			case receivedBytes.Name:
-				data := data.(*metric.HistogramInt64Data)
-				row := data.Rows[i]
-				stats.Received.Count = row.Count
-				stats.Received.Sum = byteUnits(row.Sum)
-				stats.Sent.Min = byteUnits(row.Min)
-				stats.Sent.Max = byteUnits(row.Max)
-				stats.Received.Mean = byteUnits(row.Sum) / byteUnits(row.Count)
-			}
+	switch {
+	case ev.IsStartSpan():
+		if _, stats := r.getRPCSpan(ctx, ev); stats != nil {
+			stats.Started++
 		}
-	}
-
-	for _, set := range [][]*rpcStats{r.Inbound, r.Outbound} {
-		for _, stats := range set {
-			stats.Completed = 0
-			for _, b := range stats.Codes {
-				stats.Completed += b.Count
+	case ev.IsEndSpan():
+		span, stats := r.getRPCSpan(ctx, ev)
+		if stats != nil {
+			endRPC(ctx, ev, span, stats)
+		}
+	case ev.IsRecord():
+		sent := byteUnits(tag.SentBytes.Get(tagMap))
+		rec := byteUnits(tag.ReceivedBytes.Get(tagMap))
+		if sent != 0 || rec != 0 {
+			if _, stats := r.getRPCSpan(ctx, ev); stats != nil {
+				stats.Sent += sent
+				stats.Received += rec
 			}
-			stats.InProgress = stats.Started - stats.Completed
 		}
 	}
 	return ctx
 }
 
+func endRPC(ctx context.Context, ev event.Event, span *export.Span, stats *rpcStats) {
+	// update the basic counts
+	stats.Completed++
+
+	// get and record the status code
+	if status := getStatusCode(span); status != "" {
+		var b *rpcCodeBucket
+		for c, entry := range stats.Codes {
+			if entry.Key == status {
+				b = stats.Codes[c]
+				break
+			}
+		}
+		if b == nil {
+			b = &rpcCodeBucket{Key: status}
+			stats.Codes = append(stats.Codes, b)
+			sort.Slice(stats.Codes, func(i int, j int) bool {
+				return stats.Codes[i].Key < stats.Codes[j].Key
+			})
+		}
+		b.Count++
+	}
+
+	// calculate latency if this was an rpc span
+	elapsedTime := span.Finish().At.Sub(span.Start().At)
+	latencyMillis := timeUnits(elapsedTime) / timeUnits(time.Millisecond)
+	if stats.Latency.Count == 0 {
+		stats.Latency.Min = latencyMillis
+		stats.Latency.Max = latencyMillis
+	} else {
+		if stats.Latency.Min > latencyMillis {
+			stats.Latency.Min = latencyMillis
+		}
+		if stats.Latency.Max < latencyMillis {
+			stats.Latency.Max = latencyMillis
+		}
+	}
+	stats.Latency.Count++
+	stats.Latency.Sum += latencyMillis
+	for i := range stats.Latency.Values {
+		if stats.Latency.Values[i].Limit > latencyMillis {
+			stats.Latency.Values[i].Count++
+			break
+		}
+	}
+}
+
+func (r *rpcs) getRPCSpan(ctx context.Context, ev event.Event) (*export.Span, *rpcStats) {
+	// get the span
+	span := export.GetSpan(ctx)
+	if span == nil {
+		return nil, nil
+	}
+	// use the span start event look up the correct stats block
+	// we do this because it prevents us matching a sub span
+	return span, r.getRPCStats(span.Start())
+}
+
+func (r *rpcs) getRPCStats(tagMap event.TagMap) *rpcStats {
+	method := tag.Method.Get(tagMap)
+	if method == "" {
+		return nil
+	}
+	set := &r.Inbound
+	if tag.RPCDirection.Get(tagMap) != tag.Inbound {
+		set = &r.Outbound
+	}
+	// get the record for this method
+	index := sort.Search(len(*set), func(i int) bool {
+		return (*set)[i].Method >= method
+	})
+
+	if index < len(*set) && (*set)[index].Method == method {
+		return (*set)[index]
+	}
+
+	old := *set
+	*set = make([]*rpcStats, len(old)+1)
+	copy(*set, old[:index])
+	copy((*set)[index+1:], old[index:])
+	stats := &rpcStats{Method: method}
+	stats.Latency.Values = make([]rpcTimeBucket, len(millisecondsDistribution))
+	for i, m := range millisecondsDistribution {
+		stats.Latency.Values[i].Limit = timeUnits(m)
+	}
+	(*set)[index] = stats
+	return stats
+}
+
+func (s *rpcStats) InProgress() int64       { return s.Started - s.Completed }
+func (s *rpcStats) SentMean() byteUnits     { return s.Sent / byteUnits(s.Started) }
+func (s *rpcStats) ReceivedMean() byteUnits { return s.Received / byteUnits(s.Started) }
+
+func (h *rpcTimeHistogram) Mean() timeUnits { return h.Sum / timeUnits(h.Count) }
+
+func getStatusCode(span *export.Span) string {
+	for _, ev := range span.Events() {
+		if status := tag.StatusCode.Get(ev); status != "" {
+			return status
+		}
+	}
+	return ""
+}
+
 func (r *rpcs) getData(req *http.Request) interface{} {
 	return r
 }
diff --git a/internal/lsp/debug/serve.go b/internal/lsp/debug/serve.go
index 11364b6..c5781e5 100644
--- a/internal/lsp/debug/serve.go
+++ b/internal/lsp/debug/serve.go
@@ -547,7 +547,7 @@
 
 		if ev.IsLog() {
 			// Don't log context cancellation errors.
-			if err := event.Err.Get(ev.Map()); xerrors.Is(err, context.Canceled) {
+			if err := event.Err.Get(ev); xerrors.Is(err, context.Canceled) {
 				return ctx
 			}
 			// Make sure any log messages without an instance go to stderr.
diff --git a/internal/lsp/fake/editor.go b/internal/lsp/fake/editor.go
index 3b4e72f..1cd213a 100644
--- a/internal/lsp/fake/editor.go
+++ b/internal/lsp/fake/editor.go
@@ -57,7 +57,10 @@
 	e := NewEditor(ws)
 	e.server = protocol.ServerDispatcher(conn)
 	e.client = &Client{Editor: e}
-	conn.AddHandler(protocol.ClientHandler(e.client))
+	go conn.Run(ctx,
+		protocol.Handlers(
+			protocol.ClientHandler(e.client,
+				jsonrpc2.MethodNotFound)))
 	if err := e.initialize(ctx); err != nil {
 		return nil, err
 	}
diff --git a/internal/lsp/lsprpc/lsprpc.go b/internal/lsp/lsprpc/lsprpc.go
index 37003f1..5c971f2 100644
--- a/internal/lsp/lsprpc/lsprpc.go
+++ b/internal/lsp/lsprpc/lsprpc.go
@@ -134,18 +134,17 @@
 			event.Error(ctx, "error shutting down", err)
 		}
 	}()
-	conn.AddHandler(protocol.ServerHandler(server))
-	conn.AddHandler(protocol.Canceller{})
 	executable, err := os.Executable()
 	if err != nil {
 		log.Printf("error getting gopls path: %v", err)
 		executable = ""
 	}
-	conn.AddHandler(&handshaker{
-		client:    dc,
-		goplsPath: executable,
-	})
-	return conn.Run(protocol.WithClient(ctx, client))
+	ctx = protocol.WithClient(ctx, client)
+	return conn.Run(ctx,
+		protocol.Handlers(
+			handshaker(dc, executable,
+				protocol.ServerHandler(server,
+					jsonrpc2.MethodNotFound))))
 }
 
 // A Forwarder is a jsonrpc2.StreamServer that handles an LSP stream by
@@ -235,7 +234,7 @@
 		return nil, fmt.Errorf("dialing remote: %v", err)
 	}
 	serverConn := jsonrpc2.NewConn(jsonrpc2.NewHeaderStream(netConn, netConn))
-	go serverConn.Run(ctx)
+	go serverConn.Run(ctx, jsonrpc2.MethodNotFound)
 	var state ServerState
 	if err := serverConn.Call(ctx, sessionsMethod, nil, &state); err != nil {
 		return nil, fmt.Errorf("querying server state: %v", err)
@@ -257,14 +256,12 @@
 	server := protocol.ServerDispatcher(serverConn)
 
 	// Forward between connections.
-	serverConn.AddHandler(protocol.ClientHandler(client))
-	serverConn.AddHandler(protocol.Canceller{})
-	clientConn.AddHandler(protocol.ServerHandler(server))
-	clientConn.AddHandler(protocol.Canceller{})
-	clientConn.AddHandler(forwarderHandler{})
 	g, ctx := errgroup.WithContext(ctx)
 	g.Go(func() error {
-		return serverConn.Run(ctx)
+		return serverConn.Run(ctx,
+			protocol.Handlers(
+				protocol.ClientHandler(client,
+					jsonrpc2.MethodNotFound)))
 	})
 	// Don't run the clientConn yet, so that we can complete the handshake before
 	// processing any client messages.
@@ -302,7 +299,11 @@
 		})
 	}
 	g.Go(func() error {
-		return clientConn.Run(ctx)
+		return clientConn.Run(ctx,
+			protocol.Handlers(
+				forwarderHandler(
+					protocol.ServerHandler(server,
+						jsonrpc2.MethodNotFound))))
 	})
 
 	return g.Wait()
@@ -414,28 +415,19 @@
 // forwarderHandler intercepts 'exit' messages to prevent the shared gopls
 // instance from exiting. In the future it may also intercept 'shutdown' to
 // provide more graceful shutdown of the client connection.
-type forwarderHandler struct {
-	jsonrpc2.EmptyHandler
-}
-
-func (forwarderHandler) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool {
-	// TODO(golang.org/issues/34111): we should more gracefully disconnect here,
-	// once that process exists.
-	if r.Method == "exit" {
-		ForwarderExitFunc(0)
-		// Still return true here to prevent the message from being delivered: in
-		// tests, ForwarderExitFunc may be overridden to something that doesn't
-		// exit the process.
-		return true
+func forwarderHandler(handler jsonrpc2.Handler) jsonrpc2.Handler {
+	return func(ctx context.Context, r *jsonrpc2.Request) error {
+		// TODO(golang.org/issues/34111): we should more gracefully disconnect here,
+		// once that process exists.
+		if r.Method == "exit" {
+			ForwarderExitFunc(0)
+			// reply nil here to consume the message: in
+			// tests, ForwarderExitFunc may be overridden to something that doesn't
+			// exit the process.
+			return r.Reply(ctx, nil, nil)
+		}
+		return handler(ctx, r)
 	}
-	return false
-}
-
-type handshaker struct {
-	jsonrpc2.EmptyHandler
-	client    *debugClient
-	instance  *debug.Instance
-	goplsPath string
 }
 
 // A handshakeRequest identifies a client to the LSP server.
@@ -493,55 +485,51 @@
 	sessionsMethod  = "gopls/sessions"
 )
 
-func (h *handshaker) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool {
-	switch r.Method {
-	case handshakeMethod:
-		var req handshakeRequest
-		if err := json.Unmarshal(*r.Params, &req); err != nil {
-			sendError(ctx, r, err)
-			return true
-		}
-		h.client.debugAddress = req.DebugAddr
-		h.client.logfile = req.Logfile
-		h.client.serverID = req.ServerID
-		h.client.goplsPath = req.GoplsPath
-		resp := handshakeResponse{
-			ClientID:  h.client.id,
-			SessionID: cache.DebugSession{Session: h.client.session}.ID(),
-			GoplsPath: h.goplsPath,
-		}
-		if di := debug.GetInstance(ctx); di != nil {
-			resp.Logfile = di.Logfile
-			resp.DebugAddr = di.ListenedDebugAddress
-		}
-
-		if err := r.Reply(ctx, resp, nil); err != nil {
-			event.Error(ctx, "replying to handshake", err)
-		}
-		return true
-	case sessionsMethod:
-		resp := ServerState{
-			GoplsPath:       h.goplsPath,
-			CurrentClientID: h.client.ID(),
-		}
-		if di := debug.GetInstance(ctx); di != nil {
-			resp.Logfile = di.Logfile
-			resp.DebugAddr = di.ListenedDebugAddress
-			for _, c := range di.State.Clients() {
-				resp.Clients = append(resp.Clients, ClientSession{
-					ClientID:  c.ID(),
-					SessionID: c.Session().ID(),
-					Logfile:   c.Logfile(),
-					DebugAddr: c.DebugAddress(),
-				})
+func handshaker(client *debugClient, goplsPath string, handler jsonrpc2.Handler) jsonrpc2.Handler {
+	return func(ctx context.Context, r *jsonrpc2.Request) error {
+		switch r.Method {
+		case handshakeMethod:
+			var req handshakeRequest
+			if err := json.Unmarshal(*r.Params, &req); err != nil {
+				sendError(ctx, r, err)
+				return nil
 			}
+			client.debugAddress = req.DebugAddr
+			client.logfile = req.Logfile
+			client.serverID = req.ServerID
+			client.goplsPath = req.GoplsPath
+			resp := handshakeResponse{
+				ClientID:  client.id,
+				SessionID: cache.DebugSession{Session: client.session}.ID(),
+				GoplsPath: goplsPath,
+			}
+			if di := debug.GetInstance(ctx); di != nil {
+				resp.Logfile = di.Logfile
+				resp.DebugAddr = di.ListenedDebugAddress
+			}
+
+			return r.Reply(ctx, resp, nil)
+		case sessionsMethod:
+			resp := ServerState{
+				GoplsPath:       goplsPath,
+				CurrentClientID: client.ID(),
+			}
+			if di := debug.GetInstance(ctx); di != nil {
+				resp.Logfile = di.Logfile
+				resp.DebugAddr = di.ListenedDebugAddress
+				for _, c := range di.State.Clients() {
+					resp.Clients = append(resp.Clients, ClientSession{
+						ClientID:  c.ID(),
+						SessionID: c.Session().ID(),
+						Logfile:   c.Logfile(),
+						DebugAddr: c.DebugAddress(),
+					})
+				}
+			}
+			return r.Reply(ctx, resp, nil)
 		}
-		if err := r.Reply(ctx, resp, nil); err != nil {
-			event.Error(ctx, "replying to sessions request", err)
-		}
-		return true
+		return handler(ctx, r)
 	}
-	return false
 }
 
 func sendError(ctx context.Context, req *jsonrpc2.Request, err error) {
diff --git a/internal/lsp/lsprpc/lsprpc_test.go b/internal/lsp/lsprpc/lsprpc_test.go
index bb2c3b3..37a3e9a 100644
--- a/internal/lsp/lsprpc/lsprpc_test.go
+++ b/internal/lsp/lsprpc/lsprpc_test.go
@@ -11,6 +11,7 @@
 	"testing"
 	"time"
 
+	"golang.org/x/tools/internal/jsonrpc2"
 	"golang.org/x/tools/internal/jsonrpc2/servertest"
 	"golang.org/x/tools/internal/lsp/cache"
 	"golang.org/x/tools/internal/lsp/debug"
@@ -54,7 +55,7 @@
 	ts := servertest.NewPipeServer(ctx, ss)
 	defer ts.Close()
 	cc := ts.Connect(ctx)
-	cc.AddHandler(protocol.ClientHandler(client))
+	go cc.Run(ctx, protocol.ClientHandler(client, jsonrpc2.MethodNotFound))
 
 	protocol.ServerDispatcher(cc).DidOpen(ctx, &protocol.DidOpenTextDocumentParams{})
 
@@ -127,7 +128,11 @@
 	for _, test := range tests {
 		t.Run(test.serverType, func(t *testing.T) {
 			cc := test.ts.Connect(baseCtx)
-			cc.AddHandler(protocol.Canceller{})
+			sd := protocol.ServerDispatcher(cc)
+			go cc.Run(baseCtx,
+				protocol.Handlers(
+					jsonrpc2.MethodNotFound))
+
 			ctx := context.Background()
 			ctx1, cancel1 := context.WithCancel(ctx)
 			var (
@@ -137,11 +142,11 @@
 			wg.Add(2)
 			go func() {
 				defer wg.Done()
-				_, err1 = protocol.ServerDispatcher(cc).Hover(ctx1, &protocol.HoverParams{})
+				_, err1 = sd.Hover(ctx1, &protocol.HoverParams{})
 			}()
 			go func() {
 				defer wg.Done()
-				_, err2 = protocol.ServerDispatcher(cc).Resolve(ctx, &protocol.CompletionItem{})
+				_, err2 = sd.Resolve(ctx, &protocol.CompletionItem{})
 			}()
 			// Wait for the Hover request to start.
 			<-server.started
@@ -153,7 +158,7 @@
 			if err2 != nil {
 				t.Errorf("uncancelled Hover(): err: %v", err2)
 			}
-			if _, err := protocol.ServerDispatcher(cc).Resolve(ctx, &protocol.CompletionItem{}); err != nil {
+			if _, err := sd.Resolve(ctx, &protocol.CompletionItem{}); err != nil {
 				t.Errorf("subsequent Hover(): %v", err)
 			}
 		})
diff --git a/internal/lsp/protocol/protocol.go b/internal/lsp/protocol/protocol.go
index ebb0642..00610ad 100644
--- a/internal/lsp/protocol/protocol.go
+++ b/internal/lsp/protocol/protocol.go
@@ -19,86 +19,61 @@
 	RequestCancelledError = -32800
 )
 
-type clientHandler struct {
-	jsonrpc2.EmptyHandler
-	client Client
-}
-
-// ClientHandler returns a jsonrpc2.Handler that handles the LSP client
-// protocol.
-func ClientHandler(client Client) jsonrpc2.Handler {
-	return &clientHandler{client: client}
-}
-
-type serverHandler struct {
-	jsonrpc2.EmptyHandler
-	server Server
-}
-
-// ServerHandler returns a jsonrpc2.Handler that handles the LSP server
-// protocol.
-func ServerHandler(server Server) jsonrpc2.Handler {
-	return &serverHandler{server: server}
-}
-
 // ClientDispatcher returns a Client that dispatches LSP requests across the
 // given jsonrpc2 connection.
 func ClientDispatcher(conn *jsonrpc2.Conn) Client {
+	conn.OnCancelled(cancelCall)
 	return &clientDispatcher{Conn: conn}
 }
 
 // ServerDispatcher returns a Server that dispatches LSP requests across the
 // given jsonrpc2 connection.
 func ServerDispatcher(conn *jsonrpc2.Conn) Server {
+	conn.OnCancelled(cancelCall)
 	return &serverDispatcher{Conn: conn}
 }
 
-// Canceller is a jsonrpc2.Handler that handles LSP request cancellation.
-type Canceller struct{ jsonrpc2.EmptyHandler }
-
-func (Canceller) Request(ctx context.Context, conn *jsonrpc2.Conn, direction jsonrpc2.Direction, r *jsonrpc2.WireRequest) context.Context {
-	if direction == jsonrpc2.Receive && r.Method == "$/cancelRequest" {
-		var params CancelParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			event.Error(ctx, "", err)
-		} else {
-			v := jsonrpc2.ID{}
-			if n, ok := params.ID.(float64); ok {
-				v.Number = int64(n)
-			} else if s, ok := params.ID.(string); ok {
-				v.Name = s
-			} else {
-				event.Error(ctx, fmt.Sprintf("Request ID %v malformed", params.ID), nil)
-				return ctx
-			}
-			conn.Cancel(v)
-		}
-	}
-	return ctx
+func Handlers(handler jsonrpc2.Handler) jsonrpc2.Handler {
+	return CancelHandler(
+		CancelHandler(
+			jsonrpc2.AsyncHandler(
+				jsonrpc2.MustReply(handler))))
 }
 
-func (Canceller) Cancel(ctx context.Context, conn *jsonrpc2.Conn, id jsonrpc2.ID, cancelled bool) bool {
-	if cancelled {
-		return false
+func CancelHandler(handler jsonrpc2.Handler) jsonrpc2.Handler {
+	handler, canceller := jsonrpc2.CancelHandler(handler)
+	return func(ctx context.Context, req *jsonrpc2.Request) error {
+		if req.Method != "$/cancelRequest" {
+			return handler(ctx, req)
+		}
+		var params CancelParams
+		if err := json.Unmarshal(*req.Params, &params); err != nil {
+			return sendParseError(ctx, req, err)
+		}
+		v := jsonrpc2.ID{}
+		if n, ok := params.ID.(float64); ok {
+			v.Number = int64(n)
+		} else if s, ok := params.ID.(string); ok {
+			v.Name = s
+		} else {
+			return sendParseError(ctx, req, fmt.Errorf("Request ID %v malformed", params.ID))
+		}
+		canceller(v)
+		return req.Reply(ctx, nil, nil)
 	}
+}
+
+func cancelCall(ctx context.Context, conn *jsonrpc2.Conn, id jsonrpc2.ID) {
 	ctx = xcontext.Detach(ctx)
 	ctx, done := event.StartSpan(ctx, "protocol.canceller")
 	defer done()
 	// Note that only *jsonrpc2.ID implements json.Marshaler.
 	conn.Notify(ctx, "$/cancelRequest", &CancelParams{ID: &id})
-	return true
 }
 
-func (Canceller) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool {
-	// Hide cancellations from downstream handlers.
-	return r.Method == "$/cancelRequest"
-}
-
-func sendParseError(ctx context.Context, req *jsonrpc2.Request, err error) {
+func sendParseError(ctx context.Context, req *jsonrpc2.Request, err error) error {
 	if _, ok := err.(*jsonrpc2.Error); !ok {
 		err = jsonrpc2.NewErrorf(jsonrpc2.CodeParseError, "%v", err)
 	}
-	if err := req.Reply(ctx, nil, err); err != nil {
-		event.Error(ctx, "", err)
-	}
+	return req.Reply(ctx, nil, err)
 }
diff --git a/internal/lsp/protocol/tsclient.go b/internal/lsp/protocol/tsclient.go
index c873327..2cf5af0 100644
--- a/internal/lsp/protocol/tsclient.go
+++ b/internal/lsp/protocol/tsclient.go
@@ -3,7 +3,7 @@
 // Package protocol contains data types and code for LSP jsonrpcs
 // generated automatically from vscode-languageserver-node
 // commit: 151b520c995ee3d76729b5c46258ab273d989726
-// last fetched Fri Mar 13 2020 17:02:20 GMT-0400 (Eastern Daylight Time)
+// last fetched Mon Mar 30 2020 21:01:17 GMT-0400 (Eastern Daylight Time)
 
 // Code generated (see typescript/README.md) DO NOT EDIT.
 
@@ -12,7 +12,6 @@
 	"encoding/json"
 
 	"golang.org/x/tools/internal/jsonrpc2"
-	"golang.org/x/tools/internal/telemetry/event"
 	"golang.org/x/tools/internal/xcontext"
 )
 
@@ -31,145 +30,100 @@
 	ApplyEdit(context.Context, *ApplyWorkspaceEditParams) (*ApplyWorkspaceEditResponse, error)
 }
 
-func (h clientHandler) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool {
-	if delivered {
-		return false
-	}
-	if ctx.Err() != nil {
-		ctx := xcontext.Detach(ctx)
-		r.Reply(ctx, nil, jsonrpc2.NewErrorf(RequestCancelledError, ""))
-		return true
-	}
-	switch r.Method {
-	case "window/showMessage": // notif
-		var params ShowMessageParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
+func ClientHandler(client Client, handler jsonrpc2.Handler) jsonrpc2.Handler {
+	return func(ctx context.Context, r *jsonrpc2.Request) error {
+		if ctx.Err() != nil {
+			ctx := xcontext.Detach(ctx)
+			return r.Reply(ctx, nil, jsonrpc2.NewErrorf(RequestCancelledError, ""))
 		}
-		if err := h.client.ShowMessage(ctx, &params); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "window/logMessage": // notif
-		var params LogMessageParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		if err := h.client.LogMessage(ctx, &params); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "telemetry/event": // notif
-		var params interface{}
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		if err := h.client.Event(ctx, &params); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/publishDiagnostics": // notif
-		var params PublishDiagnosticsParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		if err := h.client.PublishDiagnostics(ctx, &params); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "$/progress": // notif
-		var params ProgressParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		if err := h.client.Progress(ctx, &params); err != nil {
-			event.Error(ctx, "", 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 {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "workspace/configuration": // req
-		var params ParamConfiguration
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.client.Configuration(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "window/workDoneProgress/create": // req
-		var params WorkDoneProgressCreateParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		err := h.client.WorkDoneProgressCreate(ctx, &params)
-		if err := r.Reply(ctx, nil, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "client/registerCapability": // req
-		var params RegistrationParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		err := h.client.RegisterCapability(ctx, &params)
-		if err := r.Reply(ctx, nil, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "client/unregisterCapability": // req
-		var params UnregistrationParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		err := h.client.UnregisterCapability(ctx, &params)
-		if err := r.Reply(ctx, nil, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "window/showMessageRequest": // req
-		var params ShowMessageRequestParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.client.ShowMessageRequest(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "workspace/applyEdit": // req
-		var params ApplyWorkspaceEditParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.client.ApplyEdit(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	default:
-		return false
+		switch r.Method {
+		case "window/showMessage": // notif
+			var params ShowMessageParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			err := client.ShowMessage(ctx, &params)
+			return r.Reply(ctx, nil, err)
+		case "window/logMessage": // notif
+			var params LogMessageParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			err := client.LogMessage(ctx, &params)
+			return r.Reply(ctx, nil, err)
+		case "telemetry/event": // notif
+			var params interface{}
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			err := client.Event(ctx, &params)
+			return r.Reply(ctx, nil, err)
+		case "textDocument/publishDiagnostics": // notif
+			var params PublishDiagnosticsParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			err := client.PublishDiagnostics(ctx, &params)
+			return r.Reply(ctx, nil, err)
+		case "$/progress": // notif
+			var params ProgressParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			err := client.Progress(ctx, &params)
+			return r.Reply(ctx, nil, err)
+		case "workspace/workspaceFolders": // req
+			if r.Params != nil {
+				return r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeInvalidParams, "Expected no params"))
+			}
+			resp, err := client.WorkspaceFolders(ctx)
+			return r.Reply(ctx, resp, err)
+		case "workspace/configuration": // req
+			var params ParamConfiguration
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := client.Configuration(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "window/workDoneProgress/create": // req
+			var params WorkDoneProgressCreateParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			err := client.WorkDoneProgressCreate(ctx, &params)
+			return r.Reply(ctx, nil, err)
+		case "client/registerCapability": // req
+			var params RegistrationParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			err := client.RegisterCapability(ctx, &params)
+			return r.Reply(ctx, nil, err)
+		case "client/unregisterCapability": // req
+			var params UnregistrationParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			err := client.UnregisterCapability(ctx, &params)
+			return r.Reply(ctx, nil, err)
+		case "window/showMessageRequest": // req
+			var params ShowMessageRequestParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := client.ShowMessageRequest(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "workspace/applyEdit": // req
+			var params ApplyWorkspaceEditParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := client.ApplyEdit(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		default:
+			return handler(ctx, r)
 
+		}
 	}
 }
 
diff --git a/internal/lsp/protocol/tsprotocol.go b/internal/lsp/protocol/tsprotocol.go
index 221c41a..488cb78 100644
--- a/internal/lsp/protocol/tsprotocol.go
+++ b/internal/lsp/protocol/tsprotocol.go
@@ -1,7 +1,7 @@
 // Package protocol contains data types and code for LSP jsonrpcs
 // generated automatically from vscode-languageserver-node
 // commit: 151b520c995ee3d76729b5c46258ab273d989726
-// last fetched Fri Mar 13 2020 17:02:20 GMT-0400 (Eastern Daylight Time)
+// last fetched Mon Mar 30 2020 21:01:17 GMT-0400 (Eastern Daylight Time)
 package protocol
 
 // Code generated (see typescript/README.md) DO NOT EDIT.
diff --git a/internal/lsp/protocol/tsserver.go b/internal/lsp/protocol/tsserver.go
index cefd5db..fc675d8 100644
--- a/internal/lsp/protocol/tsserver.go
+++ b/internal/lsp/protocol/tsserver.go
@@ -3,7 +3,7 @@
 // Package protocol contains data types and code for LSP jsonrpcs
 // generated automatically from vscode-languageserver-node
 // commit: 151b520c995ee3d76729b5c46258ab273d989726
-// last fetched Fri Mar 13 2020 17:02:20 GMT-0400 (Eastern Daylight Time)
+// last fetched Mon Mar 30 2020 21:01:17 GMT-0400 (Eastern Daylight Time)
 
 // Code generated (see typescript/README.md) DO NOT EDIT.
 
@@ -12,7 +12,6 @@
 	"encoding/json"
 
 	"golang.org/x/tools/internal/jsonrpc2"
-	"golang.org/x/tools/internal/telemetry/event"
 	"golang.org/x/tools/internal/xcontext"
 )
 
@@ -69,548 +68,359 @@
 	NonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error)
 }
 
-func (h serverHandler) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool {
-	if delivered {
-		return false
-	}
-	if ctx.Err() != nil {
-		ctx := xcontext.Detach(ctx)
-		r.Reply(ctx, nil, jsonrpc2.NewErrorf(RequestCancelledError, ""))
-		return true
-	}
-	switch r.Method {
-	case "workspace/didChangeWorkspaceFolders": // notif
-		var params DidChangeWorkspaceFoldersParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
+func ServerHandler(server Server, handler jsonrpc2.Handler) jsonrpc2.Handler {
+	return func(ctx context.Context, r *jsonrpc2.Request) error {
+		if ctx.Err() != nil {
+			ctx := xcontext.Detach(ctx)
+			return r.Reply(ctx, nil, jsonrpc2.NewErrorf(RequestCancelledError, ""))
 		}
-		if err := h.server.DidChangeWorkspaceFolders(ctx, &params); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "window/workDoneProgress/cancel": // notif
-		var params WorkDoneProgressCancelParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		if err := h.server.WorkDoneProgressCancel(ctx, &params); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "initialized": // notif
-		var params InitializedParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		if err := h.server.Initialized(ctx, &params); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "exit": // notif
-		if err := h.server.Exit(ctx); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "workspace/didChangeConfiguration": // notif
-		var params DidChangeConfigurationParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		if err := h.server.DidChangeConfiguration(ctx, &params); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/didOpen": // notif
-		var params DidOpenTextDocumentParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		if err := h.server.DidOpen(ctx, &params); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/didChange": // notif
-		var params DidChangeTextDocumentParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		if err := h.server.DidChange(ctx, &params); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/didClose": // notif
-		var params DidCloseTextDocumentParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		if err := h.server.DidClose(ctx, &params); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/didSave": // notif
-		var params DidSaveTextDocumentParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		if err := h.server.DidSave(ctx, &params); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/willSave": // notif
-		var params WillSaveTextDocumentParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		if err := h.server.WillSave(ctx, &params); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "workspace/didChangeWatchedFiles": // notif
-		var params DidChangeWatchedFilesParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		if err := h.server.DidChangeWatchedFiles(ctx, &params); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "$/setTraceNotification": // notif
-		var params SetTraceParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		if err := h.server.SetTraceNotification(ctx, &params); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "$/logTraceNotification": // notif
-		var params LogTraceParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		if err := h.server.LogTraceNotification(ctx, &params); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/implementation": // req
-		var params ImplementationParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.Implementation(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/typeDefinition": // req
-		var params TypeDefinitionParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.TypeDefinition(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/documentColor": // req
-		var params DocumentColorParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.DocumentColor(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/colorPresentation": // req
-		var params ColorPresentationParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.ColorPresentation(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/foldingRange": // req
-		var params FoldingRangeParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.FoldingRange(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/declaration": // req
-		var params DeclarationParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.Declaration(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/selectionRange": // req
-		var params SelectionRangeParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.SelectionRange(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "initialize": // req
-		var params ParamInitialize
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.Initialize(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", 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 {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/willSaveWaitUntil": // req
-		var params WillSaveTextDocumentParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.WillSaveWaitUntil(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/completion": // req
-		var params CompletionParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.Completion(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "completionItem/resolve": // req
-		var params CompletionItem
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.Resolve(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/hover": // req
-		var params HoverParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.Hover(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/signatureHelp": // req
-		var params SignatureHelpParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.SignatureHelp(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/definition": // req
-		var params DefinitionParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.Definition(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/references": // req
-		var params ReferenceParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.References(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/documentHighlight": // req
-		var params DocumentHighlightParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.DocumentHighlight(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/documentSymbol": // req
-		var params DocumentSymbolParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.DocumentSymbol(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/codeAction": // req
-		var params CodeActionParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.CodeAction(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "workspace/symbol": // req
-		var params WorkspaceSymbolParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.Symbol(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/codeLens": // req
-		var params CodeLensParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.CodeLens(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "codeLens/resolve": // req
-		var params CodeLens
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.ResolveCodeLens(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/documentLink": // req
-		var params DocumentLinkParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.DocumentLink(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "documentLink/resolve": // req
-		var params DocumentLink
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.ResolveDocumentLink(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/formatting": // req
-		var params DocumentFormattingParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.Formatting(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/rangeFormatting": // req
-		var params DocumentRangeFormattingParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.RangeFormatting(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/onTypeFormatting": // req
-		var params DocumentOnTypeFormattingParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.OnTypeFormatting(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/rename": // req
-		var params RenameParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.Rename(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/prepareRename": // req
-		var params PrepareRenameParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.PrepareRename(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "workspace/executeCommand": // req
-		var params ExecuteCommandParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.ExecuteCommand(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/prepareCallHierarchy": // req
-		var params CallHierarchyPrepareParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.PrepareCallHierarchy(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "callHierarchy/incomingCalls": // req
-		var params CallHierarchyIncomingCallsParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.IncomingCalls(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "callHierarchy/outgoingCalls": // req
-		var params CallHierarchyOutgoingCallsParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.OutgoingCalls(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/semanticTokens": // req
-		var params SemanticTokensParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.SemanticTokens(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/semanticTokens/edits": // req
-		var params SemanticTokensEditsParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.SemanticTokensEdits(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	case "textDocument/semanticTokens/range": // req
-		var params SemanticTokensRangeParams
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.SemanticTokensRange(ctx, &params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
-	default:
-		var params interface{}
-		if err := json.Unmarshal(*r.Params, &params); err != nil {
-			sendParseError(ctx, r, err)
-			return true
-		}
-		resp, err := h.server.NonstandardRequest(ctx, r.Method, params)
-		if err := r.Reply(ctx, resp, err); err != nil {
-			event.Error(ctx, "", err)
-		}
-		return true
+		switch r.Method {
+		case "workspace/didChangeWorkspaceFolders": // notif
+			var params DidChangeWorkspaceFoldersParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			err := server.DidChangeWorkspaceFolders(ctx, &params)
+			return r.Reply(ctx, nil, err)
+		case "window/workDoneProgress/cancel": // notif
+			var params WorkDoneProgressCancelParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			err := server.WorkDoneProgressCancel(ctx, &params)
+			return r.Reply(ctx, nil, err)
+		case "initialized": // notif
+			var params InitializedParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			err := server.Initialized(ctx, &params)
+			return r.Reply(ctx, nil, err)
+		case "exit": // notif
+			return server.Exit(ctx)
+		case "workspace/didChangeConfiguration": // notif
+			var params DidChangeConfigurationParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			err := server.DidChangeConfiguration(ctx, &params)
+			return r.Reply(ctx, nil, err)
+		case "textDocument/didOpen": // notif
+			var params DidOpenTextDocumentParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			err := server.DidOpen(ctx, &params)
+			return r.Reply(ctx, nil, err)
+		case "textDocument/didChange": // notif
+			var params DidChangeTextDocumentParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			err := server.DidChange(ctx, &params)
+			return r.Reply(ctx, nil, err)
+		case "textDocument/didClose": // notif
+			var params DidCloseTextDocumentParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			err := server.DidClose(ctx, &params)
+			return r.Reply(ctx, nil, err)
+		case "textDocument/didSave": // notif
+			var params DidSaveTextDocumentParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			err := server.DidSave(ctx, &params)
+			return r.Reply(ctx, nil, err)
+		case "textDocument/willSave": // notif
+			var params WillSaveTextDocumentParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			err := server.WillSave(ctx, &params)
+			return r.Reply(ctx, nil, err)
+		case "workspace/didChangeWatchedFiles": // notif
+			var params DidChangeWatchedFilesParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			err := server.DidChangeWatchedFiles(ctx, &params)
+			return r.Reply(ctx, nil, err)
+		case "$/setTraceNotification": // notif
+			var params SetTraceParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			err := server.SetTraceNotification(ctx, &params)
+			return r.Reply(ctx, nil, err)
+		case "$/logTraceNotification": // notif
+			var params LogTraceParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			err := server.LogTraceNotification(ctx, &params)
+			return r.Reply(ctx, nil, err)
+		case "textDocument/implementation": // req
+			var params ImplementationParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.Implementation(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "textDocument/typeDefinition": // req
+			var params TypeDefinitionParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.TypeDefinition(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "textDocument/documentColor": // req
+			var params DocumentColorParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.DocumentColor(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "textDocument/colorPresentation": // req
+			var params ColorPresentationParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.ColorPresentation(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "textDocument/foldingRange": // req
+			var params FoldingRangeParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.FoldingRange(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "textDocument/declaration": // req
+			var params DeclarationParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.Declaration(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "textDocument/selectionRange": // req
+			var params SelectionRangeParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.SelectionRange(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "initialize": // req
+			var params ParamInitialize
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.Initialize(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "shutdown": // req
+			if r.Params != nil {
+				return r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeInvalidParams, "Expected no params"))
+			}
+			err := server.Shutdown(ctx)
+			return r.Reply(ctx, nil, err)
+		case "textDocument/willSaveWaitUntil": // req
+			var params WillSaveTextDocumentParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.WillSaveWaitUntil(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "textDocument/completion": // req
+			var params CompletionParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.Completion(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "completionItem/resolve": // req
+			var params CompletionItem
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.Resolve(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "textDocument/hover": // req
+			var params HoverParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.Hover(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "textDocument/signatureHelp": // req
+			var params SignatureHelpParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.SignatureHelp(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "textDocument/definition": // req
+			var params DefinitionParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.Definition(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "textDocument/references": // req
+			var params ReferenceParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.References(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "textDocument/documentHighlight": // req
+			var params DocumentHighlightParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.DocumentHighlight(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "textDocument/documentSymbol": // req
+			var params DocumentSymbolParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.DocumentSymbol(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "textDocument/codeAction": // req
+			var params CodeActionParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.CodeAction(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "workspace/symbol": // req
+			var params WorkspaceSymbolParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.Symbol(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "textDocument/codeLens": // req
+			var params CodeLensParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.CodeLens(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "codeLens/resolve": // req
+			var params CodeLens
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.ResolveCodeLens(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "textDocument/documentLink": // req
+			var params DocumentLinkParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.DocumentLink(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "documentLink/resolve": // req
+			var params DocumentLink
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.ResolveDocumentLink(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "textDocument/formatting": // req
+			var params DocumentFormattingParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.Formatting(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "textDocument/rangeFormatting": // req
+			var params DocumentRangeFormattingParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.RangeFormatting(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "textDocument/onTypeFormatting": // req
+			var params DocumentOnTypeFormattingParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.OnTypeFormatting(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "textDocument/rename": // req
+			var params RenameParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.Rename(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "textDocument/prepareRename": // req
+			var params PrepareRenameParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.PrepareRename(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "workspace/executeCommand": // req
+			var params ExecuteCommandParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.ExecuteCommand(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "textDocument/prepareCallHierarchy": // req
+			var params CallHierarchyPrepareParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.PrepareCallHierarchy(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "callHierarchy/incomingCalls": // req
+			var params CallHierarchyIncomingCallsParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.IncomingCalls(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "callHierarchy/outgoingCalls": // req
+			var params CallHierarchyOutgoingCallsParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.OutgoingCalls(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "textDocument/semanticTokens": // req
+			var params SemanticTokensParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.SemanticTokens(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "textDocument/semanticTokens/edits": // req
+			var params SemanticTokensEditsParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.SemanticTokensEdits(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		case "textDocument/semanticTokens/range": // req
+			var params SemanticTokensRangeParams
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.SemanticTokensRange(ctx, &params)
+			return r.Reply(ctx, resp, err)
+		default:
+			var params interface{}
+			if err := json.Unmarshal(*r.Params, &params); err != nil {
+				return sendParseError(ctx, r, err)
+			}
+			resp, err := server.NonstandardRequest(ctx, r.Method, params)
+			return r.Reply(ctx, resp, err)
 
+		}
 	}
 }
 
diff --git a/internal/lsp/protocol/typescript/code.ts b/internal/lsp/protocol/typescript/code.ts
index 388f439..a006acb 100644
--- a/internal/lsp/protocol/typescript/code.ts
+++ b/internal/lsp/protocol/typescript/code.ts
@@ -908,8 +908,7 @@
 
 // commonly used output
 const notNil = `if r.Params != nil {
-  r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeInvalidParams, "Expected no params"))
-  return true
+  return r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeInvalidParams, "Expected no params"))
 }`;
 
 // Go code for notifications. Side is client or server, m is the request
@@ -925,18 +924,12 @@
   if (a != '' && a != 'void') {
     case1 = `var params ${a}
     if err := json.Unmarshal(*r.Params, &params); err != nil {
-      sendParseError(ctx, r, err)
-      return true
+      return sendParseError(ctx, r, err)
     }
-    if err := h.${side.name}.${nm}(ctx, &params); err != nil {
-      event.Error(ctx, "", err)
-    }
-    return true`;
+    err:= ${side.name}.${nm}(ctx, &params)
+    return r.Reply(ctx, nil, err)`
   } else {
-    case1 = `if err := h.${side.name}.${nm}(ctx); err != nil {
-      event.Error(ctx, "", err)
-    }
-    return true`;
+    case1 = `return ${side.name}.${nm}(ctx)`;
   }
   side.cases.push(`${caseHdr}\n${case1}`);
 
@@ -966,26 +959,19 @@
     if (extraTypes.has('Param' + nm)) a = 'Param' + nm
     case1 = `var params ${a}
     if err := json.Unmarshal(*r.Params, &params); err != nil {
-      sendParseError(ctx, r, err)
-      return true
+      return sendParseError(ctx, r, err)
     }`;
   }
   const arg2 = a == '' ? '' : ', &params';
-  let case2 = `if err := h.${side.name}.${nm}(ctx${arg2}); err != nil {
+  let case2 = `if err := ${side.name}.${nm}(ctx${arg2}); err != nil {
     event.Error(ctx, "", err)
   }`;
   if (b != '' && b != 'void') {
-    case2 = `resp, err := h.${side.name}.${nm}(ctx${arg2})
-    if err := r.Reply(ctx, resp, err); err != nil {
-      event.Error(ctx, "", err)
-    }
-    return true`;
+    case2 = `resp, err := ${side.name}.${nm}(ctx${arg2})
+    return r.Reply(ctx, resp, err)`;
   } else {  // response is nil
-    case2 = `err := h.${side.name}.${nm}(ctx${arg2})
-    if err := r.Reply(ctx, nil, err); err != nil {
-      event.Error(ctx, "", err)
-    }
-    return true`
+    case2 = `err := ${side.name}.${nm}(ctx${arg2})
+    return r.Reply(ctx, nil, err)`
   }
 
   side.cases.push(`${caseHdr}\n${case1}\n${case2}`);
@@ -1087,7 +1073,6 @@
           "encoding/json"
 
           "golang.org/x/tools/internal/jsonrpc2"
-          "golang.org/x/tools/internal/telemetry/event"
           "golang.org/x/tools/internal/xcontext"
         )
         `);
@@ -1095,19 +1080,16 @@
   f(`type ${a} interface {`);
   side.methods.forEach((v) => {f(v)});
   f('}\n');
-  f(`func (h ${
-    side.name}Handler) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool {
-            if delivered {
-              return false
-            }
+  f(`func ${a}Handler(${side.name} ${a}, handler jsonrpc2.Handler) jsonrpc2.Handler {
+        return func(ctx context.Context, r *jsonrpc2.Request) error {
             if ctx.Err() != nil {
               ctx := xcontext.Detach(ctx)
-              r.Reply(ctx, nil, jsonrpc2.NewErrorf(RequestCancelledError, ""))
-              return true
+              return r.Reply(ctx, nil, jsonrpc2.NewErrorf(RequestCancelledError, ""))
             }
             switch r.Method {`);
   side.cases.forEach((v) => {f(v)});
   f(`
+          }
         }
       }`);
   f(`
@@ -1132,18 +1114,14 @@
     }
   `)
   client.cases.push(`default:
-    return false`)
+    return handler(ctx, r)`)
   server.cases.push(`default:
   var params interface{}
   if err := json.Unmarshal(*r.Params, &params); err != nil {
-    sendParseError(ctx, r, err)
-    return true
+    return sendParseError(ctx, r, err)
   }
-  resp, err := h.server.NonstandardRequest(ctx, r.Method, params)
-  if err := r.Reply(ctx, resp, err); err != nil {
-    event.Error(ctx, "", err)
-  }
-  return true
+  resp, err := server.NonstandardRequest(ctx, r.Method, params)
+  return r.Reply(ctx, resp, err)
 `)
 }
 
diff --git a/internal/lsp/source/completion.go b/internal/lsp/source/completion.go
index c43a394..e3defa2 100644
--- a/internal/lsp/source/completion.go
+++ b/internal/lsp/source/completion.go
@@ -136,9 +136,6 @@
 	qf       types.Qualifier
 	opts     *completionOptions
 
-	// ctx is the context associated with this completion request.
-	ctx context.Context
-
 	// filename is the name of the file associated with this completion request.
 	filename string
 
@@ -247,13 +244,6 @@
 	return p.content[p.cursor-p.spanRange.Start:]
 }
 
-func (c *completer) deepCompletionContext() (context.Context, context.CancelFunc) {
-	if c.opts.budget == 0 {
-		return context.WithCancel(c.ctx)
-	}
-	return context.WithDeadline(c.ctx, c.startTime.Add(c.opts.budget))
-}
-
 func (c *completer) setSurrounding(ident *ast.Ident) {
 	if c.surrounding != nil {
 		return
@@ -292,7 +282,7 @@
 
 // found adds a candidate completion. We will also search through the object's
 // members for more candidates.
-func (c *completer) found(cand candidate) {
+func (c *completer) found(ctx context.Context, cand candidate) {
 	obj := cand.obj
 
 	if obj.Pkg() != nil && obj.Pkg() != c.pkg.GetTypes() && !obj.Exported() {
@@ -341,7 +331,7 @@
 
 		// We only care about named types (i.e. don't want builtin types).
 		if _, isNamed := obj.Type().(*types.Named); isNamed {
-			c.literal(obj.Type(), cand.imp)
+			c.literal(ctx, obj.Type(), cand.imp)
 		}
 	}
 
@@ -370,13 +360,13 @@
 		// Avoid calling c.item() for deep candidates that wouldn't be in the top
 		// MaxDeepCompletions anyway.
 		if !c.inDeepCompletion() || c.deepState.isHighScore(cand.score) {
-			if item, err := c.item(cand); err == nil {
+			if item, err := c.item(ctx, cand); err == nil {
 				c.items = append(c.items, item)
 			}
 		}
 	}
 
-	c.deepSearch(cand)
+	c.deepSearch(ctx, cand)
 }
 
 // candidate represents a completion candidate.
@@ -482,7 +472,6 @@
 		pkg:                       pkg,
 		snapshot:                  snapshot,
 		qf:                        qualifier(file, pkg.GetTypes(), pkg.GetTypesInfo()),
-		ctx:                       ctx,
 		filename:                  fh.Identity().URI.Filename(),
 		file:                      file,
 		path:                      path,
@@ -512,32 +501,40 @@
 		c.deepState.maxDepth = -1
 	}
 
+	var cancel context.CancelFunc
+	if c.opts.budget == 0 {
+		ctx, cancel = context.WithCancel(ctx)
+	} else {
+		ctx, cancel = context.WithDeadline(ctx, c.startTime.Add(c.opts.budget))
+	}
+	defer cancel()
+
 	if surrounding := c.containingIdent(src); surrounding != nil {
 		c.setSurrounding(surrounding)
 	}
 
-	c.inference = expectedCandidate(c)
+	c.inference = expectedCandidate(ctx, c)
 
 	defer c.sortItems()
 
 	// If we're inside a comment return comment completions
 	for _, comment := range file.Comments {
 		if comment.Pos() < rng.Start && rng.Start <= comment.End() {
-			c.populateCommentCompletions(comment)
+			c.populateCommentCompletions(ctx, comment)
 			return c.items, c.getSurrounding(), nil
 		}
 	}
 
 	// Struct literals are handled entirely separately.
 	if c.wantStructFieldCompletions() {
-		if err := c.structLiteralFieldName(); err != nil {
+		if err := c.structLiteralFieldName(ctx); err != nil {
 			return nil, nil, err
 		}
 		return c.items, c.getSurrounding(), nil
 	}
 
 	if lt := c.wantLabelCompletion(); lt != labelNone {
-		c.labels(lt)
+		c.labels(ctx, lt)
 		return c.items, c.getSurrounding(), nil
 	}
 
@@ -555,7 +552,7 @@
 	case *ast.Ident:
 		// Is this the Sel part of a selector?
 		if sel, ok := path[1].(*ast.SelectorExpr); ok && sel.Sel == n {
-			if err := c.selector(sel); err != nil {
+			if err := c.selector(ctx, sel); err != nil {
 				return nil, nil, err
 			}
 			return c.items, c.getSurrounding(), nil
@@ -573,19 +570,19 @@
 				return nil, nil, ErrIsDefinition{objStr: objStr}
 			}
 		}
-		if err := c.lexical(); err != nil {
+		if err := c.lexical(ctx); err != nil {
 			return nil, nil, err
 		}
 	// The function name hasn't been typed yet, but the parens are there:
 	//   recv.‸(arg)
 	case *ast.TypeAssertExpr:
 		// Create a fake selector expression.
-		if err := c.selector(&ast.SelectorExpr{X: n.X}); err != nil {
+		if err := c.selector(ctx, &ast.SelectorExpr{X: n.X}); err != nil {
 			return nil, nil, err
 		}
 
 	case *ast.SelectorExpr:
-		if err := c.selector(n); err != nil {
+		if err := c.selector(ctx, n); err != nil {
 			return nil, nil, err
 		}
 
@@ -595,7 +592,7 @@
 
 	default:
 		// fallback to lexical completions
-		if err := c.lexical(); err != nil {
+		if err := c.lexical(ctx); err != nil {
 			return nil, nil, err
 		}
 	}
@@ -687,7 +684,7 @@
 
 // populateCommentCompletions yields completions for an exported
 // variable immediately preceding comment.
-func (c *completer) populateCommentCompletions(comment *ast.CommentGroup) {
+func (c *completer) populateCommentCompletions(ctx context.Context, comment *ast.CommentGroup) {
 
 	// Using the comment position find the line after
 	fset := c.snapshot.View().Session().Cache().FileSet()
@@ -720,7 +717,7 @@
 						}
 
 						exportedVar := c.pkg.GetTypesInfo().ObjectOf(name)
-						c.found(candidate{obj: exportedVar, score: stdScore})
+						c.found(ctx, candidate{obj: exportedVar, score: stdScore})
 					}
 				}
 			}
@@ -748,11 +745,11 @@
 )
 
 // selector finds completions for the specified selector expression.
-func (c *completer) selector(sel *ast.SelectorExpr) error {
+func (c *completer) selector(ctx context.Context, sel *ast.SelectorExpr) error {
 	// Is sel a qualified identifier?
 	if id, ok := sel.X.(*ast.Ident); ok {
 		if pkgName, ok := c.pkg.GetTypesInfo().Uses[id].(*types.PkgName); ok {
-			c.packageMembers(pkgName.Imported(), stdScore, nil)
+			c.packageMembers(ctx, pkgName.Imported(), stdScore, nil)
 			return nil
 		}
 	}
@@ -760,21 +757,21 @@
 	// Invariant: sel is a true selector.
 	tv, ok := c.pkg.GetTypesInfo().Types[sel.X]
 	if ok {
-		return c.methodsAndFields(tv.Type, tv.Addressable(), nil)
+		return c.methodsAndFields(ctx, tv.Type, tv.Addressable(), nil)
 	}
 
 	// Try unimported packages.
 	if id, ok := sel.X.(*ast.Ident); ok && c.opts.unimported {
-		if err := c.unimportedMembers(id); err != nil {
+		if err := c.unimportedMembers(ctx, id); err != nil {
 			return err
 		}
 	}
 	return nil
 }
 
-func (c *completer) unimportedMembers(id *ast.Ident) error {
+func (c *completer) unimportedMembers(ctx context.Context, id *ast.Ident) error {
 	// Try loaded packages first. They're relevant, fast, and fully typed.
-	known, err := c.snapshot.CachedImportPaths(c.ctx)
+	known, err := c.snapshot.CachedImportPaths(ctx)
 	if err != nil {
 		return err
 	}
@@ -789,8 +786,8 @@
 
 	var relevances map[string]int
 	if len(paths) != 0 {
-		c.snapshot.View().RunProcessEnvFunc(c.ctx, func(opts *imports.Options) error {
-			relevances = imports.ScoreImportPaths(c.ctx, opts.Env, paths)
+		c.snapshot.View().RunProcessEnvFunc(ctx, func(opts *imports.Options) error {
+			relevances = imports.ScoreImportPaths(ctx, opts.Env, paths)
 			return nil
 		})
 	}
@@ -807,14 +804,15 @@
 		if imports.ImportPathToAssumedName(path) != pkg.GetTypes().Name() {
 			imp.name = pkg.GetTypes().Name()
 		}
-		c.packageMembers(pkg.GetTypes(), stdScore+.01*float64(relevance), imp)
+		c.packageMembers(ctx, pkg.GetTypes(), stdScore+.01*float64(relevance), imp)
 		if len(c.items) >= unimportedMemberTarget {
 			return nil
 		}
 	}
 
-	ctx, cancel := c.deepCompletionContext()
+	ctx, cancel := context.WithCancel(ctx)
 	defer cancel()
+
 	var mu sync.Mutex
 	add := func(pkgExport imports.PackageExport) {
 		mu.Lock()
@@ -827,7 +825,7 @@
 		pkg := types.NewPackage(pkgExport.Fix.StmtInfo.ImportPath, pkgExport.Fix.IdentName)
 		for _, export := range pkgExport.Exports {
 			score := stdScore + 0.01*float64(pkgExport.Fix.Relevance)
-			c.found(candidate{
+			c.found(ctx, candidate{
 				obj:   types.NewVar(0, pkg, export, nil),
 				score: score,
 				imp: &importInfo{
@@ -845,11 +843,11 @@
 	})
 }
 
-func (c *completer) packageMembers(pkg *types.Package, score float64, imp *importInfo) {
+func (c *completer) packageMembers(ctx context.Context, pkg *types.Package, score float64, imp *importInfo) {
 	scope := pkg.Scope()
 	for _, name := range scope.Names() {
 		obj := scope.Lookup(name)
-		c.found(candidate{
+		c.found(ctx, candidate{
 			obj:         obj,
 			score:       score,
 			imp:         imp,
@@ -858,7 +856,7 @@
 	}
 }
 
-func (c *completer) methodsAndFields(typ types.Type, addressable bool, imp *importInfo) error {
+func (c *completer) methodsAndFields(ctx context.Context, typ types.Type, addressable bool, imp *importInfo) error {
 	mset := c.methodSetCache[methodSetKey{typ, addressable}]
 	if mset == nil {
 		if addressable && !types.IsInterface(typ) && !isPointer(typ) {
@@ -872,7 +870,7 @@
 	}
 
 	for i := 0; i < mset.Len(); i++ {
-		c.found(candidate{
+		c.found(ctx, candidate{
 			obj:         mset.At(i).Obj(),
 			score:       stdScore,
 			imp:         imp,
@@ -882,7 +880,7 @@
 
 	// Add fields of T.
 	eachField(typ, func(v *types.Var) {
-		c.found(candidate{
+		c.found(ctx, candidate{
 			obj:         v,
 			score:       stdScore - 0.01,
 			imp:         imp,
@@ -894,7 +892,7 @@
 }
 
 // lexical finds completions in the lexical environment.
-func (c *completer) lexical() error {
+func (c *completer) lexical(ctx context.Context) error {
 	var scopes []*types.Scope // scopes[i], where i<len(path), is the possibly nil Scope of path[i].
 	for _, n := range c.path {
 		// Include *FuncType scope if pos is inside the function body.
@@ -978,7 +976,7 @@
 			// If we haven't already added a candidate for an object with this name.
 			if _, ok := seen[obj.Name()]; !ok {
 				seen[obj.Name()] = struct{}{}
-				c.found(candidate{
+				c.found(ctx, candidate{
 					obj:         obj,
 					score:       score,
 					addressable: isVar(obj),
@@ -1007,7 +1005,7 @@
 					if imports.ImportPathToAssumedName(pkg.Path()) != pkg.Name() {
 						imp.name = pkg.Name()
 					}
-					c.found(candidate{
+					c.found(ctx, candidate{
 						obj:   obj,
 						score: stdScore,
 						imp:   imp,
@@ -1018,7 +1016,7 @@
 	}
 
 	if c.opts.unimported {
-		if err := c.unimportedPackages(seen); err != nil {
+		if err := c.unimportedPackages(ctx, seen); err != nil {
 			return err
 		}
 	}
@@ -1036,7 +1034,7 @@
 		// our expected type is "[]int", this will add a candidate of
 		// "[]int{}".
 		if _, named := t.(*types.Named); !named {
-			c.literal(t, nil)
+			c.literal(ctx, t, nil)
 		}
 	}
 
@@ -1046,17 +1044,14 @@
 	return nil
 }
 
-func (c *completer) unimportedPackages(seen map[string]struct{}) error {
-	ctx, cancel := c.deepCompletionContext()
-	defer cancel()
-
-	prefix := ""
+func (c *completer) unimportedPackages(ctx context.Context, seen map[string]struct{}) error {
+	var prefix string
 	if c.surrounding != nil {
 		prefix = c.surrounding.Prefix()
 	}
 	initialItemCount := len(c.items)
 
-	known, err := c.snapshot.CachedImportPaths(c.ctx)
+	known, err := c.snapshot.CachedImportPaths(ctx)
 	if err != nil {
 		return err
 	}
@@ -1071,7 +1066,7 @@
 	var relevances map[string]int
 	if len(paths) != 0 {
 		c.snapshot.View().RunProcessEnvFunc(ctx, func(opts *imports.Options) error {
-			relevances = imports.ScoreImportPaths(c.ctx, opts.Env, paths)
+			relevances = imports.ScoreImportPaths(ctx, opts.Env, paths)
 			return nil
 		})
 	}
@@ -1086,7 +1081,7 @@
 			imp.name = pkg.GetTypes().Name()
 		}
 		score := 0.01 * float64(relevance)
-		c.found(candidate{
+		c.found(ctx, candidate{
 			obj:   types.NewPkgName(0, nil, pkg.GetTypes().Name(), pkg.GetTypes()),
 			score: score,
 			imp:   imp,
@@ -1096,6 +1091,9 @@
 		}
 	}
 
+	ctx, cancel := context.WithCancel(ctx)
+	defer cancel()
+
 	var mu sync.Mutex
 	add := func(pkg imports.ImportFix) {
 		mu.Lock()
@@ -1115,7 +1113,7 @@
 		// multiple packages of the same name as completion suggestions, since
 		// only one will be chosen.
 		obj := types.NewPkgName(0, nil, pkg.IdentName, types.NewPackage(pkg.StmtInfo.ImportPath, pkg.IdentName))
-		c.found(candidate{
+		c.found(ctx, candidate{
 			obj:   obj,
 			score: score,
 			imp: &importInfo{
@@ -1163,7 +1161,7 @@
 }
 
 // structLiteralFieldName finds completions for struct field names inside a struct literal.
-func (c *completer) structLiteralFieldName() error {
+func (c *completer) structLiteralFieldName(ctx context.Context) error {
 	clInfo := c.enclosingCompositeLiteral
 
 	// Mark fields of the composite literal that have already been set,
@@ -1190,7 +1188,7 @@
 		for i := 0; i < t.NumFields(); i++ {
 			field := t.Field(i)
 			if !addedFields[field] {
-				c.found(candidate{
+				c.found(ctx, candidate{
 					obj:   field,
 					score: highScore,
 				})
@@ -1200,10 +1198,10 @@
 		// Add lexical completions if we aren't certain we are in the key part of a
 		// key-value pair.
 		if clInfo.maybeInFieldName {
-			return c.lexical()
+			return c.lexical(ctx)
 		}
 	default:
-		return c.lexical()
+		return c.lexical(ctx)
 	}
 
 	return nil
@@ -1468,7 +1466,7 @@
 
 // expectedCandidate returns information about the expected candidate
 // for an expression at the query position.
-func expectedCandidate(c *completer) (inf candidateInference) {
+func expectedCandidate(ctx context.Context, c *completer) (inf candidateInference) {
 	inf.typeName = expectTypeName(c)
 
 	if c.enclosingCompositeLiteral != nil {
@@ -1592,7 +1590,7 @@
 						// inferred type from its parent node.
 						defer func() {
 							inf = c.builtinArgType(obj, node, inf)
-							inf.objKind = c.builtinArgKind(obj, node)
+							inf.objKind = c.builtinArgKind(ctx, obj, node)
 						}()
 
 						// The expected type of builtin arguments like append() is
diff --git a/internal/lsp/source/completion_builtin.go b/internal/lsp/source/completion_builtin.go
index fb247ab..f314146 100644
--- a/internal/lsp/source/completion_builtin.go
+++ b/internal/lsp/source/completion_builtin.go
@@ -5,6 +5,7 @@
 package source
 
 import (
+	"context"
 	"go/ast"
 	"go/types"
 )
@@ -12,8 +13,8 @@
 // builtinArgKind determines the expected object kind for a builtin
 // argument. It attempts to use the AST hints from builtin.go where
 // possible.
-func (c *completer) builtinArgKind(obj types.Object, call *ast.CallExpr) objKind {
-	astObj, err := c.snapshot.View().LookupBuiltin(c.ctx, obj.Name())
+func (c *completer) builtinArgKind(ctx context.Context, obj types.Object, call *ast.CallExpr) objKind {
+	astObj, err := c.snapshot.View().LookupBuiltin(ctx, obj.Name())
 	if err != nil {
 		return 0
 	}
diff --git a/internal/lsp/source/completion_format.go b/internal/lsp/source/completion_format.go
index e168534..b40b9f4 100644
--- a/internal/lsp/source/completion_format.go
+++ b/internal/lsp/source/completion_format.go
@@ -23,12 +23,12 @@
 )
 
 // formatCompletion creates a completion item for a given candidate.
-func (c *completer) item(cand candidate) (CompletionItem, error) {
+func (c *completer) item(ctx context.Context, cand candidate) (CompletionItem, error) {
 	obj := cand.obj
 
 	// Handle builtin types separately.
 	if obj.Parent() == types.Universe {
-		return c.formatBuiltin(cand), nil
+		return c.formatBuiltin(ctx, cand), nil
 	}
 
 	var (
@@ -46,7 +46,7 @@
 	// expandFuncCall mutates the completion label, detail, and snippet
 	// to that of an invocation of sig.
 	expandFuncCall := func(sig *types.Signature) {
-		params := formatParams(c.ctx, c.snapshot, c.pkg, sig, c.qf)
+		params := formatParams(ctx, c.snapshot, c.pkg, sig, c.qf)
 		snip = c.functionCallSnippet(label, params)
 		results, writeParens := formatResults(sig.Results(), c.qf)
 		detail = "func" + formatFunction(params, results, writeParens)
@@ -67,7 +67,7 @@
 			detail = "struct{...}" // for anonymous structs
 		} else if obj.IsField() {
 			var err error
-			detail, err = formatFieldType(c.ctx, c.snapshot, c.pkg, obj)
+			detail, err = formatFieldType(ctx, c.snapshot, c.pkg, obj)
 			if err != nil {
 				detail = types.TypeString(obj.Type(), c.qf)
 			}
@@ -115,7 +115,7 @@
 	// If this candidate needs an additional import statement,
 	// add the additional text edits needed.
 	if cand.imp != nil {
-		addlEdits, err := c.importEdits(cand.imp)
+		addlEdits, err := c.importEdits(ctx, cand.imp)
 		if err != nil {
 			return CompletionItem{}, err
 		}
@@ -144,10 +144,9 @@
 		if sel := enclosingSelector(c.path, c.pos); sel != nil {
 			edits, err := prependEdit(c.snapshot.View().Session().Cache().FileSet(), c.mapper, sel, prefixOp)
 			if err != nil {
-				event.Error(c.ctx, "error generating prefix edit", err)
-			} else {
-				protocolEdits = append(protocolEdits, edits...)
+				return CompletionItem{}, err
 			}
+			protocolEdits = append(protocolEdits, edits...)
 		} else {
 			// If there is no selector, just stick the prefix at the start.
 			insert = prefixOp + insert
@@ -190,13 +189,13 @@
 	if err != nil {
 		return item, nil
 	}
-	ident, err := findIdentifier(c.ctx, c.snapshot, pkg, file, obj.Pos())
+	ident, err := findIdentifier(ctx, c.snapshot, pkg, file, obj.Pos())
 	if err != nil {
 		return item, nil
 	}
-	hover, err := ident.Hover(c.ctx)
+	hover, err := ident.Hover(ctx)
 	if err != nil {
-		event.Error(c.ctx, "failed to find Hover", err, tag.URI.Of(uri))
+		event.Error(ctx, "failed to find Hover", err, tag.URI.Of(uri))
 		return item, nil
 	}
 	item.Documentation = hover.Synopsis
@@ -207,7 +206,7 @@
 }
 
 // importEdits produces the text edits necessary to add the given import to the current file.
-func (c *completer) importEdits(imp *importInfo) ([]protocol.TextEdit, error) {
+func (c *completer) importEdits(ctx context.Context, imp *importInfo) ([]protocol.TextEdit, error) {
 	if imp == nil {
 		return nil, nil
 	}
@@ -223,7 +222,7 @@
 		return nil, errors.Errorf("building import completion for %v: no ParseGoHandle for %s", imp.importPath, c.filename)
 	}
 
-	return computeOneImportFixEdits(c.ctx, c.snapshot.View(), ph, &imports.ImportFix{
+	return computeOneImportFixEdits(ctx, c.snapshot.View(), ph, &imports.ImportFix{
 		StmtInfo: imports.ImportInfo{
 			ImportPath: imp.importPath,
 			Name:       imp.name,
@@ -233,7 +232,7 @@
 	})
 }
 
-func (c *completer) formatBuiltin(cand candidate) CompletionItem {
+func (c *completer) formatBuiltin(ctx context.Context, cand candidate) CompletionItem {
 	obj := cand.obj
 	item := CompletionItem{
 		Label:      obj.Name(),
@@ -245,17 +244,17 @@
 		item.Kind = protocol.ConstantCompletion
 	case *types.Builtin:
 		item.Kind = protocol.FunctionCompletion
-		astObj, err := c.snapshot.View().LookupBuiltin(c.ctx, obj.Name())
+		astObj, err := c.snapshot.View().LookupBuiltin(ctx, obj.Name())
 		if err != nil {
-			event.Error(c.ctx, "no builtin package", err)
+			event.Error(ctx, "no builtin package", err)
 			break
 		}
 		decl, ok := astObj.Decl.(*ast.FuncDecl)
 		if !ok {
 			break
 		}
-		params, _ := formatFieldList(c.ctx, c.snapshot.View(), decl.Type.Params)
-		results, writeResultParens := formatFieldList(c.ctx, c.snapshot.View(), decl.Type.Results)
+		params, _ := formatFieldList(ctx, c.snapshot.View(), decl.Type.Params)
+		results, writeResultParens := formatFieldList(ctx, c.snapshot.View(), decl.Type.Results)
 		item.Label = obj.Name()
 		item.Detail = "func" + formatFunction(params, results, writeResultParens)
 		item.snippet = c.functionCallSnippet(obj.Name(), params)
diff --git a/internal/lsp/source/completion_labels.go b/internal/lsp/source/completion_labels.go
index 0a23aa8..abb2041 100644
--- a/internal/lsp/source/completion_labels.go
+++ b/internal/lsp/source/completion_labels.go
@@ -5,6 +5,7 @@
 package source
 
 import (
+	"context"
 	"go/ast"
 	"go/token"
 	"math"
@@ -49,7 +50,7 @@
 
 // labels adds completion items for labels defined in the enclosing
 // function.
-func (c *completer) labels(lt labelType) {
+func (c *completer) labels(ctx context.Context, lt labelType) {
 	if c.enclosingFunc == nil {
 		return
 	}
@@ -57,7 +58,7 @@
 	addLabel := func(score float64, l *ast.LabeledStmt) {
 		labelObj := c.pkg.GetTypesInfo().ObjectOf(l.Label)
 		if labelObj != nil {
-			c.found(candidate{obj: labelObj, score: score})
+			c.found(ctx, candidate{obj: labelObj, score: score})
 		}
 	}
 
diff --git a/internal/lsp/source/completion_literal.go b/internal/lsp/source/completion_literal.go
index 8abb899..fa6c71a 100644
--- a/internal/lsp/source/completion_literal.go
+++ b/internal/lsp/source/completion_literal.go
@@ -5,6 +5,7 @@
 package source
 
 import (
+	"context"
 	"go/ast"
 	"go/token"
 	"go/types"
@@ -19,7 +20,7 @@
 
 // literal generates composite literal, function literal, and make()
 // completion items.
-func (c *completer) literal(literalType types.Type, imp *importInfo) {
+func (c *completer) literal(ctx context.Context, literalType types.Type, imp *importInfo) {
 	if !c.opts.literal {
 		return
 	}
@@ -101,9 +102,9 @@
 		matchName = types.TypeString(t.Elem(), qf)
 	}
 
-	addlEdits, err := c.importEdits(imp)
+	addlEdits, err := c.importEdits(ctx, imp)
 	if err != nil {
-		event.Error(c.ctx, "error adding import for literal candidate", err)
+		event.Error(ctx, "error adding import for literal candidate", err)
 		return
 	}
 
@@ -116,7 +117,7 @@
 				// "foo.&Bar{}".
 				edits, err := prependEdit(c.snapshot.View().Session().Cache().FileSet(), c.mapper, sel, "&")
 				if err != nil {
-					event.Error(c.ctx, "error making edit for literal pointer completion", err)
+					event.Error(ctx, "error making edit for literal pointer completion", err)
 					return
 				}
 				addlEdits = append(addlEdits, edits...)
diff --git a/internal/lsp/source/deep_completion.go b/internal/lsp/source/deep_completion.go
index 2ad1256..2b57782 100644
--- a/internal/lsp/source/deep_completion.go
+++ b/internal/lsp/source/deep_completion.go
@@ -5,6 +5,7 @@
 package source
 
 import (
+	"context"
 	"go/types"
 	"strings"
 	"time"
@@ -162,7 +163,7 @@
 
 // deepSearch searches through cand's subordinate objects for more
 // completion items.
-func (c *completer) deepSearch(cand candidate) {
+func (c *completer) deepSearch(ctx context.Context, cand candidate) {
 	if c.deepState.maxDepth == 0 {
 		return
 	}
@@ -198,7 +199,7 @@
 			// the deep chain.
 			c.deepState.push(obj, true)
 			// The result of a function call is not addressable.
-			c.methodsAndFields(sig.Results().At(0).Type(), false, cand.imp)
+			c.methodsAndFields(ctx, sig.Results().At(0).Type(), false, cand.imp)
 			c.deepState.pop()
 		}
 	}
@@ -208,9 +209,9 @@
 
 	switch obj := obj.(type) {
 	case *types.PkgName:
-		c.packageMembers(obj.Imported(), stdScore, cand.imp)
+		c.packageMembers(ctx, obj.Imported(), stdScore, cand.imp)
 	default:
-		c.methodsAndFields(obj.Type(), cand.addressable, cand.imp)
+		c.methodsAndFields(ctx, obj.Type(), cand.addressable, cand.imp)
 	}
 
 	// Pop the object off our search stack.
diff --git a/internal/telemetry/event/event.go b/internal/telemetry/event/event.go
index c28e9d9..0ef81a8 100644
--- a/internal/telemetry/event/event.go
+++ b/internal/telemetry/event/event.go
@@ -54,7 +54,7 @@
 func (ev Event) IsRecord() bool    { return ev.typ == RecordType }
 
 func (ev Event) Format(f fmt.State, r rune) {
-	tagMap := ev.Map()
+	tagMap := TagMap(ev)
 	if !ev.At.IsZero() {
 		fmt.Fprint(f, ev.At.Format("2006/01/02 15:04:05 "))
 	}
@@ -70,6 +70,11 @@
 	}
 	for it := ev.Tags(); it.Valid(); it.Advance() {
 		tag := it.Tag()
+		// msg and err were both already printed above, so we skip them to avoid
+		// double printing
+		if tag.Key == Msg || tag.Key == Err {
+			continue
+		}
 		fmt.Fprintf(f, "\n\t%v", tag)
 	}
 }
@@ -80,17 +85,13 @@
 		NewTagIterator(ev.dynamic...))
 }
 
-func (ev Event) Map() TagMap {
-	return &eventTagMap{event: ev}
-}
-
-func (m *eventTagMap) Find(key interface{}) Tag {
-	for _, tag := range m.event.static {
+func (ev Event) Find(key interface{}) Tag {
+	for _, tag := range ev.static {
 		if tag.Key == key {
 			return tag
 		}
 	}
-	for _, tag := range m.event.dynamic {
+	for _, tag := range ev.dynamic {
 		if tag.Key == key {
 			return tag
 		}
diff --git a/internal/telemetry/event/export.go b/internal/telemetry/event/export.go
index 5b41e99..a2633b6 100644
--- a/internal/telemetry/event/export.go
+++ b/internal/telemetry/event/export.go
@@ -40,7 +40,7 @@
 	// add the current time to the event
 	ev.At = time.Now()
 	// hand the event off to the current exporter
-	return exporter(ctx, ev, ev.Map())
+	return exporter(ctx, ev, ev)
 }
 
 // dispatch is called to deliver an event to the global exporter if set.
diff --git a/internal/telemetry/event/tag.go b/internal/telemetry/event/tag.go
index 7d7a34c..e3c81eb 100644
--- a/internal/telemetry/event/tag.go
+++ b/internal/telemetry/event/tag.go
@@ -106,9 +106,9 @@
 	case *StringKey:
 		fmt.Fprintf(f, "%s=%q", key.Name(), key.From(t))
 	case *ErrorKey:
-		fmt.Fprintf(f, "%s=%q", key.Name(), key.From(t))
+		fmt.Fprintf(f, "%s=%v", key.Name(), key.From(t))
 	case *ValueKey:
-		fmt.Fprintf(f, "%s=%q", key.Name(), key.From(t))
+		fmt.Fprintf(f, "%s=%v", key.Name(), key.From(t))
 	default:
 		fmt.Fprintf(f, `%s="invalid type %T"`, key.Name(), key)
 	}
diff --git a/internal/telemetry/export/log_test.go b/internal/telemetry/export/log_test.go
new file mode 100644
index 0000000..77fa02c
--- /dev/null
+++ b/internal/telemetry/export/log_test.go
@@ -0,0 +1,37 @@
+// Copyright 2020 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 export_test
+
+import (
+	"context"
+	"errors"
+	"os"
+	"time"
+
+	"golang.org/x/tools/internal/telemetry/event"
+	"golang.org/x/tools/internal/telemetry/export"
+)
+
+func ExampleLog() {
+	ctx := context.Background()
+	event.SetExporter(timeFixer(export.LogWriter(os.Stdout, false)))
+	anInt := event.NewIntKey("myInt", "an integer")
+	aString := event.NewStringKey("myString", "a string")
+	event.Print(ctx, "my event", anInt.Of(6))
+	event.Error(ctx, "error event", errors.New("an error"), aString.Of("some string value"))
+	// Output:
+	// 2020/03/05 14:27:48 my event
+	// 	myInt=6
+	// 2020/03/05 14:27:48 error event: an error
+	// 	myString="some string value"
+}
+
+func timeFixer(output event.Exporter) event.Exporter {
+	at, _ := time.Parse(time.RFC3339Nano, "2020-03-05T14:27:48Z")
+	return func(ctx context.Context, ev event.Event, tagMap event.TagMap) context.Context {
+		ev.At = at
+		return output(ctx, ev, tagMap)
+	}
+}
diff --git a/internal/telemetry/export/ocagent/ocagent.go b/internal/telemetry/export/ocagent/ocagent.go
index 5af7861..6eec1b9 100644
--- a/internal/telemetry/export/ocagent/ocagent.go
+++ b/internal/telemetry/export/ocagent/ocagent.go
@@ -299,7 +299,7 @@
 	if !tags.Valid() {
 		return nil
 	}
-	tagMap := ev.Map()
+	tagMap := event.TagMap(ev)
 	description := event.Msg.Get(tagMap)
 	tags = event.Filter(tags, event.Msg)
 	if description == "" {
diff --git a/internal/telemetry/export/tag.go b/internal/telemetry/export/tag.go
index 6acadf0..7d29529 100644
--- a/internal/telemetry/export/tag.go
+++ b/internal/telemetry/export/tag.go
@@ -20,7 +20,7 @@
 		stored, _ := ctx.Value(labelContextKey).(event.TagMap)
 		if ev.IsLabel() || ev.IsStartSpan() {
 			// update the tag source stored in the context
-			fromEvent := ev.Map()
+			fromEvent := event.TagMap(ev)
 			if stored == nil {
 				stored = fromEvent
 			} else {