// 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 }