diff --git a/internal/lsp/cache/analysis.go b/internal/lsp/cache/analysis.go index 1f1316b42e..28956b2a83 100644 --- a/internal/lsp/cache/analysis.go +++ b/internal/lsp/cache/analysis.go @@ -17,6 +17,11 @@ import ( errors "golang.org/x/xerrors" ) +type actionKey struct { + pkg packageKey + analyzer string // analyzer name +} + func (s *snapshot) Analyze(ctx context.Context, id string, analyzers []*analysis.Analyzer) (map[*analysis.Analyzer][]*analysis.Diagnostic, error) { var roots []*actionHandle @@ -79,6 +84,10 @@ type packageFactKey struct { } func (s *snapshot) actionHandle(ctx context.Context, id packageID, mode source.ParseMode, a *analysis.Analyzer) (*actionHandle, error) { + ah := s.getAction(id, mode, a.Name) + if ah != nil { + return ah, nil + } cph := s.getPackage(id, mode) if cph == nil { return nil, errors.Errorf("no CheckPackageHandle for %s:%v", id, mode == source.ParseExported) @@ -90,7 +99,7 @@ func (s *snapshot) actionHandle(ctx context.Context, id packageID, mode source.P if err != nil { return nil, err } - ah := &actionHandle{ + ah = &actionHandle{ analyzer: a, pkg: pkg, } @@ -118,12 +127,13 @@ func (s *snapshot) actionHandle(ctx context.Context, id packageID, mode source.P ah.deps = append(ah.deps, depActionHandle) } } - h := s.view.session.cache.store.Bind(actionKey(a, cph), func(ctx context.Context) interface{} { + h := s.view.session.cache.store.Bind(buildActionKey(a, cph), func(ctx context.Context) interface{} { data := &actionData{} data.diagnostics, data.result, data.err = ah.exec(ctx, s.view.session.cache.fset) return data }) ah.handle = h + s.addAction(ah) return ah, nil } @@ -136,7 +146,7 @@ func (ah *actionHandle) analyze(ctx context.Context) ([]*analysis.Diagnostic, in return data.diagnostics, data.result, data.err } -func actionKey(a *analysis.Analyzer, cph *checkPackageHandle) string { +func buildActionKey(a *analysis.Analyzer, cph *checkPackageHandle) string { return hashContents([]byte(fmt.Sprintf("%s %s", a, string(cph.key)))) } diff --git a/internal/lsp/cache/check.go b/internal/lsp/cache/check.go index 42ce1c8a4d..620f3021a1 100644 --- a/internal/lsp/cache/check.go +++ b/internal/lsp/cache/check.go @@ -266,6 +266,7 @@ func (imp *importer) typeCheck(ctx context.Context, cph *checkPackageHandle) (*p pkg := &pkg{ view: imp.snapshot.view, id: cph.m.id, + mode: cph.mode, pkgPath: cph.m.pkgPath, files: cph.Files(), imports: make(map[packagePath]*pkg), diff --git a/internal/lsp/cache/pkg.go b/internal/lsp/cache/pkg.go index 01cc760ae9..9b3256a4ea 100644 --- a/internal/lsp/cache/pkg.go +++ b/internal/lsp/cache/pkg.go @@ -24,6 +24,7 @@ type pkg struct { // ID and package path have their own types to avoid being used interchangeably. id packageID + mode source.ParseMode pkgPath packagePath files []source.ParseGoHandle errors []packages.Error diff --git a/internal/lsp/cache/session.go b/internal/lsp/cache/session.go index 805e8b5748..a7ba68d417 100644 --- a/internal/lsp/cache/session.go +++ b/internal/lsp/cache/session.go @@ -104,6 +104,7 @@ func (s *session) NewView(ctx context.Context, name string, folder span.URI, opt metadata: make(map[packageID]*metadata), files: make(map[span.URI]source.FileHandle), importedBy: make(map[packageID][]packageID), + actions: make(map[actionKey]*actionHandle), }, ignoredURIs: make(map[span.URI]struct{}), builtin: &builtinPkg{}, diff --git a/internal/lsp/cache/snapshot.go b/internal/lsp/cache/snapshot.go index 19c77afd3a..856592e688 100644 --- a/internal/lsp/cache/snapshot.go +++ b/internal/lsp/cache/snapshot.go @@ -29,9 +29,12 @@ type snapshot struct { // It may invalidated when a file's content changes. files map[span.URI]source.FileHandle - // packages maps a file URI to a set of CheckPackageHandles to which that file belongs. + // packages maps a packageKey to a set of CheckPackageHandles to which that file belongs. // It may be invalidated when a file's content changes. packages map[packageKey]*checkPackageHandle + + // actions maps an actionkey to its actionHandle. + actions map[actionKey]*actionHandle } func (s *snapshot) View() source.View { @@ -93,6 +96,37 @@ func (s *snapshot) getPackage(id packageID, m source.ParseMode) *checkPackageHan return s.packages[key] } +func (s *snapshot) getAction(id packageID, m source.ParseMode, analyzer string) *actionHandle { + s.mu.Lock() + defer s.mu.Unlock() + + key := actionKey{ + pkg: packageKey{ + id: id, + mode: m, + }, + analyzer: analyzer, + } + return s.actions[key] +} + +func (s *snapshot) addAction(ah *actionHandle) { + s.mu.Lock() + defer s.mu.Unlock() + + key := actionKey{ + analyzer: ah.analyzer.Name, + pkg: packageKey{ + id: ah.pkg.id, + mode: ah.pkg.mode, + }, + } + if _, ok := s.actions[key]; ok { + return + } + s.actions[key] = ah +} + func (s *snapshot) getMetadataForURI(uri span.URI) (metadata []*metadata) { s.mu.Lock() defer s.mu.Unlock() @@ -175,6 +209,7 @@ func (s *snapshot) clone(ctx context.Context, withoutURI *span.URI, withoutTypes importedBy: make(map[packageID][]packageID), metadata: make(map[packageID]*metadata), packages: make(map[packageKey]*checkPackageHandle), + actions: make(map[actionKey]*actionHandle), files: make(map[span.URI]source.FileHandle), } // Copy all of the FileHandles except for the one that was invalidated. @@ -216,6 +251,16 @@ func (s *snapshot) clone(ctx context.Context, withoutURI *span.URI, withoutTypes } result.packages[k] = v } + // Copy the package analysis information. + for k, v := range s.actions { + if _, ok := withoutTypesIDs[k.pkg.id]; ok { + continue + } + if _, ok := withoutMetadataIDs[k.pkg.id]; ok { + continue + } + result.actions[k] = v + } // Copy the package metadata. for k, v := range s.metadata { if _, ok := withoutMetadataIDs[k]; ok {