internal/jsonrpc2: split main loop from construction to fix race

This changes the basic API of a jsonrpc2 connection to run the
read loop as a method rather than in a go routine launched in
the NewConn. This allows the handler to be created and bound
between construction and the read loop starting, which fixes
the race.

Fixes golang/go#30091

Change-Id: I8201175affe431819cf473e5194d70c019f58425
Reviewed-on: https://go-review.googlesource.com/c/tools/+/170003
Run-TryBot: Ian Cottrell <iancottrell@google.com>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/jsonrpc2/jsonrpc2.go b/internal/jsonrpc2/jsonrpc2.go
index e465d3ab..5c30829 100644
--- a/internal/jsonrpc2/jsonrpc2.go
+++ b/internal/jsonrpc2/jsonrpc2.go
@@ -19,13 +19,12 @@
 // 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 {
-	handle     Handler
-	cancel     Canceler
-	log        Logger
+	seq        int64 // must only be accessed using atomic operations
+	Handler    Handler
+	Canceler   Canceler
+	Logger     Logger
 	stream     Stream
-	done       chan struct{}
 	err        error
-	seq        int64      // must only be accessed using atomic operations
 	pendingMu  sync.Mutex // protects the pending map
 	pending    map[ID]chan *Response
 	handlingMu sync.Mutex // protects the handling map
@@ -57,70 +56,27 @@
 	}
 }
 
