Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / internal / lsp / fake / editor.go
1 // Copyright 2020 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package fake
6
7 import (
8         "bufio"
9         "context"
10         "fmt"
11         "os"
12         "path/filepath"
13         "regexp"
14         "strings"
15         "sync"
16
17         "golang.org/x/tools/internal/jsonrpc2"
18         "golang.org/x/tools/internal/lsp/protocol"
19         "golang.org/x/tools/internal/lsp/source"
20         "golang.org/x/tools/internal/span"
21         errors "golang.org/x/xerrors"
22 )
23
24 // Editor is a fake editor client.  It keeps track of client state and can be
25 // used for writing LSP tests.
26 type Editor struct {
27         Config EditorConfig
28
29         // Server, client, and sandbox are concurrency safe and written only
30         // at construction time, so do not require synchronization.
31         Server     protocol.Server
32         serverConn jsonrpc2.Conn
33         client     *Client
34         sandbox    *Sandbox
35         defaultEnv map[string]string
36
37         // Since this editor is intended just for testing, we use very coarse
38         // locking.
39         mu sync.Mutex
40         // Editor state.
41         buffers map[string]buffer
42         // Capabilities / Options
43         serverCapabilities protocol.ServerCapabilities
44 }
45
46 type buffer struct {
47         version int
48         path    string
49         content []string
50 }
51
52 func (b buffer) text() string {
53         return strings.Join(b.content, "\n")
54 }
55
56 // EditorConfig configures the editor's LSP session. This is similar to
57 // source.UserOptions, but we use a separate type here so that we expose only
58 // that configuration which we support.
59 //
60 // The zero value for EditorConfig should correspond to its defaults.
61 type EditorConfig struct {
62         Env        map[string]string
63         BuildFlags []string
64
65         // CodeLens is a map defining whether codelens are enabled, keyed by the
66         // codeLens command. CodeLens which are not present in this map are left in
67         // their default state.
68         CodeLens map[string]bool
69
70         // SymbolMatcher is the config associated with the "symbolMatcher" gopls
71         // config option.
72         SymbolMatcher, SymbolStyle *string
73
74         // LimitWorkspaceScope is true if the user does not want to expand their
75         // workspace scope to the entire module.
76         LimitWorkspaceScope bool
77
78         // WithoutWorkspaceFolders is used to simulate opening a single file in the
79         // editor, without a workspace root. In that case, the client sends neither
80         // workspace folders nor a root URI.
81         WithoutWorkspaceFolders bool
82
83         // EditorRootPath specifies the root path of the workspace folder used when
84         // initializing gopls in the sandbox. If empty, the Workdir is used.
85         EditorRootPath string
86
87         // EnableStaticcheck enables staticcheck analyzers.
88         EnableStaticcheck bool
89
90         // AllExperiments sets the "allExperiments" configuration, which enables
91         // all of gopls's opt-in settings.
92         AllExperiments bool
93
94         // Whether to send the current process ID, for testing data that is joined to
95         // the PID. This can only be set by one test.
96         SendPID bool
97 }
98
99 // NewEditor Creates a new Editor.
100 func NewEditor(sandbox *Sandbox, config EditorConfig) *Editor {
101         return &Editor{
102                 buffers:    make(map[string]buffer),
103                 sandbox:    sandbox,
104                 defaultEnv: sandbox.GoEnv(),
105                 Config:     config,
106         }
107 }
108
109 // Connect configures the editor to communicate with an LSP server on conn. It
110 // is not concurrency safe, and should be called at most once, before using the
111 // editor.
112 //
113 // It returns the editor, so that it may be called as follows:
114 //   editor, err := NewEditor(s).Connect(ctx, conn)
115 func (e *Editor) Connect(ctx context.Context, conn jsonrpc2.Conn, hooks ClientHooks) (*Editor, error) {
116         e.serverConn = conn
117         e.Server = protocol.ServerDispatcher(conn)
118         e.client = &Client{editor: e, hooks: hooks}
119         conn.Go(ctx,
120                 protocol.Handlers(
121                         protocol.ClientHandler(e.client,
122                                 jsonrpc2.MethodNotFound)))
123         if err := e.initialize(ctx, e.Config.WithoutWorkspaceFolders, e.Config.EditorRootPath); err != nil {
124                 return nil, err
125         }
126         e.sandbox.Workdir.AddWatcher(e.onFileChanges)
127         return e, nil
128 }
129
130 // Shutdown issues the 'shutdown' LSP notification.
131 func (e *Editor) Shutdown(ctx context.Context) error {
132         if e.Server != nil {
133                 if err := e.Server.Shutdown(ctx); err != nil {
134                         return errors.Errorf("Shutdown: %w", err)
135                 }
136         }
137         return nil
138 }
139
140 // Exit issues the 'exit' LSP notification.
141 func (e *Editor) Exit(ctx context.Context) error {
142         if e.Server != nil {
143                 // Not all LSP clients issue the exit RPC, but we do so here to ensure that
144                 // we gracefully handle it on multi-session servers.
145                 if err := e.Server.Exit(ctx); err != nil {
146                         return errors.Errorf("Exit: %w", err)
147                 }
148         }
149         return nil
150 }
151
152 // Close issues the shutdown and exit sequence an editor should.
153 func (e *Editor) Close(ctx context.Context) error {
154         if err := e.Shutdown(ctx); err != nil {
155                 return err
156         }
157         if err := e.Exit(ctx); err != nil {
158                 return err
159         }
160         // called close on the editor should result in the connection closing
161         select {
162         case <-e.serverConn.Done():
163                 // connection closed itself
164                 return nil
165         case <-ctx.Done():
166                 return errors.Errorf("connection not closed: %w", ctx.Err())
167         }
168 }
169
170 // Client returns the LSP client for this editor.
171 func (e *Editor) Client() *Client {
172         return e.client
173 }
174
175 func (e *Editor) overlayEnv() map[string]string {
176         env := make(map[string]string)
177         for k, v := range e.defaultEnv {
178                 env[k] = v
179         }
180         for k, v := range e.Config.Env {
181                 env[k] = v
182         }
183         return env
184 }
185
186 func (e *Editor) configuration() map[string]interface{} {
187         config := map[string]interface{}{
188                 "verboseWorkDoneProgress": true,
189                 "env":                     e.overlayEnv(),
190                 "expandWorkspaceToModule": !e.Config.LimitWorkspaceScope,
191                 "completionBudget":        "10s",
192         }
193
194         if e.Config.BuildFlags != nil {
195                 config["buildFlags"] = e.Config.BuildFlags
196         }
197
198         if e.Config.CodeLens != nil {
199                 config["codelens"] = e.Config.CodeLens
200         }
201         if e.Config.SymbolMatcher != nil {
202                 config["symbolMatcher"] = *e.Config.SymbolMatcher
203         }
204         if e.Config.SymbolStyle != nil {
205                 config["symbolStyle"] = *e.Config.SymbolStyle
206         }
207         if e.Config.EnableStaticcheck {
208                 config["staticcheck"] = true
209         }
210         if e.Config.AllExperiments {
211                 config["allExperiments"] = true
212         }
213
214         // TODO(rFindley): uncomment this if/when diagnostics delay is on by
215         // default... and probably change to the new settings name.
216         // config["experimentalDiagnosticsDelay"] = "10ms"
217
218         // ExperimentalWorkspaceModule is only set as a mode, not a configuration.
219         return config
220 }
221
222 func (e *Editor) initialize(ctx context.Context, withoutWorkspaceFolders bool, editorRootPath string) error {
223         params := &protocol.ParamInitialize{}
224         params.ClientInfo.Name = "fakeclient"
225         params.ClientInfo.Version = "v1.0.0"
226         if !withoutWorkspaceFolders {
227                 rootURI := e.sandbox.Workdir.RootURI()
228                 if editorRootPath != "" {
229                         rootURI = toURI(e.sandbox.Workdir.AbsPath(editorRootPath))
230                 }
231                 params.WorkspaceFolders = []protocol.WorkspaceFolder{{
232                         URI:  string(rootURI),
233                         Name: filepath.Base(rootURI.SpanURI().Filename()),
234                 }}
235         }
236         params.Capabilities.Workspace.Configuration = true
237         params.Capabilities.Window.WorkDoneProgress = true
238         // TODO: set client capabilities
239         params.InitializationOptions = e.configuration()
240         if e.Config.SendPID {
241                 params.ProcessID = float64(os.Getpid())
242         }
243
244         // This is a bit of a hack, since the fake editor doesn't actually support
245         // watching changed files that match a specific glob pattern. However, the
246         // editor does send didChangeWatchedFiles notifications, so set this to
247         // true.
248         params.Capabilities.Workspace.DidChangeWatchedFiles.DynamicRegistration = true
249
250         params.Trace = "messages"
251         // TODO: support workspace folders.
252         if e.Server != nil {
253                 resp, err := e.Server.Initialize(ctx, params)
254                 if err != nil {
255                         return errors.Errorf("initialize: %w", err)
256                 }
257                 e.mu.Lock()
258                 e.serverCapabilities = resp.Capabilities
259                 e.mu.Unlock()
260
261                 if err := e.Server.Initialized(ctx, &protocol.InitializedParams{}); err != nil {
262                         return errors.Errorf("initialized: %w", err)
263                 }
264         }
265         // TODO: await initial configuration here, or expect gopls to manage that?
266         return nil
267 }
268
269 func (e *Editor) onFileChanges(ctx context.Context, evts []FileEvent) {
270         if e.Server == nil {
271                 return
272         }
273         var lspevts []protocol.FileEvent
274         for _, evt := range evts {
275                 lspevts = append(lspevts, evt.ProtocolEvent)
276         }
277         e.Server.DidChangeWatchedFiles(ctx, &protocol.DidChangeWatchedFilesParams{
278                 Changes: lspevts,
279         })
280 }
281
282 // OpenFile creates a buffer for the given workdir-relative file.
283 func (e *Editor) OpenFile(ctx context.Context, path string) error {
284         content, err := e.sandbox.Workdir.ReadFile(path)
285         if err != nil {
286                 return err
287         }
288         return e.CreateBuffer(ctx, path, content)
289 }
290
291 func newBuffer(path, content string) buffer {
292         return buffer{
293                 version: 1,
294                 path:    path,
295                 content: strings.Split(content, "\n"),
296         }
297 }
298
299 func textDocumentItem(wd *Workdir, buf buffer) protocol.TextDocumentItem {
300         uri := wd.URI(buf.path)
301         languageID := ""
302         if strings.HasSuffix(buf.path, ".go") {
303                 // TODO: what about go.mod files? What is their language ID?
304                 languageID = "go"
305         }
306         return protocol.TextDocumentItem{
307                 URI:        uri,
308                 LanguageID: languageID,
309                 Version:    float64(buf.version),
310                 Text:       buf.text(),
311         }
312 }
313
314 // CreateBuffer creates a new unsaved buffer corresponding to the workdir path,
315 // containing the given textual content.
316 func (e *Editor) CreateBuffer(ctx context.Context, path, content string) error {
317         buf := newBuffer(path, content)
318         e.mu.Lock()
319         e.buffers[path] = buf
320         item := textDocumentItem(e.sandbox.Workdir, buf)
321         e.mu.Unlock()
322
323         if e.Server != nil {
324                 if err := e.Server.DidOpen(ctx, &protocol.DidOpenTextDocumentParams{
325                         TextDocument: item,
326                 }); err != nil {
327                         return errors.Errorf("DidOpen: %w", err)
328                 }
329         }
330         return nil
331 }
332
333 // CloseBuffer removes the current buffer (regardless of whether it is saved).
334 func (e *Editor) CloseBuffer(ctx context.Context, path string) error {
335         e.mu.Lock()
336         _, ok := e.buffers[path]
337         if !ok {
338                 e.mu.Unlock()
339                 return ErrUnknownBuffer
340         }
341         delete(e.buffers, path)
342         e.mu.Unlock()
343
344         if e.Server != nil {
345                 if err := e.Server.DidClose(ctx, &protocol.DidCloseTextDocumentParams{
346                         TextDocument: e.textDocumentIdentifier(path),
347                 }); err != nil {
348                         return errors.Errorf("DidClose: %w", err)
349                 }
350         }
351         return nil
352 }
353
354 func (e *Editor) textDocumentIdentifier(path string) protocol.TextDocumentIdentifier {
355         return protocol.TextDocumentIdentifier{
356                 URI: e.sandbox.Workdir.URI(path),
357         }
358 }
359
360 // SaveBuffer writes the content of the buffer specified by the given path to
361 // the filesystem.
362 func (e *Editor) SaveBuffer(ctx context.Context, path string) error {
363         if err := e.OrganizeImports(ctx, path); err != nil {
364                 return errors.Errorf("organizing imports before save: %w", err)
365         }
366         if err := e.FormatBuffer(ctx, path); err != nil {
367                 return errors.Errorf("formatting before save: %w", err)
368         }
369         return e.SaveBufferWithoutActions(ctx, path)
370 }
371
372 func (e *Editor) SaveBufferWithoutActions(ctx context.Context, path string) error {
373         e.mu.Lock()
374         buf, ok := e.buffers[path]
375         if !ok {
376                 e.mu.Unlock()
377                 return fmt.Errorf(fmt.Sprintf("unknown buffer: %q", path))
378         }
379         content := buf.text()
380         includeText := false
381         syncOptions, ok := e.serverCapabilities.TextDocumentSync.(protocol.TextDocumentSyncOptions)
382         if ok {
383                 includeText = syncOptions.Save.IncludeText
384         }
385         e.mu.Unlock()
386
387         docID := e.textDocumentIdentifier(buf.path)
388         if e.Server != nil {
389                 if err := e.Server.WillSave(ctx, &protocol.WillSaveTextDocumentParams{
390                         TextDocument: docID,
391                         Reason:       protocol.Manual,
392                 }); err != nil {
393                         return errors.Errorf("WillSave: %w", err)
394                 }
395         }
396         if err := e.sandbox.Workdir.WriteFile(ctx, path, content); err != nil {
397                 return errors.Errorf("writing %q: %w", path, err)
398         }
399         if e.Server != nil {
400                 params := &protocol.DidSaveTextDocumentParams{
401                         TextDocument: protocol.VersionedTextDocumentIdentifier{
402                                 Version:                float64(buf.version),
403                                 TextDocumentIdentifier: docID,
404                         },
405                 }
406                 if includeText {
407                         params.Text = &content
408                 }
409                 if err := e.Server.DidSave(ctx, params); err != nil {
410                         return errors.Errorf("DidSave: %w", err)
411                 }
412         }
413         return nil
414 }
415
416 // contentPosition returns the (Line, Column) position corresponding to offset
417 // in the buffer referenced by path.
418 func contentPosition(content string, offset int) (Pos, error) {
419         scanner := bufio.NewScanner(strings.NewReader(content))
420         start := 0
421         line := 0
422         for scanner.Scan() {
423                 end := start + len([]rune(scanner.Text())) + 1
424                 if offset < end {
425                         return Pos{Line: line, Column: offset - start}, nil
426                 }
427                 start = end
428                 line++
429         }
430         if err := scanner.Err(); err != nil {
431                 return Pos{}, errors.Errorf("scanning content: %w", err)
432         }
433         // Scan() will drop the last line if it is empty. Correct for this.
434         if (strings.HasSuffix(content, "\n") || content == "") && offset == start {
435                 return Pos{Line: line, Column: 0}, nil
436         }
437         return Pos{}, fmt.Errorf("position %d out of bounds in %q (line = %d, start = %d)", offset, content, line, start)
438 }
439
440 // ErrNoMatch is returned if a regexp search fails.
441 var (
442         ErrNoMatch       = errors.New("no match")
443         ErrUnknownBuffer = errors.New("unknown buffer")
444 )
445
446 // regexpRange returns the start and end of the first occurrence of either re
447 // or its singular subgroup. It returns ErrNoMatch if the regexp doesn't match.
448 func regexpRange(content, re string) (Pos, Pos, error) {
449         var start, end int
450         rec, err := regexp.Compile(re)
451         if err != nil {
452                 return Pos{}, Pos{}, err
453         }
454         indexes := rec.FindStringSubmatchIndex(content)
455         if indexes == nil {
456                 return Pos{}, Pos{}, ErrNoMatch
457         }
458         switch len(indexes) {
459         case 2:
460                 // no subgroups: return the range of the regexp expression
461                 start, end = indexes[0], indexes[1]
462         case 4:
463                 // one subgroup: return its range
464                 start, end = indexes[2], indexes[3]
465         default:
466                 return Pos{}, Pos{}, fmt.Errorf("invalid search regexp %q: expect either 0 or 1 subgroups, got %d", re, len(indexes)/2-1)
467         }
468         startPos, err := contentPosition(content, start)
469         if err != nil {
470                 return Pos{}, Pos{}, err
471         }
472         endPos, err := contentPosition(content, end)
473         if err != nil {
474                 return Pos{}, Pos{}, err
475         }
476         return startPos, endPos, nil
477 }
478
479 // RegexpRange returns the first range in the buffer bufName matching re. See
480 // RegexpSearch for more information on matching.
481 func (e *Editor) RegexpRange(bufName, re string) (Pos, Pos, error) {
482         e.mu.Lock()
483         defer e.mu.Unlock()
484         buf, ok := e.buffers[bufName]
485         if !ok {
486                 return Pos{}, Pos{}, ErrUnknownBuffer
487         }
488         return regexpRange(buf.text(), re)
489 }
490
491 // RegexpSearch returns the position of the first match for re in the buffer
492 // bufName. For convenience, RegexpSearch supports the following two modes:
493 //  1. If re has no subgroups, return the position of the match for re itself.
494 //  2. If re has one subgroup, return the position of the first subgroup.
495 // It returns an error re is invalid, has more than one subgroup, or doesn't
496 // match the buffer.
497 func (e *Editor) RegexpSearch(bufName, re string) (Pos, error) {
498         start, _, err := e.RegexpRange(bufName, re)
499         return start, err
500 }
501
502 // RegexpReplace edits the buffer corresponding to path by replacing the first
503 // instance of re, or its first subgroup, with the replace text. See
504 // RegexpSearch for more explanation of these two modes.
505 // It returns an error if re is invalid, has more than one subgroup, or doesn't
506 // match the buffer.
507 func (e *Editor) RegexpReplace(ctx context.Context, path, re, replace string) error {
508         e.mu.Lock()
509         defer e.mu.Unlock()
510         buf, ok := e.buffers[path]
511         if !ok {
512                 return ErrUnknownBuffer
513         }
514         content := buf.text()
515         start, end, err := regexpRange(content, re)
516         if err != nil {
517                 return err
518         }
519         return e.editBufferLocked(ctx, path, []Edit{{
520                 Start: start,
521                 End:   end,
522                 Text:  replace,
523         }})
524 }
525
526 // EditBuffer applies the given test edits to the buffer identified by path.
527 func (e *Editor) EditBuffer(ctx context.Context, path string, edits []Edit) error {
528         e.mu.Lock()
529         defer e.mu.Unlock()
530         return e.editBufferLocked(ctx, path, edits)
531 }
532
533 func (e *Editor) SetBufferContent(ctx context.Context, path, content string) error {
534         e.mu.Lock()
535         defer e.mu.Unlock()
536         lines := strings.Split(content, "\n")
537         return e.setBufferContentLocked(ctx, path, lines, nil)
538 }
539
540 // BufferText returns the content of the buffer with the given name.
541 func (e *Editor) BufferText(name string) string {
542         e.mu.Lock()
543         defer e.mu.Unlock()
544         return e.buffers[name].text()
545 }
546
547 // BufferVersion returns the current version of the buffer corresponding to
548 // name (or 0 if it is not being edited).
549 func (e *Editor) BufferVersion(name string) int {
550         e.mu.Lock()
551         defer e.mu.Unlock()
552         return e.buffers[name].version
553 }
554
555 func (e *Editor) editBufferLocked(ctx context.Context, path string, edits []Edit) error {
556         buf, ok := e.buffers[path]
557         if !ok {
558                 return fmt.Errorf("unknown buffer %q", path)
559         }
560         content := make([]string, len(buf.content))
561         copy(content, buf.content)
562         content, err := editContent(content, edits)
563         if err != nil {
564                 return err
565         }
566         return e.setBufferContentLocked(ctx, path, content, edits)
567 }
568
569 func (e *Editor) setBufferContentLocked(ctx context.Context, path string, content []string, fromEdits []Edit) error {
570         buf, ok := e.buffers[path]
571         if !ok {
572                 return fmt.Errorf("unknown buffer %q", path)
573         }
574         buf.content = content
575         buf.version++
576         e.buffers[path] = buf
577         // A simple heuristic: if there is only one edit, send it incrementally.
578         // Otherwise, send the entire content.
579         var evts []protocol.TextDocumentContentChangeEvent
580         if len(fromEdits) == 1 {
581                 evts = append(evts, fromEdits[0].toProtocolChangeEvent())
582         } else {
583                 evts = append(evts, protocol.TextDocumentContentChangeEvent{
584                         Text: buf.text(),
585                 })
586         }
587         params := &protocol.DidChangeTextDocumentParams{
588                 TextDocument: protocol.VersionedTextDocumentIdentifier{
589                         Version:                float64(buf.version),
590                         TextDocumentIdentifier: e.textDocumentIdentifier(buf.path),
591                 },
592                 ContentChanges: evts,
593         }
594         if e.Server != nil {
595                 if err := e.Server.DidChange(ctx, params); err != nil {
596                         return errors.Errorf("DidChange: %w", err)
597                 }
598         }
599         return nil
600 }
601
602 // GoToDefinition jumps to the definition of the symbol at the given position
603 // in an open buffer.
604 func (e *Editor) GoToDefinition(ctx context.Context, path string, pos Pos) (string, Pos, error) {
605         if err := e.checkBufferPosition(path, pos); err != nil {
606                 return "", Pos{}, err
607         }
608         params := &protocol.DefinitionParams{}
609         params.TextDocument.URI = e.sandbox.Workdir.URI(path)
610         params.Position = pos.ToProtocolPosition()
611
612         resp, err := e.Server.Definition(ctx, params)
613         if err != nil {
614                 return "", Pos{}, errors.Errorf("definition: %w", err)
615         }
616         if len(resp) == 0 {
617                 return "", Pos{}, nil
618         }
619         newPath := e.sandbox.Workdir.URIToPath(resp[0].URI)
620         newPos := fromProtocolPosition(resp[0].Range.Start)
621         if err := e.OpenFile(ctx, newPath); err != nil {
622                 return "", Pos{}, errors.Errorf("OpenFile: %w", err)
623         }
624         return newPath, newPos, nil
625 }
626
627 // Symbol performs a workspace symbol search using query
628 func (e *Editor) Symbol(ctx context.Context, query string) ([]SymbolInformation, error) {
629         params := &protocol.WorkspaceSymbolParams{}
630         params.Query = query
631
632         resp, err := e.Server.Symbol(ctx, params)
633         if err != nil {
634                 return nil, errors.Errorf("symbol: %w", err)
635         }
636         var res []SymbolInformation
637         for _, si := range resp {
638                 ploc := si.Location
639                 path := e.sandbox.Workdir.URIToPath(ploc.URI)
640                 start := fromProtocolPosition(ploc.Range.Start)
641                 end := fromProtocolPosition(ploc.Range.End)
642                 rnge := Range{
643                         Start: start,
644                         End:   end,
645                 }
646                 loc := Location{
647                         Path:  path,
648                         Range: rnge,
649                 }
650                 res = append(res, SymbolInformation{
651                         Name:     si.Name,
652                         Kind:     si.Kind,
653                         Location: loc,
654                 })
655         }
656         return res, nil
657 }
658
659 // OrganizeImports requests and performs the source.organizeImports codeAction.
660 func (e *Editor) OrganizeImports(ctx context.Context, path string) error {
661         return e.codeAction(ctx, path, nil, nil, protocol.SourceOrganizeImports)
662 }
663
664 // RefactorRewrite requests and performs the source.refactorRewrite codeAction.
665 func (e *Editor) RefactorRewrite(ctx context.Context, path string, rng *protocol.Range) error {
666         return e.codeAction(ctx, path, rng, nil, protocol.RefactorRewrite)
667 }
668
669 // ApplyQuickFixes requests and performs the quickfix codeAction.
670 func (e *Editor) ApplyQuickFixes(ctx context.Context, path string, rng *protocol.Range, diagnostics []protocol.Diagnostic) error {
671         return e.codeAction(ctx, path, rng, diagnostics, protocol.QuickFix, protocol.SourceFixAll)
672 }
673
674 func (e *Editor) codeAction(ctx context.Context, path string, rng *protocol.Range, diagnostics []protocol.Diagnostic, only ...protocol.CodeActionKind) error {
675         if e.Server == nil {
676                 return nil
677         }
678         params := &protocol.CodeActionParams{}
679         params.TextDocument.URI = e.sandbox.Workdir.URI(path)
680         params.Context.Only = only
681         if diagnostics != nil {
682                 params.Context.Diagnostics = diagnostics
683         }
684         if rng != nil {
685                 params.Range = *rng
686         }
687         actions, err := e.Server.CodeAction(ctx, params)
688         if err != nil {
689                 return errors.Errorf("textDocument/codeAction: %w", err)
690         }
691         for _, action := range actions {
692                 var match bool
693                 for _, o := range only {
694                         if action.Kind == o {
695                                 match = true
696                                 break
697                         }
698                 }
699                 if !match {
700                         continue
701                 }
702                 for _, change := range action.Edit.DocumentChanges {
703                         path := e.sandbox.Workdir.URIToPath(change.TextDocument.URI)
704                         if float64(e.buffers[path].version) != change.TextDocument.Version {
705                                 // Skip edits for old versions.
706                                 continue
707                         }
708                         edits := convertEdits(change.Edits)
709                         if err := e.EditBuffer(ctx, path, edits); err != nil {
710                                 return errors.Errorf("editing buffer %q: %w", path, err)
711                         }
712                 }
713                 // Execute any commands. The specification says that commands are
714                 // executed after edits are applied.
715                 if action.Command != nil {
716                         if _, err := e.ExecuteCommand(ctx, &protocol.ExecuteCommandParams{
717                                 Command:   action.Command.Command,
718                                 Arguments: action.Command.Arguments,
719                         }); err != nil {
720                                 return err
721                         }
722                 }
723         }
724         return nil
725 }
726
727 func (e *Editor) ExecuteCommand(ctx context.Context, params *protocol.ExecuteCommandParams) (interface{}, error) {
728         if e.Server == nil {
729                 return nil, nil
730         }
731         var match bool
732         // Ensure that this command was actually listed as a supported command.
733         for _, command := range e.serverCapabilities.ExecuteCommandProvider.Commands {
734                 if command == params.Command {
735                         match = true
736                         break
737                 }
738         }
739         if !match {
740                 return nil, fmt.Errorf("unsupported command %q", params.Command)
741         }
742         return e.Server.ExecuteCommand(ctx, params)
743 }
744
745 func convertEdits(protocolEdits []protocol.TextEdit) []Edit {
746         var edits []Edit
747         for _, lspEdit := range protocolEdits {
748                 edits = append(edits, fromProtocolTextEdit(lspEdit))
749         }
750         return edits
751 }
752
753 // FormatBuffer gofmts a Go file.
754 func (e *Editor) FormatBuffer(ctx context.Context, path string) error {
755         if e.Server == nil {
756                 return nil
757         }
758         e.mu.Lock()
759         version := e.buffers[path].version
760         e.mu.Unlock()
761         params := &protocol.DocumentFormattingParams{}
762         params.TextDocument.URI = e.sandbox.Workdir.URI(path)
763         resp, err := e.Server.Formatting(ctx, params)
764         if err != nil {
765                 return errors.Errorf("textDocument/formatting: %w", err)
766         }
767         e.mu.Lock()
768         defer e.mu.Unlock()
769         if versionAfter := e.buffers[path].version; versionAfter != version {
770                 return fmt.Errorf("before receipt of formatting edits, buffer version changed from %d to %d", version, versionAfter)
771         }
772         edits := convertEdits(resp)
773         return e.editBufferLocked(ctx, path, edits)
774 }
775
776 func (e *Editor) checkBufferPosition(path string, pos Pos) error {
777         e.mu.Lock()
778         defer e.mu.Unlock()
779         buf, ok := e.buffers[path]
780         if !ok {
781                 return fmt.Errorf("buffer %q is not open", path)
782         }
783         if !inText(pos, buf.content) {
784                 return fmt.Errorf("position %v is invalid in buffer %q", pos, path)
785         }
786         return nil
787 }
788
789 // RunGenerate runs `go generate` non-recursively in the workdir-relative dir
790 // path. It does not report any resulting file changes as a watched file
791 // change, so must be followed by a call to Workdir.CheckForFileChanges once
792 // the generate command has completed.
793 func (e *Editor) RunGenerate(ctx context.Context, dir string) error {
794         if e.Server == nil {
795                 return nil
796         }
797         absDir := e.sandbox.Workdir.AbsPath(dir)
798         jsonArgs, err := source.MarshalArgs(span.URIFromPath(absDir), false)
799         if err != nil {
800                 return err
801         }
802         params := &protocol.ExecuteCommandParams{
803                 Command:   source.CommandGenerate.ID(),
804                 Arguments: jsonArgs,
805         }
806         if _, err := e.ExecuteCommand(ctx, params); err != nil {
807                 return fmt.Errorf("running generate: %v", err)
808         }
809         // Unfortunately we can't simply poll the workdir for file changes here,
810         // because server-side command may not have completed. In regtests, we can
811         // Await this state change, but here we must delegate that responsibility to
812         // the caller.
813         return nil
814 }
815
816 // CodeLens executes a codelens request on the server.
817 func (e *Editor) CodeLens(ctx context.Context, path string) ([]protocol.CodeLens, error) {
818         if e.Server == nil {
819                 return nil, nil
820         }
821         e.mu.Lock()
822         _, ok := e.buffers[path]
823         e.mu.Unlock()
824         if !ok {
825                 return nil, fmt.Errorf("buffer %q is not open", path)
826         }
827         params := &protocol.CodeLensParams{
828                 TextDocument: e.textDocumentIdentifier(path),
829         }
830         lens, err := e.Server.CodeLens(ctx, params)
831         if err != nil {
832                 return nil, err
833         }
834         return lens, nil
835 }
836
837 // Completion executes a completion request on the server.
838 func (e *Editor) Completion(ctx context.Context, path string, pos Pos) (*protocol.CompletionList, error) {
839         if e.Server == nil {
840                 return nil, nil
841         }
842         e.mu.Lock()
843         _, ok := e.buffers[path]
844         e.mu.Unlock()
845         if !ok {
846                 return nil, fmt.Errorf("buffer %q is not open", path)
847         }
848         params := &protocol.CompletionParams{
849                 TextDocumentPositionParams: protocol.TextDocumentPositionParams{
850                         TextDocument: e.textDocumentIdentifier(path),
851                         Position:     pos.ToProtocolPosition(),
852                 },
853         }
854         completions, err := e.Server.Completion(ctx, params)
855         if err != nil {
856                 return nil, err
857         }
858         return completions, nil
859 }
860
861 // References executes a reference request on the server.
862 func (e *Editor) References(ctx context.Context, path string, pos Pos) ([]protocol.Location, error) {
863         if e.Server == nil {
864                 return nil, nil
865         }
866         e.mu.Lock()
867         _, ok := e.buffers[path]
868         e.mu.Unlock()
869         if !ok {
870                 return nil, fmt.Errorf("buffer %q is not open", path)
871         }
872         params := &protocol.ReferenceParams{
873                 TextDocumentPositionParams: protocol.TextDocumentPositionParams{
874                         TextDocument: e.textDocumentIdentifier(path),
875                         Position:     pos.ToProtocolPosition(),
876                 },
877                 Context: protocol.ReferenceContext{
878                         IncludeDeclaration: true,
879                 },
880         }
881         locations, err := e.Server.References(ctx, params)
882         if err != nil {
883                 return nil, err
884         }
885         return locations, nil
886 }
887
888 // CodeAction executes a codeAction request on the server.
889 func (e *Editor) CodeAction(ctx context.Context, path string, rng *protocol.Range) ([]protocol.CodeAction, error) {
890         if e.Server == nil {
891                 return nil, nil
892         }
893         e.mu.Lock()
894         _, ok := e.buffers[path]
895         e.mu.Unlock()
896         if !ok {
897                 return nil, fmt.Errorf("buffer %q is not open", path)
898         }
899         params := &protocol.CodeActionParams{
900                 TextDocument: e.textDocumentIdentifier(path),
901         }
902         if rng != nil {
903                 params.Range = *rng
904         }
905         lens, err := e.Server.CodeAction(ctx, params)
906         if err != nil {
907                 return nil, err
908         }
909         return lens, nil
910 }
911
912 // Hover triggers a hover at the given position in an open buffer.
913 func (e *Editor) Hover(ctx context.Context, path string, pos Pos) (*protocol.MarkupContent, Pos, error) {
914         if err := e.checkBufferPosition(path, pos); err != nil {
915                 return nil, Pos{}, err
916         }
917         params := &protocol.HoverParams{}
918         params.TextDocument.URI = e.sandbox.Workdir.URI(path)
919         params.Position = pos.ToProtocolPosition()
920
921         resp, err := e.Server.Hover(ctx, params)
922         if err != nil {
923                 return nil, Pos{}, errors.Errorf("hover: %w", err)
924         }
925         if resp == nil {
926                 return nil, Pos{}, nil
927         }
928         return &resp.Contents, fromProtocolPosition(resp.Range.Start), nil
929 }
930
931 func (e *Editor) DocumentLink(ctx context.Context, path string) ([]protocol.DocumentLink, error) {
932         if e.Server == nil {
933                 return nil, nil
934         }
935         params := &protocol.DocumentLinkParams{}
936         params.TextDocument.URI = e.sandbox.Workdir.URI(path)
937         return e.Server.DocumentLink(ctx, params)
938 }