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