1// Copyright 2019 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package cache 6 7import ( 8 "context" 9 "crypto/sha1" 10 "fmt" 11 "go/token" 12 "strconv" 13 "sync/atomic" 14 15 "golang.org/x/tools/internal/lsp/debug" 16 "golang.org/x/tools/internal/lsp/source" 17 "golang.org/x/tools/internal/memoize" 18 "golang.org/x/tools/internal/span" 19) 20 21func New() source.Cache { 22 index := atomic.AddInt64(&cacheIndex, 1) 23 c := &cache{ 24 fs: &nativeFileSystem{}, 25 id: strconv.FormatInt(index, 10), 26 fset: token.NewFileSet(), 27 } 28 debug.AddCache(debugCache{c}) 29 return c 30} 31 32type cache struct { 33 fs source.FileSystem 34 id string 35 fset *token.FileSet 36 37 store memoize.Store 38} 39 40type fileKey struct { 41 identity source.FileIdentity 42} 43 44type fileHandle struct { 45 cache *cache 46 underlying source.FileHandle 47 handle *memoize.Handle 48} 49 50type fileData struct { 51 memoize.NoCopy 52 bytes []byte 53 hash string 54 err error 55} 56 57func (c *cache) GetFile(uri span.URI, kind source.FileKind) source.FileHandle { 58 underlying := c.fs.GetFile(uri, kind) 59 key := fileKey{ 60 identity: underlying.Identity(), 61 } 62 h := c.store.Bind(key, func(ctx context.Context) interface{} { 63 data := &fileData{} 64 data.bytes, data.hash, data.err = underlying.Read(ctx) 65 return data 66 }) 67 return &fileHandle{ 68 cache: c, 69 underlying: underlying, 70 handle: h, 71 } 72} 73 74func (c *cache) NewSession(ctx context.Context) source.Session { 75 index := atomic.AddInt64(&sessionIndex, 1) 76 s := &session{ 77 cache: c, 78 id: strconv.FormatInt(index, 10), 79 options: source.DefaultOptions, 80 overlays: make(map[span.URI]*overlay), 81 filesWatchMap: NewWatchMap(), 82 } 83 debug.AddSession(debugSession{s}) 84 return s 85} 86 87func (c *cache) FileSet() *token.FileSet { 88 return c.fset 89} 90 91func (h *fileHandle) FileSystem() source.FileSystem { 92 return h.cache 93} 94 95func (h *fileHandle) Identity() source.FileIdentity { 96 return h.underlying.Identity() 97} 98 99func (h *fileHandle) Read(ctx context.Context) ([]byte, string, error) { 100 v := h.handle.Get(ctx) 101 if v == nil { 102 return nil, "", ctx.Err() 103 } 104 data := v.(*fileData) 105 return data.bytes, data.hash, data.err 106} 107 108func hashContents(contents []byte) string { 109 // TODO: consider whether sha1 is the best choice here 110 // This hash is used for internal identity detection only 111 return fmt.Sprintf("%x", sha1.Sum(contents)) 112} 113 114var cacheIndex, sessionIndex, viewIndex int64 115 116type debugCache struct{ *cache } 117 118func (c *cache) ID() string { return c.id } 119func (c debugCache) FileSet() *token.FileSet { return c.fset } 120