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
5package lsp
6
7import (
8	"bytes"
9	"context"
10	"encoding/json"
11	"fmt"
12	"io"
13	"io/ioutil"
14	"os"
15	"path/filepath"
16	"strings"
17
18	"golang.org/x/mod/modfile"
19	"golang.org/x/tools/internal/event"
20	"golang.org/x/tools/internal/gocommand"
21	"golang.org/x/tools/internal/lsp/cache"
22	"golang.org/x/tools/internal/lsp/command"
23	"golang.org/x/tools/internal/lsp/debug"
24	"golang.org/x/tools/internal/lsp/protocol"
25	"golang.org/x/tools/internal/lsp/source"
26	"golang.org/x/tools/internal/span"
27	"golang.org/x/tools/internal/xcontext"
28	errors "golang.org/x/xerrors"
29)
30
31func (s *Server) executeCommand(ctx context.Context, params *protocol.ExecuteCommandParams) (interface{}, error) {
32	var found bool
33	for _, name := range s.session.Options().SupportedCommands {
34		if name == params.Command {
35			found = true
36			break
37		}
38	}
39	if !found {
40		return nil, fmt.Errorf("%s is not a supported command", params.Command)
41	}
42
43	handler := &commandHandler{
44		s:      s,
45		params: params,
46	}
47	return command.Dispatch(ctx, params, handler)
48}
49
50type commandHandler struct {
51	s      *Server
52	params *protocol.ExecuteCommandParams
53}
54
55// commandConfig configures common command set-up and execution.
56type commandConfig struct {
57	async       bool                 // whether to run the command asynchronously. Async commands can only return errors.
58	requireSave bool                 // whether all files must be saved for the command to work
59	progress    string               // title to use for progress reporting. If empty, no progress will be reported.
60	forURI      protocol.DocumentURI // URI to resolve to a snapshot. If unset, snapshot will be nil.
61}
62
63// commandDeps is evaluated from a commandConfig. Note that not all fields may
64// be populated, depending on which configuration is set. See comments in-line
65// for details.
66type commandDeps struct {
67	snapshot source.Snapshot            // present if cfg.forURI was set
68	fh       source.VersionedFileHandle // present if cfg.forURI was set
69	work     *workDone                  // present cfg.progress was set
70}
71
72type commandFunc func(context.Context, commandDeps) error
73
74func (c *commandHandler) run(ctx context.Context, cfg commandConfig, run commandFunc) (err error) {
75	if cfg.requireSave {
76		for _, overlay := range c.s.session.Overlays() {
77			if !overlay.Saved() {
78				return errors.New("All files must be saved first")
79			}
80		}
81	}
82	var deps commandDeps
83	if cfg.forURI != "" {
84		var ok bool
85		var release func()
86		deps.snapshot, deps.fh, ok, release, err = c.s.beginFileRequest(ctx, cfg.forURI, source.UnknownKind)
87		defer release()
88		if !ok {
89			return err
90		}
91	}
92	ctx, cancel := context.WithCancel(xcontext.Detach(ctx))
93	if cfg.progress != "" {
94		deps.work = c.s.progress.start(ctx, cfg.progress, "Running...", c.params.WorkDoneToken, cancel)
95	}
96	runcmd := func() error {
97		defer cancel()
98		err := run(ctx, deps)
99		switch {
100		case errors.Is(err, context.Canceled):
101			deps.work.end("canceled")
102		case err != nil:
103			event.Error(ctx, "command error", err)
104			deps.work.end("failed")
105		default:
106			deps.work.end("completed")
107		}
108		return err
109	}
110	if cfg.async {
111		go func() {
112			if err := runcmd(); err != nil {
113				if showMessageErr := c.s.client.ShowMessage(ctx, &protocol.ShowMessageParams{
114					Type:    protocol.Error,
115					Message: err.Error(),
116				}); showMessageErr != nil {
117					event.Error(ctx, fmt.Sprintf("failed to show message: %q", err.Error()), showMessageErr)
118				}
119			}
120		}()
121		return nil
122	}
123	return runcmd()
124}
125
126func (c *commandHandler) ApplyFix(ctx context.Context, args command.ApplyFixArgs) error {
127	return c.run(ctx, commandConfig{
128		// Note: no progress here. Applying fixes should be quick.
129		forURI: args.URI,
130	}, func(ctx context.Context, deps commandDeps) error {
131		edits, err := source.ApplyFix(ctx, args.Fix, deps.snapshot, deps.fh, args.Range)
132		if err != nil {
133			return err
134		}
135		r, err := c.s.client.ApplyEdit(ctx, &protocol.ApplyWorkspaceEditParams{
136			Edit: protocol.WorkspaceEdit{
137				DocumentChanges: edits,
138			},
139		})
140		if err != nil {
141			return err
142		}
143		if !r.Applied {
144			return errors.New(r.FailureReason)
145		}
146		return nil
147	})
148}
149
150func (c *commandHandler) RegenerateCgo(ctx context.Context, args command.URIArg) error {
151	return c.run(ctx, commandConfig{
152		progress: "Regenerating Cgo",
153	}, func(ctx context.Context, deps commandDeps) error {
154		mod := source.FileModification{
155			URI:    args.URI.SpanURI(),
156			Action: source.InvalidateMetadata,
157		}
158		return c.s.didModifyFiles(ctx, []source.FileModification{mod}, FromRegenerateCgo)
159	})
160}
161
162func (c *commandHandler) CheckUpgrades(ctx context.Context, args command.CheckUpgradesArgs) error {
163	return c.run(ctx, commandConfig{
164		forURI:   args.URI,
165		progress: "Checking for upgrades",
166	}, func(ctx context.Context, deps commandDeps) error {
167		upgrades, err := c.s.getUpgrades(ctx, deps.snapshot, args.URI.SpanURI(), args.Modules)
168		if err != nil {
169			return err
170		}
171		deps.snapshot.View().RegisterModuleUpgrades(upgrades)
172		// Re-diagnose the snapshot to publish the new module diagnostics.
173		c.s.diagnoseSnapshot(deps.snapshot, nil, false)
174		return nil
175	})
176}
177
178func (c *commandHandler) AddDependency(ctx context.Context, args command.DependencyArgs) error {
179	return c.GoGetModule(ctx, args)
180}
181
182func (c *commandHandler) UpgradeDependency(ctx context.Context, args command.DependencyArgs) error {
183	return c.GoGetModule(ctx, args)
184}
185
186func (c *commandHandler) GoGetModule(ctx context.Context, args command.DependencyArgs) error {
187	return c.run(ctx, commandConfig{
188		progress: "Running go get",
189		forURI:   args.URI,
190	}, func(ctx context.Context, deps commandDeps) error {
191		return c.s.runGoModUpdateCommands(ctx, deps.snapshot, args.URI.SpanURI(), func(invoke func(...string) (*bytes.Buffer, error)) error {
192			return runGoGetModule(invoke, args.AddRequire, args.GoCmdArgs)
193		})
194	})
195}
196
197// TODO(rFindley): UpdateGoSum, Tidy, and Vendor could probably all be one command.
198func (c *commandHandler) UpdateGoSum(ctx context.Context, args command.URIArgs) error {
199	return c.run(ctx, commandConfig{
200		progress: "Updating go.sum",
201	}, func(ctx context.Context, deps commandDeps) error {
202		for _, uri := range args.URIs {
203			snapshot, fh, ok, release, err := c.s.beginFileRequest(ctx, uri, source.UnknownKind)
204			defer release()
205			if !ok {
206				return err
207			}
208			if err := c.s.runGoModUpdateCommands(ctx, snapshot, fh.URI(), func(invoke func(...string) (*bytes.Buffer, error)) error {
209				_, err := invoke("list", "all")
210				return err
211			}); err != nil {
212				return err
213			}
214		}
215		return nil
216	})
217}
218
219func (c *commandHandler) Tidy(ctx context.Context, args command.URIArgs) error {
220	return c.run(ctx, commandConfig{
221		requireSave: true,
222		progress:    "Running go mod tidy",
223	}, func(ctx context.Context, deps commandDeps) error {
224		for _, uri := range args.URIs {
225			snapshot, fh, ok, release, err := c.s.beginFileRequest(ctx, uri, source.UnknownKind)
226			defer release()
227			if !ok {
228				return err
229			}
230			if err := c.s.runGoModUpdateCommands(ctx, snapshot, fh.URI(), func(invoke func(...string) (*bytes.Buffer, error)) error {
231				_, err := invoke("mod", "tidy")
232				return err
233			}); err != nil {
234				return err
235			}
236		}
237		return nil
238	})
239}
240
241func (c *commandHandler) Vendor(ctx context.Context, args command.URIArg) error {
242	return c.run(ctx, commandConfig{
243		requireSave: true,
244		progress:    "Running go mod vendor",
245		forURI:      args.URI,
246	}, func(ctx context.Context, deps commandDeps) error {
247		_, err := deps.snapshot.RunGoCommandDirect(ctx, source.Normal|source.AllowNetwork, &gocommand.Invocation{
248			Verb:       "mod",
249			Args:       []string{"vendor"},
250			WorkingDir: filepath.Dir(args.URI.SpanURI().Filename()),
251		})
252		return err
253	})
254}
255
256func (c *commandHandler) RemoveDependency(ctx context.Context, args command.RemoveDependencyArgs) error {
257	return c.run(ctx, commandConfig{
258		progress: "Removing dependency",
259		forURI:   args.URI,
260	}, func(ctx context.Context, deps commandDeps) error {
261		// If the module is tidied apart from the one unused diagnostic, we can
262		// run `go get module@none`, and then run `go mod tidy`. Otherwise, we
263		// must make textual edits.
264		// TODO(rstambler): In Go 1.17+, we will be able to use the go command
265		// without checking if the module is tidy.
266		if args.OnlyDiagnostic {
267			return c.s.runGoModUpdateCommands(ctx, deps.snapshot, args.URI.SpanURI(), func(invoke func(...string) (*bytes.Buffer, error)) error {
268				if err := runGoGetModule(invoke, false, []string{args.ModulePath + "@none"}); err != nil {
269					return err
270				}
271				_, err := invoke("mod", "tidy")
272				return err
273			})
274		}
275		pm, err := deps.snapshot.ParseMod(ctx, deps.fh)
276		if err != nil {
277			return err
278		}
279		edits, err := dropDependency(deps.snapshot, pm, args.ModulePath)
280		if err != nil {
281			return err
282		}
283		response, err := c.s.client.ApplyEdit(ctx, &protocol.ApplyWorkspaceEditParams{
284			Edit: protocol.WorkspaceEdit{
285				DocumentChanges: []protocol.TextDocumentEdit{{
286					TextDocument: protocol.OptionalVersionedTextDocumentIdentifier{
287						Version: deps.fh.Version(),
288						TextDocumentIdentifier: protocol.TextDocumentIdentifier{
289							URI: protocol.URIFromSpanURI(deps.fh.URI()),
290						},
291					},
292					Edits: edits,
293				}},
294			},
295		})
296		if err != nil {
297			return err
298		}
299		if !response.Applied {
300			return fmt.Errorf("edits not applied because of %s", response.FailureReason)
301		}
302		return nil
303	})
304}
305
306// dropDependency returns the edits to remove the given require from the go.mod
307// file.
308func dropDependency(snapshot source.Snapshot, pm *source.ParsedModule, modulePath string) ([]protocol.TextEdit, error) {
309	// We need a private copy of the parsed go.mod file, since we're going to
310	// modify it.
311	copied, err := modfile.Parse("", pm.Mapper.Content, nil)
312	if err != nil {
313		return nil, err
314	}
315	if err := copied.DropRequire(modulePath); err != nil {
316		return nil, err
317	}
318	copied.Cleanup()
319	newContent, err := copied.Format()
320	if err != nil {
321		return nil, err
322	}
323	// Calculate the edits to be made due to the change.
324	diff, err := snapshot.View().Options().ComputeEdits(pm.URI, string(pm.Mapper.Content), string(newContent))
325	if err != nil {
326		return nil, err
327	}
328	return source.ToProtocolEdits(pm.Mapper, diff)
329}
330
331func (c *commandHandler) Test(ctx context.Context, uri protocol.DocumentURI, tests, benchmarks []string) error {
332	return c.RunTests(ctx, command.RunTestsArgs{
333		URI:        uri,
334		Tests:      tests,
335		Benchmarks: benchmarks,
336	})
337}
338
339func (c *commandHandler) RunTests(ctx context.Context, args command.RunTestsArgs) error {
340	return c.run(ctx, commandConfig{
341		async:       true,
342		progress:    "Running go test",
343		requireSave: true,
344		forURI:      args.URI,
345	}, func(ctx context.Context, deps commandDeps) error {
346		if err := c.runTests(ctx, deps.snapshot, deps.work, args.URI, args.Tests, args.Benchmarks); err != nil {
347			return errors.Errorf("running tests failed: %w", err)
348		}
349		return nil
350	})
351}
352
353func (c *commandHandler) runTests(ctx context.Context, snapshot source.Snapshot, work *workDone, uri protocol.DocumentURI, tests, benchmarks []string) error {
354	// TODO: fix the error reporting when this runs async.
355	pkgs, err := snapshot.PackagesForFile(ctx, uri.SpanURI(), source.TypecheckWorkspace)
356	if err != nil {
357		return err
358	}
359	if len(pkgs) == 0 {
360		return fmt.Errorf("package could not be found for file: %s", uri.SpanURI().Filename())
361	}
362	pkgPath := pkgs[0].ForTest()
363
364	// create output
365	buf := &bytes.Buffer{}
366	ew := &eventWriter{ctx: ctx, operation: "test"}
367	out := io.MultiWriter(ew, workDoneWriter{work}, buf)
368
369	// Run `go test -run Func` on each test.
370	var failedTests int
371	for _, funcName := range tests {
372		inv := &gocommand.Invocation{
373			Verb:       "test",
374			Args:       []string{pkgPath, "-v", "-count=1", "-run", fmt.Sprintf("^%s$", funcName)},
375			WorkingDir: filepath.Dir(uri.SpanURI().Filename()),
376		}
377		if err := snapshot.RunGoCommandPiped(ctx, source.Normal, inv, out, out); err != nil {
378			if errors.Is(err, context.Canceled) {
379				return err
380			}
381			failedTests++
382		}
383	}
384
385	// Run `go test -run=^$ -bench Func` on each test.
386	var failedBenchmarks int
387	for _, funcName := range benchmarks {
388		inv := &gocommand.Invocation{
389			Verb:       "test",
390			Args:       []string{pkgPath, "-v", "-run=^$", "-bench", fmt.Sprintf("^%s$", funcName)},
391			WorkingDir: filepath.Dir(uri.SpanURI().Filename()),
392		}
393		if err := snapshot.RunGoCommandPiped(ctx, source.Normal, inv, out, out); err != nil {
394			if errors.Is(err, context.Canceled) {
395				return err
396			}
397			failedBenchmarks++
398		}
399	}
400
401	var title string
402	if len(tests) > 0 && len(benchmarks) > 0 {
403		title = "tests and benchmarks"
404	} else if len(tests) > 0 {
405		title = "tests"
406	} else if len(benchmarks) > 0 {
407		title = "benchmarks"
408	} else {
409		return errors.New("No functions were provided")
410	}
411	message := fmt.Sprintf("all %s passed", title)
412	if failedTests > 0 && failedBenchmarks > 0 {
413		message = fmt.Sprintf("%d / %d tests failed and %d / %d benchmarks failed", failedTests, len(tests), failedBenchmarks, len(benchmarks))
414	} else if failedTests > 0 {
415		message = fmt.Sprintf("%d / %d tests failed", failedTests, len(tests))
416	} else if failedBenchmarks > 0 {
417		message = fmt.Sprintf("%d / %d benchmarks failed", failedBenchmarks, len(benchmarks))
418	}
419	if failedTests > 0 || failedBenchmarks > 0 {
420		message += "\n" + buf.String()
421	}
422
423	return c.s.client.ShowMessage(ctx, &protocol.ShowMessageParams{
424		Type:    protocol.Info,
425		Message: message,
426	})
427}
428
429func (c *commandHandler) Generate(ctx context.Context, args command.GenerateArgs) error {
430	title := "Running go generate ."
431	if args.Recursive {
432		title = "Running go generate ./..."
433	}
434	return c.run(ctx, commandConfig{
435		requireSave: true,
436		progress:    title,
437		forURI:      args.Dir,
438	}, func(ctx context.Context, deps commandDeps) error {
439		er := &eventWriter{ctx: ctx, operation: "generate"}
440
441		pattern := "."
442		if args.Recursive {
443			pattern = "./..."
444		}
445		inv := &gocommand.Invocation{
446			Verb:       "generate",
447			Args:       []string{"-x", pattern},
448			WorkingDir: args.Dir.SpanURI().Filename(),
449		}
450		stderr := io.MultiWriter(er, workDoneWriter{deps.work})
451		if err := deps.snapshot.RunGoCommandPiped(ctx, source.Normal, inv, er, stderr); err != nil {
452			return err
453		}
454		return nil
455	})
456}
457
458func (c *commandHandler) GoGetPackage(ctx context.Context, args command.GoGetPackageArgs) error {
459	return c.run(ctx, commandConfig{
460		forURI:   args.URI,
461		progress: "Running go get",
462	}, func(ctx context.Context, deps commandDeps) error {
463		// Run on a throwaway go.mod, otherwise it'll write to the real one.
464		stdout, err := deps.snapshot.RunGoCommandDirect(ctx, source.WriteTemporaryModFile|source.AllowNetwork, &gocommand.Invocation{
465			Verb:       "list",
466			Args:       []string{"-f", "{{.Module.Path}}@{{.Module.Version}}", args.Pkg},
467			WorkingDir: filepath.Dir(args.URI.SpanURI().Filename()),
468		})
469		if err != nil {
470			return err
471		}
472		ver := strings.TrimSpace(stdout.String())
473		return c.s.runGoModUpdateCommands(ctx, deps.snapshot, args.URI.SpanURI(), func(invoke func(...string) (*bytes.Buffer, error)) error {
474			if args.AddRequire {
475				if err := addModuleRequire(invoke, []string{ver}); err != nil {
476					return err
477				}
478			}
479			_, err := invoke(append([]string{"get", "-d"}, args.Pkg)...)
480			return err
481		})
482	})
483}
484
485func (s *Server) runGoModUpdateCommands(ctx context.Context, snapshot source.Snapshot, uri span.URI, run func(invoke func(...string) (*bytes.Buffer, error)) error) error {
486	tmpModfile, newModBytes, newSumBytes, err := snapshot.RunGoCommands(ctx, true, filepath.Dir(uri.Filename()), run)
487	if err != nil {
488		return err
489	}
490	if !tmpModfile {
491		return nil
492	}
493	modURI := snapshot.GoModForFile(uri)
494	sumURI := span.URIFromPath(strings.TrimSuffix(modURI.Filename(), ".mod") + ".sum")
495	modEdits, err := applyFileEdits(ctx, snapshot, modURI, newModBytes)
496	if err != nil {
497		return err
498	}
499	sumEdits, err := applyFileEdits(ctx, snapshot, sumURI, newSumBytes)
500	if err != nil {
501		return err
502	}
503	changes := append(sumEdits, modEdits...)
504	if len(changes) == 0 {
505		return nil
506	}
507	response, err := s.client.ApplyEdit(ctx, &protocol.ApplyWorkspaceEditParams{
508		Edit: protocol.WorkspaceEdit{
509			DocumentChanges: changes,
510		},
511	})
512	if err != nil {
513		return err
514	}
515	if !response.Applied {
516		return fmt.Errorf("edits not applied because of %s", response.FailureReason)
517	}
518	return nil
519}
520
521func applyFileEdits(ctx context.Context, snapshot source.Snapshot, uri span.URI, newContent []byte) ([]protocol.TextDocumentEdit, error) {
522	fh, err := snapshot.GetVersionedFile(ctx, uri)
523	if err != nil {
524		return nil, err
525	}
526	oldContent, err := fh.Read()
527	if err != nil && !os.IsNotExist(err) {
528		return nil, err
529	}
530	if bytes.Equal(oldContent, newContent) {
531		return nil, nil
532	}
533
534	// Sending a workspace edit to a closed file causes VS Code to open the
535	// file and leave it unsaved. We would rather apply the changes directly,
536	// especially to go.sum, which should be mostly invisible to the user.
537	if !snapshot.IsOpen(uri) {
538		err := ioutil.WriteFile(uri.Filename(), newContent, 0666)
539		return nil, err
540	}
541
542	m := &protocol.ColumnMapper{
543		URI:       fh.URI(),
544		Converter: span.NewContentConverter(fh.URI().Filename(), oldContent),
545		Content:   oldContent,
546	}
547	diff, err := snapshot.View().Options().ComputeEdits(uri, string(oldContent), string(newContent))
548	if err != nil {
549		return nil, err
550	}
551	edits, err := source.ToProtocolEdits(m, diff)
552	if err != nil {
553		return nil, err
554	}
555	return []protocol.TextDocumentEdit{{
556		TextDocument: protocol.OptionalVersionedTextDocumentIdentifier{
557			Version: fh.Version(),
558			TextDocumentIdentifier: protocol.TextDocumentIdentifier{
559				URI: protocol.URIFromSpanURI(uri),
560			},
561		},
562		Edits: edits,
563	}}, nil
564}
565
566func runGoGetModule(invoke func(...string) (*bytes.Buffer, error), addRequire bool, args []string) error {
567	if addRequire {
568		if err := addModuleRequire(invoke, args); err != nil {
569			return err
570		}
571	}
572	_, err := invoke(append([]string{"get", "-d"}, args...)...)
573	return err
574}
575
576func addModuleRequire(invoke func(...string) (*bytes.Buffer, error), args []string) error {
577	// Using go get to create a new dependency results in an
578	// `// indirect` comment we may not want. The only way to avoid it
579	// is to add the require as direct first. Then we can use go get to
580	// update go.sum and tidy up.
581	_, err := invoke(append([]string{"mod", "edit", "-require"}, args...)...)
582	return err
583}
584
585func (s *Server) getUpgrades(ctx context.Context, snapshot source.Snapshot, uri span.URI, modules []string) (map[string]string, error) {
586	stdout, err := snapshot.RunGoCommandDirect(ctx, source.Normal|source.AllowNetwork, &gocommand.Invocation{
587		Verb:       "list",
588		Args:       append([]string{"-m", "-u", "-json"}, modules...),
589		WorkingDir: filepath.Dir(uri.Filename()),
590		ModFlag:    "readonly",
591	})
592	if err != nil {
593		return nil, err
594	}
595
596	upgrades := map[string]string{}
597	for dec := json.NewDecoder(stdout); dec.More(); {
598		mod := &gocommand.ModuleJSON{}
599		if err := dec.Decode(mod); err != nil {
600			return nil, err
601		}
602		if mod.Update == nil {
603			continue
604		}
605		upgrades[mod.Path] = mod.Update.Version
606	}
607	return upgrades, nil
608}
609
610func (c *commandHandler) GCDetails(ctx context.Context, uri protocol.DocumentURI) error {
611	return c.ToggleGCDetails(ctx, command.URIArg{URI: uri})
612}
613
614func (c *commandHandler) ToggleGCDetails(ctx context.Context, args command.URIArg) error {
615	return c.run(ctx, commandConfig{
616		requireSave: true,
617		progress:    "Toggling GC Details",
618		forURI:      args.URI,
619	}, func(ctx context.Context, deps commandDeps) error {
620		pkg, err := deps.snapshot.PackageForFile(ctx, deps.fh.URI(), source.TypecheckWorkspace, source.NarrowestPackage)
621		if err != nil {
622			return err
623		}
624		c.s.gcOptimizationDetailsMu.Lock()
625		if _, ok := c.s.gcOptimizationDetails[pkg.ID()]; ok {
626			delete(c.s.gcOptimizationDetails, pkg.ID())
627			c.s.clearDiagnosticSource(gcDetailsSource)
628		} else {
629			c.s.gcOptimizationDetails[pkg.ID()] = struct{}{}
630		}
631		c.s.gcOptimizationDetailsMu.Unlock()
632		c.s.diagnoseSnapshot(deps.snapshot, nil, false)
633		return nil
634	})
635}
636
637func (c *commandHandler) GenerateGoplsMod(ctx context.Context, args command.URIArg) error {
638	// TODO: go back to using URI
639	return c.run(ctx, commandConfig{
640		requireSave: true,
641		progress:    "Generating gopls.mod",
642	}, func(ctx context.Context, deps commandDeps) error {
643		views := c.s.session.Views()
644		if len(views) != 1 {
645			return fmt.Errorf("cannot resolve view: have %d views", len(views))
646		}
647		v := views[0]
648		snapshot, release := v.Snapshot(ctx)
649		defer release()
650		modFile, err := cache.BuildGoplsMod(ctx, snapshot.View().Folder(), snapshot)
651		if err != nil {
652			return errors.Errorf("getting workspace mod file: %w", err)
653		}
654		content, err := modFile.Format()
655		if err != nil {
656			return errors.Errorf("formatting mod file: %w", err)
657		}
658		filename := filepath.Join(snapshot.View().Folder().Filename(), "gopls.mod")
659		if err := ioutil.WriteFile(filename, content, 0644); err != nil {
660			return errors.Errorf("writing mod file: %w", err)
661		}
662		return nil
663	})
664}
665
666func (c *commandHandler) ListKnownPackages(ctx context.Context, args command.URIArg) (command.ListKnownPackagesResult, error) {
667	var result command.ListKnownPackagesResult
668	err := c.run(ctx, commandConfig{
669		progress: "Listing packages",
670		forURI:   args.URI,
671	}, func(ctx context.Context, deps commandDeps) error {
672		var err error
673		result.Packages, err = source.KnownPackages(ctx, deps.snapshot, deps.fh)
674		return err
675	})
676	return result, err
677}
678func (c *commandHandler) AddImport(ctx context.Context, args command.AddImportArgs) error {
679	return c.run(ctx, commandConfig{
680		progress: "Adding import",
681		forURI:   args.URI,
682	}, func(ctx context.Context, deps commandDeps) error {
683		edits, err := source.AddImport(ctx, deps.snapshot, deps.fh, args.ImportPath)
684		if err != nil {
685			return fmt.Errorf("could not add import: %v", err)
686		}
687		if _, err := c.s.client.ApplyEdit(ctx, &protocol.ApplyWorkspaceEditParams{
688			Edit: protocol.WorkspaceEdit{
689				DocumentChanges: documentChanges(deps.fh, edits),
690			},
691		}); err != nil {
692			return fmt.Errorf("could not apply import edits: %v", err)
693		}
694		return nil
695	})
696}
697
698func (c *commandHandler) WorkspaceMetadata(ctx context.Context) (command.WorkspaceMetadataResult, error) {
699	var result command.WorkspaceMetadataResult
700	for _, view := range c.s.session.Views() {
701		result.Workspaces = append(result.Workspaces, command.Workspace{
702			Name:      view.Name(),
703			ModuleDir: view.TempWorkspace().Filename(),
704		})
705	}
706	return result, nil
707}
708
709func (c *commandHandler) StartDebugging(ctx context.Context, args command.DebuggingArgs) (result command.DebuggingResult, _ error) {
710	addr := args.Addr
711	if addr == "" {
712		addr = "localhost:0"
713	}
714	di := debug.GetInstance(ctx)
715	if di == nil {
716		return result, errors.New("internal error: server has no debugging instance")
717	}
718	listenedAddr, err := di.Serve(ctx, addr)
719	if err != nil {
720		return result, errors.Errorf("starting debug server: %w", err)
721	}
722	result.URLs = []string{"http://" + listenedAddr}
723	return result, nil
724}
725