зеркало из https://github.com/golang/tools.git
gopls/internal/cmd: move span.{Span,Point} to cmd
These types are only used by the command-line interface. Only URI remains in the span package. Mapper exposes the UTF-8 conversions using the more flexible (line, col8 int) notation without using span. Details: - span.Span -> cmd.Span (and span -> _span) - span.Point -> cmd.Point (and point -> _point) - span.{Compare,Sort}Spans -> cmd.{compare,sort}Spans - remove SetRange hack in packagestest since spans are no longer used by tests. - span.Parse -> cmd.parseSpan. Remove ParseInDir (always ".") - remove global var weirdness from cmd/semantictokens.go. - fix outer/inner mistake in marker test. - protocol.Mapper.SpanLocation (et al) are now cmd.cmdfile.spanLocation (etc) Change-Id: Icd3758f975018cd191e1e54f8ddcd5bc0db18c45 Reviewed-on: https://go-review.googlesource.com/c/tools/+/542155 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Alan Donovan <adonovan@google.com> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Родитель
944d4e7334
Коммит
3292b366da
|
@ -128,21 +128,6 @@ type Range struct {
|
|||
Start, End token.Pos // both valid and within range of TokFile
|
||||
}
|
||||
|
||||
// A rangeSetter abstracts a variable that can be set from a Range value.
|
||||
//
|
||||
// The parameter conversion machinery will automatically construct a
|
||||
// variable of type T and call the SetRange method on its address if
|
||||
// *T implements rangeSetter. This allows alternative notations of
|
||||
// source ranges to interoperate transparently with this package.
|
||||
//
|
||||
// This type intentionally does not mention Range itself, to avoid a
|
||||
// dependency from the application's range type upon this package.
|
||||
//
|
||||
// Currently this is a secret back door for use only by gopls.
|
||||
type rangeSetter interface {
|
||||
SetRange(file *token.File, start, end token.Pos)
|
||||
}
|
||||
|
||||
// Mark adds a new marker to the known set.
|
||||
func (e *Exported) Mark(name string, r Range) {
|
||||
if e.markers == nil {
|
||||
|
@ -243,15 +228,14 @@ func (e *Exported) getMarkers() error {
|
|||
}
|
||||
|
||||
var (
|
||||
noteType = reflect.TypeOf((*expect.Note)(nil))
|
||||
identifierType = reflect.TypeOf(expect.Identifier(""))
|
||||
posType = reflect.TypeOf(token.Pos(0))
|
||||
positionType = reflect.TypeOf(token.Position{})
|
||||
rangeType = reflect.TypeOf(Range{})
|
||||
rangeSetterType = reflect.TypeOf((*rangeSetter)(nil)).Elem()
|
||||
fsetType = reflect.TypeOf((*token.FileSet)(nil))
|
||||
regexType = reflect.TypeOf((*regexp.Regexp)(nil))
|
||||
exportedType = reflect.TypeOf((*Exported)(nil))
|
||||
noteType = reflect.TypeOf((*expect.Note)(nil))
|
||||
identifierType = reflect.TypeOf(expect.Identifier(""))
|
||||
posType = reflect.TypeOf(token.Pos(0))
|
||||
positionType = reflect.TypeOf(token.Position{})
|
||||
rangeType = reflect.TypeOf(Range{})
|
||||
fsetType = reflect.TypeOf((*token.FileSet)(nil))
|
||||
regexType = reflect.TypeOf((*regexp.Regexp)(nil))
|
||||
exportedType = reflect.TypeOf((*Exported)(nil))
|
||||
)
|
||||
|
||||
// converter converts from a marker's argument parsed from the comment to
|
||||
|
@ -310,17 +294,6 @@ func (e *Exported) buildConverter(pt reflect.Type) (converter, error) {
|
|||
}
|
||||
return reflect.ValueOf(r), remains, nil
|
||||
}, nil
|
||||
case reflect.PtrTo(pt).AssignableTo(rangeSetterType):
|
||||
// (*pt).SetRange method exists: call it.
|
||||
return func(n *expect.Note, args []interface{}) (reflect.Value, []interface{}, error) {
|
||||
r, remains, err := e.rangeConverter(n, args)
|
||||
if err != nil {
|
||||
return reflect.Value{}, nil, err
|
||||
}
|
||||
v := reflect.New(pt)
|
||||
v.Interface().(rangeSetter).SetRange(r.TokFile, r.Start, r.End)
|
||||
return v.Elem(), remains, nil
|
||||
}, nil
|
||||
case pt == identifierType:
|
||||
return func(n *expect.Note, args []interface{}) (reflect.Value, []interface{}, error) {
|
||||
if len(args) < 1 {
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
"strings"
|
||||
|
||||
"golang.org/x/tools/gopls/internal/lsp/protocol"
|
||||
"golang.org/x/tools/gopls/internal/span"
|
||||
"golang.org/x/tools/internal/tool"
|
||||
)
|
||||
|
||||
|
@ -46,13 +45,13 @@ func (c *callHierarchy) Run(ctx context.Context, args ...string) error {
|
|||
}
|
||||
defer conn.terminate(ctx)
|
||||
|
||||
from := span.Parse(args[0])
|
||||
from := parseSpan(args[0])
|
||||
file, err := conn.openFile(ctx, from.URI())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
loc, err := file.mapper.SpanLocation(from)
|
||||
loc, err := file.spanLocation(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -115,7 +114,7 @@ func callItemPrintString(ctx context.Context, conn *connection, item protocol.Ca
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
itemSpan, err := itemFile.mapper.RangeSpan(item.Range)
|
||||
itemSpan, err := itemFile.rangeSpan(item.Range)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -127,7 +126,7 @@ func callItemPrintString(ctx context.Context, conn *connection, item protocol.Ca
|
|||
return "", err
|
||||
}
|
||||
for _, rng := range calls {
|
||||
call, err := callsFile.mapper.RangeSpan(rng)
|
||||
call, err := callsFile.rangeSpan(rng)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ func (c *check) Run(ctx context.Context, args ...string) error {
|
|||
|
||||
for _, file := range checking {
|
||||
for _, d := range file.diagnostics {
|
||||
spn, err := file.mapper.RangeSpan(d.Range)
|
||||
spn, err := file.rangeSpan(d.Range)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not convert position %v for %q", d.Range, d.Message)
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
@ -20,6 +21,7 @@ import (
|
|||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
bugpkg "golang.org/x/tools/gopls/internal/bug"
|
||||
"golang.org/x/tools/gopls/internal/lsp"
|
||||
"golang.org/x/tools/gopls/internal/lsp/browser"
|
||||
"golang.org/x/tools/gopls/internal/lsp/cache"
|
||||
|
@ -800,3 +802,95 @@ func (c *connection) terminate(ctx context.Context) {
|
|||
func (c *cmdClient) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// -- conversions to span (UTF-8) domain --
|
||||
|
||||
// locationSpan converts a protocol (UTF-16) Location to a (UTF-8) span.
|
||||
// Precondition: the URIs of Location and Mapper match.
|
||||
func (f *cmdFile) locationSpan(loc protocol.Location) (Span, error) {
|
||||
// TODO(adonovan): check that l.URI matches m.URI.
|
||||
return f.rangeSpan(loc.Range)
|
||||
}
|
||||
|
||||
// rangeSpan converts a protocol (UTF-16) range to a (UTF-8) span.
|
||||
// The resulting span has valid Positions and Offsets.
|
||||
func (f *cmdFile) rangeSpan(r protocol.Range) (Span, error) {
|
||||
start, end, err := f.mapper.RangeOffsets(r)
|
||||
if err != nil {
|
||||
return Span{}, err
|
||||
}
|
||||
return f.offsetSpan(start, end)
|
||||
}
|
||||
|
||||
// offsetSpan converts a byte-offset interval to a (UTF-8) span.
|
||||
// The resulting span contains line, column, and offset information.
|
||||
func (f *cmdFile) offsetSpan(start, end int) (Span, error) {
|
||||
if start > end {
|
||||
return Span{}, fmt.Errorf("start offset (%d) > end (%d)", start, end)
|
||||
}
|
||||
startPoint, err := offsetPoint(f.mapper, start)
|
||||
if err != nil {
|
||||
return Span{}, fmt.Errorf("start: %v", err)
|
||||
}
|
||||
endPoint, err := offsetPoint(f.mapper, end)
|
||||
if err != nil {
|
||||
return Span{}, fmt.Errorf("end: %v", err)
|
||||
}
|
||||
return newSpan(f.mapper.URI, startPoint, endPoint), nil
|
||||
}
|
||||
|
||||
// offsetPoint converts a byte offset to a span (UTF-8) point.
|
||||
// The resulting point contains line, column, and offset information.
|
||||
func offsetPoint(m *protocol.Mapper, offset int) (point, error) {
|
||||
if !(0 <= offset && offset <= len(m.Content)) {
|
||||
return point{}, fmt.Errorf("invalid offset %d (want 0-%d)", offset, len(m.Content))
|
||||
}
|
||||
line, col8 := m.OffsetLineCol8(offset)
|
||||
return newPoint(line, col8, offset), nil
|
||||
}
|
||||
|
||||
// -- conversions from span (UTF-8) domain --
|
||||
|
||||
// spanLocation converts a (UTF-8) span to a protocol (UTF-16) range.
|
||||
// Precondition: the URIs of spanLocation and Mapper match.
|
||||
func (f *cmdFile) spanLocation(s Span) (protocol.Location, error) {
|
||||
rng, err := f.spanRange(s)
|
||||
if err != nil {
|
||||
return protocol.Location{}, err
|
||||
}
|
||||
return f.mapper.RangeLocation(rng), nil
|
||||
}
|
||||
|
||||
// spanRange converts a (UTF-8) span to a protocol (UTF-16) range.
|
||||
// Precondition: the URIs of Span and Mapper match.
|
||||
func (f *cmdFile) spanRange(s Span) (protocol.Range, error) {
|
||||
// Assert that we aren't using the wrong mapper.
|
||||
// We check only the base name, and case insensitively,
|
||||
// because we can't assume clean paths, no symbolic links,
|
||||
// case-sensitive directories. The authoritative answer
|
||||
// requires querying the file system, and we don't want
|
||||
// to do that.
|
||||
if !strings.EqualFold(filepath.Base(string(f.mapper.URI)), filepath.Base(string(s.URI()))) {
|
||||
return protocol.Range{}, bugpkg.Errorf("mapper is for file %q instead of %q", f.mapper.URI, s.URI())
|
||||
}
|
||||
start, err := pointPosition(f.mapper, s.Start())
|
||||
if err != nil {
|
||||
return protocol.Range{}, fmt.Errorf("start: %w", err)
|
||||
}
|
||||
end, err := pointPosition(f.mapper, s.End())
|
||||
if err != nil {
|
||||
return protocol.Range{}, fmt.Errorf("end: %w", err)
|
||||
}
|
||||
return protocol.Range{Start: start, End: end}, nil
|
||||
}
|
||||
|
||||
// pointPosition converts a valid span (UTF-8) point to a protocol (UTF-16) position.
|
||||
func pointPosition(m *protocol.Mapper, p point) (protocol.Position, error) {
|
||||
if p.HasPosition() {
|
||||
return m.LineCol8Position(p.Line(), p.Column())
|
||||
}
|
||||
if p.HasOffset() {
|
||||
return m.OffsetPosition(p.Offset())
|
||||
}
|
||||
return protocol.Position{}, fmt.Errorf("point has neither offset nor line/column")
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ import (
|
|||
"golang.org/x/tools/gopls/internal/lsp/command"
|
||||
"golang.org/x/tools/gopls/internal/lsp/protocol"
|
||||
"golang.org/x/tools/gopls/internal/lsp/source"
|
||||
"golang.org/x/tools/gopls/internal/span"
|
||||
"golang.org/x/tools/internal/tool"
|
||||
)
|
||||
|
||||
|
@ -107,12 +106,12 @@ func (r *codelens) Run(ctx context.Context, args ...string) error {
|
|||
}
|
||||
defer conn.terminate(ctx)
|
||||
|
||||
filespan := span.Parse(filename)
|
||||
filespan := parseSpan(filename)
|
||||
file, err := conn.openFile(ctx, filespan.URI())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
loc, err := file.mapper.SpanLocation(filespan)
|
||||
loc, err := file.spanLocation(filespan)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -126,7 +125,7 @@ func (r *codelens) Run(ctx context.Context, args ...string) error {
|
|||
}
|
||||
|
||||
for _, lens := range lenses {
|
||||
sp, err := file.mapper.RangeSpan(lens.Range)
|
||||
sp, err := file.rangeSpan(lens.Range)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -14,14 +14,13 @@ import (
|
|||
|
||||
"golang.org/x/tools/gopls/internal/lsp/protocol"
|
||||
"golang.org/x/tools/gopls/internal/lsp/source"
|
||||
"golang.org/x/tools/gopls/internal/span"
|
||||
"golang.org/x/tools/internal/tool"
|
||||
)
|
||||
|
||||
// A Definition is the result of a 'definition' query.
|
||||
type Definition struct {
|
||||
Span span.Span `json:"span"` // span of the definition
|
||||
Description string `json:"description"` // description of the denoted object
|
||||
Span Span `json:"span"` // span of the definition
|
||||
Description string `json:"description"` // description of the denoted object
|
||||
}
|
||||
|
||||
// These constant is printed in the help, and then used in a test to verify the
|
||||
|
@ -79,12 +78,12 @@ func (d *definition) Run(ctx context.Context, args ...string) error {
|
|||
return err
|
||||
}
|
||||
defer conn.terminate(ctx)
|
||||
from := span.Parse(args[0])
|
||||
from := parseSpan(args[0])
|
||||
file, err := conn.openFile(ctx, from.URI())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
loc, err := file.mapper.SpanLocation(from)
|
||||
loc, err := file.spanLocation(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -103,7 +102,7 @@ func (d *definition) Run(ctx context.Context, args ...string) error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("%v: %v", from, err)
|
||||
}
|
||||
definition, err := file.mapper.LocationSpan(locs[0])
|
||||
definition, err := file.locationSpan(locs[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("%v: %v", from, err)
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"fmt"
|
||||
|
||||
"golang.org/x/tools/gopls/internal/lsp/protocol"
|
||||
"golang.org/x/tools/gopls/internal/span"
|
||||
"golang.org/x/tools/internal/tool"
|
||||
)
|
||||
|
||||
|
@ -43,7 +42,7 @@ func (r *foldingRanges) Run(ctx context.Context, args ...string) error {
|
|||
}
|
||||
defer conn.terminate(ctx)
|
||||
|
||||
from := span.Parse(args[0])
|
||||
from := parseSpan(args[0])
|
||||
if _, err := conn.openFile(ctx, from.URI()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"fmt"
|
||||
|
||||
"golang.org/x/tools/gopls/internal/lsp/protocol"
|
||||
"golang.org/x/tools/gopls/internal/span"
|
||||
)
|
||||
|
||||
// format implements the format verb for gopls.
|
||||
|
@ -49,12 +48,12 @@ func (c *format) Run(ctx context.Context, args ...string) error {
|
|||
}
|
||||
defer conn.terminate(ctx)
|
||||
for _, arg := range args {
|
||||
spn := span.Parse(arg)
|
||||
spn := parseSpan(arg)
|
||||
file, err := conn.openFile(ctx, spn.URI())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
loc, err := file.mapper.SpanLocation(spn)
|
||||
loc, err := file.spanLocation(spn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"fmt"
|
||||
|
||||
"golang.org/x/tools/gopls/internal/lsp/protocol"
|
||||
"golang.org/x/tools/gopls/internal/span"
|
||||
"golang.org/x/tools/internal/tool"
|
||||
)
|
||||
|
||||
|
@ -45,13 +44,13 @@ func (r *highlight) Run(ctx context.Context, args ...string) error {
|
|||
}
|
||||
defer conn.terminate(ctx)
|
||||
|
||||
from := span.Parse(args[0])
|
||||
from := parseSpan(args[0])
|
||||
file, err := conn.openFile(ctx, from.URI())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
loc, err := file.mapper.SpanLocation(from)
|
||||
loc, err := file.spanLocation(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -64,16 +63,16 @@ func (r *highlight) Run(ctx context.Context, args ...string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
var results []span.Span
|
||||
var results []Span
|
||||
for _, h := range highlights {
|
||||
s, err := file.mapper.RangeSpan(h.Range)
|
||||
s, err := file.rangeSpan(h.Range)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
results = append(results, s)
|
||||
}
|
||||
// Sort results to make tests deterministic since DocumentHighlight uses a map.
|
||||
span.SortSpans(results)
|
||||
sortSpans(results)
|
||||
|
||||
for _, s := range results {
|
||||
fmt.Println(s)
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
"sort"
|
||||
|
||||
"golang.org/x/tools/gopls/internal/lsp/protocol"
|
||||
"golang.org/x/tools/gopls/internal/span"
|
||||
"golang.org/x/tools/internal/tool"
|
||||
)
|
||||
|
||||
|
@ -46,13 +45,13 @@ func (i *implementation) Run(ctx context.Context, args ...string) error {
|
|||
}
|
||||
defer conn.terminate(ctx)
|
||||
|
||||
from := span.Parse(args[0])
|
||||
from := parseSpan(args[0])
|
||||
file, err := conn.openFile(ctx, from.URI())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
loc, err := file.mapper.SpanLocation(from)
|
||||
loc, err := file.spanLocation(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -71,7 +70,7 @@ func (i *implementation) Run(ctx context.Context, args ...string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
span, err := f.mapper.LocationSpan(impl)
|
||||
span, err := f.locationSpan(impl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"fmt"
|
||||
|
||||
"golang.org/x/tools/gopls/internal/lsp/protocol"
|
||||
"golang.org/x/tools/gopls/internal/span"
|
||||
"golang.org/x/tools/internal/tool"
|
||||
)
|
||||
|
||||
|
@ -50,7 +49,7 @@ func (t *imports) Run(ctx context.Context, args ...string) error {
|
|||
}
|
||||
defer conn.terminate(ctx)
|
||||
|
||||
from := span.Parse(args[0])
|
||||
from := parseSpan(args[0])
|
||||
uri := from.URI()
|
||||
file, err := conn.openFile(ctx, uri)
|
||||
if err != nil {
|
||||
|
|
|
@ -12,7 +12,6 @@ import (
|
|||
"os"
|
||||
|
||||
"golang.org/x/tools/gopls/internal/lsp/protocol"
|
||||
"golang.org/x/tools/gopls/internal/span"
|
||||
"golang.org/x/tools/internal/tool"
|
||||
)
|
||||
|
||||
|
@ -51,7 +50,7 @@ func (l *links) Run(ctx context.Context, args ...string) error {
|
|||
}
|
||||
defer conn.terminate(ctx)
|
||||
|
||||
from := span.Parse(args[0])
|
||||
from := parseSpan(args[0])
|
||||
uri := from.URI()
|
||||
|
||||
if _, err := conn.openFile(ctx, uri); err != nil {
|
||||
|
|
|
@ -2,33 +2,23 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package span
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/tools/gopls/internal/span"
|
||||
)
|
||||
|
||||
// Parse returns the location represented by the input.
|
||||
// parseSpan returns the location represented by the input.
|
||||
// Only file paths are accepted, not URIs.
|
||||
// The returned span will be normalized, and thus if printed may produce a
|
||||
// different string.
|
||||
//
|
||||
// TODO(adonovan): used only from cmd package; move there and simplify.
|
||||
func Parse(input string) Span {
|
||||
return parseInDir(input, ".")
|
||||
}
|
||||
func parseSpan(input string) Span {
|
||||
uri := span.URIFromPath
|
||||
|
||||
// parseInDir is like Parse, but interprets paths relative to wd.
|
||||
func parseInDir(input, wd string) Span {
|
||||
uri := func(path string) URI {
|
||||
if !filepath.IsAbs(path) {
|
||||
path = filepath.Join(wd, path)
|
||||
}
|
||||
return URIFromPath(path)
|
||||
}
|
||||
// :0:0#0-0:0#0
|
||||
valid := input
|
||||
var hold, offset int
|
||||
|
@ -46,19 +36,19 @@ func parseInDir(input, wd string) Span {
|
|||
}
|
||||
switch {
|
||||
case suf.sep == ":":
|
||||
return New(uri(suf.remains), NewPoint(suf.num, hold, offset), Point{})
|
||||
return newSpan(uri(suf.remains), newPoint(suf.num, hold, offset), point{})
|
||||
case suf.sep == "-":
|
||||
// we have a span, fall out of the case to continue
|
||||
default:
|
||||
// separator not valid, rewind to either the : or the start
|
||||
return New(uri(valid), NewPoint(hold, 0, offset), Point{})
|
||||
return newSpan(uri(valid), newPoint(hold, 0, offset), point{})
|
||||
}
|
||||
// only the span form can get here
|
||||
// at this point we still don't know what the numbers we have mean
|
||||
// if have not yet seen a : then we might have either a line or a column depending
|
||||
// on whether start has a column or not
|
||||
// we build an end point and will fix it later if needed
|
||||
end := NewPoint(suf.num, hold, offset)
|
||||
end := newPoint(suf.num, hold, offset)
|
||||
hold, offset = 0, 0
|
||||
suf = rstripSuffix(suf.remains)
|
||||
if suf.sep == "#" {
|
||||
|
@ -67,20 +57,20 @@ func parseInDir(input, wd string) Span {
|
|||
}
|
||||
if suf.sep != ":" {
|
||||
// turns out we don't have a span after all, rewind
|
||||
return New(uri(valid), end, Point{})
|
||||
return newSpan(uri(valid), end, point{})
|
||||
}
|
||||
valid = suf.remains
|
||||
hold = suf.num
|
||||
suf = rstripSuffix(suf.remains)
|
||||
if suf.sep != ":" {
|
||||
// line#offset only
|
||||
return New(uri(valid), NewPoint(hold, 0, offset), end)
|
||||
return newSpan(uri(valid), newPoint(hold, 0, offset), end)
|
||||
}
|
||||
// we have a column, so if end only had one number, it is also the column
|
||||
if !hadCol {
|
||||
end = NewPoint(suf.num, end.v.Line, end.v.Offset)
|
||||
end = newPoint(suf.num, end.v.Line, end.v.Offset)
|
||||
}
|
||||
return New(uri(suf.remains), NewPoint(suf.num, hold, offset), end)
|
||||
return newSpan(uri(suf.remains), newPoint(suf.num, hold, offset), end)
|
||||
}
|
||||
|
||||
type suffix struct {
|
|
@ -11,7 +11,6 @@ import (
|
|||
"fmt"
|
||||
|
||||
"golang.org/x/tools/gopls/internal/lsp/protocol"
|
||||
"golang.org/x/tools/gopls/internal/span"
|
||||
"golang.org/x/tools/internal/tool"
|
||||
)
|
||||
|
||||
|
@ -50,12 +49,12 @@ func (r *prepareRename) Run(ctx context.Context, args ...string) error {
|
|||
}
|
||||
defer conn.terminate(ctx)
|
||||
|
||||
from := span.Parse(args[0])
|
||||
from := parseSpan(args[0])
|
||||
file, err := conn.openFile(ctx, from.URI())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
loc, err := file.mapper.SpanLocation(from)
|
||||
loc, err := file.spanLocation(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -70,7 +69,7 @@ func (r *prepareRename) Run(ctx context.Context, args ...string) error {
|
|||
return ErrInvalidRenamePosition
|
||||
}
|
||||
|
||||
s, err := file.mapper.RangeSpan(result.Range)
|
||||
s, err := file.rangeSpan(result.Range)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
"sort"
|
||||
|
||||
"golang.org/x/tools/gopls/internal/lsp/protocol"
|
||||
"golang.org/x/tools/gopls/internal/span"
|
||||
"golang.org/x/tools/internal/tool"
|
||||
)
|
||||
|
||||
|
@ -50,12 +49,12 @@ func (r *references) Run(ctx context.Context, args ...string) error {
|
|||
}
|
||||
defer conn.terminate(ctx)
|
||||
|
||||
from := span.Parse(args[0])
|
||||
from := parseSpan(args[0])
|
||||
file, err := conn.openFile(ctx, from.URI())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
loc, err := file.mapper.SpanLocation(from)
|
||||
loc, err := file.spanLocation(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -77,7 +76,7 @@ func (r *references) Run(ctx context.Context, args ...string) error {
|
|||
}
|
||||
// convert location to span for user-friendly 1-indexed line
|
||||
// and column numbers
|
||||
span, err := f.mapper.LocationSpan(l)
|
||||
span, err := f.locationSpan(l)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"fmt"
|
||||
|
||||
"golang.org/x/tools/gopls/internal/lsp/protocol"
|
||||
"golang.org/x/tools/gopls/internal/span"
|
||||
"golang.org/x/tools/internal/tool"
|
||||
)
|
||||
|
||||
|
@ -52,12 +51,12 @@ func (r *rename) Run(ctx context.Context, args ...string) error {
|
|||
}
|
||||
defer conn.terminate(ctx)
|
||||
|
||||
from := span.Parse(args[0])
|
||||
from := parseSpan(args[0])
|
||||
file, err := conn.openFile(ctx, from.URI())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
loc, err := file.mapper.SpanLocation(from)
|
||||
loc, err := file.spanLocation(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -9,8 +9,6 @@ import (
|
|||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"log"
|
||||
"os"
|
||||
"unicode/utf8"
|
||||
|
@ -47,8 +45,6 @@ type semtok struct {
|
|||
app *Application
|
||||
}
|
||||
|
||||
var colmap *protocol.Mapper
|
||||
|
||||
func (c *semtok) Name() string { return "semtok" }
|
||||
func (c *semtok) Parent() string { return c.app.Name() }
|
||||
func (c *semtok) Usage() string { return "<filename>" }
|
||||
|
@ -85,11 +81,7 @@ func (c *semtok) Run(ctx context.Context, args ...string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
buf, err := os.ReadFile(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lines := bytes.Split(buf, []byte{'\n'})
|
||||
lines := bytes.Split(file.mapper.Content, []byte{'\n'})
|
||||
p := &protocol.SemanticTokensRangeParams{
|
||||
TextDocument: protocol.TextDocumentIdentifier{
|
||||
URI: protocol.URIFromSpanURI(uri),
|
||||
|
@ -104,23 +96,7 @@ func (c *semtok) Run(ctx context.Context, args ...string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fset := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset, args[0], buf, 0)
|
||||
if err != nil {
|
||||
log.Printf("parsing %s failed %v", args[0], err)
|
||||
return err
|
||||
}
|
||||
tok := fset.File(f.Pos())
|
||||
if tok == nil {
|
||||
// can't happen; just parsed this file
|
||||
return fmt.Errorf("can't find %s in fset", args[0])
|
||||
}
|
||||
colmap = protocol.NewMapper(uri, buf)
|
||||
err = decorate(file.uri.Filename(), resp.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return decorate(file, resp.Data)
|
||||
}
|
||||
|
||||
type mark struct {
|
||||
|
@ -159,16 +135,12 @@ func markLine(m mark, lines [][]byte) {
|
|||
lines[m.line-1] = l
|
||||
}
|
||||
|
||||
func decorate(file string, result []uint32) error {
|
||||
buf, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
marks := newMarks(result)
|
||||
func decorate(file *cmdFile, result []uint32) error {
|
||||
marks := newMarks(file, result)
|
||||
if len(marks) == 0 {
|
||||
return nil
|
||||
}
|
||||
lines := bytes.Split(buf, []byte{'\n'})
|
||||
lines := bytes.Split(file.mapper.Content, []byte{'\n'})
|
||||
for i := len(marks) - 1; i >= 0; i-- {
|
||||
mx := marks[i]
|
||||
markLine(mx, lines)
|
||||
|
@ -177,7 +149,7 @@ func decorate(file string, result []uint32) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func newMarks(d []uint32) []mark {
|
||||
func newMarks(file *cmdFile, d []uint32) []mark {
|
||||
ans := []mark{}
|
||||
// the following two loops could be merged, at the cost
|
||||
// of making the logic slightly more complicated to understand
|
||||
|
@ -206,7 +178,7 @@ func newMarks(d []uint32) []mark {
|
|||
Character: lspChar[i] + d[5*i+2],
|
||||
},
|
||||
}
|
||||
spn, err := colmap.RangeSpan(pr)
|
||||
spn, err := file.rangeSpan(pr)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"fmt"
|
||||
|
||||
"golang.org/x/tools/gopls/internal/lsp/protocol"
|
||||
"golang.org/x/tools/gopls/internal/span"
|
||||
"golang.org/x/tools/internal/tool"
|
||||
)
|
||||
|
||||
|
@ -45,13 +44,13 @@ func (r *signature) Run(ctx context.Context, args ...string) error {
|
|||
}
|
||||
defer conn.terminate(ctx)
|
||||
|
||||
from := span.Parse(args[0])
|
||||
from := parseSpan(args[0])
|
||||
file, err := conn.openFile(ctx, from.URI())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
loc, err := file.mapper.SpanLocation(from)
|
||||
loc, err := file.spanLocation(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -2,19 +2,18 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package span contains support for representing with positions and ranges in
|
||||
// text files.
|
||||
package span
|
||||
package cmd
|
||||
|
||||
// Span and point represent positions and ranges in text files.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/gopls/internal/lsp/safetoken"
|
||||
"golang.org/x/tools/gopls/internal/span"
|
||||
)
|
||||
|
||||
// A Span represents a range of text within a source file. The start
|
||||
|
@ -30,54 +29,54 @@ import (
|
|||
// (UTF-16). The latter requires access to file contents.
|
||||
//
|
||||
// See overview comments at ../lsp/protocol/mapper.go.
|
||||
//
|
||||
// TODO(adonovan): unexport, once "span" package is renamed.
|
||||
// And perhaps rename to cmd.{range,point} to match protocol.{Range,Point}?
|
||||
type Span struct {
|
||||
v span
|
||||
v _span
|
||||
}
|
||||
|
||||
// Point represents a single point within a file.
|
||||
// point represents a single point within a file.
|
||||
// In general this should only be used as part of a Span, as on its own it
|
||||
// does not carry enough information.
|
||||
type Point struct {
|
||||
v point
|
||||
type point struct {
|
||||
v _point
|
||||
}
|
||||
|
||||
// The private span/point types have public fields to support JSON
|
||||
// encoding, but the public Span/Point types hide these fields by
|
||||
// encoding, but the public Span/point types hide these fields by
|
||||
// defining methods that shadow them. (This is used by a few of the
|
||||
// command-line tool subcommands, which emit spans and have a -json
|
||||
// flag.)
|
||||
|
||||
type span struct {
|
||||
URI URI `json:"uri"`
|
||||
Start point `json:"start"`
|
||||
End point `json:"end"`
|
||||
// TODO(adonovan): simplify now that it's all internal to cmd.
|
||||
|
||||
type _span struct {
|
||||
URI span.URI `json:"uri"`
|
||||
Start _point `json:"start"`
|
||||
End _point `json:"end"`
|
||||
}
|
||||
|
||||
type point struct {
|
||||
type _point struct {
|
||||
Line int `json:"line"` // 1-based line number
|
||||
Column int `json:"column"` // 1-based, UTF-8 codes (bytes)
|
||||
Offset int `json:"offset"` // 0-based byte offset
|
||||
}
|
||||
|
||||
// Invalid is a span that reports false from IsValid
|
||||
var Invalid = Span{v: span{Start: invalidPoint.v, End: invalidPoint.v}}
|
||||
|
||||
var invalidPoint = Point{v: point{Line: 0, Column: 0, Offset: -1}}
|
||||
|
||||
func New(uri URI, start, end Point) Span {
|
||||
s := Span{v: span{URI: uri, Start: start.v, End: end.v}}
|
||||
func newSpan(uri span.URI, start, end point) Span {
|
||||
s := Span{v: _span{URI: uri, Start: start.v, End: end.v}}
|
||||
s.v.clean()
|
||||
return s
|
||||
}
|
||||
|
||||
func NewPoint(line, col, offset int) Point {
|
||||
p := Point{v: point{Line: line, Column: col, Offset: offset}}
|
||||
func newPoint(line, col, offset int) point {
|
||||
p := point{v: _point{Line: line, Column: col, Offset: offset}}
|
||||
p.v.clean()
|
||||
return p
|
||||
}
|
||||
|
||||
// SortSpans sorts spans into a stable but unspecified order.
|
||||
func SortSpans(spans []Span) {
|
||||
// sortSpans sorts spans into a stable but unspecified order.
|
||||
func sortSpans(spans []Span) {
|
||||
sort.SliceStable(spans, func(i, j int) bool {
|
||||
return compare(spans[i], spans[j]) < 0
|
||||
})
|
||||
|
@ -97,7 +96,7 @@ func compare(a, b Span) int {
|
|||
return comparePoint(a.v.End, b.v.End)
|
||||
}
|
||||
|
||||
func comparePoint(a, b point) int {
|
||||
func comparePoint(a, b _point) int {
|
||||
if !a.hasPosition() {
|
||||
if a.Offset < b.Offset {
|
||||
return -1
|
||||
|
@ -126,51 +125,51 @@ func (s Span) HasPosition() bool { return s.v.Start.hasPosition() }
|
|||
func (s Span) HasOffset() bool { return s.v.Start.hasOffset() }
|
||||
func (s Span) IsValid() bool { return s.v.Start.isValid() }
|
||||
func (s Span) IsPoint() bool { return s.v.Start == s.v.End }
|
||||
func (s Span) URI() URI { return s.v.URI }
|
||||
func (s Span) Start() Point { return Point{s.v.Start} }
|
||||
func (s Span) End() Point { return Point{s.v.End} }
|
||||
func (s Span) URI() span.URI { return s.v.URI }
|
||||
func (s Span) Start() point { return point{s.v.Start} }
|
||||
func (s Span) End() point { return point{s.v.End} }
|
||||
func (s *Span) MarshalJSON() ([]byte, error) { return json.Marshal(&s.v) }
|
||||
func (s *Span) UnmarshalJSON(b []byte) error { return json.Unmarshal(b, &s.v) }
|
||||
|
||||
func (p Point) HasPosition() bool { return p.v.hasPosition() }
|
||||
func (p Point) HasOffset() bool { return p.v.hasOffset() }
|
||||
func (p Point) IsValid() bool { return p.v.isValid() }
|
||||
func (p *Point) MarshalJSON() ([]byte, error) { return json.Marshal(&p.v) }
|
||||
func (p *Point) UnmarshalJSON(b []byte) error { return json.Unmarshal(b, &p.v) }
|
||||
func (p Point) Line() int {
|
||||
func (p point) HasPosition() bool { return p.v.hasPosition() }
|
||||
func (p point) HasOffset() bool { return p.v.hasOffset() }
|
||||
func (p point) IsValid() bool { return p.v.isValid() }
|
||||
func (p *point) MarshalJSON() ([]byte, error) { return json.Marshal(&p.v) }
|
||||
func (p *point) UnmarshalJSON(b []byte) error { return json.Unmarshal(b, &p.v) }
|
||||
func (p point) Line() int {
|
||||
if !p.v.hasPosition() {
|
||||
panic(fmt.Errorf("position not set in %v", p.v))
|
||||
}
|
||||
return p.v.Line
|
||||
}
|
||||
func (p Point) Column() int {
|
||||
func (p point) Column() int {
|
||||
if !p.v.hasPosition() {
|
||||
panic(fmt.Errorf("position not set in %v", p.v))
|
||||
}
|
||||
return p.v.Column
|
||||
}
|
||||
func (p Point) Offset() int {
|
||||
func (p point) Offset() int {
|
||||
if !p.v.hasOffset() {
|
||||
panic(fmt.Errorf("offset not set in %v", p.v))
|
||||
}
|
||||
return p.v.Offset
|
||||
}
|
||||
|
||||
func (p point) hasPosition() bool { return p.Line > 0 }
|
||||
func (p point) hasOffset() bool { return p.Offset >= 0 }
|
||||
func (p point) isValid() bool { return p.hasPosition() || p.hasOffset() }
|
||||
func (p point) isZero() bool {
|
||||
func (p _point) hasPosition() bool { return p.Line > 0 }
|
||||
func (p _point) hasOffset() bool { return p.Offset >= 0 }
|
||||
func (p _point) isValid() bool { return p.hasPosition() || p.hasOffset() }
|
||||
func (p _point) isZero() bool {
|
||||
return (p.Line == 1 && p.Column == 1) || (!p.hasPosition() && p.Offset == 0)
|
||||
}
|
||||
|
||||
func (s *span) clean() {
|
||||
func (s *_span) clean() {
|
||||
//this presumes the points are already clean
|
||||
if !s.End.isValid() || (s.End == point{}) {
|
||||
if !s.End.isValid() || (s.End == _point{}) {
|
||||
s.End = s.Start
|
||||
}
|
||||
}
|
||||
|
||||
func (p *point) clean() {
|
||||
func (p *_point) clean() {
|
||||
if p.Line < 0 {
|
||||
p.Line = 0
|
||||
}
|
||||
|
@ -187,7 +186,11 @@ func (p *point) clean() {
|
|||
}
|
||||
|
||||
// Format implements fmt.Formatter to print the Location in a standard form.
|
||||
// The format produced is one that can be read back in using Parse.
|
||||
// The format produced is one that can be read back in using parseSpan.
|
||||
//
|
||||
// TODO(adonovan): this is esoteric, and the formatting options are
|
||||
// never used outside of TestFormat. Replace with something simpler
|
||||
// along the lines of MappedRange.String.
|
||||
func (s Span) Format(f fmt.State, c rune) {
|
||||
fullForm := f.Flag('+')
|
||||
preferOffset := f.Flag('#')
|
||||
|
@ -237,13 +240,3 @@ func (s Span) Format(f fmt.State, c rune) {
|
|||
fmt.Fprintf(f, "#%d", s.v.End.Offset)
|
||||
}
|
||||
}
|
||||
|
||||
// SetRange implements packagestest.rangeSetter, allowing
|
||||
// gopls' test suites to use Spans instead of Range in parameters.
|
||||
func (span *Span) SetRange(file *token.File, start, end token.Pos) {
|
||||
point := func(pos token.Pos) Point {
|
||||
posn := safetoken.Position(file, pos)
|
||||
return NewPoint(posn.Line, posn.Column, posn.Offset)
|
||||
}
|
||||
*span = New(URIFromPath(file.Name()), point(start), point(end))
|
||||
}
|
|
@ -2,23 +2,21 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package span_test
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/gopls/internal/span"
|
||||
)
|
||||
|
||||
func TestFormat(t *testing.T) {
|
||||
func TestSpanFormat(t *testing.T) {
|
||||
formats := []string{"%v", "%#v", "%+v"}
|
||||
|
||||
// Element 0 is the input, and the elements 0-2 are the expected
|
||||
// output in [%v %#v %+v] formats. Thus the first must be in
|
||||
// canonical form (invariant under span.Parse + fmt.Sprint).
|
||||
// canonical form (invariant under parseSpan + fmt.Sprint).
|
||||
// The '#' form displays offsets; the '+' form outputs a URI.
|
||||
// If len=4, element 0 is a noncanonical input and 1-3 are expected outputs.
|
||||
for _, test := range [][]string{
|
||||
|
@ -35,7 +33,7 @@ func TestFormat(t *testing.T) {
|
|||
{"C:/file_h:3:7#26-4:8#37", // not canonical
|
||||
"C:/file_h:3:7-4:8", "C:/file_h:#26-#37", "file:///C:/file_h:3:7#26-4:8#37"}} {
|
||||
input := test[0]
|
||||
spn := span.Parse(input)
|
||||
spn := parseSpan(input)
|
||||
wants := test[0:3]
|
||||
if len(test) == 4 {
|
||||
wants = test[1:4]
|
|
@ -81,13 +81,13 @@ func (s *suggestedFix) Run(ctx context.Context, args ...string) error {
|
|||
}
|
||||
defer conn.terminate(ctx)
|
||||
|
||||
from := span.Parse(args[0])
|
||||
from := parseSpan(args[0])
|
||||
uri := from.URI()
|
||||
file, err := conn.openFile(ctx, uri)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rng, err := file.mapper.SpanRange(from)
|
||||
rng, err := file.spanRange(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ import (
|
|||
"sort"
|
||||
|
||||
"golang.org/x/tools/gopls/internal/lsp/protocol"
|
||||
"golang.org/x/tools/gopls/internal/span"
|
||||
"golang.org/x/tools/internal/tool"
|
||||
)
|
||||
|
||||
|
@ -43,7 +42,7 @@ func (r *symbols) Run(ctx context.Context, args ...string) error {
|
|||
}
|
||||
defer conn.terminate(ctx)
|
||||
|
||||
from := span.Parse(args[0])
|
||||
from := parseSpan(args[0])
|
||||
p := protocol.DocumentSymbolParams{
|
||||
TextDocument: protocol.TextDocumentIdentifier{
|
||||
URI: protocol.URIFromSpanURI(from.URI()),
|
||||
|
|
|
@ -78,7 +78,7 @@ func (r *workspaceSymbol) Run(ctx context.Context, args ...string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
span, err := f.mapper.LocationSpan(s.Location)
|
||||
span, err := f.locationSpan(s.Location)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -29,10 +29,10 @@ package protocol
|
|||
// recovery whose computed positions are out of bounds (EOF+1).
|
||||
// - #41029, whereby the wrong line number is returned for the EOF position.
|
||||
//
|
||||
// 3. the span package.
|
||||
// 3. the cmd package.
|
||||
//
|
||||
// span.Point = (line, col8, offset).
|
||||
// span.Span = (uri URI, start, end span.Point)
|
||||
// cmd.point = (line, col8, offset).
|
||||
// cmd.Span = (uri URI, start, end cmd.point)
|
||||
//
|
||||
// Line and column are 1-based.
|
||||
// Columns are measured in bytes (UTF-8 codes).
|
||||
|
@ -44,9 +44,6 @@ package protocol
|
|||
// they are also useful for parsing user-provided positions (e.g. in
|
||||
// the CLI) before we have access to file contents.
|
||||
//
|
||||
// TODO(adonovan): all the span-based methods of Mapper are now
|
||||
// used only by gopls/internal/cmd. Move them into that package.
|
||||
//
|
||||
// 4. protocol, the LSP RPC message format.
|
||||
//
|
||||
// protocol.Position = (Line, Character uint32)
|
||||
|
@ -57,7 +54,7 @@ package protocol
|
|||
// Characters (columns) are measured in UTF-16 codes.
|
||||
//
|
||||
// protocol.Mapper holds the (URI, Content) of a file, enabling
|
||||
// efficient mapping between byte offsets, span ranges, and
|
||||
// efficient mapping between byte offsets, cmd ranges, and
|
||||
// protocol ranges.
|
||||
//
|
||||
// protocol.MappedRange holds a protocol.Mapper and valid (start,
|
||||
|
@ -69,13 +66,11 @@ import (
|
|||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/tools/gopls/internal/bug"
|
||||
"golang.org/x/tools/gopls/internal/lsp/safetoken"
|
||||
"golang.org/x/tools/gopls/internal/span"
|
||||
)
|
||||
|
@ -84,7 +79,7 @@ import (
|
|||
// between byte offsets and notations of position such as:
|
||||
//
|
||||
// - (line, col8) pairs, where col8 is a 1-based UTF-8 column number
|
||||
// (bytes), as used by the go/token and span packages.
|
||||
// (bytes), as used by the go/token and cmd packages.
|
||||
//
|
||||
// - (line, col16) pairs, where col16 is a 1-based UTF-16 column
|
||||
// number, as used by the LSP protocol.
|
||||
|
@ -134,73 +129,28 @@ func (m *Mapper) initLines() {
|
|||
})
|
||||
}
|
||||
|
||||
// -- conversions from span (UTF-8) domain --
|
||||
|
||||
// SpanLocation converts a (UTF-8) span to a protocol (UTF-16) range.
|
||||
// Precondition: the URIs of SpanLocation and Mapper match.
|
||||
func (m *Mapper) SpanLocation(s span.Span) (Location, error) {
|
||||
rng, err := m.SpanRange(s)
|
||||
if err != nil {
|
||||
return Location{}, err
|
||||
}
|
||||
return m.RangeLocation(rng), nil
|
||||
}
|
||||
|
||||
// SpanRange converts a (UTF-8) span to a protocol (UTF-16) range.
|
||||
// Precondition: the URIs of Span and Mapper match.
|
||||
func (m *Mapper) SpanRange(s span.Span) (Range, error) {
|
||||
// Assert that we aren't using the wrong mapper.
|
||||
// We check only the base name, and case insensitively,
|
||||
// because we can't assume clean paths, no symbolic links,
|
||||
// case-sensitive directories. The authoritative answer
|
||||
// requires querying the file system, and we don't want
|
||||
// to do that.
|
||||
if !strings.EqualFold(filepath.Base(string(m.URI)), filepath.Base(string(s.URI()))) {
|
||||
return Range{}, bug.Errorf("mapper is for file %q instead of %q", m.URI, s.URI())
|
||||
}
|
||||
start, err := m.PointPosition(s.Start())
|
||||
if err != nil {
|
||||
return Range{}, fmt.Errorf("start: %w", err)
|
||||
}
|
||||
end, err := m.PointPosition(s.End())
|
||||
if err != nil {
|
||||
return Range{}, fmt.Errorf("end: %w", err)
|
||||
}
|
||||
return Range{Start: start, End: end}, nil
|
||||
}
|
||||
|
||||
// PointPosition converts a valid span (UTF-8) point to a protocol (UTF-16) position.
|
||||
func (m *Mapper) PointPosition(p span.Point) (Position, error) {
|
||||
if p.HasPosition() {
|
||||
return m.LineCol8Position(p.Line(), p.Column())
|
||||
}
|
||||
if p.HasOffset() {
|
||||
return m.OffsetPosition(p.Offset())
|
||||
}
|
||||
return Position{}, fmt.Errorf("point has neither offset nor line/column")
|
||||
}
|
||||
|
||||
// LineCol8Position converts a valid line and UTF-8 column number,
|
||||
// both 1-based, to a protocol (UTF-16) position.
|
||||
func (m *Mapper) LineCol8Position(line, col8 int) (Position, error) {
|
||||
m.initLines()
|
||||
if line-1 >= len(m.lineStart) {
|
||||
line0 := line - 1 // 0-based
|
||||
if !(0 <= line0 && line0 < len(m.lineStart)) {
|
||||
return Position{}, fmt.Errorf("line number %d out of range (max %d)", line, len(m.lineStart))
|
||||
}
|
||||
|
||||
// content[start:end] is the preceding partial line.
|
||||
start := m.lineStart[line-1]
|
||||
start := m.lineStart[line0]
|
||||
end := start + col8 - 1
|
||||
|
||||
// Validate column.
|
||||
if end > len(m.Content) {
|
||||
return Position{}, fmt.Errorf("column is beyond end of file")
|
||||
} else if line < len(m.lineStart) && end >= m.lineStart[line] {
|
||||
} else if line0+1 < len(m.lineStart) && end >= m.lineStart[line0+1] {
|
||||
return Position{}, fmt.Errorf("column is beyond end of line")
|
||||
}
|
||||
|
||||
char := UTF16Len(m.Content[start:end])
|
||||
return Position{Line: uint32(line - 1), Character: uint32(char)}, nil
|
||||
return Position{Line: uint32(line0), Character: uint32(char)}, nil
|
||||
}
|
||||
|
||||
// -- conversions from byte offsets --
|
||||
|
@ -230,23 +180,6 @@ func (m *Mapper) OffsetRange(start, end int) (Range, error) {
|
|||
return Range{Start: startPosition, End: endPosition}, nil
|
||||
}
|
||||
|
||||
// OffsetSpan converts a byte-offset interval to a (UTF-8) span.
|
||||
// The resulting span contains line, column, and offset information.
|
||||
func (m *Mapper) OffsetSpan(start, end int) (span.Span, error) {
|
||||
if start > end {
|
||||
return span.Span{}, fmt.Errorf("start offset (%d) > end (%d)", start, end)
|
||||
}
|
||||
startPoint, err := m.OffsetPoint(start)
|
||||
if err != nil {
|
||||
return span.Span{}, fmt.Errorf("start: %v", err)
|
||||
}
|
||||
endPoint, err := m.OffsetPoint(end)
|
||||
if err != nil {
|
||||
return span.Span{}, fmt.Errorf("end: %v", err)
|
||||
}
|
||||
return span.New(m.URI, startPoint, endPoint), nil
|
||||
}
|
||||
|
||||
// OffsetPosition converts a byte offset to a protocol (UTF-16) position.
|
||||
func (m *Mapper) OffsetPosition(offset int) (Position, error) {
|
||||
if !(0 <= offset && offset <= len(m.Content)) {
|
||||
|
@ -275,14 +208,14 @@ func (m *Mapper) lineCol16(offset int) (int, int) {
|
|||
return line, col16
|
||||
}
|
||||
|
||||
// lineCol8 converts a valid byte offset to line and UTF-8 column numbers, both 0-based.
|
||||
func (m *Mapper) lineCol8(offset int) (int, int) {
|
||||
// OffsetLineCol8 converts a valid byte offset to line and UTF-8 column numbers, both 1-based.
|
||||
func (m *Mapper) OffsetLineCol8(offset int) (int, int) {
|
||||
line, start, cr := m.line(offset)
|
||||
col8 := offset - start
|
||||
if cr {
|
||||
col8-- // retreat from \r at line end
|
||||
}
|
||||
return line, col8
|
||||
return line + 1, col8 + 1
|
||||
}
|
||||
|
||||
// line returns:
|
||||
|
@ -310,16 +243,6 @@ func (m *Mapper) line(offset int) (int, int, bool) {
|
|||
return line, m.lineStart[line], cr
|
||||
}
|
||||
|
||||
// OffsetPoint converts a byte offset to a span (UTF-8) point.
|
||||
// The resulting point contains line, column, and offset information.
|
||||
func (m *Mapper) OffsetPoint(offset int) (span.Point, error) {
|
||||
if !(0 <= offset && offset <= len(m.Content)) {
|
||||
return span.Point{}, fmt.Errorf("invalid offset %d (want 0-%d)", offset, len(m.Content))
|
||||
}
|
||||
line, col8 := m.lineCol8(offset)
|
||||
return span.NewPoint(line+1, col8+1, offset), nil
|
||||
}
|
||||
|
||||
// OffsetMappedRange returns a MappedRange for the given byte offsets.
|
||||
// A MappedRange can be converted to any other form.
|
||||
func (m *Mapper) OffsetMappedRange(start, end int) (MappedRange, error) {
|
||||
|
@ -331,23 +254,6 @@ func (m *Mapper) OffsetMappedRange(start, end int) (MappedRange, error) {
|
|||
|
||||
// -- conversions from protocol (UTF-16) domain --
|
||||
|
||||
// LocationSpan converts a protocol (UTF-16) Location to a (UTF-8) span.
|
||||
// Precondition: the URIs of Location and Mapper match.
|
||||
func (m *Mapper) LocationSpan(l Location) (span.Span, error) {
|
||||
// TODO(adonovan): check that l.URI matches m.URI.
|
||||
return m.RangeSpan(l.Range)
|
||||
}
|
||||
|
||||
// RangeSpan converts a protocol (UTF-16) range to a (UTF-8) span.
|
||||
// The resulting span has valid Positions and Offsets.
|
||||
func (m *Mapper) RangeSpan(r Range) (span.Span, error) {
|
||||
start, end, err := m.RangeOffsets(r)
|
||||
if err != nil {
|
||||
return span.Span{}, err
|
||||
}
|
||||
return m.OffsetSpan(start, end)
|
||||
}
|
||||
|
||||
// RangeOffsets converts a protocol (UTF-16) range to start/end byte offsets.
|
||||
func (m *Mapper) RangeOffsets(r Range) (int, int, error) {
|
||||
start, err := m.PositionOffset(r.Start)
|
||||
|
@ -405,18 +311,6 @@ func (m *Mapper) PositionOffset(p Position) (int, error) {
|
|||
return offset + col8, nil
|
||||
}
|
||||
|
||||
// PositionPoint converts a protocol (UTF-16) position to a span (UTF-8) point.
|
||||
// The resulting point has a valid Position and Offset.
|
||||
func (m *Mapper) PositionPoint(p Position) (span.Point, error) {
|
||||
offset, err := m.PositionOffset(p)
|
||||
if err != nil {
|
||||
return span.Point{}, err
|
||||
}
|
||||
line, col8 := m.lineCol8(offset)
|
||||
|
||||
return span.NewPoint(line+1, col8+1, offset), nil
|
||||
}
|
||||
|
||||
// -- go/token domain convenience methods --
|
||||
|
||||
// PosPosition converts a token pos to a protocol (UTF-16) position.
|
||||
|
@ -478,7 +372,7 @@ func (m *Mapper) NodeMappedRange(tf *token.File, node ast.Node) (MappedRange, er
|
|||
|
||||
// A MappedRange represents a valid byte-offset range of a file.
|
||||
// Through its Mapper it can be converted into other forms such
|
||||
// as protocol.Range or span.Span.
|
||||
// as protocol.Range or UTF-8.
|
||||
//
|
||||
// Construct one by calling Mapper.OffsetMappedRange with start/end offsets.
|
||||
// From the go/token domain, call safetoken.Offsets first,
|
||||
|
@ -516,18 +410,20 @@ func (mr MappedRange) Location() Location {
|
|||
return mr.Mapper.RangeLocation(mr.Range())
|
||||
}
|
||||
|
||||
// Span returns the range in span (UTF-8) form.
|
||||
func (mr MappedRange) Span() span.Span {
|
||||
spn, err := mr.Mapper.OffsetSpan(mr.start, mr.end)
|
||||
if err != nil {
|
||||
panic(err) // can't happen
|
||||
}
|
||||
return spn
|
||||
}
|
||||
|
||||
// String formats the range in span (UTF-8) notation.
|
||||
// String formats the range in UTF-8 notation.
|
||||
func (mr MappedRange) String() string {
|
||||
return fmt.Sprint(mr.Span())
|
||||
var s strings.Builder
|
||||
startLine, startCol8 := mr.Mapper.OffsetLineCol8(mr.start)
|
||||
fmt.Fprintf(&s, "%d:%d", startLine, startCol8)
|
||||
if mr.end != mr.start {
|
||||
endLine, endCol8 := mr.Mapper.OffsetLineCol8(mr.end)
|
||||
if endLine == startLine {
|
||||
fmt.Fprintf(&s, "-%d", endCol8)
|
||||
} else {
|
||||
fmt.Fprintf(&s, "-%d:%d", endLine, endCol8)
|
||||
}
|
||||
}
|
||||
return s.String()
|
||||
}
|
||||
|
||||
// LocationTextDocumentPositionParams converts its argument to its result.
|
||||
|
|
|
@ -13,9 +13,9 @@ import (
|
|||
"golang.org/x/tools/gopls/internal/span"
|
||||
)
|
||||
|
||||
// This file tests Mapper's logic for converting between
|
||||
// span.Point and UTF-16 columns. (The strange form attests to an
|
||||
// earlier abstraction.)
|
||||
// This file tests Mapper's logic for converting between offsets,
|
||||
// UTF-8 columns, and UTF-16 columns. (The strange form attests to
|
||||
// earlier abstractions.)
|
||||
|
||||
// 𐐀 is U+10400 = [F0 90 90 80] in UTF-8, [D801 DC00] in UTF-16.
|
||||
var funnyString = []byte("𐐀23\n𐐀45")
|
||||
|
@ -233,9 +233,16 @@ func TestToUTF16(t *testing.T) {
|
|||
if e.issue != nil && !*e.issue {
|
||||
t.Skip("expected to fail")
|
||||
}
|
||||
p := span.NewPoint(e.line, e.col, e.offset)
|
||||
m := protocol.NewMapper("", e.input)
|
||||
pos, err := m.PointPosition(p)
|
||||
var pos protocol.Position
|
||||
var err error
|
||||
if e.line > 0 {
|
||||
pos, err = m.LineCol8Position(e.line, e.col)
|
||||
} else if e.offset >= 0 {
|
||||
pos, err = m.OffsetPosition(e.offset)
|
||||
} else {
|
||||
err = fmt.Errorf("point has neither offset nor line/column")
|
||||
}
|
||||
if err != nil {
|
||||
if err.Error() != e.err {
|
||||
t.Fatalf("expected error %v; got %v", e.err, err)
|
||||
|
@ -249,12 +256,12 @@ func TestToUTF16(t *testing.T) {
|
|||
if got != e.resUTF16col {
|
||||
t.Fatalf("expected result %v; got %v", e.resUTF16col, got)
|
||||
}
|
||||
pre, post := getPrePost(e.input, p.Offset())
|
||||
pre, post := getPrePost(e.input, e.offset)
|
||||
if string(pre) != e.pre {
|
||||
t.Fatalf("expected #%d pre %q; got %q", p.Offset(), e.pre, pre)
|
||||
t.Fatalf("expected #%d pre %q; got %q", e.offset, e.pre, pre)
|
||||
}
|
||||
if string(post) != e.post {
|
||||
t.Fatalf("expected #%d, post %q; got %q", p.Offset(), e.post, post)
|
||||
t.Fatalf("expected #%d, post %q; got %q", e.offset, e.post, post)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -264,7 +271,7 @@ func TestFromUTF16(t *testing.T) {
|
|||
for _, e := range fromUTF16Tests {
|
||||
t.Run(e.scenario, func(t *testing.T) {
|
||||
m := protocol.NewMapper("", []byte(e.input))
|
||||
p, err := m.PositionPoint(protocol.Position{
|
||||
offset, err := m.PositionOffset(protocol.Position{
|
||||
Line: uint32(e.line - 1),
|
||||
Character: uint32(e.utf16col - 1),
|
||||
})
|
||||
|
@ -277,18 +284,22 @@ func TestFromUTF16(t *testing.T) {
|
|||
if e.err != "" {
|
||||
t.Fatalf("unexpected success; wanted %v", e.err)
|
||||
}
|
||||
if p.Column() != e.resCol {
|
||||
t.Fatalf("expected resulting col %v; got %v", e.resCol, p.Column())
|
||||
if offset != e.resOffset {
|
||||
t.Fatalf("expected offset %v; got %v", e.resOffset, offset)
|
||||
}
|
||||
if p.Offset() != e.resOffset {
|
||||
t.Fatalf("expected resulting offset %v; got %v", e.resOffset, p.Offset())
|
||||
line, col8 := m.OffsetLineCol8(offset)
|
||||
if line != e.line {
|
||||
t.Fatalf("expected resulting line %v; got %v", e.line, line)
|
||||
}
|
||||
pre, post := getPrePost(e.input, p.Offset())
|
||||
if col8 != e.resCol {
|
||||
t.Fatalf("expected resulting col %v; got %v", e.resCol, col8)
|
||||
}
|
||||
pre, post := getPrePost(e.input, offset)
|
||||
if string(pre) != e.pre {
|
||||
t.Fatalf("expected #%d pre %q; got %q", p.Offset(), e.pre, pre)
|
||||
t.Fatalf("expected #%d pre %q; got %q", offset, e.pre, pre)
|
||||
}
|
||||
if string(post) != e.post {
|
||||
t.Fatalf("expected #%d post %q; got %q", p.Offset(), e.post, post)
|
||||
t.Fatalf("expected #%d post %q; got %q", offset, e.post, post)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -428,12 +439,12 @@ func TestBytesOffset(t *testing.T) {
|
|||
fname := fmt.Sprintf("test %d", i)
|
||||
uri := span.URIFromPath(fname)
|
||||
mapper := protocol.NewMapper(uri, []byte(test.text))
|
||||
got, err := mapper.PositionPoint(test.pos)
|
||||
got, err := mapper.PositionOffset(test.pos)
|
||||
if err != nil && test.want != -1 {
|
||||
t.Errorf("%d: unexpected error: %v", i, err)
|
||||
}
|
||||
if err == nil && got.Offset() != test.want {
|
||||
t.Errorf("want %d for %q(Line:%d,Character:%d), but got %d", test.want, test.text, int(test.pos.Line), int(test.pos.Character), got.Offset())
|
||||
if err == nil && got != test.want {
|
||||
t.Errorf("want %d for %q(Line:%d,Character:%d), but got %d", test.want, test.text, int(test.pos.Line), int(test.pos.Character), got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1218,21 +1218,24 @@ func (run *markerTestRun) fmtLocDetails(loc protocol.Location, includeTxtPos boo
|
|||
run.env.T.Errorf("internal error: %v", err)
|
||||
return "<invalid location>"
|
||||
}
|
||||
s, err := m.LocationSpan(loc)
|
||||
start, end, err := m.RangeOffsets(loc.Range)
|
||||
if err != nil {
|
||||
run.env.T.Errorf("error formatting location %s: %v", loc, err)
|
||||
return "<invalid location>"
|
||||
}
|
||||
|
||||
innerSpan := fmt.Sprintf("%d:%d", s.Start().Line(), s.Start().Column()) // relative to the embedded file
|
||||
outerSpan := fmt.Sprintf("%d:%d", lines+s.Start().Line(), s.Start().Column()) // relative to the archive file
|
||||
if s.Start() != s.End() {
|
||||
if s.End().Line() == s.Start().Line() {
|
||||
innerSpan += fmt.Sprintf("-%d", s.End().Column())
|
||||
outerSpan += fmt.Sprintf("-%d", s.End().Column())
|
||||
var (
|
||||
startLine, startCol8 = m.OffsetLineCol8(start)
|
||||
endLine, endCol8 = m.OffsetLineCol8(end)
|
||||
)
|
||||
innerSpan := fmt.Sprintf("%d:%d", startLine, startCol8) // relative to the embedded file
|
||||
outerSpan := fmt.Sprintf("%d:%d", lines+startLine, startCol8) // relative to the archive file
|
||||
if start != end {
|
||||
if endLine == startLine {
|
||||
innerSpan += fmt.Sprintf("-%d", endCol8)
|
||||
outerSpan += fmt.Sprintf("-%d", endCol8)
|
||||
} else {
|
||||
innerSpan += fmt.Sprintf("-%d:%d", s.End().Line(), s.End().Column())
|
||||
innerSpan += fmt.Sprintf("-%d:%d", lines+s.End().Line(), s.End().Column())
|
||||
innerSpan += fmt.Sprintf("-%d:%d", endLine, endCol8)
|
||||
outerSpan += fmt.Sprintf("-%d:%d", lines+endLine, endCol8)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
|
||||
package span
|
||||
|
||||
// TODO(adonovan): rename this package. Perhaps merge span.URI with
|
||||
// protocol.DocumentURI and make these methods on it? Or is span.URI
|
||||
// supposed to establish stronger invariants? urls.FromPath?
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
@ -104,6 +108,8 @@ func URIFromURI(s string) URI {
|
|||
|
||||
// SameExistingFile reports whether two spans denote the
|
||||
// same existing file by querying the file system.
|
||||
//
|
||||
// TODO(adonovan): inline sole use of this in view de-duping.
|
||||
func SameExistingFile(a, b URI) bool {
|
||||
fa, err := filename(a)
|
||||
if err != nil {
|
||||
|
|
Загрузка…
Ссылка в новой задаче