internal/lsp/lsprpc: partial integration of the jsonrpc_v2 library

Update the protocol package to allow wrapping the jsonrpc2 API, and add
wrappers for v1 and v2 of the API, so that we may switch between them.

Add simple bindings for the lsprpc package for jsonrpc2_v2 package, and
get them working well enough to pass a version TestClientLogging test.
This seemed like a reasonable checkpoint.

Also add some type safety to client closing: all LSP clients must
implement io.Closer.

Change-Id: Ib2e6906e0db0c94102a7e794de932d6b61d54670
Reviewed-on: https://go-review.googlesource.com/c/tools/+/320850
Trust: Robert Findley <rfindley@google.com>
Trust: Ian Cottrell <iancottrell@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
diff --git a/internal/jsonrpc2_v2/jsonrpc2.go b/internal/jsonrpc2_v2/jsonrpc2.go
index 1279ba3..4e853d5 100644
--- a/internal/jsonrpc2_v2/jsonrpc2.go
+++ b/internal/jsonrpc2_v2/jsonrpc2.go
@@ -51,6 +51,12 @@
 	return nil, ErrNotHandled
 }
 
+type HandlerFunc func(ctx context.Context, req *Request) (interface{}, error)
+
+func (f HandlerFunc) Handle(ctx context.Context, req *Request) (interface{}, error) {
+	return f(ctx, req)
+}
+
 // async is a small helper for things with an asynchronous result that you can
 // wait for.
 type async struct {
diff --git a/internal/lsp/general.go b/internal/lsp/general.go
index 7165db9..5bdc4bf 100644
--- a/internal/lsp/general.go
+++ b/internal/lsp/general.go
@@ -9,7 +9,6 @@
 	"context"
 	"encoding/json"
 	"fmt"
-	"io"
 	"os"
 	"path"
 	"path/filepath"
@@ -498,8 +497,7 @@
 	s.stateMu.Lock()
 	defer s.stateMu.Unlock()
 
-	// TODO: We need a better way to find the conn close method.
-	s.client.(io.Closer).Close()
+	s.client.Close()
 
 	if s.state != serverShutDown {
 		// TODO: We should be able to do better than this.
diff --git a/internal/lsp/lsp_test.go b/internal/lsp/lsp_test.go
index 621e42a..7208216 100644
--- a/internal/lsp/lsp_test.go
+++ b/internal/lsp/lsp_test.go
@@ -101,6 +101,10 @@
 	runner *runner
 }
 
+func (c testClient) Close() error {
+	return nil
+}
+
 // Trivially implement PublishDiagnostics so that we can call
 // server.publishReports below to de-dup sent diagnostics.
 func (c testClient) PublishDiagnostics(context.Context, *protocol.PublishDiagnosticsParams) error {
diff --git a/internal/lsp/lsprpc/binder.go b/internal/lsp/lsprpc/binder.go
new file mode 100644
index 0000000..15fdda2
--- /dev/null
+++ b/internal/lsp/lsprpc/binder.go
@@ -0,0 +1,78 @@
+// Copyright 2021 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 lsprpc
+
+import (
+	"context"
+
+	jsonrpc2_v2 "golang.org/x/tools/internal/jsonrpc2_v2"
+	"golang.org/x/tools/internal/lsp/protocol"
+)
+
+type ServerFunc func(context.Context, protocol.ClientCloser) protocol.Server
+type ClientFunc func(context.Context, protocol.Server) protocol.Client
+
+// ServerBinder binds incoming connections to a new server.
+type ServerBinder struct {
+	newServer ServerFunc
+}
+
+func NewServerBinder(newServer ServerFunc) *ServerBinder {
+	return &ServerBinder{newServer}
+}
+
+func (b *ServerBinder) Bind(ctx context.Context, conn *jsonrpc2_v2.Connection) (jsonrpc2_v2.ConnectionOptions, error) {
+	client := protocol.ClientDispatcherV2(conn)
+	server := b.newServer(ctx, client)
+	serverHandler := protocol.ServerHandlerV2(server)
+	// Wrap the server handler to inject the client into each request context, so
+	// that log events are reflected back to the client.
+	wrapped := jsonrpc2_v2.HandlerFunc(func(ctx context.Context, req *jsonrpc2_v2.Request) (interface{}, error) {
+		ctx = protocol.WithClient(ctx, client)
+		return serverHandler.Handle(ctx, req)
+	})
+	return jsonrpc2_v2.ConnectionOptions{
+		Handler: wrapped,
+	}, nil
+}
+
+type ForwardBinder struct {
+	dialer jsonrpc2_v2.Dialer
+}
+
+func NewForwardBinder(dialer jsonrpc2_v2.Dialer) *ForwardBinder {
+	return &ForwardBinder{
+		dialer: dialer,
+	}
+}
+
+func (b *ForwardBinder) Bind(ctx context.Context, conn *jsonrpc2_v2.Connection) (opts jsonrpc2_v2.ConnectionOptions, _ error) {
+	client := protocol.ClientDispatcherV2(conn)
+	clientBinder := NewClientBinder(func(context.Context, protocol.Server) protocol.Client { return client })
+	serverConn, err := jsonrpc2_v2.Dial(context.Background(), b.dialer, clientBinder)
+	if err != nil {
+		return opts, err
+	}
+	server := protocol.ServerDispatcherV2(serverConn)
+	return jsonrpc2_v2.ConnectionOptions{
+		Handler: protocol.ServerHandlerV2(server),
+	}, nil
+}
+
+type ClientBinder struct {
+	newClient ClientFunc
+}
+
+func NewClientBinder(newClient ClientFunc) *ClientBinder {
+	return &ClientBinder{newClient}
+}
+
+func (b *ClientBinder) Bind(ctx context.Context, conn *jsonrpc2_v2.Connection) (jsonrpc2_v2.ConnectionOptions, error) {
+	server := protocol.ServerDispatcherV2(conn)
+	client := b.newClient(ctx, server)
+	return jsonrpc2_v2.ConnectionOptions{
+		Handler: protocol.ClientHandlerV2(client),
+	}, nil
+}
diff --git a/internal/lsp/lsprpc/binder_test.go b/internal/lsp/lsprpc/binder_test.go
new file mode 100644
index 0000000..aa9c9d4
--- /dev/null
+++ b/internal/lsp/lsprpc/binder_test.go
@@ -0,0 +1,68 @@
+// Copyright 2021 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.
+
+// TODO(rFindley): move this to lsprpc_test once it no longer shares with
+//                 lsprpc_test.go.
+
+package lsprpc
+
+import (
+	"context"
+	"log"
+	"regexp"
+	"testing"
+	"time"
+
+	jsonrpc2_v2 "golang.org/x/tools/internal/jsonrpc2_v2"
+	"golang.org/x/tools/internal/lsp/protocol"
+)
+
+func TestClientLoggingV2(t *testing.T) {
+	ctx, cancel := context.WithCancel(context.Background())
+	defer cancel()
+
+	listener, err := jsonrpc2_v2.NetPipe(ctx)
+	if err != nil {
+		t.Fatal(err)
+	}
+	newServer := func(ctx context.Context, client protocol.ClientCloser) protocol.Server {
+		return pingServer{}
+	}
+	serverBinder := NewServerBinder(newServer)
+	server, err := jsonrpc2_v2.Serve(ctx, listener, serverBinder)
+	if err != nil {
+		t.Fatal(err)
+	}
+	client := fakeClient{logs: make(chan string, 10)}
+	clientBinder := NewClientBinder(func(context.Context, protocol.Server) protocol.Client { return client })
+	conn, err := jsonrpc2_v2.Dial(ctx, listener.Dialer(), clientBinder)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if err := protocol.ServerDispatcherV2(conn).DidOpen(ctx, &protocol.DidOpenTextDocumentParams{}); err != nil {
+		t.Errorf("DidOpen: %v", err)
+	}
+	select {
+	case got := <-client.logs:
+		want := "ping"
+		matched, err := regexp.MatchString(want, got)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if !matched {
+			t.Errorf("got log %q, want a log containing %q", got, want)
+		}
+	case <-time.After(1 * time.Second):
+		t.Error("timeout waiting for client log")
+	}
+	if err := listener.Close(); err != nil {
+		t.Error(err)
+	}
+	if err := conn.Close(); err != nil {
+		t.Fatal(err)
+	}
+	if err := server.Wait(); err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/internal/lsp/lsprpc/lsprpc.go b/internal/lsp/lsprpc/lsprpc.go
index 04cf5ca..730f9f7 100644
--- a/internal/lsp/lsprpc/lsprpc.go
+++ b/internal/lsp/lsprpc/lsprpc.go
@@ -22,6 +22,7 @@
 	"golang.org/x/tools/internal/event"
 	"golang.org/x/tools/internal/gocommand"
 	"golang.org/x/tools/internal/jsonrpc2"
+	jsonrpc2_v2 "golang.org/x/tools/internal/jsonrpc2_v2"
 	"golang.org/x/tools/internal/lsp"
 	"golang.org/x/tools/internal/lsp/cache"
 	"golang.org/x/tools/internal/lsp/command"
@@ -56,6 +57,19 @@
 	return &StreamServer{cache: cache, daemon: daemon}
 }
 
+func (s *StreamServer) Binder() *ServerBinder {
+	newServer := func(ctx context.Context, client protocol.ClientCloser) protocol.Server {
+		session := s.cache.NewSession(ctx)
+		server := s.serverForTest
+		if server == nil {
+			server = lsp.NewServer(session, client)
+			debug.GetInstance(ctx).AddService(server, session)
+		}
+		return server
+	}
+	return NewServerBinder(newServer)
+}
+
 // ServeStream implements the jsonrpc2.StreamServer interface, by handling
 // incoming streams using a new lsp server.
 func (s *StreamServer) ServeStream(ctx context.Context, conn jsonrpc2.Conn) error {
@@ -280,6 +294,14 @@
 	return err
 }
 
+func (f *Forwarder) Binder() *ForwardBinder {
+	network, address := realNetworkAddress(f.network, f.addr, f.goplsPath)
+	dialer := jsonrpc2_v2.NetDialer(network, address, net.Dialer{
+		Timeout: 5 * time.Second,
+	})
+	return NewForwardBinder(dialer)
+}
+
 func (f *Forwarder) handshake(ctx context.Context) {
 	var (
 		hreq = handshakeRequest{
@@ -328,19 +350,21 @@
 	return connectToRemote(ctx, network, address, goplsPath, rcfg)
 }
 
+func realNetworkAddress(inNetwork, inAddr, goplsPath string) (network, address string) {
+	if inNetwork != AutoNetwork {
+		return inNetwork, inAddr
+	}
+	// The "auto" network is a fake network used for service discovery. It
+	// resolves a known address based on gopls binary path.
+	return autoNetworkAddress(goplsPath, inAddr)
+}
+
 func connectToRemote(ctx context.Context, inNetwork, inAddr, goplsPath string, rcfg remoteConfig) (net.Conn, error) {
 	var (
 		netConn          net.Conn
 		err              error
-		network, address = inNetwork, inAddr
+		network, address = realNetworkAddress(inNetwork, inAddr, goplsPath)
 	)
-	if inNetwork == AutoNetwork {
-		// f.network is overloaded to support a concept of 'automatic' addresses,
-		// which signals that the gopls remote address should be automatically
-		// derived.
-		// So we need to resolve a real network and address here.
-		network, address = autoNetworkAddress(goplsPath, inAddr)
-	}
 	// Attempt to verify that we own the remote. This is imperfect, but if we can
 	// determine that the remote is owned by a different user, we should fail.
 	ok, err := verifyRemoteOwnership(network, address)
diff --git a/internal/lsp/lsprpc/lsprpc_test.go b/internal/lsp/lsprpc/lsprpc_test.go
index ef2555d..1bdde59 100644
--- a/internal/lsp/lsprpc/lsprpc_test.go
+++ b/internal/lsp/lsprpc/lsprpc_test.go
@@ -64,7 +64,9 @@
 	cc := ts.Connect(ctx)
 	cc.Go(ctx, protocol.ClientHandler(client, jsonrpc2.MethodNotFound))
 
-	protocol.ServerDispatcher(cc).DidOpen(ctx, &protocol.DidOpenTextDocumentParams{})
+	if err := protocol.ServerDispatcher(cc).DidOpen(ctx, &protocol.DidOpenTextDocumentParams{}); err != nil {
+		t.Errorf("DidOpen: %v", err)
+	}
 
 	select {
 	case got := <-client.logs:
diff --git a/internal/lsp/protocol/protocol.go b/internal/lsp/protocol/protocol.go
index 40bb083..05adf41 100644
--- a/internal/lsp/protocol/protocol.go
+++ b/internal/lsp/protocol/protocol.go
@@ -8,36 +8,99 @@
 	"context"
 	"encoding/json"
 	"fmt"
+	"io"
 
 	"golang.org/x/tools/internal/event"
 	"golang.org/x/tools/internal/jsonrpc2"
+	jsonrpc2_v2 "golang.org/x/tools/internal/jsonrpc2_v2"
 	"golang.org/x/tools/internal/xcontext"
 	errors "golang.org/x/xerrors"
 )
 
 var (
 	// RequestCancelledError should be used when a request is cancelled early.
-	RequestCancelledError = jsonrpc2.NewError(-32800, "JSON RPC cancelled")
+	RequestCancelledError   = jsonrpc2.NewError(-32800, "JSON RPC cancelled")
+	RequestCancelledErrorV2 = jsonrpc2_v2.NewError(-32800, "JSON RPC cancelled")
 )
 
-// ClientDispatcher returns a Client that dispatches LSP requests across the
-// given jsonrpc2 connection.
-func ClientDispatcher(conn jsonrpc2.Conn) Client {
-	return &clientDispatcher{Conn: conn}
+type ClientCloser interface {
+	Client
+	io.Closer
+}
+
+type connSender interface {
+	io.Closer
+
+	Notify(ctx context.Context, method string, params interface{}) error
+	Call(ctx context.Context, method string, params, result interface{}) error
 }
 
 type clientDispatcher struct {
-	jsonrpc2.Conn
+	sender connSender
+}
+
+func (c *clientDispatcher) Close() error {
+	return c.sender.Close()
+}
+
+// ClientDispatcher returns a Client that dispatches LSP requests across the
+// given jsonrpc2 connection.
+func ClientDispatcher(conn jsonrpc2.Conn) ClientCloser {
+	return &clientDispatcher{sender: clientConn{conn}}
+}
+
+type clientConn struct {
+	conn jsonrpc2.Conn
+}
+
+func (c clientConn) Close() error {
+	return c.conn.Close()
+}
+
+func (c clientConn) Notify(ctx context.Context, method string, params interface{}) error {
+	return c.conn.Notify(ctx, method, params)
+}
+
+func (c clientConn) Call(ctx context.Context, method string, params interface{}, result interface{}) error {
+	id, err := c.conn.Call(ctx, method, params, result)
+	if ctx.Err() != nil {
+		cancelCall(ctx, c, id)
+	}
+	return err
+}
+
+func ClientDispatcherV2(conn *jsonrpc2_v2.Connection) ClientCloser {
+	return &clientDispatcher{clientConnV2{conn}}
+}
+
+type clientConnV2 struct {
+	conn *jsonrpc2_v2.Connection
+}
+
+func (c clientConnV2) Close() error {
+	return c.conn.Close()
+}
+
+func (c clientConnV2) Notify(ctx context.Context, method string, params interface{}) error {
+	return c.conn.Notify(ctx, method, params)
+}
+
+func (c clientConnV2) Call(ctx context.Context, method string, params interface{}, result interface{}) error {
+	return c.conn.Call(ctx, method, params).Await(ctx, result)
 }
 
 // ServerDispatcher returns a Server that dispatches LSP requests across the
 // given jsonrpc2 connection.
 func ServerDispatcher(conn jsonrpc2.Conn) Server {
-	return &serverDispatcher{Conn: conn}
+	return &serverDispatcher{sender: clientConn{conn}}
+}
+
+func ServerDispatcherV2(conn *jsonrpc2_v2.Connection) Server {
+	return &serverDispatcher{sender: clientConnV2{conn}}
 }
 
 type serverDispatcher struct {
-	jsonrpc2.Conn
+	sender connSender
 }
 
 func ClientHandler(client Client, handler jsonrpc2.Handler) jsonrpc2.Handler {
@@ -54,6 +117,28 @@
 	}
 }
 
+func ClientHandlerV2(client Client) jsonrpc2_v2.Handler {
+	return jsonrpc2_v2.HandlerFunc(func(ctx context.Context, req *jsonrpc2_v2.Request) (interface{}, error) {
+		if ctx.Err() != nil {
+			return nil, RequestCancelledErrorV2
+		}
+		req1 := req2to1(req)
+		var (
+			result interface{}
+			resErr error
+		)
+		replier := func(_ context.Context, res interface{}, err error) error {
+			result, resErr = res, err
+			return nil
+		}
+		_, err := clientDispatch(ctx, client, replier, req1)
+		if err != nil {
+			return nil, err
+		}
+		return result, resErr
+	})
+}
+
 func ServerHandler(server Server, handler jsonrpc2.Handler) jsonrpc2.Handler {
 	return func(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error {
 		if ctx.Err() != nil {
@@ -76,6 +161,54 @@
 
 	}
 }
+
+func ServerHandlerV2(server Server) jsonrpc2_v2.Handler {
+	return jsonrpc2_v2.HandlerFunc(func(ctx context.Context, req *jsonrpc2_v2.Request) (interface{}, error) {
+		if ctx.Err() != nil {
+			return nil, RequestCancelledErrorV2
+		}
+		req1 := req2to1(req)
+		var (
+			result interface{}
+			resErr error
+		)
+		replier := func(_ context.Context, res interface{}, err error) error {
+			result, resErr = res, err
+			return nil
+		}
+		_, err := serverDispatch(ctx, server, replier, req1)
+		if err != nil {
+			return nil, err
+		}
+		return result, resErr
+	})
+}
+
+func req2to1(req2 *jsonrpc2_v2.Request) jsonrpc2.Request {
+	if req2.ID.IsValid() {
+		raw := req2.ID.Raw()
+		var idv1 jsonrpc2.ID
+		switch v := raw.(type) {
+		case int64:
+			idv1 = jsonrpc2.NewIntID(v)
+		case string:
+			idv1 = jsonrpc2.NewStringID(v)
+		default:
+			panic(fmt.Sprintf("unsupported ID type %T", raw))
+		}
+		req1, err := jsonrpc2.NewCall(idv1, req2.Method, req2.Params)
+		if err != nil {
+			panic(err)
+		}
+		return req1
+	}
+	req1, err := jsonrpc2.NewNotification(req2.Method, req2.Params)
+	if err != nil {
+		panic(err)
+	}
+	return req1
+}
+
 func Handlers(handler jsonrpc2.Handler) jsonrpc2.Handler {
 	return CancelHandler(
 		jsonrpc2.AsyncHandler(
@@ -120,17 +253,17 @@
 func Call(ctx context.Context, conn jsonrpc2.Conn, method string, params interface{}, result interface{}) error {
 	id, err := conn.Call(ctx, method, params, result)
 	if ctx.Err() != nil {
-		cancelCall(ctx, conn, id)
+		cancelCall(ctx, clientConn{conn}, id)
 	}
 	return err
 }
 
-func cancelCall(ctx context.Context, conn jsonrpc2.Conn, id jsonrpc2.ID) {
+func cancelCall(ctx context.Context, sender connSender, id jsonrpc2.ID) {
 	ctx = xcontext.Detach(ctx)
 	ctx, done := event.Start(ctx, "protocol.canceller")
 	defer done()
 	// Note that only *jsonrpc2.ID implements json.Marshaler.
-	conn.Notify(ctx, "$/cancelRequest", &CancelParams{ID: &id})
+	sender.Notify(ctx, "$/cancelRequest", &CancelParams{ID: &id})
 }
 
 func sendParseError(ctx context.Context, reply jsonrpc2.Replier, err error) error {
diff --git a/internal/lsp/protocol/tsclient.go b/internal/lsp/protocol/tsclient.go
index 1f33c02..79e57b9 100644
--- a/internal/lsp/protocol/tsclient.go
+++ b/internal/lsp/protocol/tsclient.go
@@ -7,7 +7,7 @@
 // Package protocol contains data types and code for LSP jsonrpcs
 // generated automatically from vscode-languageserver-node
 // commit: d58c00bbf8837b9fd0144924db5e7b1c543d839e
-// last fetched Sat Apr 17 2021 08:26:29 GMT-0400 (Eastern Daylight Time)
+// last fetched Tue May 18 2021 13:45:07 GMT-0400 (Eastern Daylight Time)
 
 // Code generated (see typescript/README.md) DO NOT EDIT.
 
@@ -134,27 +134,27 @@
 }
 
 func (s *clientDispatcher) ShowMessage(ctx context.Context, params *ShowMessageParams) error {
-	return s.Conn.Notify(ctx, "window/showMessage", params)
+	return s.sender.Notify(ctx, "window/showMessage", params)
 }
 
 func (s *clientDispatcher) LogMessage(ctx context.Context, params *LogMessageParams) error {
-	return s.Conn.Notify(ctx, "window/logMessage", params)
+	return s.sender.Notify(ctx, "window/logMessage", params)
 }
 
 func (s *clientDispatcher) Event(ctx context.Context, params *interface{}) error {
-	return s.Conn.Notify(ctx, "telemetry/event", params)
+	return s.sender.Notify(ctx, "telemetry/event", params)
 }
 
 func (s *clientDispatcher) PublishDiagnostics(ctx context.Context, params *PublishDiagnosticsParams) error {
-	return s.Conn.Notify(ctx, "textDocument/publishDiagnostics", params)
+	return s.sender.Notify(ctx, "textDocument/publishDiagnostics", params)
 }
 
 func (s *clientDispatcher) Progress(ctx context.Context, params *ProgressParams) error {
-	return s.Conn.Notify(ctx, "$/progress", params)
+	return s.sender.Notify(ctx, "$/progress", params)
 }
 func (s *clientDispatcher) WorkspaceFolders(ctx context.Context) ([]WorkspaceFolder /*WorkspaceFolder[] | null*/, error) {
 	var result []WorkspaceFolder /*WorkspaceFolder[] | null*/
-	if err := Call(ctx, s.Conn, "workspace/workspaceFolders", nil, &result); err != nil {
+	if err := s.sender.Call(ctx, "workspace/workspaceFolders", nil, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -162,35 +162,35 @@
 
 func (s *clientDispatcher) Configuration(ctx context.Context, params *ParamConfiguration) ([]interface{}, error) {
 	var result []interface{}
-	if err := Call(ctx, s.Conn, "workspace/configuration", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "workspace/configuration", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
 }
 
 func (s *clientDispatcher) WorkDoneProgressCreate(ctx context.Context, params *WorkDoneProgressCreateParams) error {
-	return Call(ctx, s.Conn, "window/workDoneProgress/create", params, nil) // Call, not Notify
+	return s.sender.Call(ctx, "window/workDoneProgress/create", params, nil) // Call, not Notify
 }
 
 func (s *clientDispatcher) ShowDocument(ctx context.Context, params *ShowDocumentParams) (*ShowDocumentResult, error) {
 	var result *ShowDocumentResult
-	if err := Call(ctx, s.Conn, "window/showDocument", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "window/showDocument", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
 }
 
 func (s *clientDispatcher) RegisterCapability(ctx context.Context, params *RegistrationParams) error {
-	return Call(ctx, s.Conn, "client/registerCapability", params, nil) // Call, not Notify
+	return s.sender.Call(ctx, "client/registerCapability", params, nil) // Call, not Notify
 }
 
 func (s *clientDispatcher) UnregisterCapability(ctx context.Context, params *UnregistrationParams) error {
-	return Call(ctx, s.Conn, "client/unregisterCapability", params, nil) // Call, not Notify
+	return s.sender.Call(ctx, "client/unregisterCapability", params, nil) // Call, not Notify
 }
 
 func (s *clientDispatcher) ShowMessageRequest(ctx context.Context, params *ShowMessageRequestParams) (*MessageActionItem /*MessageActionItem | null*/, error) {
 	var result *MessageActionItem /*MessageActionItem | null*/
-	if err := Call(ctx, s.Conn, "window/showMessageRequest", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "window/showMessageRequest", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -198,7 +198,7 @@
 
 func (s *clientDispatcher) ApplyEdit(ctx context.Context, params *ApplyWorkspaceEditParams) (*ApplyWorkspaceEditResponse, error) {
 	var result *ApplyWorkspaceEditResponse
-	if err := Call(ctx, s.Conn, "workspace/applyEdit", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "workspace/applyEdit", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
diff --git a/internal/lsp/protocol/tsprotocol.go b/internal/lsp/protocol/tsprotocol.go
index 51d6142..ef83dea 100644
--- a/internal/lsp/protocol/tsprotocol.go
+++ b/internal/lsp/protocol/tsprotocol.go
@@ -5,7 +5,7 @@
 // Package protocol contains data types and code for LSP jsonrpcs
 // generated automatically from vscode-languageserver-node
 // commit: d58c00bbf8837b9fd0144924db5e7b1c543d839e
-// last fetched Sat Apr 17 2021 08:26:29 GMT-0400 (Eastern Daylight Time)
+// last fetched Tue May 18 2021 13:45:07 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 a479968..cc50a6d 100644
--- a/internal/lsp/protocol/tsserver.go
+++ b/internal/lsp/protocol/tsserver.go
@@ -7,7 +7,7 @@
 // Package protocol contains data types and code for LSP jsonrpcs
 // generated automatically from vscode-languageserver-node
 // commit: d58c00bbf8837b9fd0144924db5e7b1c543d839e
-// last fetched Sat Apr 17 2021 08:26:29 GMT-0400 (Eastern Daylight Time)
+// last fetched Tue May 18 2021 13:45:07 GMT-0400 (Eastern Daylight Time)
 
 // Code generated (see typescript/README.md) DO NOT EDIT.
 
@@ -530,71 +530,71 @@
 }
 
 func (s *serverDispatcher) DidChangeWorkspaceFolders(ctx context.Context, params *DidChangeWorkspaceFoldersParams) error {
-	return s.Conn.Notify(ctx, "workspace/didChangeWorkspaceFolders", params)
+	return s.sender.Notify(ctx, "workspace/didChangeWorkspaceFolders", params)
 }
 
 func (s *serverDispatcher) WorkDoneProgressCancel(ctx context.Context, params *WorkDoneProgressCancelParams) error {
-	return s.Conn.Notify(ctx, "window/workDoneProgress/cancel", params)
+	return s.sender.Notify(ctx, "window/workDoneProgress/cancel", params)
 }
 
 func (s *serverDispatcher) DidCreateFiles(ctx context.Context, params *CreateFilesParams) error {
-	return s.Conn.Notify(ctx, "workspace/didCreateFiles", params)
+	return s.sender.Notify(ctx, "workspace/didCreateFiles", params)
 }
 
 func (s *serverDispatcher) DidRenameFiles(ctx context.Context, params *RenameFilesParams) error {
-	return s.Conn.Notify(ctx, "workspace/didRenameFiles", params)
+	return s.sender.Notify(ctx, "workspace/didRenameFiles", params)
 }
 
 func (s *serverDispatcher) DidDeleteFiles(ctx context.Context, params *DeleteFilesParams) error {
-	return s.Conn.Notify(ctx, "workspace/didDeleteFiles", params)
+	return s.sender.Notify(ctx, "workspace/didDeleteFiles", params)
 }
 
 func (s *serverDispatcher) Initialized(ctx context.Context, params *InitializedParams) error {
-	return s.Conn.Notify(ctx, "initialized", params)
+	return s.sender.Notify(ctx, "initialized", params)
 }
 
 func (s *serverDispatcher) Exit(ctx context.Context) error {
-	return s.Conn.Notify(ctx, "exit", nil)
+	return s.sender.Notify(ctx, "exit", nil)
 }
 
 func (s *serverDispatcher) DidChangeConfiguration(ctx context.Context, params *DidChangeConfigurationParams) error {
-	return s.Conn.Notify(ctx, "workspace/didChangeConfiguration", params)
+	return s.sender.Notify(ctx, "workspace/didChangeConfiguration", params)
 }
 
 func (s *serverDispatcher) DidOpen(ctx context.Context, params *DidOpenTextDocumentParams) error {
-	return s.Conn.Notify(ctx, "textDocument/didOpen", params)
+	return s.sender.Notify(ctx, "textDocument/didOpen", params)
 }
 
 func (s *serverDispatcher) DidChange(ctx context.Context, params *DidChangeTextDocumentParams) error {
-	return s.Conn.Notify(ctx, "textDocument/didChange", params)
+	return s.sender.Notify(ctx, "textDocument/didChange", params)
 }
 
 func (s *serverDispatcher) DidClose(ctx context.Context, params *DidCloseTextDocumentParams) error {
-	return s.Conn.Notify(ctx, "textDocument/didClose", params)
+	return s.sender.Notify(ctx, "textDocument/didClose", params)
 }
 
 func (s *serverDispatcher) DidSave(ctx context.Context, params *DidSaveTextDocumentParams) error {
-	return s.Conn.Notify(ctx, "textDocument/didSave", params)
+	return s.sender.Notify(ctx, "textDocument/didSave", params)
 }
 
 func (s *serverDispatcher) WillSave(ctx context.Context, params *WillSaveTextDocumentParams) error {
-	return s.Conn.Notify(ctx, "textDocument/willSave", params)
+	return s.sender.Notify(ctx, "textDocument/willSave", params)
 }
 
 func (s *serverDispatcher) DidChangeWatchedFiles(ctx context.Context, params *DidChangeWatchedFilesParams) error {
-	return s.Conn.Notify(ctx, "workspace/didChangeWatchedFiles", params)
+	return s.sender.Notify(ctx, "workspace/didChangeWatchedFiles", params)
 }
 
 func (s *serverDispatcher) SetTrace(ctx context.Context, params *SetTraceParams) error {
-	return s.Conn.Notify(ctx, "$/setTrace", params)
+	return s.sender.Notify(ctx, "$/setTrace", params)
 }
 
 func (s *serverDispatcher) LogTrace(ctx context.Context, params *LogTraceParams) error {
-	return s.Conn.Notify(ctx, "$/logTrace", params)
+	return s.sender.Notify(ctx, "$/logTrace", params)
 }
 func (s *serverDispatcher) Implementation(ctx context.Context, params *ImplementationParams) (Definition /*Definition | DefinitionLink[] | null*/, error) {
 	var result Definition /*Definition | DefinitionLink[] | null*/
-	if err := Call(ctx, s.Conn, "textDocument/implementation", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/implementation", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -602,7 +602,7 @@
 
 func (s *serverDispatcher) TypeDefinition(ctx context.Context, params *TypeDefinitionParams) (Definition /*Definition | DefinitionLink[] | null*/, error) {
 	var result Definition /*Definition | DefinitionLink[] | null*/
-	if err := Call(ctx, s.Conn, "textDocument/typeDefinition", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/typeDefinition", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -610,7 +610,7 @@
 
 func (s *serverDispatcher) DocumentColor(ctx context.Context, params *DocumentColorParams) ([]ColorInformation, error) {
 	var result []ColorInformation
-	if err := Call(ctx, s.Conn, "textDocument/documentColor", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/documentColor", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -618,7 +618,7 @@
 
 func (s *serverDispatcher) ColorPresentation(ctx context.Context, params *ColorPresentationParams) ([]ColorPresentation, error) {
 	var result []ColorPresentation
-	if err := Call(ctx, s.Conn, "textDocument/colorPresentation", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/colorPresentation", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -626,7 +626,7 @@
 
 func (s *serverDispatcher) FoldingRange(ctx context.Context, params *FoldingRangeParams) ([]FoldingRange /*FoldingRange[] | null*/, error) {
 	var result []FoldingRange /*FoldingRange[] | null*/
-	if err := Call(ctx, s.Conn, "textDocument/foldingRange", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/foldingRange", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -634,7 +634,7 @@
 
 func (s *serverDispatcher) Declaration(ctx context.Context, params *DeclarationParams) (Declaration /*Declaration | DeclarationLink[] | null*/, error) {
 	var result Declaration /*Declaration | DeclarationLink[] | null*/
-	if err := Call(ctx, s.Conn, "textDocument/declaration", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/declaration", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -642,7 +642,7 @@
 
 func (s *serverDispatcher) SelectionRange(ctx context.Context, params *SelectionRangeParams) ([]SelectionRange /*SelectionRange[] | null*/, error) {
 	var result []SelectionRange /*SelectionRange[] | null*/
-	if err := Call(ctx, s.Conn, "textDocument/selectionRange", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/selectionRange", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -650,7 +650,7 @@
 
 func (s *serverDispatcher) PrepareCallHierarchy(ctx context.Context, params *CallHierarchyPrepareParams) ([]CallHierarchyItem /*CallHierarchyItem[] | null*/, error) {
 	var result []CallHierarchyItem /*CallHierarchyItem[] | null*/
-	if err := Call(ctx, s.Conn, "textDocument/prepareCallHierarchy", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/prepareCallHierarchy", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -658,7 +658,7 @@
 
 func (s *serverDispatcher) IncomingCalls(ctx context.Context, params *CallHierarchyIncomingCallsParams) ([]CallHierarchyIncomingCall /*CallHierarchyIncomingCall[] | null*/, error) {
 	var result []CallHierarchyIncomingCall /*CallHierarchyIncomingCall[] | null*/
-	if err := Call(ctx, s.Conn, "callHierarchy/incomingCalls", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "callHierarchy/incomingCalls", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -666,7 +666,7 @@
 
 func (s *serverDispatcher) OutgoingCalls(ctx context.Context, params *CallHierarchyOutgoingCallsParams) ([]CallHierarchyOutgoingCall /*CallHierarchyOutgoingCall[] | null*/, error) {
 	var result []CallHierarchyOutgoingCall /*CallHierarchyOutgoingCall[] | null*/
-	if err := Call(ctx, s.Conn, "callHierarchy/outgoingCalls", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "callHierarchy/outgoingCalls", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -674,7 +674,7 @@
 
 func (s *serverDispatcher) SemanticTokensFull(ctx context.Context, params *SemanticTokensParams) (*SemanticTokens /*SemanticTokens | null*/, error) {
 	var result *SemanticTokens /*SemanticTokens | null*/
-	if err := Call(ctx, s.Conn, "textDocument/semanticTokens/full", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/semanticTokens/full", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -682,7 +682,7 @@
 
 func (s *serverDispatcher) SemanticTokensFullDelta(ctx context.Context, params *SemanticTokensDeltaParams) (interface{} /* SemanticTokens | SemanticTokensDelta | float64*/, error) {
 	var result interface{} /* SemanticTokens | SemanticTokensDelta | float64*/
-	if err := Call(ctx, s.Conn, "textDocument/semanticTokens/full/delta", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/semanticTokens/full/delta", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -690,19 +690,19 @@
 
 func (s *serverDispatcher) SemanticTokensRange(ctx context.Context, params *SemanticTokensRangeParams) (*SemanticTokens /*SemanticTokens | null*/, error) {
 	var result *SemanticTokens /*SemanticTokens | null*/
-	if err := Call(ctx, s.Conn, "textDocument/semanticTokens/range", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/semanticTokens/range", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
 }
 
 func (s *serverDispatcher) SemanticTokensRefresh(ctx context.Context) error {
-	return Call(ctx, s.Conn, "workspace/semanticTokens/refresh", nil, nil)
+	return s.sender.Call(ctx, "workspace/semanticTokens/refresh", nil, nil)
 }
 
 func (s *serverDispatcher) LinkedEditingRange(ctx context.Context, params *LinkedEditingRangeParams) (*LinkedEditingRanges /*LinkedEditingRanges | null*/, error) {
 	var result *LinkedEditingRanges /*LinkedEditingRanges | null*/
-	if err := Call(ctx, s.Conn, "textDocument/linkedEditingRange", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/linkedEditingRange", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -710,7 +710,7 @@
 
 func (s *serverDispatcher) WillCreateFiles(ctx context.Context, params *CreateFilesParams) (*WorkspaceEdit /*WorkspaceEdit | null*/, error) {
 	var result *WorkspaceEdit /*WorkspaceEdit | null*/
-	if err := Call(ctx, s.Conn, "workspace/willCreateFiles", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "workspace/willCreateFiles", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -718,7 +718,7 @@
 
 func (s *serverDispatcher) WillRenameFiles(ctx context.Context, params *RenameFilesParams) (*WorkspaceEdit /*WorkspaceEdit | null*/, error) {
 	var result *WorkspaceEdit /*WorkspaceEdit | null*/
-	if err := Call(ctx, s.Conn, "workspace/willRenameFiles", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "workspace/willRenameFiles", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -726,7 +726,7 @@
 
 func (s *serverDispatcher) WillDeleteFiles(ctx context.Context, params *DeleteFilesParams) (*WorkspaceEdit /*WorkspaceEdit | null*/, error) {
 	var result *WorkspaceEdit /*WorkspaceEdit | null*/
-	if err := Call(ctx, s.Conn, "workspace/willDeleteFiles", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "workspace/willDeleteFiles", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -734,7 +734,7 @@
 
 func (s *serverDispatcher) Moniker(ctx context.Context, params *MonikerParams) ([]Moniker /*Moniker[] | null*/, error) {
 	var result []Moniker /*Moniker[] | null*/
-	if err := Call(ctx, s.Conn, "textDocument/moniker", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/moniker", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -742,19 +742,19 @@
 
 func (s *serverDispatcher) Initialize(ctx context.Context, params *ParamInitialize) (*InitializeResult, error) {
 	var result *InitializeResult
-	if err := Call(ctx, s.Conn, "initialize", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "initialize", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
 }
 
 func (s *serverDispatcher) Shutdown(ctx context.Context) error {
-	return Call(ctx, s.Conn, "shutdown", nil, nil)
+	return s.sender.Call(ctx, "shutdown", nil, nil)
 }
 
 func (s *serverDispatcher) WillSaveWaitUntil(ctx context.Context, params *WillSaveTextDocumentParams) ([]TextEdit /*TextEdit[] | null*/, error) {
 	var result []TextEdit /*TextEdit[] | null*/
-	if err := Call(ctx, s.Conn, "textDocument/willSaveWaitUntil", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/willSaveWaitUntil", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -762,7 +762,7 @@
 
 func (s *serverDispatcher) Completion(ctx context.Context, params *CompletionParams) (*CompletionList /*CompletionItem[] | CompletionList | null*/, error) {
 	var result *CompletionList /*CompletionItem[] | CompletionList | null*/
-	if err := Call(ctx, s.Conn, "textDocument/completion", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/completion", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -770,7 +770,7 @@
 
 func (s *serverDispatcher) Resolve(ctx context.Context, params *CompletionItem) (*CompletionItem, error) {
 	var result *CompletionItem
-	if err := Call(ctx, s.Conn, "completionItem/resolve", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "completionItem/resolve", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -778,7 +778,7 @@
 
 func (s *serverDispatcher) Hover(ctx context.Context, params *HoverParams) (*Hover /*Hover | null*/, error) {
 	var result *Hover /*Hover | null*/
-	if err := Call(ctx, s.Conn, "textDocument/hover", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/hover", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -786,7 +786,7 @@
 
 func (s *serverDispatcher) SignatureHelp(ctx context.Context, params *SignatureHelpParams) (*SignatureHelp /*SignatureHelp | null*/, error) {
 	var result *SignatureHelp /*SignatureHelp | null*/
-	if err := Call(ctx, s.Conn, "textDocument/signatureHelp", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/signatureHelp", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -794,7 +794,7 @@
 
 func (s *serverDispatcher) Definition(ctx context.Context, params *DefinitionParams) (Definition /*Definition | DefinitionLink[] | null*/, error) {
 	var result Definition /*Definition | DefinitionLink[] | null*/
-	if err := Call(ctx, s.Conn, "textDocument/definition", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/definition", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -802,7 +802,7 @@
 
 func (s *serverDispatcher) References(ctx context.Context, params *ReferenceParams) ([]Location /*Location[] | null*/, error) {
 	var result []Location /*Location[] | null*/
-	if err := Call(ctx, s.Conn, "textDocument/references", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/references", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -810,7 +810,7 @@
 
 func (s *serverDispatcher) DocumentHighlight(ctx context.Context, params *DocumentHighlightParams) ([]DocumentHighlight /*DocumentHighlight[] | null*/, error) {
 	var result []DocumentHighlight /*DocumentHighlight[] | null*/
-	if err := Call(ctx, s.Conn, "textDocument/documentHighlight", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/documentHighlight", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -818,7 +818,7 @@
 
 func (s *serverDispatcher) DocumentSymbol(ctx context.Context, params *DocumentSymbolParams) ([]interface{} /*SymbolInformation[] | DocumentSymbol[] | null*/, error) {
 	var result []interface{} /*SymbolInformation[] | DocumentSymbol[] | null*/
-	if err := Call(ctx, s.Conn, "textDocument/documentSymbol", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/documentSymbol", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -826,7 +826,7 @@
 
 func (s *serverDispatcher) CodeAction(ctx context.Context, params *CodeActionParams) ([]CodeAction /*(Command | CodeAction)[] | null*/, error) {
 	var result []CodeAction /*(Command | CodeAction)[] | null*/
-	if err := Call(ctx, s.Conn, "textDocument/codeAction", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/codeAction", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -834,7 +834,7 @@
 
 func (s *serverDispatcher) ResolveCodeAction(ctx context.Context, params *CodeAction) (*CodeAction, error) {
 	var result *CodeAction
-	if err := Call(ctx, s.Conn, "codeAction/resolve", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "codeAction/resolve", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -842,7 +842,7 @@
 
 func (s *serverDispatcher) Symbol(ctx context.Context, params *WorkspaceSymbolParams) ([]SymbolInformation /*SymbolInformation[] | null*/, error) {
 	var result []SymbolInformation /*SymbolInformation[] | null*/
-	if err := Call(ctx, s.Conn, "workspace/symbol", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "workspace/symbol", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -850,7 +850,7 @@
 
 func (s *serverDispatcher) CodeLens(ctx context.Context, params *CodeLensParams) ([]CodeLens /*CodeLens[] | null*/, error) {
 	var result []CodeLens /*CodeLens[] | null*/
-	if err := Call(ctx, s.Conn, "textDocument/codeLens", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/codeLens", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -858,19 +858,19 @@
 
 func (s *serverDispatcher) ResolveCodeLens(ctx context.Context, params *CodeLens) (*CodeLens, error) {
 	var result *CodeLens
-	if err := Call(ctx, s.Conn, "codeLens/resolve", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "codeLens/resolve", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
 }
 
 func (s *serverDispatcher) CodeLensRefresh(ctx context.Context) error {
-	return Call(ctx, s.Conn, "workspace/codeLens/refresh", nil, nil)
+	return s.sender.Call(ctx, "workspace/codeLens/refresh", nil, nil)
 }
 
 func (s *serverDispatcher) DocumentLink(ctx context.Context, params *DocumentLinkParams) ([]DocumentLink /*DocumentLink[] | null*/, error) {
 	var result []DocumentLink /*DocumentLink[] | null*/
-	if err := Call(ctx, s.Conn, "textDocument/documentLink", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/documentLink", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -878,7 +878,7 @@
 
 func (s *serverDispatcher) ResolveDocumentLink(ctx context.Context, params *DocumentLink) (*DocumentLink, error) {
 	var result *DocumentLink
-	if err := Call(ctx, s.Conn, "documentLink/resolve", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "documentLink/resolve", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -886,7 +886,7 @@
 
 func (s *serverDispatcher) Formatting(ctx context.Context, params *DocumentFormattingParams) ([]TextEdit /*TextEdit[] | null*/, error) {
 	var result []TextEdit /*TextEdit[] | null*/
-	if err := Call(ctx, s.Conn, "textDocument/formatting", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/formatting", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -894,7 +894,7 @@
 
 func (s *serverDispatcher) RangeFormatting(ctx context.Context, params *DocumentRangeFormattingParams) ([]TextEdit /*TextEdit[] | null*/, error) {
 	var result []TextEdit /*TextEdit[] | null*/
-	if err := Call(ctx, s.Conn, "textDocument/rangeFormatting", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/rangeFormatting", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -902,7 +902,7 @@
 
 func (s *serverDispatcher) OnTypeFormatting(ctx context.Context, params *DocumentOnTypeFormattingParams) ([]TextEdit /*TextEdit[] | null*/, error) {
 	var result []TextEdit /*TextEdit[] | null*/
-	if err := Call(ctx, s.Conn, "textDocument/onTypeFormatting", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/onTypeFormatting", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -910,7 +910,7 @@
 
 func (s *serverDispatcher) Rename(ctx context.Context, params *RenameParams) (*WorkspaceEdit /*WorkspaceEdit | null*/, error) {
 	var result *WorkspaceEdit /*WorkspaceEdit | null*/
-	if err := Call(ctx, s.Conn, "textDocument/rename", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/rename", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -918,7 +918,7 @@
 
 func (s *serverDispatcher) PrepareRename(ctx context.Context, params *PrepareRenameParams) (*Range /*Range | { range: Range, placeholder: string } | { defaultBehavior: boolean } | null*/, error) {
 	var result *Range /*Range | { range: Range, placeholder: string } | { defaultBehavior: boolean } | null*/
-	if err := Call(ctx, s.Conn, "textDocument/prepareRename", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/prepareRename", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -926,7 +926,7 @@
 
 func (s *serverDispatcher) ExecuteCommand(ctx context.Context, params *ExecuteCommandParams) (interface{} /*any | null*/, error) {
 	var result interface{} /*any | null*/
-	if err := Call(ctx, s.Conn, "workspace/executeCommand", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "workspace/executeCommand", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -934,7 +934,7 @@
 
 func (s *serverDispatcher) Diagnostic(ctx context.Context, params *string) (*string, error) {
 	var result *string
-	if err := Call(ctx, s.Conn, "textDocument/diagnostic", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "textDocument/diagnostic", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
@@ -942,19 +942,19 @@
 
 func (s *serverDispatcher) DiagnosticWorkspace(ctx context.Context, params *WorkspaceDiagnosticParams) (*WorkspaceDiagnosticReport, error) {
 	var result *WorkspaceDiagnosticReport
-	if err := Call(ctx, s.Conn, "workspace/diagnostic", params, &result); err != nil {
+	if err := s.sender.Call(ctx, "workspace/diagnostic", params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
 }
 
 func (s *serverDispatcher) DiagnosticRefresh(ctx context.Context) error {
-	return Call(ctx, s.Conn, "workspace/diagnostic/refresh", nil, nil)
+	return s.sender.Call(ctx, "workspace/diagnostic/refresh", nil, nil)
 }
 
 func (s *serverDispatcher) NonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error) {
 	var result interface{}
-	if err := Call(ctx, s.Conn, method, params, &result); err != nil {
+	if err := s.sender.Call(ctx, method, params, &result); err != nil {
 		return nil, err
 	}
 	return result, nil
diff --git a/internal/lsp/protocol/typescript/code.ts b/internal/lsp/protocol/typescript/code.ts
index abf7345..9461c24 100644
--- a/internal/lsp/protocol/typescript/code.ts
+++ b/internal/lsp/protocol/typescript/code.ts
@@ -1191,7 +1191,7 @@
   const arg3 = a == '' || a == 'void' ? 'nil' : 'params';
   side.calls.push(`
   func (s *${side.name}Dispatcher) ${sig(nm, a, '', true)} {
-    return s.Conn.Notify(ctx, "${m}", ${arg3})
+    return s.sender.Notify(ctx, "${m}", ${arg3})
   }`);
 }
 
@@ -1241,18 +1241,18 @@
   side.cases.push(`${caseHdr}\n${case1}\n${case2}`);
 
   const callHdr = `func (s *${side.name}Dispatcher) ${sig(nm, a, b, true)} {`;
-  let callBody = `return Call(ctx, s.Conn, "${m}", nil, nil)\n}`;
+  let callBody = `return s.sender.Call(ctx, "${m}", nil, nil)\n}`;
   if (b != '' && b != 'void') {
     const p2 = a == '' ? 'nil' : 'params';
     const returnType = indirect(b) ? `*${b}` : b;
     callBody = `var result ${returnType}
-			if err := Call(ctx, s.Conn, "${m}", ${p2}, &result); err != nil {
+			if err := s.sender.Call(ctx, "${m}", ${p2}, &result); err != nil {
 				return nil, err
       }
       return result, nil
     }`;
   } else if (a != '') {
-    callBody = `return Call(ctx, s.Conn, "${m}", params, nil) // Call, not Notify
+    callBody = `return s.sender.Call(ctx, "${m}", params, nil) // Call, not Notify
   }`;
   }
   side.calls.push(`${callHdr}\n${callBody}\n`);
@@ -1359,7 +1359,7 @@
   server.calls.push(
     `func (s *serverDispatcher) NonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error) {
       var result interface{}
-      if err := Call(ctx, s.Conn, method, params, &result); err != nil {
+      if err := s.sender.Call(ctx, method, params, &result); err != nil {
         return nil, err
       }
       return result, nil
diff --git a/internal/lsp/server.go b/internal/lsp/server.go
index dd6d6e2..99786fe 100644
--- a/internal/lsp/server.go
+++ b/internal/lsp/server.go
@@ -21,7 +21,7 @@
 
 // NewServer creates an LSP server and binds it to handle incoming client
 // messages on on the supplied stream.
-func NewServer(session source.Session, client protocol.Client) *Server {
+func NewServer(session source.Session, client protocol.ClientCloser) *Server {
 	return &Server{
 		diagnostics:           map[span.URI]*fileReports{},
 		gcOptimizationDetails: make(map[string]struct{}),
@@ -60,7 +60,7 @@
 
 // Server implements the protocol.Server interface.
 type Server struct {
-	client protocol.Client
+	client protocol.ClientCloser
 
 	stateMu sync.Mutex
 	state   serverState