some deletions
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / internal / lsp / fake / editor.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201028153306-37f0764111ff/internal/lsp/fake/editor.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201028153306-37f0764111ff/internal/lsp/fake/editor.go
deleted file mode 100644 (file)
index dba8997..0000000
+++ /dev/null
@@ -1,919 +0,0 @@
-// 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 fake
-
-import (
-       "bufio"
-       "context"
-       "fmt"
-       "path/filepath"
-       "regexp"
-       "strings"
-       "sync"
-
-       "golang.org/x/tools/internal/jsonrpc2"
-       "golang.org/x/tools/internal/lsp/protocol"
-       "golang.org/x/tools/internal/lsp/source"
-       "golang.org/x/tools/internal/span"
-       errors "golang.org/x/xerrors"
-)
-
-// Editor is a fake editor client.  It keeps track of client state and can be
-// used for writing LSP tests.
-type Editor struct {
-       Config EditorConfig
-
-       // Server, client, and sandbox are concurrency safe and written only
-       // at construction time, so do not require synchronization.
-       Server     protocol.Server
-       serverConn jsonrpc2.Conn
-       client     *Client
-       sandbox    *Sandbox
-       defaultEnv map[string]string
-
-       // Since this editor is intended just for testing, we use very coarse
-       // locking.
-       mu sync.Mutex
-       // Editor state.
-       buffers map[string]buffer
-       // Capabilities / Options
-       serverCapabilities protocol.ServerCapabilities
-}
-
-type buffer struct {
-       version int
-       path    string
-       content []string
-}
-
-func (b buffer) text() string {
-       return strings.Join(b.content, "\n")
-}
-
-// EditorConfig configures the editor's LSP session. This is similar to
-// source.UserOptions, but we use a separate type here so that we expose only
-// that configuration which we support.
-//
-// The zero value for EditorConfig should correspond to its defaults.
-type EditorConfig struct {
-       Env        map[string]string
-       BuildFlags []string
-
-       // CodeLens is a map defining whether codelens are enabled, keyed by the
-       // codeLens command. CodeLens which are not present in this map are left in
-       // their default state.
-       CodeLens map[string]bool
-
-       // SymbolMatcher is the config associated with the "symbolMatcher" gopls
-       // config option.
-       SymbolMatcher, SymbolStyle *string
-
-       // LimitWorkspaceScope is true if the user does not want to expand their
-       // workspace scope to the entire module.
-       LimitWorkspaceScope bool
-
-       // WithoutWorkspaceFolders is used to simulate opening a single file in the
-       // editor, without a workspace root. In that case, the client sends neither
-       // workspace folders nor a root URI.
-       WithoutWorkspaceFolders bool
-
-       // EditorRootPath specifies the root path of the workspace folder used when
-       // initializing gopls in the sandbox. If empty, the Workdir is used.
-       EditorRootPath string
-
-       // EnableStaticcheck enables staticcheck analyzers.
-       EnableStaticcheck bool
-
-       // AllExperiments sets the "allExperiments" configuration, which enables
-       // all of gopls's opt-in settings.
-       AllExperiments bool
-}
-
-// NewEditor Creates a new Editor.
-func NewEditor(sandbox *Sandbox, config EditorConfig) *Editor {
-       return &Editor{
-               buffers:    make(map[string]buffer),
-               sandbox:    sandbox,
-               defaultEnv: sandbox.GoEnv(),
-               Config:     config,
-       }
-}
-
-// Connect configures the editor to communicate with an LSP server on conn. It
-// is not concurrency safe, and should be called at most once, before using the
-// editor.
-//
-// It returns the editor, so that it may be called as follows:
-//   editor, err := NewEditor(s).Connect(ctx, conn)
-func (e *Editor) Connect(ctx context.Context, conn jsonrpc2.Conn, hooks ClientHooks) (*Editor, error) {
-       e.serverConn = conn
-       e.Server = protocol.ServerDispatcher(conn)
-       e.client = &Client{editor: e, hooks: hooks}
-       conn.Go(ctx,
-               protocol.Handlers(
-                       protocol.ClientHandler(e.client,
-                               jsonrpc2.MethodNotFound)))
-       if err := e.initialize(ctx, e.Config.WithoutWorkspaceFolders, e.Config.EditorRootPath); err != nil {
-               return nil, err
-       }
-       e.sandbox.Workdir.AddWatcher(e.onFileChanges)
-       return e, nil
-}
-
-// Shutdown issues the 'shutdown' LSP notification.
-func (e *Editor) Shutdown(ctx context.Context) error {
-       if e.Server != nil {
-               if err := e.Server.Shutdown(ctx); err != nil {
-                       return errors.Errorf("Shutdown: %w", err)
-               }
-       }
-       return nil
-}
-
-// Exit issues the 'exit' LSP notification.
-func (e *Editor) Exit(ctx context.Context) error {
-       if e.Server != nil {
-               // Not all LSP clients issue the exit RPC, but we do so here to ensure that
-               // we gracefully handle it on multi-session servers.
-               if err := e.Server.Exit(ctx); err != nil {
-                       return errors.Errorf("Exit: %w", err)
-               }
-       }
-       return nil
-}
-
-// Close issues the shutdown and exit sequence an editor should.
-func (e *Editor) Close(ctx context.Context) error {
-       if err := e.Shutdown(ctx); err != nil {
-               return err
-       }
-       if err := e.Exit(ctx); err != nil {
-               return err
-       }
-       // called close on the editor should result in the connection closing
-       select {
-       case <-e.serverConn.Done():
-               // connection closed itself
-               return nil
-       case <-ctx.Done():
-               return errors.Errorf("connection not closed: %w", ctx.Err())
-       }
-}
-
-// Client returns the LSP client for this editor.
-func (e *Editor) Client() *Client {
-       return e.client
-}
-
-func (e *Editor) overlayEnv() map[string]string {
-       env := make(map[string]string)
-       for k, v := range e.defaultEnv {
-               env[k] = v
-       }
-       for k, v := range e.Config.Env {
-               env[k] = v
-       }
-       return env
-}
-
-func (e *Editor) configuration() map[string]interface{} {
-       config := map[string]interface{}{
-               "verboseWorkDoneProgress": true,
-               "env":                     e.overlayEnv(),
-               "expandWorkspaceToModule": !e.Config.LimitWorkspaceScope,
-               "completionBudget":        "10s",
-       }
-
-       if e.Config.BuildFlags != nil {
-               config["buildFlags"] = e.Config.BuildFlags
-       }
-
-       if e.Config.CodeLens != nil {
-               config["codelens"] = e.Config.CodeLens
-       }
-       if e.Config.SymbolMatcher != nil {
-               config["symbolMatcher"] = *e.Config.SymbolMatcher
-       }
-       if e.Config.SymbolStyle != nil {
-               config["symbolStyle"] = *e.Config.SymbolStyle
-       }
-       if e.Config.EnableStaticcheck {
-               config["staticcheck"] = true
-       }
-       if e.Config.AllExperiments {
-               config["allExperiments"] = true
-       }
-
-       // TODO(rFindley): uncomment this if/when diagnostics delay is on by
-       // default... and probably change to the new settings name.
-       // config["experimentalDiagnosticsDelay"] = "10ms"
-
-       // ExperimentalWorkspaceModule is only set as a mode, not a configuration.
-       return config
-}
-
-func (e *Editor) initialize(ctx context.Context, withoutWorkspaceFolders bool, editorRootPath string) error {
-       params := &protocol.ParamInitialize{}
-       params.ClientInfo.Name = "fakeclient"
-       params.ClientInfo.Version = "v1.0.0"
-       if !withoutWorkspaceFolders {
-               rootURI := e.sandbox.Workdir.RootURI()
-               if editorRootPath != "" {
-                       rootURI = toURI(e.sandbox.Workdir.AbsPath(editorRootPath))
-               }
-               params.WorkspaceFolders = []protocol.WorkspaceFolder{{
-                       URI:  string(rootURI),
-                       Name: filepath.Base(rootURI.SpanURI().Filename()),
-               }}
-       }
-       params.Capabilities.Workspace.Configuration = true
-       params.Capabilities.Window.WorkDoneProgress = true
-       // TODO: set client capabilities
-       params.InitializationOptions = e.configuration()
-
-       // This is a bit of a hack, since the fake editor doesn't actually support
-       // watching changed files that match a specific glob pattern. However, the
-       // editor does send didChangeWatchedFiles notifications, so set this to
-       // true.
-       params.Capabilities.Workspace.DidChangeWatchedFiles.DynamicRegistration = true
-
-       params.Trace = "messages"
-       // TODO: support workspace folders.
-       if e.Server != nil {
-               resp, err := e.Server.Initialize(ctx, params)
-               if err != nil {
-                       return errors.Errorf("initialize: %w", err)
-               }
-               e.mu.Lock()
-               e.serverCapabilities = resp.Capabilities
-               e.mu.Unlock()
-
-               if err := e.Server.Initialized(ctx, &protocol.InitializedParams{}); err != nil {
-                       return errors.Errorf("initialized: %w", err)
-               }
-       }
-       // TODO: await initial configuration here, or expect gopls to manage that?
-       return nil
-}
-
-func (e *Editor) onFileChanges(ctx context.Context, evts []FileEvent) {
-       if e.Server == nil {
-               return
-       }
-       var lspevts []protocol.FileEvent
-       for _, evt := range evts {
-               lspevts = append(lspevts, evt.ProtocolEvent)
-       }
-       e.Server.DidChangeWatchedFiles(ctx, &protocol.DidChangeWatchedFilesParams{
-               Changes: lspevts,
-       })
-}
-
-// OpenFile creates a buffer for the given workdir-relative file.
-func (e *Editor) OpenFile(ctx context.Context, path string) error {
-       content, err := e.sandbox.Workdir.ReadFile(path)
-       if err != nil {
-               return err
-       }
-       return e.CreateBuffer(ctx, path, content)
-}
-
-func newBuffer(path, content string) buffer {
-       return buffer{
-               version: 1,
-               path:    path,
-               content: strings.Split(content, "\n"),
-       }
-}
-
-func textDocumentItem(wd *Workdir, buf buffer) protocol.TextDocumentItem {
-       uri := wd.URI(buf.path)
-       languageID := ""
-       if strings.HasSuffix(buf.path, ".go") {
-               // TODO: what about go.mod files? What is their language ID?
-               languageID = "go"
-       }
-       return protocol.TextDocumentItem{
-               URI:        uri,
-               LanguageID: languageID,
-               Version:    float64(buf.version),
-               Text:       buf.text(),
-       }
-}
-
-// CreateBuffer creates a new unsaved buffer corresponding to the workdir path,
-// containing the given textual content.
-func (e *Editor) CreateBuffer(ctx context.Context, path, content string) error {
-       buf := newBuffer(path, content)
-       e.mu.Lock()
-       e.buffers[path] = buf
-       item := textDocumentItem(e.sandbox.Workdir, buf)
-       e.mu.Unlock()
-
-       if e.Server != nil {
-               if err := e.Server.DidOpen(ctx, &protocol.DidOpenTextDocumentParams{
-                       TextDocument: item,
-               }); err != nil {
-                       return errors.Errorf("DidOpen: %w", err)
-               }
-       }
-       return nil
-}
-
-// CloseBuffer removes the current buffer (regardless of whether it is saved).
-func (e *Editor) CloseBuffer(ctx context.Context, path string) error {
-       e.mu.Lock()
-       _, ok := e.buffers[path]
-       if !ok {
-               e.mu.Unlock()
-               return ErrUnknownBuffer
-       }
-       delete(e.buffers, path)
-       e.mu.Unlock()
-
-       if e.Server != nil {
-               if err := e.Server.DidClose(ctx, &protocol.DidCloseTextDocumentParams{
-                       TextDocument: e.textDocumentIdentifier(path),
-               }); err != nil {
-                       return errors.Errorf("DidClose: %w", err)
-               }
-       }
-       return nil
-}
-
-func (e *Editor) textDocumentIdentifier(path string) protocol.TextDocumentIdentifier {
-       return protocol.TextDocumentIdentifier{
-               URI: e.sandbox.Workdir.URI(path),
-       }
-}
-
-// SaveBuffer writes the content of the buffer specified by the given path to
-// the filesystem.
-func (e *Editor) SaveBuffer(ctx context.Context, path string) error {
-       if err := e.OrganizeImports(ctx, path); err != nil {
-               return errors.Errorf("organizing imports before save: %w", err)
-       }
-       if err := e.FormatBuffer(ctx, path); err != nil {
-               return errors.Errorf("formatting before save: %w", err)
-       }
-       return e.SaveBufferWithoutActions(ctx, path)
-}
-
-func (e *Editor) SaveBufferWithoutActions(ctx context.Context, path string) error {
-       e.mu.Lock()
-       buf, ok := e.buffers[path]
-       if !ok {
-               e.mu.Unlock()
-               return fmt.Errorf(fmt.Sprintf("unknown buffer: %q", path))
-       }
-       content := buf.text()
-       includeText := false
-       syncOptions, ok := e.serverCapabilities.TextDocumentSync.(protocol.TextDocumentSyncOptions)
-       if ok {
-               includeText = syncOptions.Save.IncludeText
-       }
-       e.mu.Unlock()
-
-       docID := e.textDocumentIdentifier(buf.path)
-       if e.Server != nil {
-               if err := e.Server.WillSave(ctx, &protocol.WillSaveTextDocumentParams{
-                       TextDocument: docID,
-                       Reason:       protocol.Manual,
-               }); err != nil {
-                       return errors.Errorf("WillSave: %w", err)
-               }
-       }
-       if err := e.sandbox.Workdir.WriteFile(ctx, path, content); err != nil {
-               return errors.Errorf("writing %q: %w", path, err)
-       }
-       if e.Server != nil {
-               params := &protocol.DidSaveTextDocumentParams{
-                       TextDocument: protocol.VersionedTextDocumentIdentifier{
-                               Version:                float64(buf.version),
-                               TextDocumentIdentifier: docID,
-                       },
-               }
-               if includeText {
-                       params.Text = &content
-               }
-               if err := e.Server.DidSave(ctx, params); err != nil {
-                       return errors.Errorf("DidSave: %w", err)
-               }
-       }
-       return nil
-}
-
-// contentPosition returns the (Line, Column) position corresponding to offset
-// in the buffer referenced by path.
-func contentPosition(content string, offset int) (Pos, error) {
-       scanner := bufio.NewScanner(strings.NewReader(content))
-       start := 0
-       line := 0
-       for scanner.Scan() {
-               end := start + len([]rune(scanner.Text())) + 1
-               if offset < end {
-                       return Pos{Line: line, Column: offset - start}, nil
-               }
-               start = end
-               line++
-       }
-       if err := scanner.Err(); err != nil {
-               return Pos{}, errors.Errorf("scanning content: %w", err)
-       }
-       // Scan() will drop the last line if it is empty. Correct for this.
-       if (strings.HasSuffix(content, "\n") || content == "") && offset == start {
-               return Pos{Line: line, Column: 0}, nil
-       }
-       return Pos{}, fmt.Errorf("position %d out of bounds in %q (line = %d, start = %d)", offset, content, line, start)
-}
-
-// ErrNoMatch is returned if a regexp search fails.
-var (
-       ErrNoMatch       = errors.New("no match")
-       ErrUnknownBuffer = errors.New("unknown buffer")
-)
-
-// regexpRange returns the start and end of the first occurrence of either re
-// or its singular subgroup. It returns ErrNoMatch if the regexp doesn't match.
-func regexpRange(content, re string) (Pos, Pos, error) {
-       var start, end int
-       rec, err := regexp.Compile(re)
-       if err != nil {
-               return Pos{}, Pos{}, err
-       }
-       indexes := rec.FindStringSubmatchIndex(content)
-       if indexes == nil {
-               return Pos{}, Pos{}, ErrNoMatch
-       }
-       switch len(indexes) {
-       case 2:
-               // no subgroups: return the range of the regexp expression
-               start, end = indexes[0], indexes[1]
-       case 4:
-               // one subgroup: return its range
-               start, end = indexes[2], indexes[3]
-       default:
-               return Pos{}, Pos{}, fmt.Errorf("invalid search regexp %q: expect either 0 or 1 subgroups, got %d", re, len(indexes)/2-1)
-       }
-       startPos, err := contentPosition(content, start)
-       if err != nil {
-               return Pos{}, Pos{}, err
-       }
-       endPos, err := contentPosition(content, end)
-       if err != nil {
-               return Pos{}, Pos{}, err
-       }
-       return startPos, endPos, nil
-}
-
-// RegexpRange returns the first range in the buffer bufName matching re. See
-// RegexpSearch for more information on matching.
-func (e *Editor) RegexpRange(bufName, re string) (Pos, Pos, error) {
-       e.mu.Lock()
-       defer e.mu.Unlock()
-       buf, ok := e.buffers[bufName]
-       if !ok {
-               return Pos{}, Pos{}, ErrUnknownBuffer
-       }
-       return regexpRange(buf.text(), re)
-}
-
-// RegexpSearch returns the position of the first match for re in the buffer
-// bufName. For convenience, RegexpSearch supports the following two modes:
-//  1. If re has no subgroups, return the position of the match for re itself.
-//  2. If re has one subgroup, return the position of the first subgroup.
-// It returns an error re is invalid, has more than one subgroup, or doesn't
-// match the buffer.
-func (e *Editor) RegexpSearch(bufName, re string) (Pos, error) {
-       start, _, err := e.RegexpRange(bufName, re)
-       return start, err
-}
-
-// RegexpReplace edits the buffer corresponding to path by replacing the first
-// instance of re, or its first subgroup, with the replace text. See
-// RegexpSearch for more explanation of these two modes.
-// It returns an error if re is invalid, has more than one subgroup, or doesn't
-// match the buffer.
-func (e *Editor) RegexpReplace(ctx context.Context, path, re, replace string) error {
-       e.mu.Lock()
-       defer e.mu.Unlock()
-       buf, ok := e.buffers[path]
-       if !ok {
-               return ErrUnknownBuffer
-       }
-       content := buf.text()
-       start, end, err := regexpRange(content, re)
-       if err != nil {
-               return err
-       }
-       return e.editBufferLocked(ctx, path, []Edit{{
-               Start: start,
-               End:   end,
-               Text:  replace,
-       }})
-}
-
-// EditBuffer applies the given test edits to the buffer identified by path.
-func (e *Editor) EditBuffer(ctx context.Context, path string, edits []Edit) error {
-       e.mu.Lock()
-       defer e.mu.Unlock()
-       return e.editBufferLocked(ctx, path, edits)
-}
-
-// BufferText returns the content of the buffer with the given name.
-func (e *Editor) BufferText(name string) string {
-       e.mu.Lock()
-       defer e.mu.Unlock()
-       return e.buffers[name].text()
-}
-
-// BufferVersion returns the current version of the buffer corresponding to
-// name (or 0 if it is not being edited).
-func (e *Editor) BufferVersion(name string) int {
-       e.mu.Lock()
-       defer e.mu.Unlock()
-       return e.buffers[name].version
-}
-
-func (e *Editor) editBufferLocked(ctx context.Context, path string, edits []Edit) error {
-       buf, ok := e.buffers[path]
-       if !ok {
-               return fmt.Errorf("unknown buffer %q", path)
-       }
-       var (
-               content = make([]string, len(buf.content))
-               err     error
-               evts    []protocol.TextDocumentContentChangeEvent
-       )
-       copy(content, buf.content)
-       content, err = editContent(content, edits)
-       if err != nil {
-               return err
-       }
-
-       buf.content = content
-       buf.version++
-       e.buffers[path] = buf
-       // A simple heuristic: if there is only one edit, send it incrementally.
-       // Otherwise, send the entire content.
-       if len(edits) == 1 {
-               evts = append(evts, edits[0].toProtocolChangeEvent())
-       } else {
-               evts = append(evts, protocol.TextDocumentContentChangeEvent{
-                       Text: buf.text(),
-               })
-       }
-       params := &protocol.DidChangeTextDocumentParams{
-               TextDocument: protocol.VersionedTextDocumentIdentifier{
-                       Version:                float64(buf.version),
-                       TextDocumentIdentifier: e.textDocumentIdentifier(buf.path),
-               },
-               ContentChanges: evts,
-       }
-       if e.Server != nil {
-               if err := e.Server.DidChange(ctx, params); err != nil {
-                       return errors.Errorf("DidChange: %w", err)
-               }
-       }
-       return nil
-}
-
-// GoToDefinition jumps to the definition of the symbol at the given position
-// in an open buffer.
-func (e *Editor) GoToDefinition(ctx context.Context, path string, pos Pos) (string, Pos, error) {
-       if err := e.checkBufferPosition(path, pos); err != nil {
-               return "", Pos{}, err
-       }
-       params := &protocol.DefinitionParams{}
-       params.TextDocument.URI = e.sandbox.Workdir.URI(path)
-       params.Position = pos.ToProtocolPosition()
-
-       resp, err := e.Server.Definition(ctx, params)
-       if err != nil {
-               return "", Pos{}, errors.Errorf("definition: %w", err)
-       }
-       if len(resp) == 0 {
-               return "", Pos{}, nil
-       }
-       newPath := e.sandbox.Workdir.URIToPath(resp[0].URI)
-       newPos := fromProtocolPosition(resp[0].Range.Start)
-       if err := e.OpenFile(ctx, newPath); err != nil {
-               return "", Pos{}, errors.Errorf("OpenFile: %w", err)
-       }
-       return newPath, newPos, nil
-}
-
-// Symbol performs a workspace symbol search using query
-func (e *Editor) Symbol(ctx context.Context, query string) ([]SymbolInformation, error) {
-       params := &protocol.WorkspaceSymbolParams{}
-       params.Query = query
-
-       resp, err := e.Server.Symbol(ctx, params)
-       if err != nil {
-               return nil, errors.Errorf("symbol: %w", err)
-       }
-       var res []SymbolInformation
-       for _, si := range resp {
-               ploc := si.Location
-               path := e.sandbox.Workdir.URIToPath(ploc.URI)
-               start := fromProtocolPosition(ploc.Range.Start)
-               end := fromProtocolPosition(ploc.Range.End)
-               rnge := Range{
-                       Start: start,
-                       End:   end,
-               }
-               loc := Location{
-                       Path:  path,
-                       Range: rnge,
-               }
-               res = append(res, SymbolInformation{
-                       Name:     si.Name,
-                       Kind:     si.Kind,
-                       Location: loc,
-               })
-       }
-       return res, nil
-}
-
-// OrganizeImports requests and performs the source.organizeImports codeAction.
-func (e *Editor) OrganizeImports(ctx context.Context, path string) error {
-       return e.codeAction(ctx, path, nil, nil, protocol.SourceOrganizeImports)
-}
-
-// RefactorRewrite requests and performs the source.refactorRewrite codeAction.
-func (e *Editor) RefactorRewrite(ctx context.Context, path string, rng *protocol.Range) error {
-       return e.codeAction(ctx, path, rng, nil, protocol.RefactorRewrite)
-}
-
-// ApplyQuickFixes requests and performs the quickfix codeAction.
-func (e *Editor) ApplyQuickFixes(ctx context.Context, path string, rng *protocol.Range, diagnostics []protocol.Diagnostic) error {
-       return e.codeAction(ctx, path, rng, diagnostics, protocol.QuickFix, protocol.SourceFixAll)
-}
-
-func (e *Editor) codeAction(ctx context.Context, path string, rng *protocol.Range, diagnostics []protocol.Diagnostic, only ...protocol.CodeActionKind) error {
-       if e.Server == nil {
-               return nil
-       }
-       params := &protocol.CodeActionParams{}
-       params.TextDocument.URI = e.sandbox.Workdir.URI(path)
-       params.Context.Only = only
-       if diagnostics != nil {
-               params.Context.Diagnostics = diagnostics
-       }
-       if rng != nil {
-               params.Range = *rng
-       }
-       actions, err := e.Server.CodeAction(ctx, params)
-       if err != nil {
-               return errors.Errorf("textDocument/codeAction: %w", err)
-       }
-       for _, action := range actions {
-               var match bool
-               for _, o := range only {
-                       if action.Kind == o {
-                               match = true
-                               break
-                       }
-               }
-               if !match {
-                       continue
-               }
-               for _, change := range action.Edit.DocumentChanges {
-                       path := e.sandbox.Workdir.URIToPath(change.TextDocument.URI)
-                       if float64(e.buffers[path].version) != change.TextDocument.Version {
-                               // Skip edits for old versions.
-                               continue
-                       }
-                       edits := convertEdits(change.Edits)
-                       if err := e.EditBuffer(ctx, path, edits); err != nil {
-                               return errors.Errorf("editing buffer %q: %w", path, err)
-                       }
-               }
-               // Execute any commands. The specification says that commands are
-               // executed after edits are applied.
-               if action.Command != nil {
-                       if _, err := e.ExecuteCommand(ctx, &protocol.ExecuteCommandParams{
-                               Command:   action.Command.Command,
-                               Arguments: action.Command.Arguments,
-                       }); err != nil {
-                               return err
-                       }
-               }
-       }
-       return nil
-}
-
-func (e *Editor) ExecuteCommand(ctx context.Context, params *protocol.ExecuteCommandParams) (interface{}, error) {
-       if e.Server == nil {
-               return nil, nil
-       }
-       var match bool
-       // Ensure that this command was actually listed as a supported command.
-       for _, command := range e.serverCapabilities.ExecuteCommandProvider.Commands {
-               if command == params.Command {
-                       match = true
-                       break
-               }
-       }
-       if !match {
-               return nil, fmt.Errorf("unsupported command %q", params.Command)
-       }
-       return e.Server.ExecuteCommand(ctx, params)
-}
-
-func convertEdits(protocolEdits []protocol.TextEdit) []Edit {
-       var edits []Edit
-       for _, lspEdit := range protocolEdits {
-               edits = append(edits, fromProtocolTextEdit(lspEdit))
-       }
-       return edits
-}
-
-// FormatBuffer gofmts a Go file.
-func (e *Editor) FormatBuffer(ctx context.Context, path string) error {
-       if e.Server == nil {
-               return nil
-       }
-       e.mu.Lock()
-       version := e.buffers[path].version
-       e.mu.Unlock()
-       params := &protocol.DocumentFormattingParams{}
-       params.TextDocument.URI = e.sandbox.Workdir.URI(path)
-       resp, err := e.Server.Formatting(ctx, params)
-       if err != nil {
-               return errors.Errorf("textDocument/formatting: %w", err)
-       }
-       e.mu.Lock()
-       defer e.mu.Unlock()
-       if versionAfter := e.buffers[path].version; versionAfter != version {
-               return fmt.Errorf("before receipt of formatting edits, buffer version changed from %d to %d", version, versionAfter)
-       }
-       edits := convertEdits(resp)
-       return e.editBufferLocked(ctx, path, edits)
-}
-
-func (e *Editor) checkBufferPosition(path string, pos Pos) error {
-       e.mu.Lock()
-       defer e.mu.Unlock()
-       buf, ok := e.buffers[path]
-       if !ok {
-               return fmt.Errorf("buffer %q is not open", path)
-       }
-       if !inText(pos, buf.content) {
-               return fmt.Errorf("position %v is invalid in buffer %q", pos, path)
-       }
-       return nil
-}
-
-// RunGenerate runs `go generate` non-recursively in the workdir-relative dir
-// path. It does not report any resulting file changes as a watched file
-// change, so must be followed by a call to Workdir.CheckForFileChanges once
-// the generate command has completed.
-func (e *Editor) RunGenerate(ctx context.Context, dir string) error {
-       if e.Server == nil {
-               return nil
-       }
-       absDir := e.sandbox.Workdir.AbsPath(dir)
-       jsonArgs, err := source.MarshalArgs(span.URIFromPath(absDir), false)
-       if err != nil {
-               return err
-       }
-       params := &protocol.ExecuteCommandParams{
-               Command:   source.CommandGenerate.ID(),
-               Arguments: jsonArgs,
-       }
-       if _, err := e.ExecuteCommand(ctx, params); err != nil {
-               return fmt.Errorf("running generate: %v", err)
-       }
-       // Unfortunately we can't simply poll the workdir for file changes here,
-       // because server-side command may not have completed. In regtests, we can
-       // Await this state change, but here we must delegate that responsibility to
-       // the caller.
-       return nil
-}
-
-// CodeLens executes a codelens request on the server.
-func (e *Editor) CodeLens(ctx context.Context, path string) ([]protocol.CodeLens, error) {
-       if e.Server == nil {
-               return nil, nil
-       }
-       e.mu.Lock()
-       _, ok := e.buffers[path]
-       e.mu.Unlock()
-       if !ok {
-               return nil, fmt.Errorf("buffer %q is not open", path)
-       }
-       params := &protocol.CodeLensParams{
-               TextDocument: e.textDocumentIdentifier(path),
-       }
-       lens, err := e.Server.CodeLens(ctx, params)
-       if err != nil {
-               return nil, err
-       }
-       return lens, nil
-}
-
-// Completion executes a completion request on the server.
-func (e *Editor) Completion(ctx context.Context, path string, pos Pos) (*protocol.CompletionList, error) {
-       if e.Server == nil {
-               return nil, nil
-       }
-       e.mu.Lock()
-       _, ok := e.buffers[path]
-       e.mu.Unlock()
-       if !ok {
-               return nil, fmt.Errorf("buffer %q is not open", path)
-       }
-       params := &protocol.CompletionParams{
-               TextDocumentPositionParams: protocol.TextDocumentPositionParams{
-                       TextDocument: e.textDocumentIdentifier(path),
-                       Position:     pos.ToProtocolPosition(),
-               },
-       }
-       completions, err := e.Server.Completion(ctx, params)
-       if err != nil {
-               return nil, err
-       }
-       return completions, nil
-}
-
-// References executes a reference request on the server.
-func (e *Editor) References(ctx context.Context, path string, pos Pos) ([]protocol.Location, error) {
-       if e.Server == nil {
-               return nil, nil
-       }
-       e.mu.Lock()
-       _, ok := e.buffers[path]
-       e.mu.Unlock()
-       if !ok {
-               return nil, fmt.Errorf("buffer %q is not open", path)
-       }
-       params := &protocol.ReferenceParams{
-               TextDocumentPositionParams: protocol.TextDocumentPositionParams{
-                       TextDocument: e.textDocumentIdentifier(path),
-                       Position:     pos.ToProtocolPosition(),
-               },
-               Context: protocol.ReferenceContext{
-                       IncludeDeclaration: true,
-               },
-       }
-       locations, err := e.Server.References(ctx, params)
-       if err != nil {
-               return nil, err
-       }
-       return locations, nil
-}
-
-// CodeAction executes a codeAction request on the server.
-func (e *Editor) CodeAction(ctx context.Context, path string, rng *protocol.Range) ([]protocol.CodeAction, error) {
-       if e.Server == nil {
-               return nil, nil
-       }
-       e.mu.Lock()
-       _, ok := e.buffers[path]
-       e.mu.Unlock()
-       if !ok {
-               return nil, fmt.Errorf("buffer %q is not open", path)
-       }
-       params := &protocol.CodeActionParams{
-               TextDocument: e.textDocumentIdentifier(path),
-       }
-       if rng != nil {
-               params.Range = *rng
-       }
-       lens, err := e.Server.CodeAction(ctx, params)
-       if err != nil {
-               return nil, err
-       }
-       return lens, nil
-}
-
-// Hover triggers a hover at the given position in an open buffer.
-func (e *Editor) Hover(ctx context.Context, path string, pos Pos) (*protocol.MarkupContent, Pos, error) {
-       if err := e.checkBufferPosition(path, pos); err != nil {
-               return nil, Pos{}, err
-       }
-       params := &protocol.HoverParams{}
-       params.TextDocument.URI = e.sandbox.Workdir.URI(path)
-       params.Position = pos.ToProtocolPosition()
-
-       resp, err := e.Server.Hover(ctx, params)
-       if err != nil {
-               return nil, Pos{}, errors.Errorf("hover: %w", err)
-       }
-       if resp == nil {
-               return nil, Pos{}, nil
-       }
-       return &resp.Contents, fromProtocolPosition(resp.Range.Start), nil
-}
-
-func (e *Editor) DocumentLink(ctx context.Context, path string) ([]protocol.DocumentLink, error) {
-       if e.Server == nil {
-               return nil, nil
-       }
-       params := &protocol.DocumentLinkParams{}
-       params.TextDocument.URI = e.sandbox.Workdir.URI(path)
-       return e.Server.DocumentLink(ctx, params)
-}