|  | // 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" | 
|  | "go/token" | 
|  |  | 
|  | "golang.org/x/tools/internal/lsp/source" | 
|  | "golang.org/x/tools/internal/memoize" | 
|  | errors "golang.org/x/xerrors" | 
|  | ) | 
|  |  | 
|  | type tokenKey struct { | 
|  | file source.FileIdentity | 
|  | } | 
|  |  | 
|  | type tokenHandle struct { | 
|  | handle *memoize.Handle | 
|  | file   source.FileHandle | 
|  | } | 
|  |  | 
|  | type tokenData struct { | 
|  | memoize.NoCopy | 
|  |  | 
|  | tok *token.File | 
|  | err error | 
|  | } | 
|  |  | 
|  | func (c *cache) TokenHandle(fh source.FileHandle) source.TokenHandle { | 
|  | key := tokenKey{ | 
|  | file: fh.Identity(), | 
|  | } | 
|  | h := c.store.Bind(key, func(ctx context.Context) interface{} { | 
|  | data := &tokenData{} | 
|  | data.tok, data.err = tokenFile(ctx, c, fh) | 
|  | return data | 
|  | }) | 
|  | return &tokenHandle{ | 
|  | handle: h, | 
|  | file:   fh, | 
|  | } | 
|  | } | 
|  |  | 
|  | func (h *tokenHandle) File() source.FileHandle { | 
|  | return h.file | 
|  | } | 
|  |  | 
|  | func (h *tokenHandle) Token(ctx context.Context) (*token.File, error) { | 
|  | v := h.handle.Get(ctx) | 
|  | if v == nil { | 
|  | return nil, ctx.Err() | 
|  | } | 
|  | data := v.(*tokenData) | 
|  | return data.tok, data.err | 
|  | } | 
|  |  | 
|  | func tokenFile(ctx context.Context, c *cache, fh source.FileHandle) (*token.File, error) { | 
|  | // First, check if we already have a parsed AST for this file's handle. | 
|  | for _, mode := range []source.ParseMode{ | 
|  | source.ParseHeader, | 
|  | source.ParseExported, | 
|  | source.ParseFull, | 
|  | } { | 
|  | pk := parseKey{ | 
|  | file: fh.Identity(), | 
|  | mode: mode, | 
|  | } | 
|  | pd, ok := c.store.Cached(pk).(*parseGoData) | 
|  | if !ok { | 
|  | continue | 
|  | } | 
|  | if pd.ast == nil { | 
|  | continue | 
|  | } | 
|  | if !pd.ast.Pos().IsValid() { | 
|  | continue | 
|  | } | 
|  | return c.FileSet().File(pd.ast.Pos()), nil | 
|  | } | 
|  | // We have not yet parsed this file. | 
|  | buf, _, err := fh.Read(ctx) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | tok := c.FileSet().AddFile(fh.Identity().URI.Filename(), -1, len(buf)) | 
|  | if tok == nil { | 
|  | return nil, errors.Errorf("no token.File for %s", fh.Identity().URI) | 
|  | } | 
|  | tok.SetLinesForContent(buf) | 
|  | return tok, nil | 
|  | } |