-// NewConn creates a new connection object that reads and writes messages from
-// the supplied stream and dispatches incoming messages to the supplied handler.
-func NewConn(ctx context.Context, s Stream, options ...interface{}) *Conn {
+// NewConn creates a new connection object around the supplied stream.
+// You must call Run for the connection to be active.
+func NewConn(s Stream) *Conn {
 	conn := &Conn{
 		stream:   s,
-		done:     make(chan struct{}),
 		pending:  make(map[ID]chan *Response),
 		handling: make(map[ID]handling),
 	}
-	for _, opt := range options {
-		switch opt := opt.(type) {
-		case Handler:
-			if conn.handle != nil {
-				panic("Duplicate Handler function in options list")
-			}
-			conn.handle = opt
-		case Canceler:
-			if conn.cancel != nil {
-				panic("Duplicate Canceler function in options list")
-			}
-			conn.cancel = opt
-		case Logger:
-			if conn.log != nil {
-				panic("Duplicate Logger function in options list")
-			}
-			conn.log = opt
-		default:
-			panic(fmt.Errorf("Unknown option type %T in options list", opt))
+	// the default handler reports a method error
+	conn.Handler = func(ctx context.Context, c *Conn, r *Request) {
+		if r.IsNotify() {
+			c.Reply(ctx, r, nil, NewErrorf(CodeMethodNotFound, "method %q not found", r.Method))
 		}
 	}
-	if conn.handle == nil {
-		// the default handler reports a method error
-		conn.handle = func(ctx context.Context, c *Conn, r *Request) {
-			if r.IsNotify() {
-				c.Reply(ctx, r, nil, NewErrorf(CodeMethodNotFound, "method %q not found", r.Method))
-			}
-		}
-	}
-	if conn.cancel == nil {
-		// the default canceller does nothing
-		conn.cancel = func(context.Context, *Conn, *Request) {}
-	}
-	if conn.log == nil {
-		// the default logger does nothing
-		conn.log = func(Direction, *ID, time.Duration, string, *json.RawMessage, *Error) {}
-	}
-	go func() {
-		conn.err = conn.run(ctx)
-		close(conn.done)
-	}()
+	// the default canceller does nothing
+	conn.Canceler = func(context.Context, *Conn, *Request) {}
+	// the default logger does nothing
+	conn.Logger = func(Direction, *ID, time.Duration, string, *json.RawMessage, *Error) {}
 	return conn
 }
 
-// Wait blocks until the connection is terminated, and returns any error that
-// cause the termination.
-func (c *Conn) Wait(ctx context.Context) error {
-	select {
-	case <-c.done:
-		return c.err
-	case <-ctx.Done():
-		return ctx.Err()
-	}
-}
-
 // 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
@@ -151,7 +107,7 @@
 	if err != nil {
 		return fmt.Errorf("marshalling notify request: %v", err)
 	}
-	c.log(Send, nil, -1, request.Method, request.Params, nil)
+	c.Logger(Send, nil, -1, request.Method, request.Params, nil)
 	return c.stream.Write(ctx, data)
 }
 
@@ -189,7 +145,7 @@
 	}()
 	// now we are ready to send
 	before := time.Now()
-	c.log(Send, request.ID, -1, request.Method, request.Params, nil)
+	c.Logger(Send, request.ID, -1, request.Method, request.Params, nil)
 	if err := c.stream.Write(ctx, data); err != nil {
 		// sending failed, we will never get a response, so don't leave it pending
 		return err
@@ -198,7 +154,7 @@
 	select {
 	case response := <-rchan:
 		elapsed := time.Since(before)
-		c.log(Send, response.ID, elapsed, request.Method, response.Result, response.Error)
+		c.Logger(Send, response.ID, elapsed, request.Method, response.Result, response.Error)
 		// is it an error response?
 		if response.Error != nil {
 			return response.Error
@@ -212,7 +168,7 @@
 		return nil
 	case <-ctx.Done():
 		// allow the handler to propagate the cancel
-		c.cancel(ctx, c, request)
+		c.Canceler(ctx, c, request)
 		return ctx.Err()
 	}
 }
@@ -255,7 +211,7 @@
 	if err != nil {
 		return err
 	}
-	c.log(Send, response.ID, elapsed, req.Method, response.Result, response.Error)
+	c.Logger(Send, response.ID, elapsed, req.Method, response.Result, response.Error)
 	if err = c.stream.Write(ctx, data); err != nil {
 		// TODO(iancottrell): if a stream write fails, we really need to shut down
 		// the whole stream
@@ -281,10 +237,11 @@
 	Error      *Error           `json:"error,omitempty"`
 }
 
-// Run starts a read loop on the supplied reader.
+// Run blocks until the connection is terminated, and returns any error that
+// 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(ctx context.Context) error {
+func (c *Conn) Run(ctx context.Context) error {
 	for {
 		// get the data for a message
 		data, err := c.stream.Read(ctx)
@@ -297,7 +254,7 @@
 		if err := json.Unmarshal(data, msg); err != nil {
 			// a badly formed message arrived, log it and continue
 			// we trust the stream to have isolated the error to just this message
-			c.log(Receive, nil, -1, "", nil, NewErrorf(0, "unmarshal failed: %v", err))
+			c.Logger(Receive, nil, -1, "", nil, NewErrorf(0, "unmarshal failed: %v", err))
 			continue
 		}
 		// work out which kind of message we have
@@ -310,9 +267,9 @@
 				ID:     msg.ID,
 			}
 			if request.IsNotify() {
-				c.log(Receive, request.ID, -1, request.Method, request.Params, nil)
+				c.Logger(Receive, request.ID, -1, request.Method, request.Params, nil)
 				// we have a Notify, forward to the handler in a go routine
-				c.handle(ctx, c, request)
+				c.Handler(ctx, c, request)
 			} else {
 				// we have a Call, forward to the handler in another go routine
 				reqCtx, cancelReq := context.WithCancel(ctx)
@@ -323,8 +280,8 @@
 					start:   time.Now(),
 				}
 				c.handlingMu.Unlock()
-				c.log(Receive, request.ID, -1, request.Method, request.Params, nil)
-				c.handle(reqCtx, c, request)
+				c.Logger(Receive, request.ID, -1, request.Method, request.Params, nil)
+				c.Handler(reqCtx, c, request)
 			}
 		case msg.ID != nil:
 			// we have a response, get the pending entry from the map
@@ -343,7 +300,7 @@
 			rchan <- response
 			close(rchan)
 		default:
-			c.log(Receive, nil, -1, "", nil, NewErrorf(0, "message not a call, notify or response, ignoring"))
+			c.Logger(Receive, nil, -1, "", nil, NewErrorf(0, "message not a call, notify or response, ignoring"))
 		}
 	}
 }
diff --git a/internal/jsonrpc2/jsonrpc2_test.go b/internal/jsonrpc2/jsonrpc2_test.go
index 264200b..5b4e0fb 100644
--- a/internal/jsonrpc2/jsonrpc2_test.go
+++ b/internal/jsonrpc2/jsonrpc2_test.go
@@ -90,42 +90,36 @@
 	}
 }
 
