mirror of
https://github.com/golang/go
synced 2024-11-18 17:14:45 -07:00
internal/lsp: reload workspace package metadata on demand
This change removes functions from the snapshot that return package IDs. We prefer PackageHandles, since getting PackageHandles in a granular fashion is not effective and causes us to spawn many `go list` processes. By only ever returning PackageHandles, we can batch metadata reloads for workspace packages. This enables us to add a check to confirm that the snapshot is in a good state before returning important data, like reverse dependencies and workspace package handles. Change-Id: Icffc8d8e0449864f207c15aa211e84cb158c163f Reviewed-on: https://go-review.googlesource.com/c/tools/+/214383 Run-TryBot: Rebecca Stambler <rstambler@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Heschi Kreinick <heschi@google.com>
This commit is contained in:
parent
8f45075ebc
commit
533f309ed4
9
internal/lsp/cache/load.go
vendored
9
internal/lsp/cache/load.go
vendored
@ -38,10 +38,15 @@ type metadata struct {
|
|||||||
func (s *snapshot) load(ctx context.Context, scope interface{}) ([]*metadata, error) {
|
func (s *snapshot) load(ctx context.Context, scope interface{}) ([]*metadata, error) {
|
||||||
var query string
|
var query string
|
||||||
switch scope := scope.(type) {
|
switch scope := scope.(type) {
|
||||||
|
case []packagePath:
|
||||||
|
for i, p := range scope {
|
||||||
|
if i != 0 {
|
||||||
|
query += " "
|
||||||
|
}
|
||||||
|
query += string(p)
|
||||||
|
}
|
||||||
case packagePath:
|
case packagePath:
|
||||||
query = string(scope)
|
query = string(scope)
|
||||||
case packageID:
|
|
||||||
query = string(scope)
|
|
||||||
case fileURI:
|
case fileURI:
|
||||||
query = fmt.Sprintf("file=%s", span.URI(scope).Filename())
|
query = fmt.Sprintf("file=%s", span.URI(scope).Filename())
|
||||||
case directoryURI:
|
case directoryURI:
|
||||||
|
2
internal/lsp/cache/session.go
vendored
2
internal/lsp/cache/session.go
vendored
@ -293,7 +293,7 @@ func (s *session) DidModifyFile(ctx context.Context, c source.FileModification)
|
|||||||
// If the file was already known in the snapshot,
|
// If the file was already known in the snapshot,
|
||||||
// then use the already known file kind. Otherwise,
|
// then use the already known file kind. Otherwise,
|
||||||
// detect the file kind. This should only be needed for file creates.
|
// detect the file kind. This should only be needed for file creates.
|
||||||
if fh := view.getSnapshot().findFileHandle(ctx, f); fh != nil {
|
if fh := view.getSnapshot().findFileHandle(f); fh != nil {
|
||||||
kind = fh.Identity().Kind
|
kind = fh.Identity().Kind
|
||||||
} else {
|
} else {
|
||||||
kind = source.DetectLanguage("", c.URI.Filename())
|
kind = source.DetectLanguage("", c.URI.Filename())
|
||||||
|
101
internal/lsp/cache/snapshot.go
vendored
101
internal/lsp/cache/snapshot.go
vendored
@ -121,21 +121,20 @@ func (s *snapshot) PackageHandles(ctx context.Context, fh source.FileHandle) ([]
|
|||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *snapshot) PackageHandle(ctx context.Context, pkgID string) (source.PackageHandle, error) {
|
func (s *snapshot) packageHandle(ctx context.Context, id packageID) (*packageHandle, error) {
|
||||||
id := packageID(pkgID)
|
m := s.getMetadata(id)
|
||||||
var meta []*metadata
|
|
||||||
if m := s.getMetadata(id); m != nil {
|
// Don't reload metadata in this function.
|
||||||
meta = append(meta, m)
|
// Callers of this function must reload metadata themselves.
|
||||||
|
if m == nil {
|
||||||
|
return nil, errors.Errorf("%s has no metadata", id)
|
||||||
}
|
}
|
||||||
// We might need to reload the package. If it is a workspace package,
|
phs, load, check := s.shouldCheck([]*metadata{m})
|
||||||
// it may be a test variant and therefore have a different scope.
|
if load {
|
||||||
var scope interface{} = id
|
return nil, errors.Errorf("%s needs loading", id)
|
||||||
if path, ok := s.isWorkspacePackage(id); ok {
|
|
||||||
scope = path
|
|
||||||
}
|
}
|
||||||
phs, err := s.packageHandles(ctx, scope, meta)
|
if check {
|
||||||
if err != nil {
|
return s.buildPackageHandle(ctx, m.id, source.ParseFull)
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
var result *packageHandle
|
var result *packageHandle
|
||||||
for _, ph := range phs {
|
for _, ph := range phs {
|
||||||
@ -239,21 +238,23 @@ func (s *snapshot) shouldCheck(m []*metadata) (phs []*packageHandle, load, check
|
|||||||
return phs, false, false
|
return phs, false, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *snapshot) GetReverseDependencies(ctx context.Context, id string) ([]string, error) {
|
func (s *snapshot) GetReverseDependencies(ctx context.Context, id string) ([]source.PackageHandle, error) {
|
||||||
// Do not return results until the view has been initialized.
|
if err := s.awaitLoaded(ctx); err != nil {
|
||||||
if err := s.awaitInitialized(ctx); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ids := make(map[packageID]struct{})
|
ids := make(map[packageID]struct{})
|
||||||
s.transitiveReverseDependencies(packageID(id), ids)
|
s.transitiveReverseDependencies(packageID(id), ids)
|
||||||
|
|
||||||
// Make sure to delete the original package ID from the map.
|
// Make sure to delete the original package ID from the map.
|
||||||
delete(ids, packageID(id))
|
delete(ids, packageID(id))
|
||||||
|
|
||||||
var results []string
|
var results []source.PackageHandle
|
||||||
for id := range ids {
|
for id := range ids {
|
||||||
results = append(results, string(id))
|
ph, err := s.packageHandle(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
results = append(results, ph)
|
||||||
}
|
}
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
@ -318,18 +319,33 @@ func (s *snapshot) addPackage(ph *packageHandle) {
|
|||||||
s.packages[ph.packageKey()] = ph
|
s.packages[ph.packageKey()] = ph
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *snapshot) workspacePackageIDs() (ids []string) {
|
func (s *snapshot) workspacePackageIDs() (ids []packageID) {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
for id := range s.workspacePackages {
|
for id := range s.workspacePackages {
|
||||||
ids = append(ids, string(id))
|
ids = append(ids, id)
|
||||||
}
|
}
|
||||||
return ids
|
return ids
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *snapshot) WorkspacePackages(ctx context.Context) ([]source.PackageHandle, error) {
|
||||||
|
if err := s.awaitLoaded(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var results []source.PackageHandle
|
||||||
|
for _, pkgID := range s.workspacePackageIDs() {
|
||||||
|
ph, err := s.packageHandle(ctx, pkgID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
results = append(results, ph)
|
||||||
|
}
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *snapshot) KnownPackages(ctx context.Context) ([]source.PackageHandle, error) {
|
func (s *snapshot) KnownPackages(ctx context.Context) ([]source.PackageHandle, error) {
|
||||||
if err := s.awaitInitialized(ctx); err != nil {
|
if err := s.awaitLoaded(ctx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Collect PackageHandles for all of the workspace packages first.
|
// Collect PackageHandles for all of the workspace packages first.
|
||||||
@ -343,7 +359,7 @@ func (s *snapshot) KnownPackages(ctx context.Context) ([]source.PackageHandle, e
|
|||||||
|
|
||||||
var results []source.PackageHandle
|
var results []source.PackageHandle
|
||||||
for pkgID := range wsPackages {
|
for pkgID := range wsPackages {
|
||||||
ph, err := s.PackageHandle(ctx, string(pkgID))
|
ph, err := s.packageHandle(ctx, pkgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -374,7 +390,13 @@ func (s *snapshot) KnownPackages(ctx context.Context) ([]source.PackageHandle, e
|
|||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *snapshot) KnownImportPaths() map[string]source.Package {
|
func (s *snapshot) CachedImportPaths(ctx context.Context) (map[string]source.Package, error) {
|
||||||
|
// Don't reload workspace package metadata.
|
||||||
|
// This function is meant to only return currently cached information.
|
||||||
|
if err := s.view.awaitInitialized(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
@ -395,7 +417,7 @@ func (s *snapshot) KnownImportPaths() map[string]source.Package {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return results
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *snapshot) getPackage(id packageID, m source.ParseMode) *packageHandle {
|
func (s *snapshot) getPackage(id packageID, m source.ParseMode) *packageHandle {
|
||||||
@ -542,13 +564,40 @@ func (s *snapshot) getFileHandle(f *fileBase) source.FileHandle {
|
|||||||
return s.files[f.URI()]
|
return s.files[f.URI()]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *snapshot) findFileHandle(ctx context.Context, f *fileBase) source.FileHandle {
|
func (s *snapshot) findFileHandle(f *fileBase) source.FileHandle {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
return s.files[f.URI()]
|
return s.files[f.URI()]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *snapshot) awaitLoaded(ctx context.Context) error {
|
||||||
|
// Do not return results until the snapshot's view has been initialized.
|
||||||
|
if err := s.view.awaitInitialized(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Make sure that the workspace is in a valid state.
|
||||||
|
return s.reloadWorkspace(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// reloadWorkspace reloads the metadata for all invalidated workspace packages.
|
||||||
|
func (s *snapshot) reloadWorkspace(ctx context.Context) error {
|
||||||
|
s.mu.Lock()
|
||||||
|
var scope []packagePath
|
||||||
|
for id, pkgPath := range s.workspacePackages {
|
||||||
|
if s.metadata[id] == nil {
|
||||||
|
scope = append(scope, pkgPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.mu.Unlock()
|
||||||
|
|
||||||
|
if len(scope) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_, err := s.load(ctx, scope)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (s *snapshot) clone(ctx context.Context, withoutURI span.URI) *snapshot {
|
func (s *snapshot) clone(ctx context.Context, withoutURI span.URI) *snapshot {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
|
20
internal/lsp/cache/view.go
vendored
20
internal/lsp/cache/view.go
vendored
@ -416,6 +416,7 @@ func (v *view) Shutdown(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (v *view) shutdown(context.Context) {
|
func (v *view) shutdown(context.Context) {
|
||||||
|
// TODO: Cancel the view's initialization.
|
||||||
v.mu.Lock()
|
v.mu.Lock()
|
||||||
defer v.mu.Unlock()
|
defer v.mu.Unlock()
|
||||||
if v.cancel != nil {
|
if v.cancel != nil {
|
||||||
@ -467,15 +468,6 @@ func (v *view) getSnapshot() *snapshot {
|
|||||||
return v.snapshot
|
return v.snapshot
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *view) WorkspacePackageIDs(ctx context.Context) ([]string, error) {
|
|
||||||
s := v.getSnapshot()
|
|
||||||
|
|
||||||
if err := s.awaitInitialized(ctx); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return s.workspacePackageIDs(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *view) initialize(ctx context.Context, s *snapshot) {
|
func (v *view) initialize(ctx context.Context, s *snapshot) {
|
||||||
v.initializeOnce.Do(func() {
|
v.initializeOnce.Do(func() {
|
||||||
defer close(v.initialized)
|
defer close(v.initialized)
|
||||||
@ -493,13 +485,13 @@ func (v *view) initialize(ctx context.Context, s *snapshot) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *snapshot) awaitInitialized(ctx context.Context) error {
|
func (v *view) awaitInitialized(ctx context.Context) error {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
case <-s.view.initialized:
|
case <-v.initialized:
|
||||||
}
|
}
|
||||||
return s.view.initializationError
|
return v.initializationError
|
||||||
}
|
}
|
||||||
|
|
||||||
// invalidateContent invalidates the content of a Go file,
|
// invalidateContent invalidates the content of a Go file,
|
||||||
@ -521,8 +513,8 @@ func (v *view) invalidateContent(ctx context.Context, uri span.URI, kind source.
|
|||||||
v.cancelBackground()
|
v.cancelBackground()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not clone a snapshot until the workspace load has been completed.
|
// Do not clone a snapshot until its view has finished initializing.
|
||||||
<-v.initialized
|
_ = v.awaitInitialized(ctx)
|
||||||
|
|
||||||
// This should be the only time we hold the view's snapshot lock for any period of time.
|
// This should be the only time we hold the view's snapshot lock for any period of time.
|
||||||
v.snapshotMu.Lock()
|
v.snapshotMu.Lock()
|
||||||
|
@ -20,25 +20,20 @@ func (s *Server) diagnoseSnapshot(ctx context.Context, snapshot source.Snapshot)
|
|||||||
ctx, done := trace.StartSpan(ctx, "lsp:background-worker")
|
ctx, done := trace.StartSpan(ctx, "lsp:background-worker")
|
||||||
defer done()
|
defer done()
|
||||||
|
|
||||||
wsPackages, err := snapshot.View().WorkspacePackageIDs(ctx)
|
wsPackages, err := snapshot.WorkspacePackages(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(ctx, "diagnoseSnapshot: no workspace packages", err, telemetry.Directory.Of(snapshot.View().Folder))
|
log.Error(ctx, "diagnoseSnapshot: no workspace packages", err, telemetry.Directory.Of(snapshot.View().Folder))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, id := range wsPackages {
|
for _, ph := range wsPackages {
|
||||||
go func(id string) {
|
go func(ph source.PackageHandle) {
|
||||||
ph, err := snapshot.PackageHandle(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(ctx, "diagnoseSnapshot: no PackageHandle for package", err, telemetry.Package.Of(id))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
reports, _, err := source.PackageDiagnostics(ctx, snapshot, ph, false, snapshot.View().Options().DisabledAnalyses)
|
reports, _, err := source.PackageDiagnostics(ctx, snapshot, ph, false, snapshot.View().Options().DisabledAnalyses)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(ctx, "diagnoseSnapshot: no diagnostics", err, telemetry.Package.Of(ph.ID()))
|
log.Error(ctx, "diagnoseSnapshot: no diagnostics", err, telemetry.Package.Of(ph.ID()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.publishReports(ctx, reports, false)
|
s.publishReports(ctx, reports, false)
|
||||||
}(id)
|
}(ph)
|
||||||
}
|
}
|
||||||
// Run diagnostics on the go.mod file.
|
// Run diagnostics on the go.mod file.
|
||||||
s.diagnoseModfile(snapshot)
|
s.diagnoseModfile(snapshot)
|
||||||
|
@ -649,7 +649,10 @@ func (c *completer) selector(sel *ast.SelectorExpr) error {
|
|||||||
|
|
||||||
func (c *completer) unimportedMembers(id *ast.Ident) error {
|
func (c *completer) unimportedMembers(id *ast.Ident) error {
|
||||||
// Try loaded packages first. They're relevant, fast, and fully typed.
|
// Try loaded packages first. They're relevant, fast, and fully typed.
|
||||||
known := c.snapshot.KnownImportPaths()
|
known, err := c.snapshot.CachedImportPaths(c.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
for path, pkg := range known {
|
for path, pkg := range known {
|
||||||
if pkg.GetTypes().Name() != id.Name {
|
if pkg.GetTypes().Name() != id.Name {
|
||||||
continue
|
continue
|
||||||
|
@ -120,11 +120,7 @@ func PackageDiagnostics(ctx context.Context, snapshot Snapshot, ph PackageHandle
|
|||||||
log.Error(ctx, "no reverse dependencies", err)
|
log.Error(ctx, "no reverse dependencies", err)
|
||||||
return reports, warningMsg, nil
|
return reports, warningMsg, nil
|
||||||
}
|
}
|
||||||
for _, id := range reverseDeps {
|
for _, ph := range reverseDeps {
|
||||||
ph, err := snapshot.PackageHandle(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, warningMsg, err
|
|
||||||
}
|
|
||||||
pkg, err := ph.Check(ctx)
|
pkg, err := ph.Check(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, warningMsg, err
|
return nil, warningMsg, err
|
||||||
|
@ -45,11 +45,7 @@ func (i *IdentifierInfo) References(ctx context.Context) ([]*ReferenceInfo, erro
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, id := range reverseDeps {
|
for _, ph := range reverseDeps {
|
||||||
ph, err := i.Snapshot.PackageHandle(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
pkg, err := ph.Check(ctx)
|
pkg, err := ph.Check(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -38,23 +38,24 @@ type Snapshot interface {
|
|||||||
// ModFiles returns the FileHandles of the go.mod files attached to the view associated with this snapshot.
|
// ModFiles returns the FileHandles of the go.mod files attached to the view associated with this snapshot.
|
||||||
ModFiles(ctx context.Context) (FileHandle, FileHandle, error)
|
ModFiles(ctx context.Context) (FileHandle, FileHandle, error)
|
||||||
|
|
||||||
// PackageHandle returns the CheckPackageHandle for the given package ID.
|
|
||||||
PackageHandle(ctx context.Context, id string) (PackageHandle, error)
|
|
||||||
|
|
||||||
// PackageHandles returns the CheckPackageHandles for the packages
|
// PackageHandles returns the CheckPackageHandles for the packages
|
||||||
// that this file belongs to.
|
// that this file belongs to.
|
||||||
PackageHandles(ctx context.Context, fh FileHandle) ([]PackageHandle, error)
|
PackageHandles(ctx context.Context, fh FileHandle) ([]PackageHandle, error)
|
||||||
|
|
||||||
// GetActiveReverseDeps returns the active files belonging to the reverse
|
// GetActiveReverseDeps returns the active files belonging to the reverse
|
||||||
// dependencies of this file's package.
|
// dependencies of this file's package.
|
||||||
GetReverseDependencies(ctx context.Context, id string) ([]string, error)
|
GetReverseDependencies(ctx context.Context, id string) ([]PackageHandle, error)
|
||||||
|
|
||||||
// KnownImportPaths returns all the imported packages loaded in this snapshot,
|
// CachedImportPaths returns all the imported packages loaded in this snapshot,
|
||||||
// indexed by their import path.
|
// indexed by their import path.
|
||||||
KnownImportPaths() map[string]Package
|
CachedImportPaths(ctx context.Context) (map[string]Package, error)
|
||||||
|
|
||||||
// KnownPackages returns all the packages loaded in this snapshot.
|
// KnownPackages returns all the packages loaded in this snapshot.
|
||||||
KnownPackages(ctx context.Context) ([]PackageHandle, error)
|
KnownPackages(ctx context.Context) ([]PackageHandle, error)
|
||||||
|
|
||||||
|
// WorkspacePackages returns the PackageHandles for the snapshot's
|
||||||
|
// top-level packages.
|
||||||
|
WorkspacePackages(ctx context.Context) ([]PackageHandle, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PackageHandle represents a handle to a specific version of a package.
|
// PackageHandle represents a handle to a specific version of a package.
|
||||||
@ -118,10 +119,6 @@ type View interface {
|
|||||||
// original one will be.
|
// original one will be.
|
||||||
SetOptions(context.Context, Options) (View, error)
|
SetOptions(context.Context, Options) (View, error)
|
||||||
|
|
||||||
// WorkspacePackageIDs returns the ids of the packages at the top-level
|
|
||||||
// of the snapshot's view.
|
|
||||||
WorkspacePackageIDs(ctx context.Context) ([]string, error)
|
|
||||||
|
|
||||||
// Snapshot returns the current snapshot for the view.
|
// Snapshot returns the current snapshot for the view.
|
||||||
Snapshot() Snapshot
|
Snapshot() Snapshot
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user