mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
internal/lsp: reduce usage of column mapper
A mapper is always uniquely tied to a file at a specific version, so just build it when we get a new *ast.File. We build the mapper using the *token.File associated with the particular *ast.File, which is why there is one per ParseGoHandle instead of FileHandle. Change-Id: Ida40981ef91f6133cdd07e9793337fcd67510fba Reviewed-on: https://go-review.googlesource.com/c/tools/+/194517 Run-TryBot: Rebecca Stambler <rstambler@golang.org> Reviewed-by: Ian Cottrell <iancottrell@google.com>
This commit is contained in:
parent
b31ee645dd
commit
5edc6aefed
4
internal/lsp/cache/check.go
vendored
4
internal/lsp/cache/check.go
vendored
@ -258,7 +258,7 @@ func (imp *importer) typeCheck(ctx context.Context, cph *checkPackageHandle, m *
|
||||
go func(i int, ph source.ParseGoHandle) {
|
||||
defer wg.Done()
|
||||
|
||||
files[i], parseErrors[i] = ph.Parse(ctx)
|
||||
files[i], _, parseErrors[i] = ph.Parse(ctx)
|
||||
}(i, ph)
|
||||
}
|
||||
wg.Wait()
|
||||
@ -350,7 +350,7 @@ func (imp *importer) cachePerFile(ctx context.Context, gof *goFile, ph source.Pa
|
||||
}
|
||||
gof.pkgs[cph.m.id] = cph
|
||||
|
||||
file, err := ph.Parse(ctx)
|
||||
file, _, err := ph.Parse(ctx)
|
||||
if file == nil {
|
||||
return errors.Errorf("no AST for %s: %v", ph.File().Identity().URI, err)
|
||||
}
|
||||
|
2
internal/lsp/cache/load.go
vendored
2
internal/lsp/cache/load.go
vendored
@ -190,7 +190,7 @@ func (v *view) shouldRunGopackages(ctx context.Context, f *goFile, fh source.Fil
|
||||
return true
|
||||
}
|
||||
// Get file content in case we don't already have it.
|
||||
parsed, err := v.session.cache.ParseGoHandle(fh, source.ParseHeader).Parse(ctx)
|
||||
parsed, _, err := v.session.cache.ParseGoHandle(fh, source.ParseHeader).Parse(ctx)
|
||||
if err == context.Canceled {
|
||||
log.Error(ctx, "parsing file header", err, tag.Of("file", f.URI()))
|
||||
return false
|
||||
|
37
internal/lsp/cache/parse.go
vendored
37
internal/lsp/cache/parse.go
vendored
@ -13,9 +13,11 @@ import (
|
||||
"go/token"
|
||||
"reflect"
|
||||
|
||||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
"golang.org/x/tools/internal/lsp/source"
|
||||
"golang.org/x/tools/internal/lsp/telemetry"
|
||||
"golang.org/x/tools/internal/memoize"
|
||||
"golang.org/x/tools/internal/span"
|
||||
"golang.org/x/tools/internal/telemetry/log"
|
||||
"golang.org/x/tools/internal/telemetry/trace"
|
||||
errors "golang.org/x/xerrors"
|
||||
@ -39,8 +41,9 @@ type parseGoHandle struct {
|
||||
type parseGoData struct {
|
||||
memoize.NoCopy
|
||||
|
||||
ast *ast.File
|
||||
err error
|
||||
ast *ast.File
|
||||
mapper *protocol.ColumnMapper
|
||||
err error
|
||||
}
|
||||
|
||||
func (c *cache) ParseGoHandle(fh source.FileHandle, mode source.ParseMode) source.ParseGoHandle {
|
||||
@ -51,6 +54,17 @@ func (c *cache) ParseGoHandle(fh source.FileHandle, mode source.ParseMode) sourc
|
||||
h := c.store.Bind(key, func(ctx context.Context) interface{} {
|
||||
data := &parseGoData{}
|
||||
data.ast, data.err = parseGo(ctx, c, fh, mode)
|
||||
tok := c.FileSet().File(data.ast.Pos())
|
||||
if tok == nil {
|
||||
return data
|
||||
}
|
||||
uri := fh.Identity().URI
|
||||
content, _, err := fh.Read(ctx)
|
||||
if err != nil {
|
||||
data.err = err
|
||||
return data
|
||||
}
|
||||
data.mapper = newColumnMapper(uri, c.FileSet(), tok, content)
|
||||
return data
|
||||
})
|
||||
return &parseGoHandle{
|
||||
@ -59,6 +73,13 @@ func (c *cache) ParseGoHandle(fh source.FileHandle, mode source.ParseMode) sourc
|
||||
mode: mode,
|
||||
}
|
||||
}
|
||||
func newColumnMapper(uri span.URI, fset *token.FileSet, tok *token.File, content []byte) *protocol.ColumnMapper {
|
||||
return &protocol.ColumnMapper{
|
||||
URI: uri,
|
||||
Converter: span.NewTokenConverter(fset, tok),
|
||||
Content: content,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *parseGoHandle) File() source.FileHandle {
|
||||
return h.file
|
||||
@ -68,22 +89,22 @@ func (h *parseGoHandle) Mode() source.ParseMode {
|
||||
return h.mode
|
||||
}
|
||||
|
||||
func (h *parseGoHandle) Parse(ctx context.Context) (*ast.File, error) {
|
||||
func (h *parseGoHandle) Parse(ctx context.Context) (*ast.File, *protocol.ColumnMapper, error) {
|
||||
v := h.handle.Get(ctx)
|
||||
if v == nil {
|
||||
return nil, ctx.Err()
|
||||
return nil, nil, ctx.Err()
|
||||
}
|
||||
data := v.(*parseGoData)
|
||||
return data.ast, data.err
|
||||
return data.ast, data.mapper, data.err
|
||||
}
|
||||
|
||||
func (h *parseGoHandle) Cached(ctx context.Context) (*ast.File, error) {
|
||||
func (h *parseGoHandle) Cached(ctx context.Context) (*ast.File, *protocol.ColumnMapper, error) {
|
||||
v := h.handle.Cached()
|
||||
if v == nil {
|
||||
return nil, errors.Errorf("no cached value for %s", h.file.Identity().URI)
|
||||
return nil, nil, errors.Errorf("no cached value for %s", h.file.Identity().URI)
|
||||
}
|
||||
data := v.(*parseGoData)
|
||||
return data.ast, data.err
|
||||
return data.ast, data.mapper, data.err
|
||||
}
|
||||
|
||||
func hashParseKey(ph source.ParseGoHandle) string {
|
||||
|
11
internal/lsp/cache/pkg.go
vendored
11
internal/lsp/cache/pkg.go
vendored
@ -7,7 +7,6 @@ package cache
|
||||
import (
|
||||
"context"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"sort"
|
||||
"sync"
|
||||
@ -155,7 +154,7 @@ func (pkg *pkg) GetHandles() []source.ParseGoHandle {
|
||||
func (pkg *pkg) GetSyntax(ctx context.Context) []*ast.File {
|
||||
var syntax []*ast.File
|
||||
for _, ph := range pkg.files {
|
||||
file, _ := ph.Cached(ctx)
|
||||
file, _, _ := ph.Cached(ctx)
|
||||
if file != nil {
|
||||
syntax = append(syntax, file)
|
||||
}
|
||||
@ -203,7 +202,7 @@ func (pkg *pkg) GetDiagnostics() []source.Diagnostic {
|
||||
return diags
|
||||
}
|
||||
|
||||
func (p *pkg) FindFile(ctx context.Context, uri span.URI, pos token.Pos) (source.ParseGoHandle, *ast.File, source.Package, error) {
|
||||
func (p *pkg) FindFile(ctx context.Context, uri span.URI) (source.ParseGoHandle, *ast.File, source.Package, error) {
|
||||
queue := []*pkg{p}
|
||||
seen := make(map[string]bool)
|
||||
|
||||
@ -214,13 +213,11 @@ func (p *pkg) FindFile(ctx context.Context, uri span.URI, pos token.Pos) (source
|
||||
|
||||
for _, ph := range pkg.files {
|
||||
if ph.File().Identity().URI == uri {
|
||||
file, err := ph.Cached(ctx)
|
||||
file, _, err := ph.Cached(ctx)
|
||||
if file == nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
if file.Pos() <= pos && pos <= file.End() {
|
||||
return ph, file, pkg, nil
|
||||
}
|
||||
return ph, file, pkg, nil
|
||||
}
|
||||
}
|
||||
for _, dep := range pkg.imports {
|
||||
|
8
internal/lsp/cache/view.go
vendored
8
internal/lsp/cache/view.go
vendored
@ -8,7 +8,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"os"
|
||||
@ -310,8 +309,11 @@ func (v *view) buildBuiltinPkg(ctx context.Context) {
|
||||
pkg := pkgs[0]
|
||||
files := make(map[string]*ast.File)
|
||||
for _, filename := range pkg.GoFiles {
|
||||
file, err := parser.ParseFile(cfg.Fset, filename, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
fh := v.session.GetFile(span.FileURI(filename))
|
||||
ph := v.session.cache.ParseGoHandle(fh, source.ParseFull)
|
||||
file, _, err := ph.Parse(ctx)
|
||||
if file == nil {
|
||||
log.Error(ctx, "failed to parse builtin", err, telemetry.File.Of(filename))
|
||||
v.builtinPkg, _ = ast.NewPackage(cfg.Fset, nil, nil, nil)
|
||||
return
|
||||
}
|
||||
|
@ -344,7 +344,12 @@ func (c *cmdClient) getFile(ctx context.Context, uri span.URI) *cmdFile {
|
||||
}
|
||||
f := c.fset.AddFile(fname, -1, len(content))
|
||||
f.SetLinesForContent(content)
|
||||
file.mapper = protocol.NewColumnMapper(uri, fname, c.fset, f, content)
|
||||
converter := span.NewContentConverter(fname, content)
|
||||
file.mapper = &protocol.ColumnMapper{
|
||||
URI: uri,
|
||||
Converter: converter,
|
||||
Content: content,
|
||||
}
|
||||
}
|
||||
return file
|
||||
}
|
||||
|
@ -28,17 +28,10 @@ func (s *Server) documentLink(ctx context.Context, params *protocol.DocumentLink
|
||||
return nil, err
|
||||
}
|
||||
fh := f.Handle(ctx)
|
||||
data, _, err := fh.Read(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
file, err := view.Session().Cache().ParseGoHandle(fh, source.ParseFull).Parse(ctx)
|
||||
file, m, err := view.Session().Cache().ParseGoHandle(fh, source.ParseFull).Parse(ctx)
|
||||
if file == nil {
|
||||
return nil, err
|
||||
}
|
||||
tok := view.Session().Cache().FileSet().File(file.Pos())
|
||||
m := protocol.NewColumnMapper(f.URI(), f.URI().Filename(), view.Session().Cache().FileSet(), tok, data)
|
||||
|
||||
var links []protocol.DocumentLink
|
||||
ast.Inspect(file, func(node ast.Node) bool {
|
||||
switch n := node.(type) {
|
||||
|
@ -352,7 +352,7 @@ func (r *runner) FoldingRange(t *testing.T, data tests.FoldingRanges) {
|
||||
}
|
||||
|
||||
func (r *runner) foldingRanges(t *testing.T, prefix string, uri span.URI, ranges []protocol.FoldingRange) {
|
||||
m, err := r.mapper(uri)
|
||||
m, err := r.data.Mapper(uri)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -484,7 +484,7 @@ func (r *runner) Format(t *testing.T, data tests.Formats) {
|
||||
}
|
||||
continue
|
||||
}
|
||||
m, err := r.mapper(uri)
|
||||
m, err := r.data.Mapper(uri)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -520,7 +520,7 @@ func (r *runner) Import(t *testing.T, data tests.Imports) {
|
||||
}
|
||||
continue
|
||||
}
|
||||
m, err := r.mapper(uri)
|
||||
m, err := r.data.Mapper(uri)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -572,7 +572,7 @@ func (r *runner) SuggestedFix(t *testing.T, data tests.SuggestedFixes) {
|
||||
}
|
||||
continue
|
||||
}
|
||||
m, err := r.mapper(f.URI())
|
||||
m, err := r.data.Mapper(f.URI())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -595,7 +595,7 @@ func (r *runner) SuggestedFix(t *testing.T, data tests.SuggestedFixes) {
|
||||
|
||||
func (r *runner) Definition(t *testing.T, data tests.Definitions) {
|
||||
for _, d := range data {
|
||||
sm, err := r.mapper(d.Src.URI())
|
||||
sm, err := r.data.Mapper(d.Src.URI())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -648,7 +648,7 @@ func (r *runner) Definition(t *testing.T, data tests.Definitions) {
|
||||
}
|
||||
} else if !d.OnlyHover {
|
||||
locURI := span.NewURI(locs[0].URI)
|
||||
lm, err := r.mapper(locURI)
|
||||
lm, err := r.data.Mapper(locURI)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -665,7 +665,7 @@ func (r *runner) Definition(t *testing.T, data tests.Definitions) {
|
||||
|
||||
func (r *runner) Highlight(t *testing.T, data tests.Highlights) {
|
||||
for name, locations := range data {
|
||||
m, err := r.mapper(locations[0].URI())
|
||||
m, err := r.data.Mapper(locations[0].URI())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -701,7 +701,7 @@ func (r *runner) Highlight(t *testing.T, data tests.Highlights) {
|
||||
|
||||
func (r *runner) Reference(t *testing.T, data tests.References) {
|
||||
for src, itemList := range data {
|
||||
sm, err := r.mapper(src.URI())
|
||||
sm, err := r.data.Mapper(src.URI())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -712,7 +712,7 @@ func (r *runner) Reference(t *testing.T, data tests.References) {
|
||||
|
||||
want := make(map[protocol.Location]bool)
|
||||
for _, pos := range itemList {
|
||||
m, err := r.mapper(pos.URI())
|
||||
m, err := r.data.Mapper(pos.URI())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -749,7 +749,7 @@ func (r *runner) Rename(t *testing.T, data tests.Renames) {
|
||||
|
||||
uri := spn.URI()
|
||||
filename := uri.Filename()
|
||||
sm, err := r.mapper(uri)
|
||||
sm, err := r.data.Mapper(uri)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -777,7 +777,7 @@ func (r *runner) Rename(t *testing.T, data tests.Renames) {
|
||||
|
||||
var res []string
|
||||
for uri, edits := range *workspaceEdits.Changes {
|
||||
m, err := r.mapper(span.URI(uri))
|
||||
m, err := r.data.Mapper(span.URI(uri))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -813,7 +813,7 @@ func (r *runner) Rename(t *testing.T, data tests.Renames) {
|
||||
|
||||
func (r *runner) PrepareRename(t *testing.T, data tests.PrepareRenames) {
|
||||
for src, want := range data {
|
||||
m, err := r.mapper(src.URI())
|
||||
m, err := r.data.Mapper(src.URI())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -928,7 +928,7 @@ func summarizeSymbols(t *testing.T, i int, want []protocol.DocumentSymbol, got [
|
||||
|
||||
func (r *runner) SignatureHelp(t *testing.T, data tests.Signatures) {
|
||||
for spn, expectedSignatures := range data {
|
||||
m, err := r.mapper(spn.URI())
|
||||
m, err := r.data.Mapper(spn.URI())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -960,6 +960,9 @@ func (r *runner) SignatureHelp(t *testing.T, data tests.Signatures) {
|
||||
}
|
||||
continue
|
||||
}
|
||||
if gotSignatures == nil {
|
||||
t.Fatalf("expected %v, got nil", expectedSignatures)
|
||||
}
|
||||
if diff := diffSignatures(spn, expectedSignatures, gotSignatures); diff != "" {
|
||||
t.Error(diff)
|
||||
}
|
||||
@ -1003,7 +1006,7 @@ func diffSignatures(spn span.Span, want *source.SignatureInformation, got *proto
|
||||
|
||||
func (r *runner) Link(t *testing.T, data tests.Links) {
|
||||
for uri, wantLinks := range data {
|
||||
m, err := r.mapper(uri)
|
||||
m, err := r.data.Mapper(uri)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -1054,27 +1057,6 @@ func (r *runner) Link(t *testing.T, data tests.Links) {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *runner) mapper(uri span.URI) (*protocol.ColumnMapper, error) {
|
||||
filename := uri.Filename()
|
||||
fset := r.data.Exported.ExpectFileSet
|
||||
var f *token.File
|
||||
fset.Iterate(func(check *token.File) bool {
|
||||
if check.Name() == filename {
|
||||
f = check
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
if f == nil {
|
||||
return nil, fmt.Errorf("no token.File for %s", uri)
|
||||
}
|
||||
content, err := r.data.Exported.FileContents(f.Name())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return protocol.NewColumnMapper(uri, filename, fset, f, content), nil
|
||||
}
|
||||
|
||||
func TestBytesOffset(t *testing.T) {
|
||||
tests := []struct {
|
||||
text string
|
||||
@ -1102,7 +1084,13 @@ func TestBytesOffset(t *testing.T) {
|
||||
fset := token.NewFileSet()
|
||||
f := fset.AddFile(fname, -1, len(test.text))
|
||||
f.SetLinesForContent([]byte(test.text))
|
||||
mapper := protocol.NewColumnMapper(span.FileURI(fname), fname, fset, f, []byte(test.text))
|
||||
uri := span.FileURI(fname)
|
||||
converter := span.NewContentConverter(fname, []byte(test.text))
|
||||
mapper := &protocol.ColumnMapper{
|
||||
URI: uri,
|
||||
Converter: converter,
|
||||
Content: []byte(test.text),
|
||||
}
|
||||
got, err := mapper.Point(test.pos)
|
||||
if err != nil && test.want != -1 {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
|
@ -8,7 +8,6 @@ package protocol
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
|
||||
"golang.org/x/tools/internal/span"
|
||||
errors "golang.org/x/xerrors"
|
||||
@ -24,20 +23,6 @@ func NewURI(uri span.URI) string {
|
||||
return string(uri)
|
||||
}
|
||||
|
||||
func NewColumnMapper(uri span.URI, filename string, fset *token.FileSet, f *token.File, content []byte) *ColumnMapper {
|
||||
var converter *span.TokenConverter
|
||||
if f == nil {
|
||||
converter = span.NewContentConverter(filename, content)
|
||||
} else {
|
||||
converter = span.NewTokenConverter(fset, f)
|
||||
}
|
||||
return &ColumnMapper{
|
||||
URI: uri,
|
||||
Converter: converter,
|
||||
Content: content,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *ColumnMapper) Location(s span.Span) (Location, error) {
|
||||
rng, err := m.Range(s)
|
||||
if err != nil {
|
||||
|
@ -388,20 +388,10 @@ func Completion(ctx context.Context, view View, f GoFile, pos protocol.Position,
|
||||
ph = h
|
||||
}
|
||||
}
|
||||
file, err := ph.Cached(ctx)
|
||||
file, m, err := ph.Cached(ctx)
|
||||
if file == nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
data, _, err := ph.File().Read(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
fset := view.Session().Cache().FileSet()
|
||||
tok := fset.File(file.Pos())
|
||||
if tok == nil {
|
||||
return nil, nil, errors.Errorf("no token.File for %s", f.URI())
|
||||
}
|
||||
m := protocol.NewColumnMapper(f.URI(), f.URI().Filename(), fset, tok, data)
|
||||
spn, err := m.PointSpan(pos)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"golang.org/x/tools/internal/span"
|
||||
"golang.org/x/tools/internal/telemetry/log"
|
||||
"golang.org/x/tools/internal/telemetry/tag"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// formatCompletion creates a completion item for a given candidate.
|
||||
@ -124,10 +125,13 @@ func (c *completer) item(cand candidate) (CompletionItem, error) {
|
||||
}
|
||||
|
||||
uri := span.FileURI(pos.Filename)
|
||||
_, file, pkg, err := c.pkg.FindFile(c.ctx, uri, obj.Pos())
|
||||
_, file, pkg, err := c.pkg.FindFile(c.ctx, uri)
|
||||
if err != nil {
|
||||
return CompletionItem{}, err
|
||||
}
|
||||
if !(file.Pos() <= obj.Pos() && obj.Pos() <= file.End()) {
|
||||
return CompletionItem{}, errors.Errorf("no file for %s", obj.Name())
|
||||
}
|
||||
ident, err := findIdentifier(c.ctx, c.view, []Package{pkg}, file, obj.Pos())
|
||||
if err != nil {
|
||||
return CompletionItem{}, err
|
||||
|
@ -190,29 +190,22 @@ func spanToRange(ctx context.Context, view View, pkg Package, spn span.Span, isT
|
||||
var (
|
||||
fh FileHandle
|
||||
file *ast.File
|
||||
m *protocol.ColumnMapper
|
||||
err error
|
||||
)
|
||||
for _, ph := range pkg.GetHandles() {
|
||||
if ph.File().Identity().URI == spn.URI() {
|
||||
fh = ph.File()
|
||||
file, err = ph.Cached(ctx)
|
||||
file, m, err = ph.Cached(ctx)
|
||||
}
|
||||
}
|
||||
if file == nil {
|
||||
return protocol.Range{}, err
|
||||
}
|
||||
fset := view.Session().Cache().FileSet()
|
||||
tok := fset.File(file.Pos())
|
||||
if tok == nil {
|
||||
return protocol.Range{}, errors.Errorf("no token.File for %s", spn.URI())
|
||||
}
|
||||
data, _, err := fh.Read(ctx)
|
||||
if err != nil {
|
||||
return protocol.Range{}, err
|
||||
}
|
||||
uri := fh.Identity().URI
|
||||
m := protocol.NewColumnMapper(uri, uri.Filename(), fset, tok, data)
|
||||
|
||||
// Try to get a range for the diagnostic.
|
||||
// TODO: Don't just limit ranges to type errors.
|
||||
if spn.IsPoint() && isTypeError {
|
||||
|
@ -20,16 +20,11 @@ func FoldingRange(ctx context.Context, view View, f GoFile, lineFoldingOnly bool
|
||||
// TODO(suzmue): consider limiting the number of folding ranges returned, and
|
||||
// implement a way to prioritize folding ranges in that case.
|
||||
fh := f.Handle(ctx)
|
||||
file, err := view.Session().Cache().ParseGoHandle(fh, ParseFull).Parse(ctx)
|
||||
ph := view.Session().Cache().ParseGoHandle(fh, ParseFull)
|
||||
file, m, err := ph.Parse(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, _, err := fh.Read(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fset := view.Session().Cache().FileSet()
|
||||
m := protocol.NewColumnMapper(f.URI(), f.URI().Filename(), fset, fset.File(file.Pos()), data)
|
||||
|
||||
// Get folding ranges for comments separately as they are not walked by ast.Inspect.
|
||||
ranges = append(ranges, commentsFoldingRange(view, m, file)...)
|
||||
|
@ -8,9 +8,7 @@ package source
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"go/ast"
|
||||
"go/format"
|
||||
"go/token"
|
||||
|
||||
"golang.org/x/tools/go/packages"
|
||||
"golang.org/x/tools/internal/imports"
|
||||
@ -34,12 +32,16 @@ func Format(ctx context.Context, view View, f File) ([]protocol.TextEdit, error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var file *ast.File
|
||||
for _, ph := range pkg.GetHandles() {
|
||||
if ph.File().Identity().URI == f.URI() {
|
||||
file, err = ph.Cached(ctx)
|
||||
var ph ParseGoHandle
|
||||
for _, h := range pkg.GetHandles() {
|
||||
if h.File().Identity().URI == f.URI() {
|
||||
ph = h
|
||||
}
|
||||
}
|
||||
if ph == nil {
|
||||
return nil, err
|
||||
}
|
||||
file, m, err := ph.Parse(ctx)
|
||||
if file == nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -52,7 +54,7 @@ func Format(ctx context.Context, view View, f File) ([]protocol.TextEdit, error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return computeTextEdits(ctx, view.Session().Cache().FileSet(), f, string(formatted))
|
||||
return computeTextEdits(ctx, ph.File(), m, string(formatted))
|
||||
}
|
||||
|
||||
fset := view.Session().Cache().FileSet()
|
||||
@ -65,7 +67,7 @@ func Format(ctx context.Context, view View, f File) ([]protocol.TextEdit, error)
|
||||
if err := format.Node(buf, fset, file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return computeTextEdits(ctx, view.Session().Cache().FileSet(), f, buf.String())
|
||||
return computeTextEdits(ctx, ph.File(), m, buf.String())
|
||||
}
|
||||
|
||||
func formatSource(ctx context.Context, file File) ([]byte, error) {
|
||||
@ -82,10 +84,6 @@ func formatSource(ctx context.Context, file File) ([]byte, error) {
|
||||
func Imports(ctx context.Context, view View, f GoFile, rng span.Range) ([]protocol.TextEdit, error) {
|
||||
ctx, done := trace.StartSpan(ctx, "source.Imports")
|
||||
defer done()
|
||||
data, _, err := f.Handle(ctx).Read(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pkg, err := f.GetPackage(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -93,7 +91,15 @@ func Imports(ctx context.Context, view View, f GoFile, rng span.Range) ([]protoc
|
||||
if hasListErrors(pkg.GetErrors()) {
|
||||
return nil, errors.Errorf("%s has list errors, not running goimports", f.URI())
|
||||
}
|
||||
|
||||
var ph ParseGoHandle
|
||||
for _, h := range pkg.GetHandles() {
|
||||
if h.File().Identity().URI == f.URI() {
|
||||
ph = h
|
||||
}
|
||||
}
|
||||
if ph == nil {
|
||||
return nil, err
|
||||
}
|
||||
options := &imports.Options{
|
||||
// Defaults.
|
||||
AllErrors: true,
|
||||
@ -105,14 +111,22 @@ func Imports(ctx context.Context, view View, f GoFile, rng span.Range) ([]protoc
|
||||
}
|
||||
var formatted []byte
|
||||
importFn := func(opts *imports.Options) error {
|
||||
formatted, err = imports.Process(f.URI().Filename(), data, opts)
|
||||
data, _, err := ph.File().Read(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
formatted, err = imports.Process(ph.File().Identity().URI.Filename(), data, opts)
|
||||
return err
|
||||
}
|
||||
err = view.RunProcessEnvFunc(ctx, importFn, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return computeTextEdits(ctx, view.Session().Cache().FileSet(), f, string(formatted))
|
||||
_, m, err := ph.Parse(ctx)
|
||||
if m == nil {
|
||||
return nil, err
|
||||
}
|
||||
return computeTextEdits(ctx, ph.File(), m, string(formatted))
|
||||
}
|
||||
|
||||
type ImportFix struct {
|
||||
@ -132,11 +146,6 @@ func AllImportsFixes(ctx context.Context, view View, f File) (edits []protocol.T
|
||||
if !ok {
|
||||
return nil, nil, errors.Errorf("no imports fixes for non-Go files: %v", err)
|
||||
}
|
||||
|
||||
data, _, err := f.Handle(ctx).Read(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
pkg, err := gof.GetPackage(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@ -154,6 +163,19 @@ func AllImportsFixes(ctx context.Context, view View, f File) (edits []protocol.T
|
||||
TabWidth: 8,
|
||||
}
|
||||
importFn := func(opts *imports.Options) error {
|
||||
var ph ParseGoHandle
|
||||
for _, h := range pkg.GetHandles() {
|
||||
if h.File().Identity().URI == f.URI() {
|
||||
ph = h
|
||||
}
|
||||
}
|
||||
if ph == nil {
|
||||
return errors.Errorf("no ParseGoHandle for %s", f.URI())
|
||||
}
|
||||
data, _, err := ph.File().Read(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fixes, err := imports.FixImports(f.URI().Filename(), data, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -163,7 +185,11 @@ func AllImportsFixes(ctx context.Context, view View, f File) (edits []protocol.T
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
edits, err = computeTextEdits(ctx, view.Session().Cache().FileSet(), f, string(formatted))
|
||||
_, m, err := ph.Parse(ctx)
|
||||
if m == nil {
|
||||
return err
|
||||
}
|
||||
edits, err = computeTextEdits(ctx, ph.File(), m, string(formatted))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -174,7 +200,7 @@ func AllImportsFixes(ctx context.Context, view View, f File) (edits []protocol.T
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
edits, err := computeTextEdits(ctx, view.Session().Cache().FileSet(), f, string(formatted))
|
||||
edits, err := computeTextEdits(ctx, ph.File(), m, string(formatted))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -242,19 +268,16 @@ func hasListErrors(errors []packages.Error) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func computeTextEdits(ctx context.Context, fset *token.FileSet, f File, formatted string) ([]protocol.TextEdit, error) {
|
||||
func computeTextEdits(ctx context.Context, fh FileHandle, m *protocol.ColumnMapper, formatted string) ([]protocol.TextEdit, error) {
|
||||
ctx, done := trace.StartSpan(ctx, "source.computeTextEdits")
|
||||
defer done()
|
||||
|
||||
data, _, err := f.Handle(ctx).Read(ctx)
|
||||
data, _, err := fh.Read(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
edits := diff.ComputeEdits(f.URI(), string(data), formatted)
|
||||
m := protocol.NewColumnMapper(f.URI(), f.URI().Filename(), fset, nil, data)
|
||||
|
||||
edits := diff.ComputeEdits(fh.Identity().URI, string(data), formatted)
|
||||
return ToProtocolEdits(m, edits)
|
||||
|
||||
}
|
||||
|
||||
func ToProtocolEdits(m *protocol.ColumnMapper, edits []diff.TextEdit) ([]protocol.TextEdit, error) {
|
||||
|
@ -226,10 +226,13 @@ func hasErrorType(obj types.Object) bool {
|
||||
|
||||
func objToNode(ctx context.Context, view View, pkg Package, obj types.Object) (ast.Decl, error) {
|
||||
uri := span.FileURI(view.Session().Cache().FileSet().Position(obj.Pos()).Filename)
|
||||
_, declAST, _, err := pkg.FindFile(ctx, uri, obj.Pos())
|
||||
_, declAST, _, err := pkg.FindFile(ctx, uri)
|
||||
if declAST == nil {
|
||||
return nil, err
|
||||
}
|
||||
if !(declAST.Pos() <= obj.Pos() && obj.Pos() <= declAST.End()) {
|
||||
return nil, errors.Errorf("no file for %s", obj.Name())
|
||||
}
|
||||
path, _ := astutil.PathEnclosingInterval(declAST, obj.Pos(), obj.Pos())
|
||||
if path == nil {
|
||||
return nil, errors.Errorf("no path for object %v", obj.Name())
|
||||
|
@ -178,7 +178,7 @@ func (i *IdentifierInfo) getPkgName(ctx context.Context) (*IdentifierInfo, error
|
||||
}
|
||||
for _, ph := range pkg.GetHandles() {
|
||||
if ph.File().Identity().URI == i.File.File().Identity().URI {
|
||||
file, err = ph.Cached(ctx)
|
||||
file, _, err = ph.Cached(ctx)
|
||||
}
|
||||
}
|
||||
if file == nil {
|
||||
|
@ -488,10 +488,12 @@ func (r *runner) Format(t *testing.T, data tests.Formats) {
|
||||
}
|
||||
data, _, err := f.Handle(ctx).Read(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
t.Fatal(err)
|
||||
}
|
||||
m, err := r.data.Mapper(f.URI())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
m := protocol.NewColumnMapper(uri, filename, r.view.Session().Cache().FileSet(), nil, data)
|
||||
diffEdits, err := source.FromProtocolEdits(m, edits)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@ -535,10 +537,12 @@ func (r *runner) Import(t *testing.T, data tests.Imports) {
|
||||
}
|
||||
data, _, err := fh.Read(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
t.Fatal(err)
|
||||
}
|
||||
m, err := r.data.Mapper(fh.Identity().URI)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
m := protocol.NewColumnMapper(uri, filename, r.view.Session().Cache().FileSet(), nil, data)
|
||||
diffEdits, err := source.FromProtocolEdits(m, edits)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@ -717,12 +721,15 @@ func (r *runner) Rename(t *testing.T, data tests.Renames) {
|
||||
if err != nil {
|
||||
t.Fatalf("failed for %v: %v", spn, err)
|
||||
}
|
||||
data, _, err := f.Handle(ctx).Read(ctx)
|
||||
fh := f.Handle(ctx)
|
||||
data, _, err := fh.Read(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
t.Fatal(err)
|
||||
}
|
||||
m, err := r.data.Mapper(fh.Identity().URI)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
m := protocol.NewColumnMapper(f.URI(), f.URI().Filename(), r.data.Exported.ExpectFileSet, nil, data)
|
||||
filename := filepath.Base(editSpn.Filename())
|
||||
diffEdits, err := source.FromProtocolEdits(m, edits)
|
||||
if err != nil {
|
||||
@ -922,13 +929,12 @@ func (r *runner) Link(t *testing.T, data tests.Links) {
|
||||
// This is a pure LSP feature, no source level functionality to be tested.
|
||||
}
|
||||
|
||||
func spanToRange(data *tests.Data, span span.Span) (*protocol.ColumnMapper, protocol.Range, error) {
|
||||
contents, err := data.Exported.FileContents(span.URI().Filename())
|
||||
func spanToRange(data *tests.Data, spn span.Span) (*protocol.ColumnMapper, protocol.Range, error) {
|
||||
m, err := data.Mapper(spn.URI())
|
||||
if err != nil {
|
||||
return nil, protocol.Range{}, err
|
||||
}
|
||||
m := protocol.NewColumnMapper(span.URI(), span.URI().Filename(), data.Exported.ExpectFileSet, nil, contents)
|
||||
srcRng, err := m.Range(span)
|
||||
srcRng, err := m.Range(spn)
|
||||
if err != nil {
|
||||
return nil, protocol.Range{}, err
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ func DocumentSymbols(ctx context.Context, view View, f GoFile) ([]protocol.Docum
|
||||
var file *ast.File
|
||||
for _, ph := range pkg.GetHandles() {
|
||||
if ph.File().Identity().URI == f.URI() {
|
||||
file, err = ph.Cached(ctx)
|
||||
file, _, err = ph.Cached(ctx)
|
||||
}
|
||||
}
|
||||
if file == nil {
|
||||
|
@ -51,7 +51,7 @@ func (s mappedRange) URI() span.URI {
|
||||
return s.m.URI
|
||||
}
|
||||
|
||||
// bestCheckPackageHandle picks the "narrowest" package for a given file.
|
||||
// bestPackage picks the "narrowest" package for a given file.
|
||||
//
|
||||
// By "narrowest" package, we mean the package with the fewest number of files
|
||||
// that includes the given file. This solves the problem of test variants,
|
||||
@ -126,20 +126,11 @@ func pkgToMapper(ctx context.Context, view View, pkg Package, uri span.URI) (*as
|
||||
if ph == nil {
|
||||
return nil, nil, errors.Errorf("no ParseGoHandle for %s", uri)
|
||||
}
|
||||
file, err := ph.Cached(ctx)
|
||||
file, m, err := ph.Cached(ctx)
|
||||
if file == nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
data, _, err := ph.File().Read(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
fset := view.Session().Cache().FileSet()
|
||||
tok := fset.File(file.Pos())
|
||||
if tok == nil {
|
||||
return nil, nil, errors.Errorf("no token.File for %s", uri)
|
||||
}
|
||||
return file, protocol.NewColumnMapper(uri, uri.Filename(), fset, tok, data), nil
|
||||
return file, m, nil
|
||||
}
|
||||
|
||||
func builtinFileToMapper(ctx context.Context, view View, f GoFile, file *ast.File) (*ast.File, *protocol.ColumnMapper, error) {
|
||||
@ -148,12 +139,13 @@ func builtinFileToMapper(ctx context.Context, view View, f GoFile, file *ast.Fil
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
fset := view.Session().Cache().FileSet()
|
||||
tok := fset.File(file.Pos())
|
||||
if tok == nil {
|
||||
return nil, nil, errors.Errorf("no token.File for %s", f.URI())
|
||||
converter := span.NewContentConverter(fh.Identity().URI.Filename(), data)
|
||||
m := &protocol.ColumnMapper{
|
||||
URI: fh.Identity().URI,
|
||||
Content: data,
|
||||
Converter: converter,
|
||||
}
|
||||
return nil, protocol.NewColumnMapper(f.URI(), f.URI().Filename(), fset, tok, data), nil
|
||||
return nil, m, nil
|
||||
}
|
||||
|
||||
func IsGenerated(ctx context.Context, view View, uri span.URI) bool {
|
||||
@ -162,7 +154,7 @@ func IsGenerated(ctx context.Context, view View, uri span.URI) bool {
|
||||
return false
|
||||
}
|
||||
ph := view.Session().Cache().ParseGoHandle(f.Handle(ctx), ParseHeader)
|
||||
parsed, err := ph.Parse(ctx)
|
||||
parsed, _, err := ph.Parse(ctx)
|
||||
if parsed == nil {
|
||||
return false
|
||||
}
|
||||
|
@ -80,10 +80,10 @@ type ParseGoHandle interface {
|
||||
|
||||
// Parse returns the parsed AST for the file.
|
||||
// If the file is not available, returns nil and an error.
|
||||
Parse(ctx context.Context) (*ast.File, error)
|
||||
Parse(ctx context.Context) (*ast.File, *protocol.ColumnMapper, error)
|
||||
|
||||
// Cached returns the AST for this handle, if it has already been stored.
|
||||
Cached(ctx context.Context) (*ast.File, error)
|
||||
Cached(ctx context.Context) (*ast.File, *protocol.ColumnMapper, error)
|
||||
}
|
||||
|
||||
// ParseMode controls the content of the AST produced when parsing a source file.
|
||||
@ -321,5 +321,5 @@ type Package interface {
|
||||
|
||||
// FindFile returns the AST and type information for a file that may
|
||||
// belong to or be part of a dependency of the given package.
|
||||
FindFile(ctx context.Context, uri span.URI, pos token.Pos) (ParseGoHandle, *ast.File, Package, error)
|
||||
FindFile(ctx context.Context, uri span.URI) (ParseGoHandle, *ast.File, Package, error)
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/expect"
|
||||
@ -99,6 +100,9 @@ type Data struct {
|
||||
fragments map[string]string
|
||||
dir string
|
||||
golden map[string]*Golden
|
||||
|
||||
mappersMu sync.Mutex
|
||||
mappers map[span.URI]*protocol.ColumnMapper
|
||||
}
|
||||
|
||||
type Tests interface {
|
||||
@ -184,6 +188,7 @@ func Load(t testing.TB, exporter packagestest.Exporter, dir string) *Data {
|
||||
dir: dir,
|
||||
fragments: map[string]string{},
|
||||
golden: map[string]*Golden{},
|
||||
mappers: map[span.URI]*protocol.ColumnMapper{},
|
||||
}
|
||||
|
||||
files := packagestest.MustCopyFileTree(dir)
|
||||
@ -426,6 +431,25 @@ func Run(t *testing.T, tests Tests, data *Data) {
|
||||
}
|
||||
}
|
||||
|
||||
func (data *Data) Mapper(uri span.URI) (*protocol.ColumnMapper, error) {
|
||||
data.mappersMu.Lock()
|
||||
defer data.mappersMu.Unlock()
|
||||
|
||||
if _, ok := data.mappers[uri]; !ok {
|
||||
content, err := data.Exported.FileContents(uri.Filename())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
converter := span.NewContentConverter(uri.Filename(), content)
|
||||
data.mappers[uri] = &protocol.ColumnMapper{
|
||||
URI: uri,
|
||||
Converter: converter,
|
||||
Content: content,
|
||||
}
|
||||
}
|
||||
return data.mappers[uri], nil
|
||||
}
|
||||
|
||||
func (data *Data) Golden(tag string, target string, update func() ([]byte, error)) []byte {
|
||||
data.t.Helper()
|
||||
fragment, found := data.fragments[target]
|
||||
@ -672,20 +696,18 @@ func (data *Data) collectPrepareRenames(src span.Span, rng span.Range, placehold
|
||||
// make the range just be the start.
|
||||
rng = span.NewRange(rng.FileSet, rng.Start, rng.Start)
|
||||
}
|
||||
contents, err := data.Exported.FileContents(src.URI().Filename())
|
||||
m, err := data.Mapper(src.URI())
|
||||
if err != nil {
|
||||
return
|
||||
data.t.Fatal(err)
|
||||
}
|
||||
m := protocol.NewColumnMapper(src.URI(), src.URI().Filename(), data.Exported.ExpectFileSet, nil, contents)
|
||||
|
||||
// Convert range to span and then to protocol.Range.
|
||||
spn, err := rng.Span()
|
||||
if err != nil {
|
||||
return
|
||||
data.t.Fatal(err)
|
||||
}
|
||||
prng, err := m.Range(spn)
|
||||
if err != nil {
|
||||
return
|
||||
data.t.Fatal(err)
|
||||
}
|
||||
data.PrepareRenames[src] = &source.PrepareItem{
|
||||
Range: prng,
|
||||
@ -694,14 +716,13 @@ func (data *Data) collectPrepareRenames(src span.Span, rng span.Range, placehold
|
||||
}
|
||||
|
||||
func (data *Data) collectSymbols(name string, spn span.Span, kind string, parentName string) {
|
||||
contents, err := data.Exported.FileContents(spn.URI().Filename())
|
||||
m, err := data.Mapper(spn.URI())
|
||||
if err != nil {
|
||||
return
|
||||
data.t.Fatal(err)
|
||||
}
|
||||
m := protocol.NewColumnMapper(spn.URI(), spn.URI().Filename(), data.Exported.ExpectFileSet, nil, contents)
|
||||
rng, err := m.Range(spn)
|
||||
if err != nil {
|
||||
return
|
||||
data.t.Fatal(err)
|
||||
}
|
||||
sym := protocol.DocumentSymbol{
|
||||
Name: name,
|
||||
|
@ -109,10 +109,14 @@ func (s *Server) applyChanges(ctx context.Context, uri span.URI, changes []proto
|
||||
if err != nil {
|
||||
return "", jsonrpc2.NewErrorf(jsonrpc2.CodeInternalError, "file not found (%v)", err)
|
||||
}
|
||||
fset := s.session.Cache().FileSet()
|
||||
for _, change := range changes {
|
||||
// Update column mapper along with the content.
|
||||
m := protocol.NewColumnMapper(uri, uri.Filename(), fset, nil, content)
|
||||
converter := span.NewContentConverter(uri.Filename(), content)
|
||||
m := &protocol.ColumnMapper{
|
||||
URI: uri,
|
||||
Converter: converter,
|
||||
Content: content,
|
||||
}
|
||||
|
||||
spn, err := m.RangeSpan(*change.Range)
|
||||
if err != nil {
|
||||
|
Loading…
x
Reference in New Issue
Block a user