// 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, errors.Errorf("Token: %v", 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 }