diff --git a/internal/lsp/cache/cache.go b/internal/lsp/cache/cache.go index 81a8c779..b14fc9dd 100644 --- a/internal/lsp/cache/cache.go +++ b/internal/lsp/cache/cache.go @@ -5,6 +5,7 @@ package cache import ( + "context" "crypto/sha1" "fmt" "go/token" @@ -14,6 +15,7 @@ import ( "golang.org/x/tools/internal/lsp/debug" "golang.org/x/tools/internal/lsp/source" "golang.org/x/tools/internal/lsp/xlog" + "golang.org/x/tools/internal/memoize" "golang.org/x/tools/internal/span" ) @@ -32,10 +34,42 @@ type cache struct { fs source.FileSystem id string fset *token.FileSet + + store memoize.Store +} + +type fileKey struct { + identity source.FileIdentity +} + +type fileHandle struct { + cache *cache + underlying source.FileHandle + handle *memoize.Handle +} + +type fileData struct { + memoize.NoCopy + bytes []byte + hash string + err error } func (c *cache) GetFile(uri span.URI) source.FileHandle { - return c.fs.GetFile(uri) + underlying := c.fs.GetFile(uri) + key := fileKey{ + identity: underlying.Identity(), + } + h := c.store.Bind(key, func(ctx context.Context) interface{} { + data := &fileData{} + data.bytes, data.hash, data.err = underlying.Read(ctx) + return data + }) + return &fileHandle{ + cache: c, + underlying: underlying, + handle: h, + } } func (c *cache) NewSession(log xlog.Logger) source.Session { @@ -55,6 +89,23 @@ func (c *cache) FileSet() *token.FileSet { return c.fset } +func (h *fileHandle) FileSystem() source.FileSystem { + return h.cache +} + +func (h *fileHandle) Identity() source.FileIdentity { + return h.underlying.Identity() +} + +func (h *fileHandle) Read(ctx context.Context) ([]byte, string, error) { + v := h.handle.Get(ctx) + if v == nil { + return nil, "", ctx.Err() + } + data := v.(*fileData) + return data.bytes, data.hash, data.err +} + func hashContents(contents []byte) string { // TODO: consider whether sha1 is the best choice here // This hash is used for internal identity detection only diff --git a/internal/lsp/cache/external.go b/internal/lsp/cache/external.go index d2b82866..65f1cb5c 100644 --- a/internal/lsp/cache/external.go +++ b/internal/lsp/cache/external.go @@ -42,6 +42,7 @@ func (h *nativeFileHandle) Identity() source.FileIdentity { } func (h *nativeFileHandle) Read(ctx context.Context) ([]byte, string, error) { + //TODO: this should fail if the version is not the same as the handle data, err := ioutil.ReadFile(h.identity.URI.Filename()) if err != nil { return nil, "", err diff --git a/internal/lsp/cache/view.go b/internal/lsp/cache/view.go index 7d81fb05..40d3c1e0 100644 --- a/internal/lsp/cache/view.go +++ b/internal/lsp/cache/view.go @@ -225,7 +225,11 @@ func (v *view) SetContent(ctx context.Context, uri span.URI, content []byte) err // including any position and type information that depends on it. func (f *goFile) invalidateContent() { f.view.pcache.mu.Lock() - defer f.view.pcache.mu.Unlock() + f.handleMu.Lock() + defer func() { + f.handleMu.Unlock() + f.view.pcache.mu.Unlock() + }() f.ast = nil f.token = nil