-func prepare(ctx context.Context, t *testing.T, withHeaders bool) (*testHandler, *testHandler) {
-	a := &testHandler{t: t}
-	b := &testHandler{t: t}
-	a.reader, b.writer = io.Pipe()
-	b.reader, a.writer = io.Pipe()
-	for _, h := range []*testHandler{a, b} {
-		h := h
-		if withHeaders {
-			h.stream = jsonrpc2.NewHeaderStream(h.reader, h.writer)
-		} else {
-			h.stream = jsonrpc2.NewStream(h.reader, h.writer)
-		}
-		args := []interface{}{jsonrpc2.Handler(handle)}
-		if *logRPC {
-			args = append(args, jsonrpc2.Log)
-		}
-		h.Conn = jsonrpc2.NewConn(ctx, h.stream, args...)
-		go func() {
-			defer func() {
-				h.reader.Close()
-				h.writer.Close()
-			}()
-			if err := h.Conn.Wait(ctx); err != nil {
-				t.Fatalf("Stream failed: %v", err)
-			}
-		}()
-	}
+func prepare(ctx context.Context, t *testing.T, withHeaders bool) (*jsonrpc2.Conn, *jsonrpc2.Conn) {
+	aR, bW := io.Pipe()
+	bR, aW := io.Pipe()
+	a := run(ctx, t, withHeaders, aR, aW)
+	b := run(ctx, t, withHeaders, bR, bW)
 	return a, b
 }
 
-type testHandler struct {
-	t      *testing.T
-	reader *io.PipeReader
-	writer *io.PipeWriter
-	stream jsonrpc2.Stream
-	*jsonrpc2.Conn
+func run(ctx context.Context, t *testing.T, withHeaders bool, r io.ReadCloser, w io.WriteCloser) *jsonrpc2.Conn {
+	var stream jsonrpc2.Stream
+	if withHeaders {
+		stream = jsonrpc2.NewHeaderStream(r, w)
+	} else {
+		stream = jsonrpc2.NewStream(r, w)
+	}
+	conn := jsonrpc2.NewConn(stream)
+	conn.Handler = handle
+	if *logRPC {
+		conn.Logger = jsonrpc2.Log
+	}
+	go func() {
+		defer func() {
+			r.Close()
+			w.Close()
+		}()
+		if err := conn.Run(ctx); err != nil {
+			t.Fatalf("Stream failed: %v", err)
+		}
+	}()
+	return conn
 }
 
 func handle(ctx context.Context, c *jsonrpc2.Conn, r *jsonrpc2.Request) {
diff --git a/internal/lsp/cmd/cmd.go b/internal/lsp/cmd/cmd.go
index e664cd1..6ac65cc 100644
--- a/internal/lsp/cmd/cmd.go
+++ b/internal/lsp/cmd/cmd.go
@@ -121,22 +121,26 @@
 	var server protocol.Server
 	switch app.Remote {
 	case "":
-		server = lsp.NewServer(client)
+		server = lsp.NewClientServer(client)
 	case "internal":
 		cr, sw, _ := os.Pipe()
 		sr, cw, _ := os.Pipe()
-		_, server = protocol.RunClient(ctx, jsonrpc2.NewHeaderStream(cr, cw), client)
-		go lsp.RunServer(ctx, jsonrpc2.NewHeaderStream(sr, sw))
+		var jc *jsonrpc2.Conn
+		jc, server = protocol.NewClient(jsonrpc2.NewHeaderStream(cr, cw), client)
+		go jc.Run(ctx)
+		go lsp.NewServer(jsonrpc2.NewHeaderStream(sr, sw)).Run(ctx)
 	default:
 		conn, err := net.Dial("tcp", app.Remote)
 		if err != nil {
 			return nil, err
 		}
 		stream := jsonrpc2.NewHeaderStream(conn, conn)
-		_, server = protocol.RunClient(ctx, stream, client)
+		var jc *jsonrpc2.Conn
+		jc, server = protocol.NewClient(stream, client)
 		if err != nil {
 			return nil, err
 		}
+		go jc.Run(ctx)
 	}
 	params := &protocol.InitializeParams{}
 	params.RootURI = string(span.FileURI(app.Config.Dir))
diff --git a/internal/lsp/cmd/serve.go b/internal/lsp/cmd/serve.go
index 2bf79f7..afa333a 100644
--- a/internal/lsp/cmd/serve.go
+++ b/internal/lsp/cmd/serve.go
@@ -119,14 +119,20 @@
 		fmt.Fprintf(out, "%s", outx.String())
 	}
 	// For debugging purposes only.
+	run := func(srv *lsp.Server) {
+		srv.Conn.Logger = logger
+		go srv.Conn.Run(ctx)
+	}
 	if s.Address != "" {
-		return lsp.RunServerOnAddress(ctx, s.Address, logger)
+		return lsp.RunServerOnAddress(ctx, s.Address, run)
 	}
 	if s.Port != 0 {
-		return lsp.RunServerOnPort(ctx, s.Port, logger)
+		return lsp.RunServerOnPort(ctx, s.Port, run)
 	}
 	stream := jsonrpc2.NewHeaderStream(os.Stdin, os.Stdout)
-	return lsp.RunServer(ctx, stream, logger)
+	srv := lsp.NewServer(stream)
+	srv.Conn.Logger = logger
+	return srv.Conn.Run(ctx)
 }
 
 func (s *Serve) forward() error {
diff --git a/internal/lsp/diagnostics.go b/internal/lsp/diagnostics.go
index f7838e9..bef6dcc 100644
--- a/internal/lsp/diagnostics.go
+++ b/internal/lsp/diagnostics.go
@@ -13,7 +13,7 @@
 	"golang.org/x/tools/internal/span"
 )
 
-func (s *server) cacheAndDiagnose(ctx context.Context, uri span.URI, content string) error {
+func (s *Server) cacheAndDiagnose(ctx context.Context, uri span.URI, content string) error {
 	if err := s.setContent(ctx, uri, []byte(content)); err != nil {
 		return err
 	}
@@ -43,7 +43,7 @@
 	return nil
 }
 
-func (s *server) setContent(ctx context.Context, uri span.URI, content []byte) error {
+func (s *Server) setContent(ctx context.Context, uri span.URI, content []byte) error {
 	return s.view.SetContent(ctx, uri, content)
 }
 
diff --git a/internal/lsp/lsp_test.go b/internal/lsp/lsp_test.go
index b1e46fc..6188470 100644
--- a/internal/lsp/lsp_test.go
+++ b/internal/lsp/lsp_test.go
@@ -70,7 +70,7 @@
 		return parser.ParseFile(fset, filename, src, parser.AllErrors|parser.ParseComments)
 	}
 
-	s := &server{
+	s := &Server{
 		view: cache.NewView("lsp_test", span.FileURI(cfg.Dir), &cfg),
 	}
 	// Do a first pass to collect special markers for completion.
@@ -285,7 +285,7 @@
 	return msg.String()
 }
 
-func (c completions) test(t *testing.T, exported *packagestest.Exported, s *server, items completionItems) {
+func (c completions) test(t *testing.T, exported *packagestest.Exported, s *Server, items completionItems) {
 	for src, itemList := range c {
 		var want []protocol.CompletionItem
 		for _, pos := range itemList {
@@ -406,7 +406,7 @@
 	return msg.String()
 }
 
-func (f formats) test(t *testing.T, s *server) {
+func (f formats) test(t *testing.T, s *Server) {
 	ctx := context.Background()
 	for filename, gofmted := range f {
 		uri := span.FileURI(filename)
@@ -444,7 +444,7 @@
 	f[pos.Filename] = stdout.String()
 }
 
-func (d definitions) test(t *testing.T, s *server, typ bool) {
+func (d definitions) test(t *testing.T, s *Server, typ bool) {
 	for src, target := range d {
 		params := &protocol.TextDocumentPositionParams{
 			TextDocument: protocol.TextDocumentIdentifier{
@@ -495,7 +495,7 @@
 	h[name] = append(h[name], loc)
 }
 
-func (h highlights) test(t *testing.T, s *server) {
+func (h highlights) test(t *testing.T, s *Server) {
 	for name, locations := range h {
 		params := &protocol.TextDocumentPositionParams{
 			TextDocument: protocol.TextDocumentIdentifier{
@@ -547,7 +547,7 @@
 	})
 }
 
-func (s symbols) test(t *testing.T, server *server) {
+func (s symbols) test(t *testing.T, server *Server) {
 	for uri, expectedSymbols := range s {
 		params := &protocol.DocumentSymbolParams{
 			TextDocument: protocol.TextDocumentIdentifier{
diff --git a/internal/lsp/protocol/protocol.go b/internal/lsp/protocol/protocol.go
index 5d11c3c..e5737f8 100644
--- a/internal/lsp/protocol/protocol.go
+++ b/internal/lsp/protocol/protocol.go
@@ -15,15 +15,17 @@
 	conn.Notify(context.Background(), "$/cancelRequest", &CancelParams{ID: *req.ID})
 }
 
-func RunClient(ctx context.Context, stream jsonrpc2.Stream, client Client, opts ...interface{}) (*jsonrpc2.Conn, Server) {
-	opts = append([]interface{}{clientHandler(client), jsonrpc2.Canceler(canceller)}, opts...)
-	conn := jsonrpc2.NewConn(ctx, stream, opts...)
+func NewClient(stream jsonrpc2.Stream, client Client) (*jsonrpc2.Conn, Server) {
+	conn := jsonrpc2.NewConn(stream)
+	conn.Handler = clientHandler(client)
+	conn.Canceler = jsonrpc2.Canceler(canceller)
 	return conn, &serverDispatcher{Conn: conn}
 }
 
-func RunServer(ctx context.Context, stream jsonrpc2.Stream, server Server, opts ...interface{}) (*jsonrpc2.Conn, Client) {
-	opts = append([]interface{}{serverHandler(server), jsonrpc2.Canceler(canceller)}, opts...)
-	conn := jsonrpc2.NewConn(ctx, stream, opts...)
+func NewServer(stream jsonrpc2.Stream, server Server) (*jsonrpc2.Conn, Client) {
+	conn := jsonrpc2.NewConn(stream)
+	conn.Handler = serverHandler(server)
+	conn.Canceler = jsonrpc2.Canceler(canceller)
 	return conn, &clientDispatcher{Conn: conn}
 }
 
diff --git a/internal/lsp/server.go b/internal/lsp/server.go
index 9f5dc34..384e4ca 100644
--- a/internal/lsp/server.go
+++ b/internal/lsp/server.go
@@ -25,32 +25,33 @@
 	"golang.org/x/tools/internal/span"
 )
 
-// NewServer
-func NewServer(client protocol.Client) protocol.Server {
-	return &server{
+// NewClientServer
+func NewClientServer(client protocol.Client) *Server {
+	return &Server{
 		client:     client,
 		configured: make(chan struct{}),
 	}
 }
 
-// RunServer starts an LSP server on the supplied stream, and waits until the
+// NewServer starts an LSP server on the supplied stream, and waits until the
 // stream is closed.
-func RunServer(ctx context.Context, stream jsonrpc2.Stream, opts ...interface{}) error {
-	s := NewServer(nil).(*server)
-	conn, client := protocol.RunServer(ctx, stream, s, opts...)
-	s.client = client
-	return conn.Wait(ctx)
+func NewServer(stream jsonrpc2.Stream) *Server {
+	s := &Server{
+		configured: make(chan struct{}),
+	}
+	s.Conn, s.client = protocol.NewServer(stream, s)
+	return s
 }
 
 // RunServerOnPort starts an LSP server on the given port and does not exit.
 // This function exists for debugging purposes.
-func RunServerOnPort(ctx context.Context, port int, opts ...interface{}) error {
-	return RunServerOnAddress(ctx, fmt.Sprintf(":%v", port))
+func RunServerOnPort(ctx context.Context, port int, h func(s *Server)) error {
+	return RunServerOnAddress(ctx, fmt.Sprintf(":%v", port), h)
 }
 
 // RunServerOnPort starts an LSP server on the given port and does not exit.
 // This function exists for debugging purposes.
-func RunServerOnAddress(ctx context.Context, addr string, opts ...interface{}) error {
+func RunServerOnAddress(ctx context.Context, addr string, h func(s *Server)) error {
 	ln, err := net.Listen("tcp", addr)
 	if err != nil {
 		return err
@@ -61,11 +62,14 @@
 			return err
 		}
 		stream := jsonrpc2.NewHeaderStream(conn, conn)
-		go RunServer(ctx, stream, opts...)
+		s := NewServer(stream)
+		h(s)
+		go s.Run(ctx)
 	}
 }
 
-type server struct {
+type Server struct {
+	Conn   *jsonrpc2.Conn
 	client protocol.Client
 
 	initializedMu sync.Mutex
@@ -81,7 +85,11 @@
 	configured chan struct{}
 }
 
-func (s *server) Initialize(ctx context.Context, params *protocol.InitializeParams) (*protocol.InitializeResult, error) {
+func (s *Server) Run(ctx context.Context) error {
+	return s.Conn.Run(ctx)
+}
+
+func (s *Server) Initialize(ctx context.Context, params *protocol.InitializeParams) (*protocol.InitializeResult, error) {
 	s.initializedMu.Lock()
 	defer s.initializedMu.Unlock()
 	if s.initialized {
@@ -151,7 +159,7 @@
 	}, nil
 }
 
-func (s *server) Initialized(ctx context.Context, params *protocol.InitializedParams) error {
+func (s *Server) Initialized(ctx context.Context, params *protocol.InitializedParams) error {
 	go func() {
 		// we hae to do this in a go routine to unblock the jsonrpc processor
 		// but we also have to block all calls to packages.Load until this is done
@@ -179,7 +187,7 @@
 	return nil
 }
 
-func (s *server) Shutdown(context.Context) error {
+func (s *Server) Shutdown(context.Context) error {
 	s.initializedMu.Lock()
 	defer s.initializedMu.Unlock()
 	if !s.initialized {
@@ -189,7 +197,7 @@
 	return nil
 }
 
-func (s *server) Exit(ctx context.Context) error {
+func (s *Server) Exit(ctx context.Context) error {
 	if s.initialized {
 		os.Exit(1)
 	}
@@ -197,31 +205,31 @@
 	return nil
 }
 
-func (s *server) DidChangeWorkspaceFolders(context.Context, *protocol.DidChangeWorkspaceFoldersParams) error {
+func (s *Server) DidChangeWorkspaceFolders(context.Context, *protocol.DidChangeWorkspaceFoldersParams) error {
 	return notImplemented("DidChangeWorkspaceFolders")
 }
 
-func (s *server) DidChangeConfiguration(context.Context, *protocol.DidChangeConfigurationParams) error {
+func (s *Server) DidChangeConfiguration(context.Context, *protocol.DidChangeConfigurationParams) error {
 	return notImplemented("DidChangeConfiguration")
 }
 
-func (s *server) DidChangeWatchedFiles(context.Context, *protocol.DidChangeWatchedFilesParams) error {
+func (s *Server) DidChangeWatchedFiles(context.Context, *protocol.DidChangeWatchedFilesParams) error {
 	return notImplemented("DidChangeWatchedFiles")
 }
 
-func (s *server) Symbols(context.Context, *protocol.WorkspaceSymbolParams) ([]protocol.SymbolInformation, error) {
+func (s *Server) Symbols(context.Context, *protocol.WorkspaceSymbolParams) ([]protocol.SymbolInformation, error) {
 	return nil, notImplemented("Symbols")
 }
 
-func (s *server) ExecuteCommand(context.Context, *protocol.ExecuteCommandParams) (interface{}, error) {
+func (s *Server) ExecuteCommand(context.Context, *protocol.ExecuteCommandParams) (interface{}, error) {
 	return nil, notImplemented("ExecuteCommand")
 }
 
-func (s *server) DidOpen(ctx context.Context, params *protocol.DidOpenTextDocumentParams) error {
+func (s *Server) DidOpen(ctx context.Context, params *protocol.DidOpenTextDocumentParams) error {
 	return s.cacheAndDiagnose(ctx, span.NewURI(params.TextDocument.URI), params.TextDocument.Text)
 }
 
-func (s *server) applyChanges(ctx context.Context, params *protocol.DidChangeTextDocumentParams) (string, error) {
+func (s *Server) applyChanges(ctx context.Context, params *protocol.DidChangeTextDocumentParams) (string, error) {
 	if len(params.ContentChanges) == 1 && params.ContentChanges[0].Range == nil {
 		// If range is empty, we expect the full content of file, i.e. a single change with no range.
 		change := params.ContentChanges[0]
@@ -257,7 +265,7 @@
 	return string(content), nil
 }
 
-func (s *server) DidChange(ctx context.Context, params *protocol.DidChangeTextDocumentParams) error {
+func (s *Server) DidChange(ctx context.Context, params *protocol.DidChangeTextDocumentParams) error {
 	if len(params.ContentChanges) < 1 {
 		return jsonrpc2.NewErrorf(jsonrpc2.CodeInternalError, "no content changes provided")
 	}
@@ -281,24 +289,24 @@
 	return s.cacheAndDiagnose(ctx, span.NewURI(params.TextDocument.URI), text)
 }
 
-func (s *server) WillSave(context.Context, *protocol.WillSaveTextDocumentParams) error {
+func (s *Server) WillSave(context.Context, *protocol.WillSaveTextDocumentParams) error {
 	return notImplemented("WillSave")
 }
 
-func (s *server) WillSaveWaitUntil(context.Context, *protocol.WillSaveTextDocumentParams) ([]protocol.TextEdit, error) {
+func (s *Server) WillSaveWaitUntil(context.Context, *protocol.WillSaveTextDocumentParams) ([]protocol.TextEdit, error) {
 	return nil, notImplemented("WillSaveWaitUntil")
 }
 
-func (s *server) DidSave(context.Context, *protocol.DidSaveTextDocumentParams) error {
+func (s *Server) DidSave(context.Context, *protocol.DidSaveTextDocumentParams) error {
 	return nil // ignore
 }
 
-func (s *server) DidClose(ctx context.Context, params *protocol.DidCloseTextDocumentParams) error {
+func (s *Server) DidClose(ctx context.Context, params *protocol.DidCloseTextDocumentParams) error {
 	s.setContent(ctx, span.NewURI(params.TextDocument.URI), nil)
 	return nil
 }
 
-func (s *server) Completion(ctx context.Context, params *protocol.CompletionParams) (*protocol.CompletionList, error) {
+func (s *Server) Completion(ctx context.Context, params *protocol.CompletionParams) (*protocol.CompletionList, error) {
 	f, m, err := newColumnMap(ctx, s.view, span.NewURI(params.TextDocument.URI))
 	if err != nil {
 		return nil, err
@@ -321,11 +329,11 @@
 	}, nil
 }
 
-func (s *server) CompletionResolve(context.Context, *protocol.CompletionItem) (*protocol.CompletionItem, error) {
+func (s *Server) CompletionResolve(context.Context, *protocol.CompletionItem) (*protocol.CompletionItem, error) {
 	return nil, notImplemented("CompletionResolve")
 }
 
-func (s *server) Hover(ctx context.Context, params *protocol.TextDocumentPositionParams) (*protocol.Hover, error) {
+func (s *Server) Hover(ctx context.Context, params *protocol.TextDocumentPositionParams) (*protocol.Hover, error) {
 	f, m, err := newColumnMap(ctx, s.view, span.NewURI(params.TextDocument.URI))
 	if err != nil {
 		return nil, err
@@ -364,7 +372,7 @@
 	}, nil
 }
 
-func (s *server) SignatureHelp(ctx context.Context, params *protocol.TextDocumentPositionParams) (*protocol.SignatureHelp, error) {
+func (s *Server) SignatureHelp(ctx context.Context, params *protocol.TextDocumentPositionParams) (*protocol.SignatureHelp, error) {
 	f, m, err := newColumnMap(ctx, s.view, span.NewURI(params.TextDocument.URI))
 	if err != nil {
 		return nil, err
@@ -384,7 +392,7 @@
 	return toProtocolSignatureHelp(info), nil
 }
 
-func (s *server) Definition(ctx context.Context, params *protocol.TextDocumentPositionParams) ([]protocol.Location, error) {
+func (s *Server) Definition(ctx context.Context, params *protocol.TextDocumentPositionParams) ([]protocol.Location, error) {
 	f, m, err := newColumnMap(ctx, s.view, span.NewURI(params.TextDocument.URI))
 	if err != nil {
 		return nil, err
@@ -416,7 +424,7 @@
 	return []protocol.Location{loc}, nil
 }
 
-func (s *server) TypeDefinition(ctx context.Context, params *protocol.TextDocumentPositionParams) ([]protocol.Location, error) {
+func (s *Server) TypeDefinition(ctx context.Context, params *protocol.TextDocumentPositionParams) ([]protocol.Location, error) {
 	f, m, err := newColumnMap(ctx, s.view, span.NewURI(params.TextDocument.URI))
 	if err != nil {
 		return nil, err
@@ -448,15 +456,15 @@
 	return []protocol.Location{loc}, nil
 }
 
-func (s *server) Implementation(context.Context, *protocol.TextDocumentPositionParams) ([]protocol.Location, error) {
+func (s *Server) Implementation(context.Context, *protocol.TextDocumentPositionParams) ([]protocol.Location, error) {
 	return nil, notImplemented("Implementation")
 }
 
-func (s *server) References(context.Context, *protocol.ReferenceParams) ([]protocol.Location, error) {
+func (s *Server) References(context.Context, *protocol.ReferenceParams) ([]protocol.Location, error) {
 	return nil, notImplemented("References")
 }
 
-func (s *server) DocumentHighlight(ctx context.Context, params *protocol.TextDocumentPositionParams) ([]protocol.DocumentHighlight, error) {
+func (s *Server) DocumentHighlight(ctx context.Context, params *protocol.TextDocumentPositionParams) ([]protocol.DocumentHighlight, error) {
 	f, m, err := newColumnMap(ctx, s.view, span.NewURI(params.TextDocument.URI))
 	if err != nil {
 		return nil, err
@@ -476,7 +484,7 @@
 	return toProtocolHighlight(m, spans), nil
 }
 
-func (s *server) DocumentSymbol(ctx context.Context, params *protocol.DocumentSymbolParams) ([]protocol.DocumentSymbol, error) {
+func (s *Server) DocumentSymbol(ctx context.Context, params *protocol.DocumentSymbolParams) ([]protocol.DocumentSymbol, error) {
 	f, m, err := newColumnMap(ctx, s.view, span.NewURI(params.TextDocument.URI))
 	if err != nil {
 		return nil, err
@@ -485,7 +493,7 @@
 	return toProtocolDocumentSymbols(m, symbols), nil
 }
 
-func (s *server) CodeAction(ctx context.Context, params *protocol.CodeActionParams) ([]protocol.CodeAction, error) {
+func (s *Server) CodeAction(ctx context.Context, params *protocol.CodeActionParams) ([]protocol.CodeAction, error) {
 	_, m, err := newColumnMap(ctx, s.view, span.NewURI(params.TextDocument.URI))
 	if err != nil {
 		return nil, err
@@ -511,36 +519,36 @@
 	}, nil
 }
 
-func (s *server) CodeLens(context.Context, *protocol.CodeLensParams) ([]protocol.CodeLens, error) {
+func (s *Server) CodeLens(context.Context, *protocol.CodeLensParams) ([]protocol.CodeLens, error) {
 	return nil, nil // ignore
 }
 
-func (s *server) CodeLensResolve(context.Context, *protocol.CodeLens) (*protocol.CodeLens, error) {
+func (s *Server) CodeLensResolve(context.Context, *protocol.CodeLens) (*protocol.CodeLens, error) {
 	return nil, notImplemented("CodeLensResolve")
 }
 
-func (s *server) DocumentLink(context.Context, *protocol.DocumentLinkParams) ([]protocol.DocumentLink, error) {
+func (s *Server) DocumentLink(context.Context, *protocol.DocumentLinkParams) ([]protocol.DocumentLink, error) {
 	return nil, nil // ignore
 }
 
-func (s *server) DocumentLinkResolve(context.Context, *protocol.DocumentLink) (*protocol.DocumentLink, error) {
+func (s *Server) DocumentLinkResolve(context.Context, *protocol.DocumentLink) (*protocol.DocumentLink, error) {
 	return nil, notImplemented("DocumentLinkResolve")
 }
 
-func (s *server) DocumentColor(context.Context, *protocol.DocumentColorParams) ([]protocol.ColorInformation, error) {
+func (s *Server) DocumentColor(context.Context, *protocol.DocumentColorParams) ([]protocol.ColorInformation, error) {
 	return nil, notImplemented("DocumentColor")
 }
 
-func (s *server) ColorPresentation(context.Context, *protocol.ColorPresentationParams) ([]protocol.ColorPresentation, error) {
+func (s *Server) ColorPresentation(context.Context, *protocol.ColorPresentationParams) ([]protocol.ColorPresentation, error) {
 	return nil, notImplemented("ColorPresentation")
 }
 
-func (s *server) Formatting(ctx context.Context, params *protocol.DocumentFormattingParams) ([]protocol.TextEdit, error) {
+func (s *Server) Formatting(ctx context.Context, params *protocol.DocumentFormattingParams) ([]protocol.TextEdit, error) {
 	spn := span.New(span.URI(params.TextDocument.URI), span.Point{}, span.Point{})
 	return formatRange(ctx, s.view, spn)
 }
 
-func (s *server) RangeFormatting(ctx context.Context, params *protocol.DocumentRangeFormattingParams) ([]protocol.TextEdit, error) {
+func (s *Server) RangeFormatting(ctx context.Context, params *protocol.DocumentRangeFormattingParams) ([]protocol.TextEdit, error) {
 	_, m, err := newColumnMap(ctx, s.view, span.NewURI(params.TextDocument.URI))
 	if err != nil {
 		return nil, err
@@ -552,26 +560,26 @@
 	return formatRange(ctx, s.view, spn)
 }
 
-func (s *server) OnTypeFormatting(context.Context, *protocol.DocumentOnTypeFormattingParams) ([]protocol.TextEdit, error) {
+func (s *Server) OnTypeFormatting(context.Context, *protocol.DocumentOnTypeFormattingParams) ([]protocol.TextEdit, error) {
 	return nil, notImplemented("OnTypeFormatting")
 }
 
-func (s *server) Rename(context.Context, *protocol.RenameParams) ([]protocol.WorkspaceEdit, error) {
+func (s *Server) Rename(context.Context, *protocol.RenameParams) ([]protocol.WorkspaceEdit, error) {
 	return nil, notImplemented("Rename")
 }
 
-func (s *server) FoldingRanges(context.Context, *protocol.FoldingRangeParams) ([]protocol.FoldingRange, error) {
+func (s *Server) FoldingRanges(context.Context, *protocol.FoldingRangeParams) ([]protocol.FoldingRange, error) {
 	return nil, notImplemented("FoldingRanges")
 }
 
-func (s *server) Error(err error) {
+func (s *Server) Error(err error) {
 	s.client.LogMessage(context.Background(), &protocol.LogMessageParams{
 		Type:    protocol.Error,
 		Message: fmt.Sprint(err),
 	})
 }
 
-func (s *server) processConfig(config interface{}) error {
+func (s *Server) processConfig(config interface{}) error {
 	//TODO: we should probably store and process more of the config
 	c, ok := config.(map[string]interface{})
 	if !ok {