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