mirror of
https://github.com/golang/go
synced 2024-11-19 00:44:40 -07:00
579903b3ad
This change adds a protocol.ColumnMapper when parsing go.mod files. This will prevent us from having to worry about line and column offsets, specifically when converting from the x/mod/modfile position to a span.Span. Updates golang/go#31999 Change-Id: Iacdfb42d61dfea9b5f70325cf5a87c9575f8f345 Reviewed-on: https://go-review.googlesource.com/c/tools/+/214699 Run-TryBot: Rohan Challa <rohan@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
103 lines
2.8 KiB
Go
103 lines
2.8 KiB
Go
// 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"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"golang.org/x/mod/modfile"
|
|
"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"
|
|
)
|
|
|
|
type parseModHandle struct {
|
|
handle *memoize.Handle
|
|
file source.FileHandle
|
|
}
|
|
|
|
type parseModData struct {
|
|
memoize.NoCopy
|
|
|
|
modfile *modfile.File
|
|
mapper *protocol.ColumnMapper
|
|
err error
|
|
}
|
|
|
|
func (c *cache) ParseModHandle(fh source.FileHandle) source.ParseModHandle {
|
|
h := c.store.Bind(fh.Identity(), func(ctx context.Context) interface{} {
|
|
data := &parseModData{}
|
|
data.modfile, data.mapper, data.err = parseMod(ctx, fh)
|
|
return data
|
|
})
|
|
return &parseModHandle{
|
|
handle: h,
|
|
file: fh,
|
|
}
|
|
}
|
|
|
|
func parseMod(ctx context.Context, fh source.FileHandle) (*modfile.File, *protocol.ColumnMapper, error) {
|
|
ctx, done := trace.StartSpan(ctx, "cache.parseMod", telemetry.File.Of(fh.Identity().URI.Filename()))
|
|
defer done()
|
|
|
|
buf, _, err := fh.Read(ctx)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
parsed, err := modfile.Parse(fh.Identity().URI.Filename(), buf, nil)
|
|
if err != nil {
|
|
// TODO(golang/go#36486): This can be removed when modfile.Parse returns structured errors.
|
|
re := regexp.MustCompile(`.*:([\d]+): (.+)`)
|
|
matches := re.FindStringSubmatch(strings.TrimSpace(err.Error()))
|
|
if len(matches) < 3 {
|
|
log.Error(ctx, "could not parse golang/x/mod error message", err)
|
|
return nil, nil, err
|
|
}
|
|
line, e := strconv.Atoi(matches[1])
|
|
if e != nil {
|
|
return nil, nil, err
|
|
}
|
|
contents := strings.Split(string(buf), "\n")[line-1]
|
|
return nil, nil, &source.Error{
|
|
Message: matches[2],
|
|
Range: protocol.Range{
|
|
Start: protocol.Position{Line: float64(line - 1), Character: float64(0)},
|
|
End: protocol.Position{Line: float64(line - 1), Character: float64(len(contents))},
|
|
},
|
|
}
|
|
}
|
|
m := &protocol.ColumnMapper{
|
|
URI: fh.Identity().URI,
|
|
Converter: span.NewContentConverter(fh.Identity().URI.Filename(), buf),
|
|
Content: buf,
|
|
}
|
|
return parsed, m, nil
|
|
}
|
|
|
|
func (pgh *parseModHandle) String() string {
|
|
return pgh.File().Identity().URI.Filename()
|
|
}
|
|
|
|
func (pgh *parseModHandle) File() source.FileHandle {
|
|
return pgh.file
|
|
}
|
|
|
|
func (pgh *parseModHandle) Parse(ctx context.Context) (*modfile.File, *protocol.ColumnMapper, error) {
|
|
v := pgh.handle.Get(ctx)
|
|
if v == nil {
|
|
return nil, nil, errors.Errorf("no parsed file for %s", pgh.File().Identity().URI)
|
|
}
|
|
data := v.(*parseModData)
|
|
return data.modfile, data.mapper, data.err
|
|
}
|