.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.1.0 / internal / lsp / cache / cache.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.0/internal/lsp/cache/cache.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.0/internal/lsp/cache/cache.go
new file mode 100644 (file)
index 0000000..fa1b530
--- /dev/null
@@ -0,0 +1,298 @@
+// 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/sha256"
+       "fmt"
+       "go/ast"
+       "go/token"
+       "go/types"
+       "html/template"
+       "io/ioutil"
+       "os"
+       "reflect"
+       "sort"
+       "strconv"
+       "sync"
+       "sync/atomic"
+       "time"
+
+       "golang.org/x/tools/internal/event"
+       "golang.org/x/tools/internal/gocommand"
+       "golang.org/x/tools/internal/lsp/debug/tag"
+       "golang.org/x/tools/internal/lsp/source"
+       "golang.org/x/tools/internal/memoize"
+       "golang.org/x/tools/internal/span"
+)
+
+func New(ctx context.Context, options func(*source.Options)) *Cache {
+       index := atomic.AddInt64(&cacheIndex, 1)
+       c := &Cache{
+               id:          strconv.FormatInt(index, 10),
+               fset:        token.NewFileSet(),
+               options:     options,
+               fileContent: map[span.URI]*fileHandle{},
+       }
+       return c
+}
+
+type Cache struct {
+       id      string
+       fset    *token.FileSet
+       options func(*source.Options)
+
+       store memoize.Store
+
+       fileMu      sync.Mutex
+       fileContent map[span.URI]*fileHandle
+}
+
+type fileHandle struct {
+       modTime time.Time
+       uri     span.URI
+       bytes   []byte
+       hash    string
+       err     error
+
+       // size is the file length as reported by Stat, for the purpose of
+       // invalidation. Probably we could just use len(bytes), but this is done
+       // defensively in case the definition of file size in the file system
+       // differs.
+       size int64
+}
+
+func (h *fileHandle) Saved() bool {
+       return true
+}
+
+func (c *Cache) GetFile(ctx context.Context, uri span.URI) (source.FileHandle, error) {
+       return c.getFile(ctx, uri)
+}
+
+func (c *Cache) getFile(ctx context.Context, uri span.URI) (*fileHandle, error) {
+       fi, statErr := os.Stat(uri.Filename())
+       if statErr != nil {
+               return &fileHandle{
+                       err: statErr,
+                       uri: uri,
+               }, nil
+       }
+
+       c.fileMu.Lock()
+       fh, ok := c.fileContent[uri]
+       c.fileMu.Unlock()
+
+       // Check mtime and file size to infer whether the file has changed. This is
+       // an imperfect heuristic. Notably on some real systems (such as WSL) the
+       // filesystem clock resolution can be large -- 1/64s was observed. Therefore
+       // it's quite possible for multiple file modifications to occur within a
+       // single logical 'tick'. This can leave the cache in an incorrect state, but
+       // unfortunately we can't afford to pay the price of reading the actual file
+       // content here. Or to be more precise, reading would be a risky change and
+       // we don't know if we can afford it.
+       //
+       // We check file size in an attempt to reduce the probability of false cache
+       // hits.
+       if ok && fh.modTime.Equal(fi.ModTime()) && fh.size == fi.Size() {
+               return fh, nil
+       }
+
+       fh, err := readFile(ctx, uri, fi)
+       if err != nil {
+               return nil, err
+       }
+       c.fileMu.Lock()
+       c.fileContent[uri] = fh
+       c.fileMu.Unlock()
+       return fh, nil
+}
+
+// ioLimit limits the number of parallel file reads per process.
+var ioLimit = make(chan struct{}, 128)
+
+func readFile(ctx context.Context, uri span.URI, fi os.FileInfo) (*fileHandle, error) {
+       select {
+       case ioLimit <- struct{}{}:
+       case <-ctx.Done():
+               return nil, ctx.Err()
+       }
+       defer func() { <-ioLimit }()
+
+       ctx, done := event.Start(ctx, "cache.readFile", tag.File.Of(uri.Filename()))
+       _ = ctx
+       defer done()
+
+       data, err := ioutil.ReadFile(uri.Filename())
+       if err != nil {
+               return &fileHandle{
+                       modTime: fi.ModTime(),
+                       size:    fi.Size(),
+                       err:     err,
+               }, nil
+       }
+       return &fileHandle{
+               modTime: fi.ModTime(),
+               size:    fi.Size(),
+               uri:     uri,
+               bytes:   data,
+               hash:    hashContents(data),
+       }, nil
+}
+
+func (c *Cache) NewSession(ctx context.Context) *Session {
+       index := atomic.AddInt64(&sessionIndex, 1)
+       options := source.DefaultOptions().Clone()
+       if c.options != nil {
+               c.options(options)
+       }
+       s := &Session{
+               cache:       c,
+               id:          strconv.FormatInt(index, 10),
+               options:     options,
+               overlays:    make(map[span.URI]*overlay),
+               gocmdRunner: &gocommand.Runner{},
+       }
+       event.Log(ctx, "New session", KeyCreateSession.Of(s))
+       return s
+}
+
+func (c *Cache) FileSet() *token.FileSet {
+       return c.fset
+}
+
+func (h *fileHandle) URI() span.URI {
+       return h.uri
+}
+
+func (h *fileHandle) Kind() source.FileKind {
+       return source.DetectLanguage("", h.uri.Filename())
+}
+
+func (h *fileHandle) Hash() string {
+       return h.hash
+}
+
+func (h *fileHandle) FileIdentity() source.FileIdentity {
+       return source.FileIdentity{
+               URI:  h.uri,
+               Hash: h.hash,
+               Kind: h.Kind(),
+       }
+}
+
+func (h *fileHandle) Read() ([]byte, error) {
+       return h.bytes, h.err
+}
+
+func hashContents(contents []byte) string {
+       return fmt.Sprintf("%x", sha256.Sum256(contents))
+}
+
+var cacheIndex, sessionIndex, viewIndex int64
+
+func (c *Cache) ID() string                     { return c.id }
+func (c *Cache) MemStats() map[reflect.Type]int { return c.store.Stats() }
+
+type packageStat struct {
+       id        packageID
+       mode      source.ParseMode
+       file      int64
+       ast       int64
+       types     int64
+       typesInfo int64
+       total     int64
+}
+
+func (c *Cache) PackageStats(withNames bool) template.HTML {
+       var packageStats []packageStat
+       c.store.DebugOnlyIterate(func(k, v interface{}) {
+               switch k.(type) {
+               case packageHandleKey:
+                       v := v.(*packageData)
+                       if v.pkg == nil {
+                               break
+                       }
+                       var typsCost, typInfoCost int64
+                       if v.pkg.types != nil {
+                               typsCost = typesCost(v.pkg.types.Scope())
+                       }
+                       if v.pkg.typesInfo != nil {
+                               typInfoCost = typesInfoCost(v.pkg.typesInfo)
+                       }
+                       stat := packageStat{
+                               id:        v.pkg.m.id,
+                               mode:      v.pkg.mode,
+                               types:     typsCost,
+                               typesInfo: typInfoCost,
+                       }
+                       for _, f := range v.pkg.compiledGoFiles {
+                               stat.file += int64(len(f.Src))
+                               stat.ast += astCost(f.File)
+                       }
+                       stat.total = stat.file + stat.ast + stat.types + stat.typesInfo
+                       packageStats = append(packageStats, stat)
+               }
+       })
+       var totalCost int64
+       for _, stat := range packageStats {
+               totalCost += stat.total
+       }
+       sort.Slice(packageStats, func(i, j int) bool {
+               return packageStats[i].total > packageStats[j].total
+       })
+       html := "<table><thead><td>Name</td><td>total = file + ast + types + types info</td></thead>\n"
+       human := func(n int64) string {
+               return fmt.Sprintf("%.2f", float64(n)/(1024*1024))
+       }
+       var printedCost int64
+       for _, stat := range packageStats {
+               name := stat.id
+               if !withNames {
+                       name = "-"
+               }
+               html += fmt.Sprintf("<tr><td>%v (%v)</td><td>%v = %v + %v + %v + %v</td></tr>\n", name, stat.mode,
+                       human(stat.total), human(stat.file), human(stat.ast), human(stat.types), human(stat.typesInfo))
+               printedCost += stat.total
+               if float64(printedCost) > float64(totalCost)*.9 {
+                       break
+               }
+       }
+       html += "</table>\n"
+       return template.HTML(html)
+}
+
+func astCost(f *ast.File) int64 {
+       if f == nil {
+               return 0
+       }
+       var count int64
+       ast.Inspect(f, func(n ast.Node) bool {
+               count += 32 // nodes are pretty small.
+               return true
+       })
+       return count
+}
+
+func typesCost(scope *types.Scope) int64 {
+       cost := 64 + int64(scope.Len())*128 // types.object looks pretty big
+       for i := 0; i < scope.NumChildren(); i++ {
+               cost += typesCost(scope.Child(i))
+       }
+       return cost
+}
+
+func typesInfoCost(info *types.Info) int64 {
+       // Most of these refer to existing objects, with the exception of InitOrder, Selections, and Types.
+       cost := 24*len(info.Defs) +
+               32*len(info.Implicits) +
+               256*len(info.InitOrder) + // these are big, but there aren't many of them.
+               32*len(info.Scopes) +
+               128*len(info.Selections) + // wild guess
+               128*len(info.Types) + // wild guess
+               32*len(info.Uses)
+       return int64(cost)
+}