internal/lsp: remove Mod handles
Continuing the massacre, remove ParseModHandle, and Mod*Handle, from the
source API.
Notably, having the snapshot available means we can simplify the go
command invocation paths a lot.
Change-Id: Ief4ef41e42f93d653f719a230004861e5e1ef70b
Reviewed-on: https://go-review.googlesource.com/c/tools/+/244769
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/lsp/cache/mod.go b/internal/lsp/cache/mod.go
index 099457f..f31fa4f 100644
--- a/internal/lsp/cache/mod.go
+++ b/internal/lsp/cache/mod.go
@@ -29,44 +29,31 @@
type parseModHandle struct {
handle *memoize.Handle
-
- mod, sum source.FileHandle
}
type parseModData struct {
memoize.NoCopy
- parsed *modfile.File
- m *protocol.ColumnMapper
-
- // parseErrors refers to syntax errors found in the go.mod file.
- parseErrors []source.Error
+ parsed *source.ParsedModule
// err is any error encountered while parsing the file.
err error
}
-func (mh *parseModHandle) Mod() source.FileHandle {
- return mh.mod
-}
-
-func (mh *parseModHandle) Sum() source.FileHandle {
- return mh.sum
-}
-
-func (mh *parseModHandle) Parse(ctx context.Context, s source.Snapshot) (*modfile.File, *protocol.ColumnMapper, []source.Error, error) {
+func (mh *parseModHandle) parse(ctx context.Context, s source.Snapshot) (*source.ParsedModule, error) {
v, err := mh.handle.Get(ctx, s.(*snapshot))
if err != nil {
- return nil, nil, nil, err
+ return nil, err
}
data := v.(*parseModData)
- return data.parsed, data.m, data.parseErrors, data.err
+ return data.parsed, data.err
}
-func (s *snapshot) ParseModHandle(ctx context.Context, modFH source.FileHandle) (source.ParseModHandle, error) {
+func (s *snapshot) ParseMod(ctx context.Context, modFH source.FileHandle) (*source.ParsedModule, error) {
if handle := s.getModHandle(modFH.URI()); handle != nil {
- return handle, nil
+ return handle.parse(ctx, s)
}
+
h := s.view.session.cache.store.Bind(modFH.Identity().String(), func(ctx context.Context, _ memoize.Arg) interface{} {
_, done := event.Start(ctx, "cache.ParseModHandle", tag.URI.Of(modFH.URI()))
defer done()
@@ -80,55 +67,52 @@
Converter: span.NewContentConverter(modFH.URI().Filename(), contents),
Content: contents,
}
- parsed, err := modfile.Parse(modFH.URI().Filename(), contents, nil)
- if err != nil {
- parseErr, _ := extractModParseErrors(modFH.URI(), m, err, contents)
- var parseErrors []source.Error
- if parseErr != nil {
- parseErrors = append(parseErrors, *parseErr)
- }
- return &parseModData{
- parseErrors: parseErrors,
- err: err,
+ data := &parseModData{
+ parsed: &source.ParsedModule{
+ Mapper: m,
+ },
+ }
+ data.parsed.File, data.err = modfile.Parse(modFH.URI().Filename(), contents, nil)
+ if data.err != nil {
+ // Attempt to convert the error to a non-fatal parse error.
+ if parseErr, extractErr := extractModParseErrors(modFH.URI(), m, data.err, contents); extractErr == nil {
+ data.err = nil
+ data.parsed.ParseErrors = []source.Error{*parseErr}
}
}
- return &parseModData{
- parsed: parsed,
- m: m,
- }
+ return data
})
+
+ pmh := &parseModHandle{handle: h}
+ s.mu.Lock()
+ s.parseModHandles[modFH.URI()] = pmh
+ s.mu.Unlock()
+
+ return pmh.parse(ctx, s)
+}
+
+func (s *snapshot) sumFH(ctx context.Context, modFH source.FileHandle) (source.FileHandle, error) {
// Get the go.sum file, either from the snapshot or directly from the
// cache. Avoid (*snapshot).GetFile here, as we don't want to add
// nonexistent file handles to the snapshot if the file does not exist.
sumURI := span.URIFromPath(sumFilename(modFH.URI()))
sumFH := s.FindFile(sumURI)
if sumFH == nil {
- fh, err := s.view.session.cache.getFile(ctx, sumURI)
- if err != nil && !os.IsNotExist(err) {
+ var err error
+ sumFH, err = s.view.session.cache.getFile(ctx, sumURI)
+ if err != nil {
return nil, err
}
- if fh.err != nil && !os.IsNotExist(fh.err) {
- return nil, fh.err
- }
- // If the file doesn't exist, we can just keep the go.sum nil.
- if err != nil || fh.err != nil {
- sumFH = nil
- } else {
- sumFH = fh
- }
}
- s.mu.Lock()
- defer s.mu.Unlock()
- s.parseModHandles[modFH.URI()] = &parseModHandle{
- handle: h,
- mod: modFH,
- sum: sumFH,
+ _, err := sumFH.Read()
+ if err != nil {
+ return nil, err
}
- return s.parseModHandles[modFH.URI()], nil
+ return sumFH, nil
}
func sumFilename(modURI span.URI) string {
- return modURI.Filename()[:len(modURI.Filename())-len("mod")] + "sum"
+ return strings.TrimSuffix(modURI.Filename(), ".mod") + ".sum"
}
// extractModParseErrors processes the raw errors returned by modfile.Parse,
@@ -197,7 +181,7 @@
err error
}
-func (mwh *modWhyHandle) Why(ctx context.Context, s source.Snapshot) (map[string]string, error) {
+func (mwh *modWhyHandle) why(ctx context.Context, s source.Snapshot) (map[string]string, error) {
v, err := mwh.handle.Get(ctx, s.(*snapshot))
if err != nil {
return nil, err
@@ -206,7 +190,7 @@
return data.why, data.err
}
-func (s *snapshot) ModWhyHandle(ctx context.Context) (source.ModWhyHandle, error) {
+func (s *snapshot) ModWhy(ctx context.Context) (map[string]string, error) {
if err := s.awaitLoaded(ctx); err != nil {
return nil, err
}
@@ -214,7 +198,6 @@
if err != nil {
return nil, err
}
- cfg := s.config(ctx)
key := modKey{
sessionID: s.view.session.id,
cfg: hashConfig(s.config(ctx)),
@@ -228,46 +211,42 @@
snapshot := arg.(*snapshot)
- pmh, err := snapshot.ParseModHandle(ctx, fh)
- if err != nil {
- return &modWhyData{err: err}
- }
-
- parsed, _, _, err := pmh.Parse(ctx, snapshot)
+ pm, err := snapshot.ParseMod(ctx, fh)
if err != nil {
return &modWhyData{err: err}
}
// No requires to explain.
- if len(parsed.Require) == 0 {
+ if len(pm.File.Require) == 0 {
return &modWhyData{}
}
// Run `go mod why` on all the dependencies.
args := []string{"why", "-m"}
- for _, req := range parsed.Require {
+ for _, req := range pm.File.Require {
args = append(args, req.Mod.Path)
}
- _, stdout, err := runGoCommand(ctx, cfg, pmh, snapshot.view.tmpMod, "mod", args)
+ stdout, err := snapshot.RunGoCommand(ctx, "mod", args)
if err != nil {
return &modWhyData{err: err}
}
whyList := strings.Split(stdout.String(), "\n\n")
- if len(whyList) != len(parsed.Require) {
+ if len(whyList) != len(pm.File.Require) {
return &modWhyData{
- err: fmt.Errorf("mismatched number of results: got %v, want %v", len(whyList), len(parsed.Require)),
+ err: fmt.Errorf("mismatched number of results: got %v, want %v", len(whyList), len(pm.File.Require)),
}
}
- why := make(map[string]string, len(parsed.Require))
- for i, req := range parsed.Require {
+ why := make(map[string]string, len(pm.File.Require))
+ for i, req := range pm.File.Require {
why[req.Mod.Path] = whyList[i]
}
return &modWhyData{why: why}
})
+
+ mwh := &modWhyHandle{handle: h}
s.mu.Lock()
- defer s.mu.Unlock()
- s.modWhyHandle = &modWhyHandle{
- handle: h,
- }
- return s.modWhyHandle, nil
+ s.modWhyHandle = mwh
+ s.mu.Unlock()
+
+ return s.modWhyHandle.why(ctx, s)
}
type modUpgradeHandle struct {
@@ -290,7 +269,7 @@
return data.upgrades, data.err
}
-func (s *snapshot) ModUpgradeHandle(ctx context.Context) (source.ModUpgradeHandle, error) {
+func (s *snapshot) ModUpgrade(ctx context.Context) (map[string]string, error) {
if err := s.awaitLoaded(ctx); err != nil {
return nil, err
}
@@ -312,28 +291,24 @@
snapshot := arg.(*snapshot)
- pmh, err := s.ParseModHandle(ctx, fh)
+ pm, err := s.ParseMod(ctx, fh)
if err != nil {
return &modUpgradeData{err: err}
}
- parsed, _, _, err := pmh.Parse(ctx, snapshot)
- if err != nil {
- return &modUpgradeData{err: err}
- }
// No requires to upgrade.
- if len(parsed.Require) == 0 {
+ if len(pm.File.Require) == 0 {
return &modUpgradeData{}
}
// Run "go list -mod readonly -u -m all" to be able to see which deps can be
// upgraded without modifying mod file.
args := []string{"-u", "-m", "all"}
- if !snapshot.view.tmpMod || containsVendor(pmh.Mod().URI()) {
+ if !snapshot.view.tmpMod || containsVendor(fh.URI()) {
// Use -mod=readonly if the module contains a vendor directory
// (see golang/go#38711).
args = append([]string{"-mod", "readonly"}, args...)
}
- _, stdout, err := runGoCommand(ctx, cfg, pmh, snapshot.view.tmpMod, "list", args)
+ stdout, err := snapshot.RunGoCommand(ctx, "list", args)
if err != nil {
return &modUpgradeData{err: err}
}
@@ -364,12 +339,12 @@
upgrades: upgrades,
}
})
+ muh := &modUpgradeHandle{handle: h}
s.mu.Lock()
- defer s.mu.Unlock()
- s.modUpgradeHandle = &modUpgradeHandle{
- handle: h,
- }
- return s.modUpgradeHandle, nil
+ s.modUpgradeHandle = muh
+ s.mu.Unlock()
+
+ return s.modUpgradeHandle.Upgrades(ctx, s)
}
// containsVendor reports whether the module has a vendor folder.
diff --git a/internal/lsp/cache/mod_tidy.go b/internal/lsp/cache/mod_tidy.go
index 3a96fc0..2ecfc77 100644
--- a/internal/lsp/cache/mod_tidy.go
+++ b/internal/lsp/cache/mod_tidy.go
@@ -19,7 +19,6 @@
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/lsp/source"
"golang.org/x/tools/internal/memoize"
- "golang.org/x/tools/internal/packagesinternal"
"golang.org/x/tools/internal/span"
)
@@ -34,57 +33,32 @@
type modTidyHandle struct {
handle *memoize.Handle
-
- pmh source.ParseModHandle
}
type modTidyData struct {
memoize.NoCopy
- // tidiedContent is the content of the tidied file.
- tidiedContent []byte
-
- // diagnostics are any errors and associated suggested fixes for
- // the go.mod file.
- diagnostics []source.Error
-
- err error
+ tidied *source.TidiedModule
+ err error
}
-func (mth *modTidyHandle) ParseModHandle() source.ParseModHandle {
- return mth.pmh
-}
-
-func (mth *modTidyHandle) Tidy(ctx context.Context, s source.Snapshot) ([]source.Error, error) {
+func (mth *modTidyHandle) tidy(ctx context.Context, s source.Snapshot) (*source.TidiedModule, error) {
v, err := mth.handle.Get(ctx, s.(*snapshot))
if err != nil {
return nil, err
}
data := v.(*modTidyData)
- return data.diagnostics, data.err
+ return data.tidied, data.err
}
-func (mth *modTidyHandle) TidiedContent(ctx context.Context, s source.Snapshot) ([]byte, error) {
- v, err := mth.handle.Get(ctx, s.(*snapshot))
- if err != nil {
- return nil, err
- }
- data := v.(*modTidyData)
- return data.tidiedContent, data.err
-}
-
-func (s *snapshot) ModTidyHandle(ctx context.Context) (source.ModTidyHandle, error) {
+func (s *snapshot) ModTidy(ctx context.Context) (*source.TidiedModule, error) {
if !s.view.tmpMod {
return nil, source.ErrTmpModfileUnsupported
}
if handle := s.getModTidyHandle(); handle != nil {
- return handle, nil
+ return handle.tidy(ctx, s)
}
- fh, err := s.GetFile(ctx, s.view.modURI)
- if err != nil {
- return nil, err
- }
- pmh, err := s.ParseModHandle(ctx, fh)
+ modFH, err := s.GetFile(ctx, s.view.modURI)
if err != nil {
return nil, err
}
@@ -111,7 +85,7 @@
view: s.view.root.Filename(),
imports: importHash,
unsavedOverlays: overlayHash,
- gomod: pmh.Mod().Identity().String(),
+ gomod: modFH.Identity().String(),
cfg: hashConfig(cfg),
}
h := s.view.session.cache.store.Bind(key, func(ctx context.Context, arg memoize.Arg) interface{} {
@@ -119,21 +93,26 @@
defer done()
snapshot := arg.(*snapshot)
- original, m, parseErrors, err := pmh.Parse(ctx, snapshot)
- if err != nil || len(parseErrors) > 0 {
+ pm, err := snapshot.ParseMod(ctx, modFH)
+ if err != nil {
+ return &modTidyData{err: err}
+ }
+ if len(pm.ParseErrors) > 0 {
return &modTidyData{
- diagnostics: parseErrors,
- err: err,
+ tidied: &source.TidiedModule{
+ Parsed: pm,
+ },
+ err: fmt.Errorf("could not parse module to tidy: %v", pm.ParseErrors),
}
}
- tmpURI, inv, cleanup, err := goCommandInvocation(ctx, cfg, pmh, "mod", []string{"tidy"})
+ tmpURI, runner, inv, cleanup, err := snapshot.goCommandInvocation(ctx, true, "mod", []string{"tidy"})
if err != nil {
return &modTidyData{err: err}
}
// Keep the temporary go.mod file around long enough to parse it.
defer cleanup()
- if _, err := packagesinternal.GetGoCmdRunner(cfg).Run(ctx, *inv); err != nil {
+ if _, err := runner.Run(ctx, *inv); err != nil {
return &modTidyData{err: err}
}
// Go directly to disk to get the temporary mod file, since it is
@@ -150,9 +129,9 @@
}
// Get the dependencies that are different between the original and
// ideal go.mod files.
- unusedDeps := make(map[string]*modfile.Require, len(original.Require))
+ unusedDeps := make(map[string]*modfile.Require, len(pm.File.Require))
missingDeps := make(map[string]*modfile.Require, len(ideal.Require))
- for _, req := range original.Require {
+ for _, req := range pm.File.Require {
unusedDeps[req.Mod.Path] = req
}
for _, req := range ideal.Require {
@@ -166,7 +145,7 @@
// First, compute any errors specific to the go.mod file. These include
// unused dependencies and modules with incorrect // indirect comments.
/// Both the diagnostic and the fix will appear on the go.mod file.
- modRequireErrs, err := modRequireErrors(m, missingDeps, unusedDeps, options)
+ modRequireErrs, err := modRequireErrors(pm.Mapper, missingDeps, unusedDeps, options)
if err != nil {
return &modTidyData{err: err}
}
@@ -179,22 +158,25 @@
// go.mod file. The fixes will be for the go.mod file, but the
// diagnostics should appear on the import statements in the Go or
// go.mod files.
- missingModuleErrs, err := missingModuleErrors(ctx, snapshot, m, workspacePkgs, ideal.Require, missingDeps, original, options)
+ missingModuleErrs, err := missingModuleErrors(ctx, snapshot, pm.Mapper, workspacePkgs, ideal.Require, missingDeps, pm.File, options)
if err != nil {
return &modTidyData{err: err}
}
return &modTidyData{
- tidiedContent: tempContents,
- diagnostics: append(modRequireErrs, missingModuleErrs...),
+ tidied: &source.TidiedModule{
+ Parsed: pm,
+ TidiedContent: tempContents,
+ Errors: append(modRequireErrs, missingModuleErrs...),
+ },
}
})
+
+ mth := &modTidyHandle{handle: h}
s.mu.Lock()
- defer s.mu.Unlock()
- s.modTidyHandle = &modTidyHandle{
- handle: h,
- pmh: pmh,
- }
- return s.modTidyHandle, nil
+ s.modTidyHandle = mth
+ s.mu.Unlock()
+
+ return mth.tidy(ctx, s)
}
func hashImports(ctx context.Context, wsPackages []source.Package) (string, error) {
diff --git a/internal/lsp/cache/snapshot.go b/internal/lsp/cache/snapshot.go
index d9fb962..6ea8e4f 100644
--- a/internal/lsp/cache/snapshot.go
+++ b/internal/lsp/cache/snapshot.go
@@ -142,83 +142,55 @@
}
func (s *snapshot) RunGoCommandDirect(ctx context.Context, verb string, args []string) error {
- cfg := s.config(ctx)
- _, _, err := runGoCommand(ctx, cfg, nil, s.view.tmpMod, verb, args)
- return err
-}
-
-func (s *snapshot) RunGoCommand(ctx context.Context, verb string, args []string) (*bytes.Buffer, error) {
- cfg := s.config(ctx)
- var pmh source.ParseModHandle
- if s.view.tmpMod {
- modFH, err := s.GetFile(ctx, s.view.modURI)
- if err != nil {
- return nil, err
- }
- pmh, err = s.ParseModHandle(ctx, modFH)
- if err != nil {
- return nil, err
- }
- }
- _, stdout, err := runGoCommand(ctx, cfg, pmh, s.view.tmpMod, verb, args)
- return stdout, err
-}
-
-func (s *snapshot) RunGoCommandPiped(ctx context.Context, verb string, args []string, stdout, stderr io.Writer) error {
- cfg := s.config(ctx)
- var pmh source.ParseModHandle
- if s.view.tmpMod {
- modFH, err := s.GetFile(ctx, s.view.modURI)
- if err != nil {
- return err
- }
- pmh, err = s.ParseModHandle(ctx, modFH)
- if err != nil {
- return err
- }
- }
- _, inv, cleanup, err := goCommandInvocation(ctx, cfg, pmh, verb, args)
+ _, runner, inv, cleanup, err := s.goCommandInvocation(ctx, false, verb, args)
if err != nil {
return err
}
defer cleanup()
- runner := packagesinternal.GetGoCmdRunner(cfg)
- return runner.RunPiped(ctx, *inv, stdout, stderr)
+ _, err = runner.Run(ctx, *inv)
+ return err
}
-// runGoCommand runs the given go command with the given config.
-// The given go.mod file is used to construct the temporary go.mod file, which
-// is then passed to the go command via the BuildFlags.
-// It assumes that modURI is only provided when the -modfile flag is enabled.
-func runGoCommand(ctx context.Context, cfg *packages.Config, pmh source.ParseModHandle, tmpMod bool, verb string, args []string) (span.URI, *bytes.Buffer, error) {
- // Don't pass in the ParseModHandle if we are not using the -modfile flag.
- var tmpPMH source.ParseModHandle
- if tmpMod {
- tmpPMH = pmh
- }
- tmpURI, inv, cleanup, err := goCommandInvocation(ctx, cfg, tmpPMH, verb, args)
+func (s *snapshot) RunGoCommand(ctx context.Context, verb string, args []string) (*bytes.Buffer, error) {
+ _, runner, inv, cleanup, err := s.goCommandInvocation(ctx, true, verb, args)
if err != nil {
- return "", nil, err
+ return nil, err
}
defer cleanup()
- runner := packagesinternal.GetGoCmdRunner(cfg)
- stdout, err := runner.Run(ctx, *inv)
- return tmpURI, stdout, err
+ return runner.Run(ctx, *inv)
+}
+
+func (s *snapshot) RunGoCommandPiped(ctx context.Context, verb string, args []string, stdout, stderr io.Writer) error {
+ _, runner, inv, cleanup, err := s.goCommandInvocation(ctx, true, verb, args)
+ if err != nil {
+ return err
+ }
+ defer cleanup()
+ return runner.RunPiped(ctx, *inv, stdout, stderr)
}
// Assumes that modURI is only provided when the -modfile flag is enabled.
-func goCommandInvocation(ctx context.Context, cfg *packages.Config, pmh source.ParseModHandle, verb string, args []string) (tmpURI span.URI, inv *gocommand.Invocation, cleanup func(), err error) {
+func (s *snapshot) goCommandInvocation(ctx context.Context, allowTempModfile bool, verb string, args []string) (tmpURI span.URI, runner *gocommand.Runner, inv *gocommand.Invocation, cleanup func(), err error) {
cleanup = func() {} // fallback
- if pmh != nil {
- tmpURI, cleanup, err = tempModFile(pmh.Mod(), pmh.Sum())
+ cfg := s.config(ctx)
+ if allowTempModfile && s.view.tmpMod {
+ modFH, err := s.GetFile(ctx, s.view.modURI)
if err != nil {
- return "", nil, nil, err
+ return "", nil, nil, cleanup, err
+ }
+ // Use the go.sum if it happens to be available.
+ sumFH, _ := s.sumFH(ctx, modFH)
+
+ tmpURI, cleanup, err = tempModFile(modFH, sumFH)
+ if err != nil {
+ return "", nil, nil, cleanup, err
}
cfg.BuildFlags = append(cfg.BuildFlags, fmt.Sprintf("-modfile=%s", tmpURI.Filename()))
}
- return tmpURI, &gocommand.Invocation{
+ runner = packagesinternal.GetGoCmdRunner(cfg)
+ return tmpURI, runner, &gocommand.Invocation{
Verb: verb,
Args: args,
Env: cfg.Env,
diff --git a/internal/lsp/cache/view.go b/internal/lsp/cache/view.go
index cbe70ec..72674d9 100644
--- a/internal/lsp/cache/view.go
+++ b/internal/lsp/cache/view.go
@@ -562,15 +562,11 @@
if err != nil {
return nil, err
}
- pmh, err := v.Snapshot().ParseModHandle(ctx, fh)
+ pm, err := v.Snapshot().ParseMod(ctx, fh)
if err != nil {
return nil, err
}
- parsed, _, _, err := pmh.Parse(ctx, v.Snapshot())
- if err != nil {
- return nil, err
- }
- for _, replace := range parsed.Replace {
+ for _, replace := range pm.File.Replace {
dirs = append(dirs, replace.New.Path)
}
return dirs, nil
diff --git a/internal/lsp/code_action.go b/internal/lsp/code_action.go
index bd19f55..850a466 100644
--- a/internal/lsp/code_action.go
+++ b/internal/lsp/code_action.go
@@ -454,20 +454,19 @@
}
func moduleQuickFixes(ctx context.Context, snapshot source.Snapshot, diagnostics []protocol.Diagnostic) ([]protocol.CodeAction, error) {
- mth, err := snapshot.ModTidyHandle(ctx)
+ modFH, err := snapshot.GetFile(ctx, snapshot.View().ModFile())
+ if err != nil {
+ return nil, err
+ }
+ tidied, err := snapshot.ModTidy(ctx)
if err == source.ErrTmpModfileUnsupported {
return nil, nil
}
if err != nil {
return nil, err
}
- errors, err := mth.Tidy(ctx, snapshot)
- if err != nil {
- return nil, err
- }
- pmh := mth.ParseModHandle()
var quickFixes []protocol.CodeAction
- for _, e := range errors {
+ for _, e := range tidied.Errors {
var diag *protocol.Diagnostic
for _, d := range diagnostics {
if sameDiagnostic(d, e) {
@@ -486,14 +485,14 @@
Edit: protocol.WorkspaceEdit{},
}
for uri, edits := range fix.Edits {
- if uri != pmh.Mod().URI() {
+ if uri != modFH.URI() {
continue
}
action.Edit.DocumentChanges = append(action.Edit.DocumentChanges, protocol.TextDocumentEdit{
TextDocument: protocol.VersionedTextDocumentIdentifier{
- Version: pmh.Mod().Version(),
+ Version: modFH.Version(),
TextDocumentIdentifier: protocol.TextDocumentIdentifier{
- URI: protocol.URIFromSpanURI(pmh.Mod().URI()),
+ URI: protocol.URIFromSpanURI(modFH.URI()),
},
},
Edits: edits,
@@ -510,25 +509,21 @@
}
func goModTidy(ctx context.Context, snapshot source.Snapshot) (*protocol.CodeAction, error) {
- mth, err := snapshot.ModTidyHandle(ctx)
+ tidied, err := snapshot.ModTidy(ctx)
if err != nil {
return nil, err
}
- uri := mth.ParseModHandle().Mod().URI()
- _, m, _, err := mth.ParseModHandle().Parse(ctx, snapshot)
+ modFH, err := snapshot.GetFile(ctx, snapshot.View().ModFile())
if err != nil {
return nil, err
}
- left, err := mth.ParseModHandle().Mod().Read()
+ left, err := modFH.Read()
if err != nil {
return nil, err
}
- right, err := mth.TidiedContent(ctx, snapshot)
- if err != nil {
- return nil, err
- }
- edits := snapshot.View().Options().ComputeEdits(uri, string(left), string(right))
- protocolEdits, err := source.ToProtocolEdits(m, edits)
+ right := tidied.TidiedContent
+ edits := snapshot.View().Options().ComputeEdits(modFH.URI(), string(left), string(right))
+ protocolEdits, err := source.ToProtocolEdits(tidied.Parsed.Mapper, edits)
if err != nil {
return nil, err
}
@@ -538,9 +533,9 @@
Edit: protocol.WorkspaceEdit{
DocumentChanges: []protocol.TextDocumentEdit{{
TextDocument: protocol.VersionedTextDocumentIdentifier{
- Version: mth.ParseModHandle().Mod().Version(),
+ Version: modFH.Version(),
TextDocumentIdentifier: protocol.TextDocumentIdentifier{
- URI: protocol.URIFromSpanURI(uri),
+ URI: protocol.URIFromSpanURI(modFH.URI()),
},
},
Edits: protocolEdits,
diff --git a/internal/lsp/link.go b/internal/lsp/link.go
index a56e673..35caccc 100644
--- a/internal/lsp/link.go
+++ b/internal/lsp/link.go
@@ -46,16 +46,12 @@
func modLinks(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle) ([]protocol.DocumentLink, error) {
view := snapshot.View()
- pmh, err := snapshot.ParseModHandle(ctx, fh)
- if err != nil {
- return nil, err
- }
- file, m, _, err := pmh.Parse(ctx, snapshot)
+ pm, err := snapshot.ParseMod(ctx, fh)
if err != nil {
return nil, err
}
var links []protocol.DocumentLink
- for _, req := range file.Require {
+ for _, req := range pm.File.Require {
if req.Syntax == nil {
continue
}
@@ -65,7 +61,7 @@
}
dep := []byte(req.Mod.Path)
s, e := req.Syntax.Start.Byte, req.Syntax.End.Byte
- i := bytes.Index(m.Content[s:e], dep)
+ i := bytes.Index(pm.Mapper.Content[s:e], dep)
if i == -1 {
continue
}
@@ -73,25 +69,25 @@
// dependency within the require statement.
start, end := token.Pos(s+i), token.Pos(s+i+len(dep))
target := fmt.Sprintf("https://%s/mod/%s", view.Options().LinkTarget, req.Mod.String())
- l, err := toProtocolLink(view, m, target, start, end, source.Mod)
+ l, err := toProtocolLink(view, pm.Mapper, target, start, end, source.Mod)
if err != nil {
return nil, err
}
links = append(links, l)
}
// TODO(ridersofrohan): handle links for replace and exclude directives.
- if syntax := file.Syntax; syntax == nil {
+ if syntax := pm.File.Syntax; syntax == nil {
return links, nil
}
// Get all the links that are contained in the comments of the file.
- for _, expr := range file.Syntax.Stmt {
+ for _, expr := range pm.File.Syntax.Stmt {
comments := expr.Comment()
if comments == nil {
continue
}
for _, section := range [][]modfile.Comment{comments.Before, comments.Suffix, comments.After} {
for _, comment := range section {
- l, err := findLinksInString(ctx, view, comment.Token, token.Pos(comment.Start.Byte), m, source.Mod)
+ l, err := findLinksInString(ctx, view, comment.Token, token.Pos(comment.Start.Byte), pm.Mapper, source.Mod)
if err != nil {
return nil, err
}
diff --git a/internal/lsp/mod/code_lens.go b/internal/lsp/mod/code_lens.go
index 5f0a0b4..13afd61 100644
--- a/internal/lsp/mod/code_lens.go
+++ b/internal/lsp/mod/code_lens.go
@@ -28,19 +28,11 @@
if err != nil {
return nil, err
}
- pmh, err := snapshot.ParseModHandle(ctx, fh)
+ pm, err := snapshot.ParseMod(ctx, fh)
if err != nil {
return nil, err
}
- file, m, _, err := pmh.Parse(ctx, snapshot)
- if err != nil {
- return nil, err
- }
- muh, err := snapshot.ModUpgradeHandle(ctx)
- if err != nil {
- return nil, err
- }
- upgrades, err := muh.Upgrades(ctx, snapshot)
+ upgrades, err := snapshot.ModUpgrade(ctx)
if err != nil {
return nil, err
}
@@ -48,14 +40,14 @@
codelens []protocol.CodeLens
allUpgrades []string
)
- for _, req := range file.Require {
+ for _, req := range pm.File.Require {
dep := req.Mod.Path
latest, ok := upgrades[dep]
if !ok {
continue
}
// Get the range of the require directive.
- rng, err := positionsToRange(uri, m, req.Syntax.Start, req.Syntax.End)
+ rng, err := positionsToRange(uri, pm.Mapper, req.Syntax.Start, req.Syntax.End)
if err != nil {
return nil, err
}
@@ -74,9 +66,9 @@
allUpgrades = append(allUpgrades, dep)
}
// If there is at least 1 upgrade, add an "Upgrade all dependencies" to the module statement.
- if module := file.Module; len(allUpgrades) > 0 && module != nil && module.Syntax != nil {
+ if module := pm.File.Module; len(allUpgrades) > 0 && module != nil && module.Syntax != nil {
// Get the range of the module directive.
- rng, err := positionsToRange(uri, m, module.Syntax.Start, module.Syntax.End)
+ rng, err := positionsToRange(uri, pm.Mapper, module.Syntax.Start, module.Syntax.End)
if err != nil {
return nil, err
}
diff --git a/internal/lsp/mod/diagnostics.go b/internal/lsp/mod/diagnostics.go
index 878294d..43fd95e 100644
--- a/internal/lsp/mod/diagnostics.go
+++ b/internal/lsp/mod/diagnostics.go
@@ -34,7 +34,7 @@
if err != nil {
return nil, err
}
- mth, err := snapshot.ModTidyHandle(ctx)
+ tidied, err := snapshot.ModTidy(ctx)
if err == source.ErrTmpModfileUnsupported {
return nil, nil
}
@@ -44,11 +44,7 @@
if err != nil {
return nil, err
}
- diagnostics, err := mth.Tidy(ctx, snapshot)
- if err != nil {
- return nil, err
- }
- for _, e := range diagnostics {
+ for _, e := range tidied.Errors {
diag := &source.Diagnostic{
Message: e.Message,
Range: e.Range,
@@ -98,16 +94,12 @@
break
}
}
- pmh, err := snapshot.ParseModHandle(ctx, fh)
- if err != nil {
- return nil, err
- }
- parsed, m, _, err := pmh.Parse(ctx, snapshot)
+ pm, err := snapshot.ParseMod(ctx, fh)
if err != nil {
return nil, err
}
toDiagnostic := func(line *modfile.Line) (*source.Diagnostic, error) {
- rng, err := rangeFromPositions(fh.URI(), m, line.Start, line.End)
+ rng, err := rangeFromPositions(fh.URI(), pm.Mapper, line.Start, line.End)
if err != nil {
return nil, err
}
@@ -119,19 +111,19 @@
}
// Check if there are any require, exclude, or replace statements that
// match this module version.
- for _, req := range parsed.Require {
+ for _, req := range pm.File.Require {
if req.Mod != v {
continue
}
return toDiagnostic(req.Syntax)
}
- for _, ex := range parsed.Exclude {
+ for _, ex := range pm.File.Exclude {
if ex.Mod != v {
continue
}
return toDiagnostic(ex.Syntax)
}
- for _, rep := range parsed.Replace {
+ for _, rep := range pm.File.Replace {
if rep.New != v && rep.Old != v {
continue
}
@@ -139,7 +131,7 @@
}
// No match for the module path was found in the go.mod file.
// Show the error on the module declaration.
- return toDiagnostic(parsed.Module.Syntax)
+ return toDiagnostic(pm.File.Module.Syntax)
}
func rangeFromPositions(uri span.URI, m *protocol.ColumnMapper, s, e modfile.Position) (protocol.Range, error) {
diff --git a/internal/lsp/mod/format.go b/internal/lsp/mod/format.go
index 5d73f04..da97ca2 100644
--- a/internal/lsp/mod/format.go
+++ b/internal/lsp/mod/format.go
@@ -12,19 +12,15 @@
ctx, done := event.Start(ctx, "mod.Format")
defer done()
- pmh, err := snapshot.ParseModHandle(ctx, fh)
+ pm, err := snapshot.ParseMod(ctx, fh)
if err != nil {
return nil, err
}
- file, m, _, err := pmh.Parse(ctx, snapshot)
- if err != nil {
- return nil, err
- }
- formatted, err := file.Format()
+ formatted, err := pm.File.Format()
if err != nil {
return nil, err
}
// Calculate the edits to be made due to the change.
- diff := snapshot.View().Options().ComputeEdits(fh.URI(), string(m.Content), string(formatted))
- return source.ToProtocolEdits(m, diff)
+ diff := snapshot.View().Options().ComputeEdits(fh.URI(), string(pm.Mapper.Content), string(formatted))
+ return source.ToProtocolEdits(pm.Mapper, diff)
}
diff --git a/internal/lsp/mod/hover.go b/internal/lsp/mod/hover.go
index b664714..f0fa26b 100644
--- a/internal/lsp/mod/hover.go
+++ b/internal/lsp/mod/hover.go
@@ -26,19 +26,15 @@
defer done()
// Get the position of the cursor.
- pmh, err := snapshot.ParseModHandle(ctx, fh)
+ pm, err := snapshot.ParseMod(ctx, fh)
if err != nil {
return nil, fmt.Errorf("getting modfile handle: %w", err)
}
- file, m, _, err := pmh.Parse(ctx, snapshot)
- if err != nil {
- return nil, err
- }
- spn, err := m.PointSpan(position)
+ spn, err := pm.Mapper.PointSpan(position)
if err != nil {
return nil, fmt.Errorf("computing cursor position: %w", err)
}
- hoverRng, err := spn.Range(m.Converter)
+ hoverRng, err := spn.Range(pm.Mapper.Converter)
if err != nil {
return nil, fmt.Errorf("computing hover range: %w", err)
}
@@ -46,10 +42,10 @@
// Confirm that the cursor is at the position of a require statement.
var req *modfile.Require
var startPos, endPos int
- for _, r := range file.Require {
+ for _, r := range pm.File.Require {
dep := []byte(r.Mod.Path)
s, e := r.Syntax.Start.Byte, r.Syntax.End.Byte
- i := bytes.Index(m.Content[s:e], dep)
+ i := bytes.Index(pm.Mapper.Content[s:e], dep)
if i == -1 {
continue
}
@@ -68,37 +64,30 @@
}
// Get the `go mod why` results for the given file.
- mwh, err := snapshot.ModWhyHandle(ctx)
+ why, err := snapshot.ModWhy(ctx)
if err != nil {
return nil, err
}
- why, err := mwh.Why(ctx, snapshot)
- if err != nil {
- return nil, fmt.Errorf("running go mod why: %w", err)
- }
- if why == nil {
- return nil, nil
- }
explanation, ok := why[req.Mod.Path]
if !ok {
return nil, nil
}
// Get the range to highlight for the hover.
- line, col, err := m.Converter.ToPosition(startPos)
+ line, col, err := pm.Mapper.Converter.ToPosition(startPos)
if err != nil {
return nil, err
}
start := span.NewPoint(line, col, startPos)
- line, col, err = m.Converter.ToPosition(endPos)
+ line, col, err = pm.Mapper.Converter.ToPosition(endPos)
if err != nil {
return nil, err
}
end := span.NewPoint(line, col, endPos)
spn = span.New(fh.URI(), start, end)
- rng, err := m.Range(spn)
+ rng, err := pm.Mapper.Range(spn)
if err != nil {
return nil, err
}
diff --git a/internal/lsp/source/view.go b/internal/lsp/source/view.go
index db76226..1aed9f5 100644
--- a/internal/lsp/source/view.go
+++ b/internal/lsp/source/view.go
@@ -74,23 +74,17 @@
// -modfile flag.
RunGoCommandDirect(ctx context.Context, verb string, args []string) error
- // ParseModHandle is used to parse go.mod files.
- ParseModHandle(ctx context.Context, fh FileHandle) (ParseModHandle, error)
+ // ParseMod is used to parse go.mod files.
+ ParseMod(ctx context.Context, fh FileHandle) (*ParsedModule, error)
- // ModWhyHandle is used get the results of `go mod why` for a given module.
- // It only works for go.mod files that can be parsed, hence it takes a
- // ParseModHandle.
- ModWhyHandle(ctx context.Context) (ModWhyHandle, error)
+ // ModWhy returns the results of `go mod why` for the snapshot's module.
+ ModWhy(ctx context.Context) (map[string]string, error)
- // ModWhyHandle is used get the possible upgrades for the dependencies of
- // a given module. It only works for go.mod files that can be parsed, hence
- // it takes a ParseModHandle.
- ModUpgradeHandle(ctx context.Context) (ModUpgradeHandle, error)
+ // ModUpgrade returns the possible updates for the snapshot's module.
+ ModUpgrade(ctx context.Context) (map[string]string, error)
- // ModWhyHandle is used get the results of `go mod tidy` for a given
- // module. It only works for go.mod files that can be parsed, hence it
- // takes a ParseModHandle.
- ModTidyHandle(ctx context.Context) (ModTidyHandle, error)
+ // ModTidy returns the results of `go mod tidy` for the snapshot's module.
+ ModTidy(ctx context.Context) (*TidiedModule, error)
// PackagesForFile returns the packages that this file belongs to.
PackagesForFile(ctx context.Context, uri span.URI) ([]Package, error)
@@ -183,6 +177,7 @@
ParsedFile() *ParsedGoFile
}
+// A ParsedGoFile contains the results of parsing a Go file.
type ParsedGoFile struct {
memoize.NoCopy
@@ -197,6 +192,27 @@
ParseErr error
}
+// A ParsedModule contains the results of parsing a go.mod file.
+type ParsedModule struct {
+ memoize.NoCopy
+
+ File *modfile.File
+ Mapper *protocol.ColumnMapper
+ ParseErrors []Error
+}
+
+// A TidedModule contains the results of running `go mod tidy` on a module.
+type TidiedModule struct {
+ memoize.NoCopy
+
+ // The parsed module, which is guaranteed to have parsed successfully.
+ Parsed *ParsedModule
+ // Diagnostics representing changes made by `go mod tidy`.
+ Errors []Error
+ // The bytes of the go.mod file after it was tidied.
+ TidiedContent []byte
+}
+
// Session represents a single connection from a client.
// This is the level at which things like open files are maintained on behalf
// of the client.
@@ -319,41 +335,6 @@
GetFile(ctx context.Context, uri span.URI) (FileHandle, error)
}
-type ParseModHandle interface {
- // Mod returns the file handle for the go.mod file.
- Mod() FileHandle
-
- // Sum returns the file handle for the analogous go.sum file. It may be nil.
- Sum() FileHandle
-
- // Parse returns the parsed go.mod file, a column mapper, and a list of
- // parse for the go.mod file.
- Parse(ctx context.Context, snapshot Snapshot) (*modfile.File, *protocol.ColumnMapper, []Error, error)
-}
-
-type ModUpgradeHandle interface {
- // Upgrades returns the latest versions for each of the module's
- // dependencies.
- Upgrades(ctx context.Context, snapshot Snapshot) (map[string]string, error)
-}
-
-type ModWhyHandle interface {
- // Why returns the results of `go mod why` for every dependency of the
- // module.
- Why(ctx context.Context, snapshot Snapshot) (map[string]string, error)
-}
-
-type ModTidyHandle interface {
- // Mod is the ParseModHandle associated with the go.mod file being tidied.
- ParseModHandle() ParseModHandle
-
- // Tidy returns the results of `go mod tidy` for the module.
- Tidy(ctx context.Context, snapshot Snapshot) ([]Error, error)
-
- // TidiedContent is the content of the tidied go.mod file.
- TidiedContent(ctx context.Context, snapshot Snapshot) ([]byte, error)
-}
-
var ErrTmpModfileUnsupported = errors.New("-modfile is unsupported for this Go version")
// ParseMode controls the content of the AST produced when parsing a source file.