Skip to content

Commit 5d1fdd8

Browse files
committed
internal/lsp: allow multiple go.mod files in a view
This change allows a view to have multiple go.mod files associated with it. This doesn't actually make any changes in internal/lsp/cache with regards to the view's modURI, but it does do the necessary plumbing in the client packages. The next CL will delete modURI. Updates golang/go#32394 Change-Id: I2312efd69c364aed4244ee3769679885a1ebc7e4 Reviewed-on: https://go-review.googlesource.com/c/tools/+/256941 Trust: Rebecca Stambler <[email protected]> Run-TryBot: Rebecca Stambler <[email protected]> gopls-CI: kokoro <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Heschi Kreinick <[email protected]>
1 parent e843550 commit 5d1fdd8

15 files changed

+171
-137
lines changed

gopls/internal/regtest/diagnostics_test.go

+4-7
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,13 @@ func TestDiagnosticErrorInEditedFile(t *testing.T) {
5959
})
6060
}
6161

62-
const onlyMod = `
62+
func TestMissingImportDiagsClearOnFirstFile(t *testing.T) {
63+
const onlyMod = `
6364
-- go.mod --
6465
module mod.com
6566
6667
go 1.12
6768
`
68-
69-
func TestMissingImportDiagsClearOnFirstFile(t *testing.T) {
70-
t.Parallel()
7169
runner.Run(t, onlyMod, func(t *testing.T, env *Env) {
7270
env.CreateBuffer("main.go", `package main
7371
@@ -85,12 +83,11 @@ func m() {
8583
})
8684
}
8785

