Closed findleyr closed 10 months ago
Change https://go.dev/cl/495256 mentions this issue: gopls/internal/lsp/cache: fix race in adhoc reloading
Change https://go.dev/cl/496880 mentions this issue: gopls/internal/lsp/cache: limit module scan to 100K files
Change https://go.dev/cl/526160 mentions this issue: gopls/internal/lsp/cache: move workingDir to workspaceInformation
Change https://go.dev/cl/526417 mentions this issue: gopls/internal/lsp/cache: move Option management to the Server
Task list migrated to issue description
Change https://go.dev/cl/538798 mentions this issue: gopls/internal/regtest/marker: port builtin/keyword completion tests
Change https://go.dev/cl/538797 mentions this issue: gopls/internal/regtest/marker: port five arbitrary completion tests
Change https://go.dev/cl/538799 mentions this issue: gopls/internal/regtest/marker: port rank and func_rank tests
Change https://go.dev/cl/538800 mentions this issue: gopls/internal/regtest/marker: port remaining completion tests
Change https://go.dev/cl/538801 mentions this issue: gopls/internal/regtest/marker: port remaining rank and snippet tests
Change https://go.dev/cl/538796 mentions this issue: gopls/internal/lsp/cache: make options immutable on the view
Change https://go.dev/cl/538766 mentions this issue: gopls/internal/lsp/cache: move 'contains' from snapshot to view
Change https://go.dev/cl/538803 mentions this issue: gopls/internal/lsp/cache: isolate getWorkspaceInformation from Session
Change https://go.dev/cl/539658 mentions this issue: gopls/internal/lsp/cache: remove forceReloadMetadata from clone
Change https://go.dev/cl/539657 mentions this issue: gopls/internal/lsp/cache: pass workspace information into createView
Change https://go.dev/cl/539676 mentions this issue: gopls/internal/lsp/cache: remove baseCtx from the View
Change https://go.dev/cl/540296 mentions this issue: gopls/internal/lsp/cache: exclusively use persistent data in snapshot
Change https://go.dev/cl/542639 mentions this issue: gopls/internal/lsp/cache: export Snapshot
Change https://go.dev/cl/542638 mentions this issue: gopls/internal/lsp/source: remove View.Snapshot
Change https://go.dev/cl/542642 mentions this issue: gopls: remove the "tempModFile" setting
Change https://go.dev/cl/542622 mentions this issue: gopls/internal/lsp/cache: remove workspaceMode
Change https://go.dev/cl/540479 mentions this issue: gopls/internal/lsp/cache: move upgrades and vulns onto the snapshot
Change https://go.dev/cl/542619 mentions this issue: gopls/internal/lsp/cache: move go.work parsing into the view
Change https://go.dev/cl/542640 mentions this issue: gopls: simplify the Snapshot and View interfaces
Change https://go.dev/cl/546017 mentions this issue: gopls/internal/server: simplify snapshot diagnostics
Change https://go.dev/cl/546415 mentions this issue: gopls/internal/server: rewrite the server diagnostic tracking
The zero-config gopls work is going to generalize and improve the adhoc package support.
The development work is still in progress. I am adding some findings from testing ad-hoc package supports (without go.mod
). cc @findleyr @adonovan
1) drop the package load error report that recommends use of go.mod, go.work, GOPATH when operating in ad-hock package mode. IMO this is somewhat obsolete after the zero-config gopls work.
2) consider to drop the hanging progress bar that reports package load error ("Error loading workspace: gopls was not able to find modules in your workspace"). Related: https://github.com/golang/go/issues/50885
Note: we can track ad-hoc package support outside a module or GOPATH as a separate issue and aim for the future milestone after v0.15 is also an option if this is too tricky.
Change https://go.dev/cl/550075 mentions this issue: gopls/internal/lsp/cache: reconcile view modes and workspace packages
Change https://go.dev/cl/550378 mentions this issue: gopls/internal/lsp/cache: simplify view definitions
Change https://go.dev/cl/550815 mentions this issue: gopls/internal/lsp/cache: add the zero-config algorithm, and a unit test
Change https://go.dev/cl/550915 mentions this issue: gopls/internal/lsp/cache: add views for unused modules in selectViews
Change https://go.dev/cl/551295 mentions this issue: gopls/internal/lsp/cache: switch to new bestViewForURI logic
Change https://go.dev/cl/551896 mentions this issue: gopls/internal/lsp/cache: finish integrating the zero-config algorithm
Change https://go.dev/cl/551895 mentions this issue: gopls/internal/lsp/cache: don't preserve sequence IDs in updated views
Change https://go.dev/cl/552315 mentions this issue: gopls/internal/lsp/cache: associate env with folders, not views
Change https://go.dev/cl/552355 mentions this issue: gopls/internal/lsp/cache: add support for checking views from tests
Change https://go.dev/cl/551897 mentions this issue: gopls/internal/lsp/cache: add dynamic build tag support
Change https://go.dev/cl/553096 mentions this issue: gopls/internal/lsp/cache: don't scan for modules when defining a view
Change https://go.dev/cl/553095 mentions this issue: gopls/internal/lsp/cache: remove validBuildConfiguration
Change https://go.dev/cl/553097 mentions this issue: gopls/internal/lsp/cache: simplify critical errors
Ok, remaining TODOs for this issue are tracked in #29202, #64887, and #64888.
The fundamental goal of this issue is achieved: gopls is zero-config with respect to workspace layout. Following https://go.dev/cl/551897, it will also be zero-config with respect to GOOS/GOARCH combinations (at which point I think we can close #29202). Closing this as complete!
Change https://go.dev/cl/555197 mentions this issue: internal/debug: show new View information in the debug page
Change https://go.dev/cl/557500 mentions this issue: gopls/internal/lsp/cache: sort workspace folders in selectViews
I think there is a problem with the automatic GOOS/GOARCH thing where it doesn't work if cgo is used. For example editing this file on any OS that isn't freebsd.
@aarzilli you are right: setting GOOS and GOARCH will, by default, disable cgo.
I haven't yet tested, but I wonder if we can make this work by setting CCFOR${GOOS}_${GOARCH}? https://pkg.go.dev/cmd/cgo#hdr-Using_cgo_with_the_go_command
I don't know how much that would help. I, for example, do not have CC_FOR_anything_other_than_linux_amd64. I think probably what needs to happen is that FakeImportC needs to be passed to go/types where appropriate (or maybe go/types needs to do it automatically when it can't do better).
I think probably what needs to happen is that FakeImportC needs to be passed to go/types where appropriate (or maybe go/types needs to do it automatically when it can't do better)
Indeed, I'll give that a try. Thanks.
Change https://go.dev/cl/560467 mentions this issue: gopls/internal/cache: remove findWorkspaceModFile
Change https://go.dev/cl/559636 mentions this issue: gopls/internal/cache: share goimports state for GOMODCACHE
Was this already released? How can I start using it?
Zero-config gopls workspaces
This issue describes a change to gopls' internal data model that will allow it to "Do The Right Thing" when the user opens a Go file. By decoupling the relationship between builds and workspace folders, we can eliminate complexity related to configuring the workspace (hence "zero-config"), and lay the groundwork for later improvements such as better support for working on multiple sets of build tags simultaneously (#29202).
After this change, users can work on multiple modules inside of a workspace regardless of whether they are related by a go.work file or explicitly open as separate workspace folders.
Background
Right now, gopls determines a unique build (called a
View
) for each workspace folder. When a workspace folder is opened, gopls performs the following steps:workspace/configuration
request withscopeUri
set to the folder URI.go env GOWORK
. b. Else, look forgo.mod
in a parent directory (recursively). c. Else, look forgo.mod
in a nested directory, if there is only one such nested directory. This was done to support polyglot workspaces where the Go project is in a nested directory, but is a source of both confusion and unpredictable startup time. d. Else, use the folder as root.go/packages.Load
.Problems
There are several problems with this model:
"experimentalWorkspaceModule"
setting,"expandWorkspaceToModule"
setting, and"directoryFilters"
setting.New Model
We can address these problems by decoupling Views from workspace folders. The set of views will be dynamic, depending on both the set of open folders and the set of open files, and will be chosen to cover all open files.
Specifically, define new
View
andFolder
types approximately as follows:A
Session
consists of a set ofView
objects describing modules (go.mod
files), workspaces (go.work
files), GOPATH directories or ad-hoc packages that the user is working on. This set is determined by both the workspace folders specified by the editor and the set of open files.View types
workspace
View is defined by ago.work
file.source
is the path to thego.work
file.module
View is defined by a singlego.mod
file.source
is the path to thego.mod
file.GOPATH
View is defined by a folder inside aGOPATH
directory, withGO111MODULE=off
orGO111MODULE=auto
and nogo.mod
file.source
is the path to the directory.adhoc
View is defined by a folder outside ofGOPATH
, with no enclosinggo.mod
file. In this case, we consider files in the same directory to be part of a package, andsource
is the path to the directory.The set of Views
We define the set of Views to ensure that we have coverage for each open folder, and each open file.
go env GOWORK
is set, create aworkspace
View.go.mod
in a parent directory. If found, create amodule
View.GOPATH
, andGO111MODULE
is not explicitly set toon
, create aGOPATH
View. IfGO111MODULE=on
explicitly, fail.adhoc
View for the workspace folder. This may not be desirable for the user if they have modules contained in nested directories. In this case we could either prompt the user, or scan for modules in nested directories, creating Views for each (but notably if we do decide to scan the filesystem, we would create a View for each go.mod or go.work file encountered, rather than fail if there are more than one).Match to an existing View
go.mod
files.go.mod
file is found, search for existingworkspace
ormodule
type Views containing this module in theirmodules
set.GOPATH
type Views whosesource
directory contains the file.Search for existing
adhoc
type Views whosesource
is equal tofilepath.Dir(file)
.If no existing View matches the file, create a new one
module
type. Apply an explicitGOWORK=off
to the View configuration to ensure that we can load the module.Initializing views
Initialize views using the following logic. This essentially matches gopls’ current behavior.
workspace
Views, loadmodulepath/...
for each workspace module.module
Views, loadmodulepath/...
for the main module.GOPATH
Views, load./...
from the View dir.ad-hoc
Views, load./
from the View dir.Type-check packages (and report their compiler diagnostics) as follows:
workspace
Views, type-check any package whose module is a workspace module.module
Views, type-check any package whose module is the main module.GOPATH
Views, type-check any package contained indir
.adhoc
Views, type-check the ad-hoc package.Resolving requests to Views
When a file-oriented request is handled by gopls (a request prefixed with
textDocument/
, such astextDocument/definition
), gopls must usually resolve package metadata associated with the file.In most cases, gopls currently chooses an existing view that best applies to the file (
cache.bestViewForURI
), but this is already problematic, because it can lead to path-dependency and incomplete results (c.f. #57558). For example: when finding references from a package imported from multiple views, gopls currently only shows references in one view.Wherever possible, gopls should multiplex queries across all Views and merge their results. This would lead to consistent behavior of cross references. In a future where gopls has better build-tag support, this could also lead to multiple locations for jump-to-definition results.
In some cases (for example
hover
orsignatureHelp
), we must pick one view. In these cases we can apply some heuristic, but it should be of secondary significance (any hover or signatureHelp result is better than none).Updating Views
Based on the algorithms used to determine Views above, the following notifications may affect the set of Views:
didOpen
anddidClose
cause gopls to re-evaluate Views, ensuring that we have a View for each open file contained in a workspace folder.didChangeConfiguration
anddidChangeWorkspaceFolders
causes gopls to updateFolder
layout and configuration. Note that configuration may affect e.g. GOWORK values and therefore may lead to a new set of Views.didChange
ordidChangeWatchedFile
may cause gopls to re-evaluate Views if the change is to ago.mod
orgo.work
file (for example, ago.mod
file deleted or added, or ago.work
file changed in any way).Following these changes, gopls will re-run the algorithm above to determine a new set of Views. It will re-use existing Views that have not changed.
Whenever new Views are created, they are reinitialized as above.
Differences from the current model
The algorithms described above are not vastly divergent from gopls’ current behavior. The significant differences may be summarized as follows:
go.mod
files are added or removed, orgo.work
files changed, we reconfigure the set of Views. This simplifies the logic of handling metadata invalidation in each view.Downsides
While this change will make gopls “do the right thing” in more cases, there are a several notable downsides:
go.work
orgo.mod
in a parent directory of the workspace folder. This means thatworkspace/symbols
requests may return empty results, or results that depend on the set of open files. Users can mitigate this by using ago.work
file.a
andb
in their workspace, anda
depends on a version ofb
in the module cache, find reference on a symbol in theb
directory of the workspace will not include references ina
. Users can mitigate this by using ago.work
file. It would also be possible for us to implement looser heuristics in our references search.Future extension to build tags
By decoupling Views from workspace folders, it becomes possible for gopls to support working on multiple sets of build tags simultaneously. One can imagine that the algorithm above to compute views based on open files could be extended to GOOS and GOARCH: if an open file is not included in an existing view because of its GOOS or GOARCH build constraints, create a new view with updated environment.
The downsides above apply: potentially increased memory, and potentially confusing UX as the behavior of certain workspace-wide queries (such as references or workspace symbols) depends on the set of open files. We can work to mitigate these downsides, and in my opinion they do not outweigh the upsides, as these queries simply don't work in the current model.
Task List
Here's an approximate plan of attack for implementing this feature, which I'm aiming to complete by the end of the year. (migrated from https://github.com/golang/go/issues/57979#issuecomment-1787445478).
This is inside baseball, but may be interesting to @adonovan and @hyangah.
Phase 1: making Views immutable:
Session.getWorkspaceInformation
logic to be unit-testable, and rename/refactor. This will be the basis of the new workspace algorithm.Folder
type, to reuse across new/multiple views.NewView
, which should no longer query workspace information, but rather should be provided immutable workspace and folder information.getWorkspaceInformation
. Whenever ago.work
file changes, create a new view (at least if the newgo.work
parses and is saved to disk).At this point, gopls should still behave identically, but Views will have immutable options and main modules. There may be a bit more churn when configuration or go.work files change, but such events should be very infrequent, and it seems reasonable to reload the workspace when they occur.
Phase 2: supporting multiple views per folder
bestViewForURI
logic to return nil if no view matches, and lift upsnapshot.contains
to theView
, since views are now immutable.Views
necessary to cover all open files (computeViews
). Compute the diff with the current set, and minimally (re)create views that are necessary.Phase 3: support for multiple GOOS/GOARCH combinations
computeViews
algorithm to consider GOOS and GOARCH combinations. I'm not sure exactly how this algorithm will work: presumably if the current GOOS/GOARCH combination doesn't match an open file, we'll pick another, but the algorithm to pick another is non-trivial.