1
0
mirror of https://github.com/golang/go synced 2024-09-30 22:48:32 -06:00

internal/lsp: return snapshot when creating a view

Previously, we returned CheckPackageHandles when creating a new view.
Now, return the view's snapshot. Also, add a WorkspacePackageIDs
function in order to run diagnostics on them.

Fixes golang/go#35548

Change-Id: Ica7e41f5a7aa3ee9413feb4de4ee16e1825db2e1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/209418
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:
Rebecca Stambler 2019-11-29 23:51:14 -05:00
parent 73c7173a9f
commit a588733072
6 changed files with 46 additions and 23 deletions

View File

@ -76,20 +76,20 @@ func (s *session) Cache() source.Cache {
return s.cache return s.cache
} }
func (s *session) NewView(ctx context.Context, name string, folder span.URI, options source.Options) (source.View, []source.PackageHandle, error) { func (s *session) NewView(ctx context.Context, name string, folder span.URI, options source.Options) (source.View, source.Snapshot, error) {
s.viewMu.Lock() s.viewMu.Lock()
defer s.viewMu.Unlock() defer s.viewMu.Unlock()
v, phs, err := s.createView(ctx, name, folder, options) v, snapshot, err := s.createView(ctx, name, folder, options)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
s.views = append(s.views, v) s.views = append(s.views, v)
// we always need to drop the view map // we always need to drop the view map
s.viewMap = make(map[span.URI]source.View) s.viewMap = make(map[span.URI]source.View)
return v, phs, nil return v, snapshot, nil
} }
func (s *session) createView(ctx context.Context, name string, folder span.URI, options source.Options) (*view, []source.PackageHandle, error) { func (s *session) createView(ctx context.Context, name string, folder span.URI, options source.Options) (*view, *snapshot, error) {
index := atomic.AddInt64(&viewIndex, 1) index := atomic.AddInt64(&viewIndex, 1)
// We want a true background context and not a detached context here // We want a true background context and not a detached context here
// the spans need to be unrelated and no tag values should pollute it. // the spans need to be unrelated and no tag values should pollute it.
@ -131,8 +131,6 @@ func (s *session) createView(ctx context.Context, name string, folder span.URI,
// Preemptively load everything in this directory. // Preemptively load everything in this directory.
// TODO(matloob): Determine if this can be done in parallel with something else. // TODO(matloob): Determine if this can be done in parallel with something else.
// Perhaps different calls to NewView can be run in parallel? // Perhaps different calls to NewView can be run in parallel?
// TODO(matloob): By default when a new file is opened, its data is invalidated
// and it's loaded again. Determine if the redundant reload can be avoided.
v.snapshotMu.Lock() v.snapshotMu.Lock()
defer v.snapshotMu.Unlock() // The code after the snapshot is used isn't expensive. defer v.snapshotMu.Unlock() // The code after the snapshot is used isn't expensive.
m, err := v.snapshot.load(ctx, source.DirectoryURI(folder)) m, err := v.snapshot.load(ctx, source.DirectoryURI(folder))
@ -141,19 +139,17 @@ func (s *session) createView(ctx context.Context, name string, folder span.URI,
log.Error(ctx, "failed to load snapshot", err, telemetry.Directory.Of(folder)) log.Error(ctx, "failed to load snapshot", err, telemetry.Directory.Of(folder))
return v, nil, nil return v, nil, nil
} }
// Prepare CheckPackageHandles for every package that's been loaded. // Prepare CheckPackageHandles for every package that's been loaded.
// (*snapshot).CheckPackageHandle makes the assumption that every package that's // (*snapshot).CheckPackageHandle makes the assumption that every package that's
// been loaded has an existing checkPackageHandle. // been loaded has an existing checkPackageHandle.
phs, err := v.snapshot.checkWorkspacePackages(ctx, m) if _, err := v.snapshot.checkWorkspacePackages(ctx, m); err != nil {
if err != nil {
// Suppress all errors. // Suppress all errors.
log.Error(ctx, "failed to check snapshot", err, telemetry.Directory.Of(folder)) log.Error(ctx, "failed to check snapshot", err, telemetry.Directory.Of(folder))
return v, nil, nil return v, nil, nil
} }
debug.AddView(debugView{v}) debug.AddView(debugView{v})
return v, phs, nil return v, v.snapshot, nil
} }
// View returns the view by name. // View returns the view by name.
@ -248,14 +244,14 @@ func (s *session) removeView(ctx context.Context, view *view) error {
return nil return nil
} }
func (s *session) updateView(ctx context.Context, view *view, options source.Options) (*view, []source.PackageHandle, error) { func (s *session) updateView(ctx context.Context, view *view, options source.Options) (*view, *snapshot, error) {
s.viewMu.Lock() s.viewMu.Lock()
defer s.viewMu.Unlock() defer s.viewMu.Unlock()
i, err := s.dropView(ctx, view) i, err := s.dropView(ctx, view)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
v, phs, err := s.createView(ctx, view.name, view.folder, options) v, snapshot, err := s.createView(ctx, view.name, view.folder, options)
if err != nil { if err != nil {
// we have dropped the old view, but could not create the new one // we have dropped the old view, but could not create the new one
// this should not happen and is very bad, but we still need to clean // this should not happen and is very bad, but we still need to clean
@ -266,7 +262,7 @@ func (s *session) updateView(ctx context.Context, view *view, options source.Opt
} }
// substitute the new view into the array where the old view was // substitute the new view into the array where the old view was
s.views[i] = v s.views[i] = v
return v, phs, nil return v, snapshot, nil
} }
func (s *session) dropView(ctx context.Context, view *view) (int, error) { func (s *session) dropView(ctx context.Context, view *view) (int, error) {

View File

@ -250,6 +250,16 @@ func (s *snapshot) checkWorkspacePackages(ctx context.Context, m []*metadata) ([
return phs, nil return phs, nil
} }
func (s *snapshot) WorkspacePackageIDs(ctx context.Context) (ids []string) {
s.mu.Lock()
defer s.mu.Unlock()
for id := range s.workspacePackages {
ids = append(ids, string(id))
}
return ids
}
func (s *snapshot) KnownPackages(ctx context.Context) []source.Package { func (s *snapshot) KnownPackages(ctx context.Context) []source.Package {
// TODO(matloob): This function exists because KnownImportPaths can't // TODO(matloob): This function exists because KnownImportPaths can't
// determine the import paths of all packages. Remove this function // determine the import paths of all packages. Remove this function

View File

@ -16,12 +16,17 @@ import (
"golang.org/x/tools/internal/telemetry/trace" "golang.org/x/tools/internal/telemetry/trace"
) )
func (s *Server) diagnoseSnapshot(snapshot source.Snapshot, phs []source.PackageHandle) { func (s *Server) diagnoseSnapshot(snapshot source.Snapshot) {
ctx := snapshot.View().BackgroundContext() ctx := snapshot.View().BackgroundContext()
ctx, done := trace.StartSpan(ctx, "lsp:background-worker") ctx, done := trace.StartSpan(ctx, "lsp:background-worker")
defer done() defer done()
for _, ph := range phs { for _, id := range snapshot.WorkspacePackageIDs(ctx) {
ph, err := snapshot.PackageHandle(ctx, id)
if err != nil {
log.Error(ctx, "diagnoseSnapshot: no PackageHandle for workspace package", err, telemetry.Package.Of(id))
continue
}
if len(ph.CompiledGoFiles()) == 0 { if len(ph.CompiledGoFiles()) == 0 {
continue continue
} }

View File

@ -165,12 +165,12 @@ func (s *Server) addFolders(ctx context.Context, folders []protocol.WorkspaceFol
for _, folder := range folders { for _, folder := range folders {
uri := span.NewURI(folder.URI) uri := span.NewURI(folder.URI)
view, workspacePackages, err := s.addView(ctx, folder.Name, span.NewURI(folder.URI)) _, snapshot, err := s.addView(ctx, folder.Name, span.NewURI(folder.URI))
if err != nil { if err != nil {
viewErrors[uri] = err viewErrors[uri] = err
continue continue
} }
go s.diagnoseSnapshot(view.Snapshot(), workspacePackages) go s.diagnoseSnapshot(snapshot)
} }
if len(viewErrors) > 0 { if len(viewErrors) > 0 {
errMsg := fmt.Sprintf("Error loading workspace folders (expected %v, got %v)\n", len(folders), len(s.session.Views())-originalViews) errMsg := fmt.Sprintf("Error loading workspace folders (expected %v, got %v)\n", len(folders), len(s.session.Views())-originalViews)

View File

@ -40,6 +40,10 @@ type Snapshot interface {
// 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)
// WorkspacePackageIDs returns the ids of the packages at the top-level
// of the snapshot's view.
WorkspacePackageIDs(ctx context.Context) []string
// 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(id string) []string GetReverseDependencies(id string) []string
@ -128,6 +132,8 @@ type View interface {
// belong to or be part of a dependency of the given package. // belong to or be part of a dependency of the given package.
FindPosInPackage(pkg Package, pos token.Pos) (*ast.File, Package, error) FindPosInPackage(pkg Package, pos token.Pos) (*ast.File, Package, error)
// FindMapperInPackage returns the mapper associated with a file that may belong to
// the given package or one of its dependencies.
FindMapperInPackage(pkg Package, uri span.URI) (*protocol.ColumnMapper, error) FindMapperInPackage(pkg Package, uri span.URI) (*protocol.ColumnMapper, error)
// Snapshot returns the current snapshot for the view. // Snapshot returns the current snapshot for the view.
@ -140,7 +146,7 @@ type View interface {
// A session may have many active views at any given time. // A session may have many active views at any given time.
type Session interface { type Session interface {
// NewView creates a new View and returns it. // NewView creates a new View and returns it.
NewView(ctx context.Context, name string, folder span.URI, options Options) (View, []PackageHandle, error) NewView(ctx context.Context, name string, folder span.URI, options Options) (View, Snapshot, error)
// Cache returns the cache that created this session. // Cache returns the cache that created this session.
Cache() Cache Cache() Cache

View File

@ -26,7 +26,7 @@ func (s *Server) changeFolders(ctx context.Context, event protocol.WorkspaceFold
return nil return nil
} }
func (s *Server) addView(ctx context.Context, name string, uri span.URI) (source.View, []source.PackageHandle, error) { func (s *Server) addView(ctx context.Context, name string, uri span.URI) (source.View, source.Snapshot, error) {
s.stateMu.Lock() s.stateMu.Lock()
state := s.state state := s.state
s.stateMu.Unlock() s.stateMu.Unlock()
@ -35,8 +35,9 @@ func (s *Server) addView(ctx context.Context, name string, uri span.URI) (source
} }
options := s.session.Options() options := s.session.Options()
s.fetchConfig(ctx, name, uri, &options) if err := s.fetchConfig(ctx, name, uri, &options); err != nil {
return nil, nil, err
}
return s.session.NewView(ctx, name, uri, options) return s.session.NewView(ctx, name, uri, options)
} }
@ -44,8 +45,13 @@ func (s *Server) updateConfiguration(ctx context.Context, changed interface{}) e
// go through all the views getting the config // go through all the views getting the config
for _, view := range s.session.Views() { for _, view := range s.session.Views() {
options := s.session.Options() options := s.session.Options()
s.fetchConfig(ctx, view.Name(), view.Folder(), &options) if err := s.fetchConfig(ctx, view.Name(), view.Folder(), &options); err != nil {
view.SetOptions(ctx, options) return err
}
if _, err := view.SetOptions(ctx, options); err != nil {
return err
}
go s.diagnoseSnapshot(view.Snapshot())
} }
return nil return nil
} }