gopls/internal/lsp: keep track of overlays on the files map
The overlays method showed up as a hot spot in the google-cloud-go repo
(via reloadOrphanedFiles), because it walks all files to find the small
number of overlays.
We already have a filesMap abstraction; use it to keep track of overlays
in a separate map.
For golang/go#60089
Change-Id: I62c6c688d012beaa4b0f255225993da961cb9dad
Reviewed-on: https://go-review.googlesource.com/c/tools/+/496442
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
diff --git a/gopls/internal/lsp/cache/maps.go b/gopls/internal/lsp/cache/maps.go
index 0ad4ac9..533c339 100644
--- a/gopls/internal/lsp/cache/maps.go
+++ b/gopls/internal/lsp/cache/maps.go
@@ -15,7 +15,8 @@
// TODO(euroelessar): Use generics once support for go1.17 is dropped.
type filesMap struct {
- impl *persistent.Map
+ impl *persistent.Map
+ overlayMap map[span.URI]*Overlay // the subset that are overlays
}
// uriLessInterface is the < relation for "any" values containing span.URIs.
@@ -25,13 +26,19 @@
func newFilesMap() filesMap {
return filesMap{
- impl: persistent.NewMap(uriLessInterface),
+ impl: persistent.NewMap(uriLessInterface),
+ overlayMap: make(map[span.URI]*Overlay),
}
}
func (m filesMap) Clone() filesMap {
+ overlays := make(map[span.URI]*Overlay, len(m.overlayMap))
+ for k, v := range m.overlayMap {
+ overlays[k] = v
+ }
return filesMap{
- impl: m.impl.Clone(),
+ impl: m.impl.Clone(),
+ overlayMap: overlays,
}
}
@@ -55,10 +62,30 @@
func (m filesMap) Set(key span.URI, value source.FileHandle) {
m.impl.Set(key, value, nil)
+
+ if o, ok := value.(*Overlay); ok {
+ m.overlayMap[key] = o
+ } else {
+ // Setting a non-overlay must delete the corresponding overlay, to preserve
+ // the accuracy of the overlay set.
+ delete(m.overlayMap, key)
+ }
}
-func (m filesMap) Delete(key span.URI) {
+func (m *filesMap) Delete(key span.URI) {
m.impl.Delete(key)
+ delete(m.overlayMap, key)
+}
+
+// overlays returns a new unordered array of overlay files.
+func (m filesMap) overlays() []*Overlay {
+ // In practice we will always have at least one overlay, so there is no need
+ // to optimize for the len=0 case by returning a nil slice.
+ overlays := make([]*Overlay, 0, len(m.overlayMap))
+ for _, o := range m.overlayMap {
+ overlays = append(overlays, o)
+ }
+ return overlays
}
func packageIDLessInterface(x, y interface{}) bool {
diff --git a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snapshot.go
index 7567589..bf22164 100644
--- a/gopls/internal/lsp/cache/snapshot.go
+++ b/gopls/internal/lsp/cache/snapshot.go
@@ -624,21 +624,11 @@
return overlays
}
-// TODO(rfindley): investigate whether it would be worthwhile to keep track of
-// overlays when we get them via GetFile.
func (s *snapshot) overlays() []*Overlay {
s.mu.Lock()
defer s.mu.Unlock()
- var overlays []*Overlay
- s.files.Range(func(uri span.URI, fh source.FileHandle) {
- overlay, ok := fh.(*Overlay)
- if !ok {
- return
- }
- overlays = append(overlays, overlay)
- })
- return overlays
+ return s.files.overlays()
}
// Package data kinds, identifying various package data that may be stored in