| // Copyright 2019 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package cache |
| |
| import ( |
| "context" |
| "crypto/sha1" |
| "fmt" |
| "go/token" |
| "strconv" |
| "sync/atomic" |
| |
| "golang.org/x/tools/internal/lsp/debug" |
| "golang.org/x/tools/internal/lsp/source" |
| "golang.org/x/tools/internal/memoize" |
| "golang.org/x/tools/internal/span" |
| ) |
| |
| func New(options func(*source.Options)) source.Cache { |
| index := atomic.AddInt64(&cacheIndex, 1) |
| c := &cache{ |
| fs: &nativeFileSystem{}, |
| id: strconv.FormatInt(index, 10), |
| fset: token.NewFileSet(), |
| options: options, |
| } |
| debug.AddCache(debugCache{c}) |
| return c |
| } |
| |
| type cache struct { |
| fs source.FileSystem |
| id string |
| fset *token.FileSet |
| options func(*source.Options) |
| |
| 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, kind source.FileKind) source.FileHandle { |
| underlying := c.fs.GetFile(uri, kind) |
| 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(ctx context.Context) source.Session { |
| index := atomic.AddInt64(&sessionIndex, 1) |
| s := &session{ |
| cache: c, |
| id: strconv.FormatInt(index, 10), |
| options: source.DefaultOptions, |
| overlays: make(map[span.URI]*overlay), |
| } |
| debug.AddSession(debugSession{s}) |
| return s |
| } |
| |
| 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 |
| return fmt.Sprintf("%x", sha1.Sum(contents)) |
| } |
| |
| var cacheIndex, sessionIndex, viewIndex int64 |
| |
| type debugCache struct{ *cache } |
| |
| func (c *cache) ID() string { return c.id } |
| func (c debugCache) FileSet() *token.FileSet { return c.fset } |