mirror of https://github.com/golang/go.git
internal/lsp: support go.work outside of experimental
This change handles the case of the user creating a go.work file during the gopls session. It also defaults to go.work/gopls.mod being used outside of experimental mode, so that you don't have to both set a setting and have a file. Change-Id: If118cd2fc95c1b5600a6c06217a3b61605b11e28 Reviewed-on: https://go-review.googlesource.com/c/tools/+/342170 Trust: Rebecca Stambler <rstambler@golang.org> Run-TryBot: Rebecca Stambler <rstambler@golang.org> gopls-CI: kokoro <noreply+kokoro@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
d9648c9910
commit
7d467dcfbb
|
|
@ -656,7 +656,6 @@ directory (
|
|||
`
|
||||
WithOptions(
|
||||
ProxyFiles(workspaceModuleProxy),
|
||||
Modes(Experimental),
|
||||
).Run(t, multiModule, func(t *testing.T, env *Env) {
|
||||
// Initially, the gopls.mod should cause only the a.com module to be
|
||||
// loaded. Validate this by jumping to a definition in b.com and ensuring
|
||||
|
|
@ -1081,3 +1080,45 @@ package main
|
|||
)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAddGoWork(t *testing.T) {
|
||||
const nomod = `
|
||||
-- a/go.mod --
|
||||
module a.com
|
||||
|
||||
go 1.16
|
||||
-- a/main.go --
|
||||
package main
|
||||
|
||||
func main() {}
|
||||
-- b/go.mod --
|
||||
module b.com
|
||||
|
||||
go 1.16
|
||||
-- b/main.go --
|
||||
package main
|
||||
|
||||
func main() {}
|
||||
`
|
||||
WithOptions(
|
||||
Modes(Singleton),
|
||||
).Run(t, nomod, func(t *testing.T, env *Env) {
|
||||
env.OpenFile("a/main.go")
|
||||
env.OpenFile("b/main.go")
|
||||
env.Await(
|
||||
DiagnosticAt("a/main.go", 0, 0),
|
||||
DiagnosticAt("b/main.go", 0, 0),
|
||||
)
|
||||
env.WriteWorkspaceFile("go.work", `go 1.16
|
||||
|
||||
directory (
|
||||
a
|
||||
b
|
||||
)
|
||||
`)
|
||||
env.Await(
|
||||
EmptyDiagnostics("a/main.go"),
|
||||
EmptyDiagnostics("b/main.go"),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ func (s *snapshot) workspaceMode() workspaceMode {
|
|||
return mode
|
||||
}
|
||||
// The workspace module has been disabled by the user.
|
||||
if !options.ExperimentalWorkspaceModule {
|
||||
if s.workspace.moduleSource != goWorkWorkspace && s.workspace.moduleSource != goplsModWorkspace && !options.ExperimentalWorkspaceModule {
|
||||
return mode
|
||||
}
|
||||
mode |= usesWorkspaceModule
|
||||
|
|
|
|||
|
|
@ -88,13 +88,11 @@ type workspace struct {
|
|||
}
|
||||
|
||||
func newWorkspace(ctx context.Context, root span.URI, fs source.FileSource, excludePath func(string) bool, go111moduleOff bool, experimental bool) (*workspace, error) {
|
||||
// In experimental mode, the user may have a gopls.mod file that defines
|
||||
// their workspace.
|
||||
if experimental {
|
||||
ws, err := parseExplicitWorkspaceFile(ctx, root, fs, excludePath)
|
||||
if err == nil {
|
||||
return ws, nil
|
||||
}
|
||||
// The user may have a gopls.mod or go.work file that defines their
|
||||
// workspace.
|
||||
ws, err := parseExplicitWorkspaceFile(ctx, root, fs, excludePath)
|
||||
if err == nil {
|
||||
return ws, nil
|
||||
}
|
||||
// Otherwise, in all other modes, search for all of the go.mod files in the
|
||||
// workspace.
|
||||
|
|
@ -296,75 +294,19 @@ func (w *workspace) invalidate(ctx context.Context, changes map[span.URI]*fileCh
|
|||
|
||||
// First handle changes to the go.work or gopls.mod file. This must be
|
||||
// considered before any changes to go.mod or go.sum files, as these files
|
||||
// determine which modules we care about. In legacy workspace mode we don't
|
||||
// consider the gopls.mod or go.work files.
|
||||
if w.moduleSource != legacyWorkspace {
|
||||
// If go.work/gopls.mod has changed we need to either re-read it if it
|
||||
// exists or walk the filesystem if it has been deleted.
|
||||
// go.work should override the gopls.mod if both exist.
|
||||
for _, src := range []workspaceSource{goplsModWorkspace, goWorkWorkspace} {
|
||||
uri := uriForSource(w.root, src)
|
||||
// File opens/closes are just no-ops.
|
||||
change, ok := changes[uri]
|
||||
if !ok || change.isUnchanged {
|
||||
continue
|
||||
}
|
||||
if change.exists {
|
||||
// Only invalidate if the file if it actually parses.
|
||||
// Otherwise, stick with the current file.
|
||||
var parsedFile *modfile.File
|
||||
var parsedModules map[span.URI]struct{}
|
||||
var err error
|
||||
switch src {
|
||||
case goWorkWorkspace:
|
||||
parsedFile, parsedModules, err = parseGoWork(ctx, w.root, uri, change.content, fs)
|
||||
case goplsModWorkspace:
|
||||
parsedFile, parsedModules, err = parseGoplsMod(w.root, uri, change.content)
|
||||
}
|
||||
if err == nil {
|
||||
changed = true
|
||||
reload = change.fileHandle.Saved()
|
||||
result.mod = parsedFile
|
||||
result.moduleSource = src
|
||||
result.knownModFiles = parsedModules
|
||||
result.activeModFiles = make(map[span.URI]struct{})
|
||||
for k, v := range parsedModules {
|
||||
result.activeModFiles[k] = v
|
||||
}
|
||||
} else {
|
||||
// An unparseable file should not invalidate the workspace:
|
||||
// nothing good could come from changing the workspace in
|
||||
// this case.
|
||||
event.Error(ctx, fmt.Sprintf("parsing %s", filepath.Base(uri.Filename())), err)
|
||||
}
|
||||
} else {
|
||||
// go.work/gopls.mod is deleted. search for modules again.
|
||||
changed = true
|
||||
reload = true
|
||||
result.moduleSource = fileSystemWorkspace
|
||||
// The parsed file is no longer valid.
|
||||
result.mod = nil
|
||||
knownModFiles, err := findModules(w.root, w.excludePath, 0)
|
||||
if err != nil {
|
||||
result.knownModFiles = nil
|
||||
result.activeModFiles = nil
|
||||
event.Error(ctx, "finding file system modules", err)
|
||||
} else {
|
||||
result.knownModFiles = knownModFiles
|
||||
result.activeModFiles = make(map[span.URI]struct{})
|
||||
for k, v := range result.knownModFiles {
|
||||
result.activeModFiles[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// determine which modules we care about. If go.work/gopls.mod has changed
|
||||
// we need to either re-read it if it exists or walk the filesystem if it
|
||||
// has been deleted. go.work should override the gopls.mod if both exist.
|
||||
if changedInner, reloadInner, found := updateExplicitWorkspaceFile(ctx, w, result, changes, fs); found {
|
||||
changed = changedInner
|
||||
reload = reloadInner
|
||||
}
|
||||
|
||||
// Next, handle go.mod changes that could affect our workspace. If we're
|
||||
// reading our tracked modules from the gopls.mod, there's nothing to do
|
||||
// here.
|
||||
if result.moduleSource != goplsModWorkspace && result.moduleSource != goWorkWorkspace {
|
||||
for uri, change := range changes {
|
||||
// Otherwise, we only care about go.mod files in the workspace directory.
|
||||
if change.isUnchanged || !isGoMod(uri) || !source.InDir(result.root.Filename(), uri.Filename()) {
|
||||
continue
|
||||
}
|
||||
|
|
@ -407,6 +349,71 @@ func (w *workspace) invalidate(ctx context.Context, changes map[span.URI]*fileCh
|
|||
return result, changed, reload
|
||||
}
|
||||
|
||||
// updateExplicitWorkspaceFile checks if any of the changes happened to a go.work or
|
||||
// gopls.mod file, and if so, updates the result accordingly.
|
||||
func updateExplicitWorkspaceFile(ctx context.Context, w, result *workspace, changes map[span.URI]*fileChange, fs source.FileSource) (changed, reload, found bool) {
|
||||
// If go.work/gopls.mod has changed we need to either re-read it if it
|
||||
// exists or walk the filesystem if it has been deleted.
|
||||
// go.work should override the gopls.mod if both exist.
|
||||
for _, src := range []workspaceSource{goplsModWorkspace, goWorkWorkspace} {
|
||||
uri := uriForSource(w.root, src)
|
||||
// File opens/closes are just no-ops.
|
||||
change, ok := changes[uri]
|
||||
if !ok || change.isUnchanged {
|
||||
continue
|
||||
}
|
||||
found = true
|
||||
if change.exists {
|
||||
// Only invalidate if the file if it actually parses.
|
||||
// Otherwise, stick with the current file.
|
||||
var parsedFile *modfile.File
|
||||
var parsedModules map[span.URI]struct{}
|
||||
var err error
|
||||
switch src {
|
||||
case goWorkWorkspace:
|
||||
parsedFile, parsedModules, err = parseGoWork(ctx, w.root, uri, change.content, fs)
|
||||
case goplsModWorkspace:
|
||||
parsedFile, parsedModules, err = parseGoplsMod(w.root, uri, change.content)
|
||||
}
|
||||
if err != nil {
|
||||
// An unparseable file should not invalidate the workspace:
|
||||
// nothing good could come from changing the workspace in
|
||||
// this case.
|
||||
event.Error(ctx, fmt.Sprintf("parsing %s", filepath.Base(uri.Filename())), err)
|
||||
}
|
||||
changed = true
|
||||
reload = change.fileHandle.Saved()
|
||||
result.mod = parsedFile
|
||||
result.moduleSource = src
|
||||
result.knownModFiles = parsedModules
|
||||
result.activeModFiles = make(map[span.URI]struct{})
|
||||
for k, v := range parsedModules {
|
||||
result.activeModFiles[k] = v
|
||||
}
|
||||
} else {
|
||||
// go.work/gopls.mod is deleted. search for modules again.
|
||||
changed = true
|
||||
reload = true
|
||||
result.moduleSource = fileSystemWorkspace
|
||||
// The parsed file is no longer valid.
|
||||
result.mod = nil
|
||||
knownModFiles, err := findModules(w.root, w.excludePath, 0)
|
||||
if err != nil {
|
||||
result.knownModFiles = nil
|
||||
result.activeModFiles = nil
|
||||
event.Error(ctx, "finding file system modules", err)
|
||||
} else {
|
||||
result.knownModFiles = knownModFiles
|
||||
result.activeModFiles = make(map[span.URI]struct{})
|
||||
for k, v := range result.knownModFiles {
|
||||
result.activeModFiles[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return changed, reload, found
|
||||
}
|
||||
|
||||
// goplsModURI returns the URI for the gopls.mod file contained in root.
|
||||
func uriForSource(root span.URI, src workspaceSource) span.URI {
|
||||
var basename string
|
||||
|
|
|
|||
Loading…
Reference in New Issue