88-
const brokenFile = `package main
86+
func TestDiagnosticErrorInNewFile(t *testing.T) {
87+
const brokenFile = `package main
8988
9089
const Foo = "abc
9190
`
92-
93-
func TestDiagnosticErrorInNewFile(t *testing.T) {
9491
runner.Run(t, brokenFile, func(t *testing.T, env *Env) {
9592
env.CreateBuffer("broken.go", brokenFile)
9693
env.Await(env.DiagnosticAtRegexp("broken.go", "\"abc"))

internal/lsp/cache/load.go

+7-4
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,6 @@ func (s *snapshot) load(ctx context.Context, scopes ...interface{}) error {
8888
ctx, done := event.Start(ctx, "cache.view.load", tag.Query.Of(query))
8989
defer done()
9090

91-
cfg := s.config(ctx)
92-
9391
cleanup := func() {}
9492

9593
var modFH, sumFH source.FileHandle
@@ -107,6 +105,8 @@ func (s *snapshot) load(ctx context.Context, scopes ...interface{}) error {
107105
}
108106
}
109107

108+
wdir := s.view.rootURI.Filename()
109+
var buildFlags []string
110110
switch {
111111
case s.view.workspaceMode&usesWorkspaceModule != 0:
112112
var (
@@ -117,16 +117,19 @@ func (s *snapshot) load(ctx context.Context, scopes ...interface{}) error {
117117
if err != nil {
118118
return err
119119
}
120-
cfg.Dir = tmpDir.Filename()
120+
wdir = tmpDir.Filename()
121121
case s.view.workspaceMode&tempModfile != 0:
122122
var tmpURI span.URI
123123
tmpURI, cleanup, err = tempModFile(modFH, sumFH)
124124
if err != nil {
125125
return err
126126
}
127-
cfg.BuildFlags = append(cfg.BuildFlags, fmt.Sprintf("-modfile=%s", tmpURI.Filename()))
127+
buildFlags = append(buildFlags, fmt.Sprintf("-modfile=%s", tmpURI.Filename()))
128128
}
129129

130+
cfg := s.config(ctx, wdir)
131+
cfg.BuildFlags = append(cfg.BuildFlags, buildFlags...)
132+
130133
modMod, err := s.view.needsModEqualsMod(ctx, modFH)
131134
if err != nil {
132135
return err

internal/lsp/cache/mod.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ func (s *snapshot) ModWhy(ctx context.Context, fh source.FileHandle) (map[string
206206
return handle.why(ctx, s)
207207
}
208208
// Make sure to use the module root as the working directory.
209-
cfg := s.configWithDir(ctx, filepath.Dir(fh.URI().Filename()))
209+
cfg := s.config(ctx, filepath.Dir(fh.URI().Filename()))
210210
key := modKey{
211211
sessionID: s.view.session.id,
212212
cfg: hashConfig(cfg),
@@ -298,7 +298,7 @@ func (s *snapshot) ModUpgrade(ctx context.Context, fh source.FileHandle) (map[st
298298
return handle.upgrades(ctx, s)
299299
}
300300
// Use the module root as the working directory.
301-
cfg := s.configWithDir(ctx, filepath.Dir(fh.URI().Filename()))
301+
cfg := s.config(ctx, filepath.Dir(fh.URI().Filename()))
302302
key := modKey{
303303
sessionID: s.view.session.id,
304304
cfg: hashConfig(cfg),

internal/lsp/cache/mod_tidy.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func (s *snapshot) ModTidy(ctx context.Context, fh source.FileHandle) (*source.T
7575
s.mu.Unlock()
7676

7777
// Make sure to use the module root in the configuration.
78-
cfg := s.configWithDir(ctx, filepath.Dir(fh.URI().Filename()))
78+
cfg := s.config(ctx, filepath.Dir(fh.URI().Filename()))
7979
key := modTidyKey{
8080
sessionID: s.view.session.id,
8181
view: s.view.folder.Filename(),
@@ -108,7 +108,7 @@ func (s *snapshot) ModTidy(ctx context.Context, fh source.FileHandle) (*source.T
108108
}
109109
// Get a new config to avoid races, since it may be modified by
110110
// goCommandInvocation.
111-
cfg := s.configWithDir(ctx, filepath.Dir(fh.URI().Filename()))
111+
cfg := s.config(ctx, filepath.Dir(fh.URI().Filename()))
112112
tmpURI, runner, inv, cleanup, err := snapshot.goCommandInvocation(ctx, cfg, true, "mod", []string{"tidy"})
113113
if err != nil {
114114
return &modTidyData{err: err}

internal/lsp/cache/snapshot.go

+32-18
Original file line numberDiff line numberDiff line change
@@ -125,18 +125,13 @@ func (s *snapshot) FileSet() *token.FileSet {
125125
return s.view.session.cache.fset
126126
}
127127

128-
// config returns a *packages.Config with the working directory set to the
129-
// view's root.
130-
func (s *snapshot) config(ctx context.Context) *packages.Config {
131-
return s.configWithDir(ctx, s.view.rootURI.Filename())
132-
}
133-
134-
// configWithDir returns the configuration used for the snapshot's interaction
135-
// with the go/packages API. It uses the given working directory.
128+
// config returns the configuration used for the snapshot's interaction with
129+
// the go/packages API. It uses the given working directory.
130+
//
136131
// TODO(rstambler): go/packages requires that we do not provide overlays for
137132
// multiple modules in on config, so buildOverlay needs to filter overlays by
138133
// module.
139-
func (s *snapshot) configWithDir(ctx context.Context, dir string) *packages.Config {
134+
func (s *snapshot) config(ctx context.Context, dir string) *packages.Config {
140135
s.view.optionsMu.Lock()
141136
env, buildFlags := s.view.envLocked()
142137
verboseOutput := s.view.options.VerboseOutput
@@ -174,8 +169,8 @@ func (s *snapshot) configWithDir(ctx context.Context, dir string) *packages.Conf
174169
return cfg
175170
}
176171

177-
func (s *snapshot) RunGoCommandDirect(ctx context.Context, verb string, args []string) error {
178-
cfg := s.config(ctx)
172+
func (s *snapshot) RunGoCommandDirect(ctx context.Context, wd, verb string, args []string) error {
173+
cfg := s.config(ctx, wd)
179174
_, runner, inv, cleanup, err := s.goCommandInvocation(ctx, cfg, false, verb, args)
180175
if err != nil {
181176
return err
@@ -186,11 +181,6 @@ func (s *snapshot) RunGoCommandDirect(ctx context.Context, verb string, args []s
186181
return err
187182
}
188183

189-
func (s *snapshot) RunGoCommand(ctx context.Context, verb string, args []string) (*bytes.Buffer, error) {
190-
cfg := s.config(ctx)
191-
return s.runGoCommandWithConfig(ctx, cfg, verb, args)
192-
}
193-
194184
func (s *snapshot) runGoCommandWithConfig(ctx context.Context, cfg *packages.Config, verb string, args []string) (*bytes.Buffer, error) {
195185
_, runner, inv, cleanup, err := s.goCommandInvocation(ctx, cfg, true, verb, args)
196186
if err != nil {
@@ -201,8 +191,8 @@ func (s *snapshot) runGoCommandWithConfig(ctx context.Context, cfg *packages.Con
201191
return runner.Run(ctx, *inv)
202192
}
203193

204-
func (s *snapshot) RunGoCommandPiped(ctx context.Context, verb string, args []string, stdout, stderr io.Writer) error {
205-
cfg := s.config(ctx)
194+
func (s *snapshot) RunGoCommandPiped(ctx context.Context, wd, verb string, args []string, stdout, stderr io.Writer) error {
195+
cfg := s.config(ctx, wd)
206196
_, runner, inv, cleanup, err := s.goCommandInvocation(ctx, cfg, true, verb, args)
207197
if err != nil {
208198
return err
@@ -607,6 +597,30 @@ func (s *snapshot) CachedImportPaths(ctx context.Context) (map[string]source.Pac
607597
return results, nil
608598
}
609599

600+
func (s *snapshot) GoModForFile(ctx context.Context, fh source.FileHandle) (source.VersionedFileHandle, error) {
601+
if fh.Kind() != source.Go {
602+
return nil, fmt.Errorf("expected Go file, got %s", fh.Kind())
603+
}
604+
if len(s.modules) == 0 {
605+
if s.view.modURI == "" {
606+
return nil, errors.New("no modules in this view")
607+
}
608+
return s.GetFile(ctx, s.view.modURI)
609+
}
610+
var match span.URI
611+
for _, m := range s.modules {
612+
// Add an os.PathSeparator to the end of each directory to make sure
613+
// that foo/apple/banana does not match foo/a.
614+
if !strings.HasPrefix(fh.URI().Filename()+string(os.PathSeparator), m.rootURI.Filename()+string(os.PathSeparator)) {
615+
continue
616+
}
617+
if len(m.modURI) > len(match) {
618+
match = m.modURI
619+
}
620+
}
621+
return s.GetFile(ctx, match)
622+
}
623+
610624
func (s *snapshot) getPackage(id packageID, mode source.ParseMode) *packageHandle {
611625
s.mu.Lock()
612626
defer s.mu.Unlock()

internal/lsp/cache/view.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,11 @@ func (v *View) ValidBuildConfiguration() bool {
213213
return v.hasValidBuildConfiguration
214214
}
215215

216-
func (v *View) ModFile() span.URI {
217-
return v.modURI
216+
func (v *View) ModFiles() []span.URI {
217+
if v.modURI == "" {
218+
return nil
219+
}
220+
return []span.URI{v.modURI}
218221
}
219222

220223
// tempModFile creates a temporary go.mod file based on the contents of the
@@ -276,7 +279,7 @@ func (v *View) Name() string {
276279
return v.name
277280
}
278281

279-
// Folder returns the root of this view.
282+
// Folder returns the folder at the base of this view.
280283
func (v *View) Folder() span.URI {
281284
return v.folder
282285
}

internal/lsp/code_action.go

+21-22
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,8 @@ func (s *Server) codeAction(ctx context.Context, params *protocol.CodeActionPara
5151
var codeActions []protocol.CodeAction
5252
switch fh.Kind() {
5353
case source.Mod:
54-
// TODO: Support code actions for views with multiple modules.
55-
if snapshot.View().ModFile() == "" {
56-
return nil, nil
57-
}
5854
if diagnostics := params.Context.Diagnostics; len(diagnostics) > 0 {
59-
modQuickFixes, err := moduleQuickFixes(ctx, snapshot, diagnostics)
55+
modQuickFixes, err := moduleQuickFixes(ctx, snapshot, fh, diagnostics)
6056
if err == source.ErrTmpModfileUnsupported {
6157
return nil, nil
6258
}
@@ -66,7 +62,7 @@ func (s *Server) codeAction(ctx context.Context, params *protocol.CodeActionPara
6662
codeActions = append(codeActions, modQuickFixes...)
6763
}
6864
if wanted[protocol.SourceOrganizeImports] {
69-
action, err := goModTidy(ctx, snapshot)
65+
action, err := goModTidy(ctx, snapshot, fh)
7066
if err == source.ErrTmpModfileUnsupported {
7167
return nil, nil
7268
}
@@ -138,7 +134,7 @@ func (s *Server) codeAction(ctx context.Context, params *protocol.CodeActionPara
138134

139135
// If there are any diagnostics relating to the go.mod file,
140136
// add their corresponding quick fixes.
141-
modQuickFixes, err := moduleQuickFixes(ctx, snapshot, diagnostics)
137+
modQuickFixes, err := moduleQuickFixes(ctx, snapshot, fh, diagnostics)
142138
if err != nil {
143139
// Not a fatal error.
144140
event.Error(ctx, "module suggested fixes failed", err, tag.Directory.Of(snapshot.View().Folder()))
@@ -457,10 +453,17 @@ func documentChanges(fh source.VersionedFileHandle, edits []protocol.TextEdit) [
457453
}
458454
}
459455

460-
func moduleQuickFixes(ctx context.Context, snapshot source.Snapshot, diagnostics []protocol.Diagnostic) ([]protocol.CodeAction, error) {
461-
modFH, err := snapshot.GetFile(ctx, snapshot.View().ModFile())
462-
if err != nil {
463-
return nil, err
456+
func moduleQuickFixes(ctx context.Context, snapshot source.Snapshot, fh source.VersionedFileHandle, diagnostics []protocol.Diagnostic) ([]protocol.CodeAction, error) {
457+
var modFH source.VersionedFileHandle
458+
switch fh.Kind() {
459+
case source.Mod:
460+
modFH = fh
461+
case source.Go:
462+
var err error
463+
modFH, err = snapshot.GoModForFile(ctx, fh)
464+
if err != nil {
465+
return nil, err
466+
}
464467
}
465468
tidied, err := snapshot.ModTidy(ctx, modFH)
466469
if err == source.ErrTmpModfileUnsupported {
@@ -512,21 +515,17 @@ func sameDiagnostic(d protocol.Diagnostic, e source.Error) bool {
512515
return d.Message == e.Message && protocol.CompareRange(d.Range, e.Range) == 0 && d.Source == e.Category
513516
}
514517

515-
func goModTidy(ctx context.Context, snapshot source.Snapshot) (*protocol.CodeAction, error) {
516-
modFH, err := snapshot.GetFile(ctx, snapshot.View().ModFile())
518+
func goModTidy(ctx context.Context, snapshot source.Snapshot, fh source.VersionedFileHandle) (*protocol.CodeAction, error) {
519+
tidied, err := snapshot.ModTidy(ctx, fh)
517520
if err != nil {
518521
return nil, err
519522
}
520-
tidied, err := snapshot.ModTidy(ctx, modFH)
521-
if err != nil {
522-
return nil, err
523-
}
524-
left, err := modFH.Read()
523+
left, err := fh.Read()
525524
if err != nil {
526525
return nil, err
527526
}
528527
right := tidied.TidiedContent
529-
edits := snapshot.View().Options().ComputeEdits(modFH.URI(), string(left), string(right))
528+
edits := snapshot.View().Options().ComputeEdits(fh.URI(), string(left), string(right))
530529
protocolEdits, err := source.ToProtocolEdits(tidied.Parsed.Mapper, edits)
531530
if err != nil {
532531
return nil, err
@@ -537,13 +536,13 @@ func goModTidy(ctx context.Context, snapshot source.Snapshot) (*protocol.CodeAct
537536
Edit: protocol.WorkspaceEdit{
538537
DocumentChanges: []protocol.TextDocumentEdit{{
539538
TextDocument: protocol.VersionedTextDocumentIdentifier{
540-
Version: modFH.Version(),
539+
Version: fh.Version(),
541540
TextDocumentIdentifier: protocol.TextDocumentIdentifier{
542-
URI: protocol.URIFromSpanURI(modFH.URI()),
541+
URI: protocol.URIFromSpanURI(fh.URI()),
543542
},
544543
},
545544
Edits: protocolEdits,
546545
}},
547546
},
548-
}, nil
547+
}, err
549548
}

internal/lsp/command.go

+10-7
Original file line numberDiff line numberDiff line change
@@ -262,9 +262,10 @@ func (s *Server) directGoModCommand(ctx context.Context, uri protocol.DocumentUR
262262
if err != nil {
263263
return err
264264
}
265+
wdir := filepath.Dir(uri.SpanURI().Filename())
265266
snapshot, release := view.Snapshot(ctx)
266267
defer release()
267-
return snapshot.RunGoCommandDirect(ctx, verb, args)
268+
return snapshot.RunGoCommandDirect(ctx, wdir, verb, args)
268269
}
269270

270271
func (s *Server) runTests(ctx context.Context, snapshot source.Snapshot, uri protocol.DocumentURI, work *workDone, tests, benchmarks []string) error {
@@ -282,11 +283,13 @@ func (s *Server) runTests(ctx context.Context, snapshot source.Snapshot, uri pro
282283
ew := &eventWriter{ctx: ctx, operation: "test"}
283284
out := io.MultiWriter(ew, workDoneWriter{work}, buf)
284285

286+
wdir := filepath.Dir(uri.SpanURI().Filename())
287+
285288
// Run `go test -run Func` on each test.
286289
var failedTests int
287290
for _, funcName := range tests {
288291
args := []string{pkgPath, "-v", "-count=1", "-run", fmt.Sprintf("^%s$", funcName)}
289-
if err := snapshot.RunGoCommandPiped(ctx, "test", args, out, out); err != nil {
292+
if err := snapshot.RunGoCommandPiped(ctx, wdir, "test", args, out, out); err != nil {
290293
if errors.Is(err, context.Canceled) {
291294
return err
292295
}
@@ -298,7 +301,7 @@ func (s *Server) runTests(ctx context.Context, snapshot source.Snapshot, uri pro
298301
var failedBenchmarks int
299302
for _, funcName := range benchmarks {
300303
args := []string{pkgPath, "-v", "-run=^$", "-bench", fmt.Sprintf("^%s$", funcName)}
301-
if err := snapshot.RunGoCommandPiped(ctx, "test", args, out, out); err != nil {
304+
if err := snapshot.RunGoCommandPiped(ctx, wdir, "test", args, out, out); err != nil {
302305
if errors.Is(err, context.Canceled) {
303306
return err
304307
}
@@ -342,15 +345,15 @@ func (s *Server) runGoGenerate(ctx context.Context, snapshot source.Snapshot, ur
342345

343346
er := &eventWriter{ctx: ctx, operation: "generate"}
344347
args := []string{"-x"}
345-
dir := uri.Filename()
348+
pattern := "."
346349
if recursive {
347-
dir = filepath.Join(dir, "...")
350+
pattern = "..."
348351
}
349-
args = append(args, dir)
352+
args = append(args, pattern)
350353

351354
stderr := io.MultiWriter(er, workDoneWriter{work})
352355

353-
if err := snapshot.RunGoCommandPiped(ctx, "generate", args, er, stderr); err != nil {
356+
if err := snapshot.RunGoCommandPiped(ctx, uri.Filename(), "generate", args, er, stderr); err != nil {
354357
return err
355358
}
356359
return nil

0 commit comments

Comments
 (0)