Skip to content

Commit facb0d3

Browse files
committed
internal/span: eliminate Converter and FileConverter
The only real implementation of position conversion was via a *token.File, so refactor the converter logic to eliminate the Converter interface, and just use a single converter implementation that uses a *token.File to convert between offsets and positions. This change is meant to be a zero-impact refactoring for non-test code. As such, I abstained from panicking in several places where it would make sense. In later CLs, once the bug reporting API lands, we can insert bug reports in these places. Change-Id: Id2e503acd80d089bc5d73e983215784015471f04 Reviewed-on: https://go-review.googlesource.com/c/tools/+/405546 gopls-CI: kokoro <[email protected]> Run-TryBot: Robert Findley <[email protected]> Reviewed-by: Alan Donovan <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 814e0d7 commit facb0d3

File tree

8 files changed

+63
-72
lines changed

8 files changed

+63
-72
lines changed

go/packages/packagestest/expect.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ func (e *Exported) rangeConverter(n *expect.Note, args []interface{}) (span.Rang
422422
// end of file identifier, look up the current file
423423
f := e.ExpectFileSet.File(n.Pos)
424424
eof := f.Pos(f.Size())
425-
return span.Range{FileSet: e.ExpectFileSet, Start: eof, End: token.NoPos}, args, nil
425+
return span.NewRange(e.ExpectFileSet, eof, token.NoPos), args, nil
426426
default:
427427
// look up an marker by name
428428
mark, ok := e.markers[string(arg)]
@@ -439,7 +439,7 @@ func (e *Exported) rangeConverter(n *expect.Note, args []interface{}) (span.Rang
439439
if start == token.NoPos {
440440
return span.Range{}, nil, fmt.Errorf("%v: pattern %s did not match", e.ExpectFileSet.Position(n.Pos), arg)
441441
}
442-
return span.Range{FileSet: e.ExpectFileSet, Start: start, End: end}, args, nil
442+
return span.NewRange(e.ExpectFileSet, start, end), args, nil
443443
case *regexp.Regexp:
444444
start, end, err := expect.MatchBefore(e.ExpectFileSet, e.FileContents, n.Pos, arg)
445445
if err != nil {
@@ -448,7 +448,7 @@ func (e *Exported) rangeConverter(n *expect.Note, args []interface{}) (span.Rang
448448
if start == token.NoPos {
449449
return span.Range{}, nil, fmt.Errorf("%v: pattern %s did not match", e.ExpectFileSet.Position(n.Pos), arg)
450450
}
451-
return span.Range{FileSet: e.ExpectFileSet, Start: start, End: end}, args, nil
451+
return span.NewRange(e.ExpectFileSet, start, end), args, nil
452452
default:
453453
return span.Range{}, nil, fmt.Errorf("cannot convert %v to pos", arg)
454454
}

internal/lsp/cache/parse.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ func parseGo(ctx context.Context, fset *token.FileSet, fh source.FileHandle, mod
324324
Tok: tok,
325325
Mapper: &protocol.ColumnMapper{
326326
URI: fh.URI(),
327-
Converter: span.NewTokenConverter(fset, tok),
327+
Converter: span.NewTokenConverter(tok),
328328
Content: src,
329329
},
330330
ParseErr: parseErr,

internal/lsp/source/format.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ func computeTextEdits(ctx context.Context, snapshot Snapshot, pgf *ParsedGoFile,
322322

323323
// ProtocolEditsFromSource converts text edits to LSP edits using the original
324324
// source.
325-
func ProtocolEditsFromSource(src []byte, edits []diff.TextEdit, converter span.Converter) ([]protocol.TextEdit, error) {
325+
func ProtocolEditsFromSource(src []byte, edits []diff.TextEdit, converter *span.TokenConverter) ([]protocol.TextEdit, error) {
326326
m := lsppos.NewMapper(src)
327327
var result []protocol.TextEdit
328328
for _, edit := range edits {

internal/lsp/source/util.go

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,8 @@ type MappedRange struct {
3636
// NewMappedRange returns a MappedRange for the given start and end token.Pos.
3737
func NewMappedRange(fset *token.FileSet, m *protocol.ColumnMapper, start, end token.Pos) MappedRange {
3838
return MappedRange{
39-
spanRange: span.Range{
40-
FileSet: fset,
41-
Start: start,
42-
End: end,
43-
Converter: m.Converter,
44-
},
45-
m: m,
39+
spanRange: span.NewRange(fset, start, end),
40+
m: m,
4641
}
4742
}
4843

internal/span/span.go

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,6 @@ var Invalid = Span{v: span{Start: invalidPoint.v, End: invalidPoint.v}}
4141

4242
var invalidPoint = Point{v: point{Line: 0, Column: 0, Offset: -1}}
4343

44-
// Converter is the interface to an object that can convert between line:column
45-
// and offset forms for a single file.
46-
type Converter interface {
47-
//ToPosition converts from an offset to a line:column pair.
48-
ToPosition(offset int) (int, int, error)
49-
//ToOffset converts from a line:column pair to an offset.
50-
ToOffset(line, col int) (int, error)
51-
}
52-
5344
func New(uri URI, start Point, end Point) Span {
5445
s := Span{v: span{URI: uri, Start: start.v, End: end.v}}
5546
s.v.clean()
@@ -217,28 +208,28 @@ func (s Span) Format(f fmt.State, c rune) {
217208
}
218209
}
219210

220-
func (s Span) WithPosition(c Converter) (Span, error) {
211+
func (s Span) WithPosition(c *TokenConverter) (Span, error) {
221212
if err := s.update(c, true, false); err != nil {
222213
return Span{}, err
223214
}
224215
return s, nil
225216
}
226217

227-
func (s Span) WithOffset(c Converter) (Span, error) {
218+
func (s Span) WithOffset(c *TokenConverter) (Span, error) {
228219
if err := s.update(c, false, true); err != nil {
229220
return Span{}, err
230221
}
231222
return s, nil
232223
}
233224

234-
func (s Span) WithAll(c Converter) (Span, error) {
225+
func (s Span) WithAll(c *TokenConverter) (Span, error) {
235226
if err := s.update(c, true, true); err != nil {
236227
return Span{}, err
237228
}
238229
return s, nil
239230
}
240231

241-
func (s *Span) update(c Converter, withPos, withOffset bool) error {
232+
func (s *Span) update(c *TokenConverter, withPos, withOffset bool) error {
242233
if !s.IsValid() {
243234
return fmt.Errorf("cannot add information to an invalid span")
244235
}
@@ -265,7 +256,7 @@ func (s *Span) update(c Converter, withPos, withOffset bool) error {
265256
return nil
266257
}
267258

268-
func (p *point) updatePosition(c Converter) error {
259+
func (p *point) updatePosition(c *TokenConverter) error {
269260
line, col, err := c.ToPosition(p.Offset)
270261
if err != nil {
271262
return err
@@ -275,7 +266,7 @@ func (p *point) updatePosition(c Converter) error {
275266
return nil
276267
}
277268

278-
func (p *point) updateOffset(c Converter) error {
269+
func (p *point) updateOffset(c *TokenConverter) error {
279270
offset, err := c.ToOffset(p.Line, p.Column)
280271
if err != nil {
281272
return err

internal/span/span_test.go

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package span_test
66

77
import (
88
"fmt"
9+
"go/token"
910
"path/filepath"
1011
"strings"
1112
"testing"
@@ -59,12 +60,15 @@ func toPath(value string) string {
5960
return filepath.FromSlash(value)
6061
}
6162

62-
type lines int
63-
64-
func (l lines) ToPosition(offset int) (int, int, error) {
65-
return (offset / int(l)) + 1, (offset % int(l)) + 1, nil
66-
}
67-
68-
func (l lines) ToOffset(line, col int) (int, error) {
69-
return (int(l) * (line - 1)) + (col - 1), nil
63+
// lines creates a new tokenConverter for a file with 1000 lines, each width
64+
// bytes wide.
65+
func lines(width int) *span.TokenConverter {
66+
fset := token.NewFileSet()
67+
f := fset.AddFile("", -1, 1000*width)
68+
var lines []int
69+
for i := 0; i < 1000; i++ {
70+
lines = append(lines, i*width)
71+
}
72+
f.SetLines(lines)
73+
return span.NewTokenConverter(f)
7074
}

internal/span/token.go

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package span
66

77
import (
8+
"errors"
89
"fmt"
910
"go/token"
1011
)
@@ -13,38 +14,37 @@ import (
1314
// It also carries the FileSet that produced the positions, so that it is
1415
// self contained.
1516
type Range struct {
16-
FileSet *token.FileSet
17-
Start token.Pos
18-
End token.Pos
19-
Converter Converter
17+
Start token.Pos
18+
End token.Pos
19+
file *token.File // possibly nil, if Start or End is invalid
2020
}
2121

22-
type FileConverter struct {
23-
file *token.File
24-
}
25-
26-
// TokenConverter is a Converter backed by a token file set and file.
27-
// It uses the file set methods to work out the conversions, which
28-
// makes it fast and does not require the file contents.
22+
// TokenConverter converts between offsets and (col, row) using a token.File.
2923
type TokenConverter struct {
30-
FileConverter
31-
fset *token.FileSet
24+
file *token.File
3225
}
3326

3427
// NewRange creates a new Range from a FileSet and two positions.
3528
// To represent a point pass a 0 as the end pos.
3629
func NewRange(fset *token.FileSet, start, end token.Pos) Range {
30+
file := fset.File(start)
31+
if file == nil {
32+
// TODO: report a bug here via the bug reporting API, once it is available.
33+
}
3734
return Range{
38-
FileSet: fset,
39-
Start: start,
40-
End: end,
35+
Start: start,
36+
End: end,
37+
file: file,
4138
}
4239
}
4340

4441
// NewTokenConverter returns an implementation of Converter backed by a
4542
// token.File.
46-
func NewTokenConverter(fset *token.FileSet, f *token.File) *TokenConverter {
47-
return &TokenConverter{fset: fset, FileConverter: FileConverter{file: f}}
43+
func NewTokenConverter(f *token.File) *TokenConverter {
44+
if f == nil {
45+
// TODO: report a bug here using the bug reporting API.
46+
}
47+
return &TokenConverter{file: f}
4848
}
4949

5050
// NewContentConverter returns an implementation of Converter for the
@@ -53,7 +53,7 @@ func NewContentConverter(filename string, content []byte) *TokenConverter {
5353
fset := token.NewFileSet()
5454
f := fset.AddFile(filename, -1, len(content))
5555
f.SetLinesForContent(content)
56-
return NewTokenConverter(fset, f)
56+
return NewTokenConverter(f)
5757
}
5858

5959
// IsPoint returns true if the range represents a single point.
@@ -68,16 +68,15 @@ func (r Range) Span() (Span, error) {
6868
if !r.Start.IsValid() {
6969
return Span{}, fmt.Errorf("start pos is not valid")
7070
}
71-
f := r.FileSet.File(r.Start)
72-
if f == nil {
73-
return Span{}, fmt.Errorf("file not found in FileSet")
71+
if r.file == nil {
72+
return Span{}, errors.New("range missing file association")
7473
}
75-
return FileSpan(f, r.Converter, r.Start, r.End)
74+
return FileSpan(r.file, NewTokenConverter(r.file), r.Start, r.End)
7675
}
7776

7877
// FileSpan returns a span within tok, using converter to translate between
7978
// offsets and positions.
80-
func FileSpan(tok *token.File, converter Converter, start, end token.Pos) (Span, error) {
79+
func FileSpan(tok *token.File, converter *TokenConverter, start, end token.Pos) (Span, error) {
8180
var s Span
8281
var err error
8382
var startFilename string
@@ -107,7 +106,7 @@ func FileSpan(tok *token.File, converter Converter, start, end token.Pos) (Span,
107106
if startFilename != tok.Name() {
108107
return Span{}, fmt.Errorf("must supply Converter for file %q containing lines from %q", tok.Name(), startFilename)
109108
}
110-
return s.WithOffset(&FileConverter{tok})
109+
return s.WithOffset(&TokenConverter{tok})
111110
}
112111

113112
func position(f *token.File, pos token.Pos) (string, int, int, error) {
@@ -136,7 +135,7 @@ func positionFromOffset(f *token.File, offset int) (string, int, int, error) {
136135
// that it does not panic on invalid positions.
137136
func offset(f *token.File, pos token.Pos) (int, error) {
138137
if int(pos) < f.Base() || int(pos) > f.Base()+f.Size() {
139-
return 0, fmt.Errorf("invalid pos")
138+
return 0, fmt.Errorf("invalid pos: %d not in [%d, %d]", pos, f.Base(), f.Base()+f.Size())
140139
}
141140
return int(pos) - f.Base(), nil
142141
}
@@ -148,28 +147,30 @@ func (s Span) Range(converter *TokenConverter) (Range, error) {
148147
if err != nil {
149148
return Range{}, err
150149
}
150+
file := converter.file
151151
// go/token will panic if the offset is larger than the file's size,
152152
// so check here to avoid panicking.
153-
if s.Start().Offset() > converter.file.Size() {
154-
return Range{}, fmt.Errorf("start offset %v is past the end of the file %v", s.Start(), converter.file.Size())
153+
if s.Start().Offset() > file.Size() {
154+
// TODO: report a bug here via the future bug reporting API.
155+
return Range{}, fmt.Errorf("start offset %v is past the end of the file %v", s.Start(), file.Size())
155156
}
156-
if s.End().Offset() > converter.file.Size() {
157-
return Range{}, fmt.Errorf("end offset %v is past the end of the file %v", s.End(), converter.file.Size())
157+
if s.End().Offset() > file.Size() {
158+
// TODO: report a bug here via the future bug reporting API.
159+
return Range{}, fmt.Errorf("end offset %v is past the end of the file %v", s.End(), file.Size())
158160
}
159161
return Range{
160-
FileSet: converter.fset,
161-
Start: converter.file.Pos(s.Start().Offset()),
162-
End: converter.file.Pos(s.End().Offset()),
163-
Converter: converter,
162+
Start: file.Pos(s.Start().Offset()),
163+
End: file.Pos(s.End().Offset()),
164+
file: file,
164165
}, nil
165166
}
166167

167-
func (l *FileConverter) ToPosition(offset int) (int, int, error) {
168+
func (l *TokenConverter) ToPosition(offset int) (int, int, error) {
168169
_, line, col, err := positionFromOffset(l.file, offset)
169170
return line, col, err
170171
}
171172

172-
func (l *FileConverter) ToOffset(line, col int) (int, error) {
173+
func (l *TokenConverter) ToOffset(line, col int) (int, error) {
173174
if line < 0 {
174175
return -1, fmt.Errorf("line is not valid")
175176
}

internal/span/token_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func TestToken(t *testing.T) {
4848
}
4949
for _, test := range tokenTests {
5050
f := files[test.URI()]
51-
c := span.NewTokenConverter(fset, f)
51+
c := span.NewTokenConverter(f)
5252
t.Run(path.Base(f.Name()), func(t *testing.T) {
5353
checkToken(t, c, span.New(
5454
test.URI(),

0 commit comments

Comments
 (0)