mirror of
https://github.com/golang/go
synced 2024-11-18 10:54:40 -07:00
dbad8e90c9
This rationalises all the position handling and conversion code out. Fixes golang/go#29149 Change-Id: I2814f3e8ba769924bc70f35df9e5bf4d97d064de Reviewed-on: https://go-review.googlesource.com/c/tools/+/166884 Reviewed-by: Rebecca Stambler <rstambler@golang.org>
115 lines
3.1 KiB
Go
115 lines
3.1 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 span
|
|
|
|
import (
|
|
"go/token"
|
|
)
|
|
|
|
// Range represents a source code range in token.Pos form.
|
|
// It also carries the FileSet that produced the positions, so that it is
|
|
// self contained.
|
|
type Range struct {
|
|
FileSet *token.FileSet
|
|
Start token.Pos
|
|
End token.Pos
|
|
}
|
|
|
|
// TokenConverter is a Converter backed by a token file set and file.
|
|
// It uses the file set methods to work out determine the conversions which
|
|
// make if fast and do not require the file contents.
|
|
type TokenConverter struct {
|
|
fset *token.FileSet
|
|
file *token.File
|
|
}
|
|
|
|
// NewRange creates a new Range from a FileSet and two positions.
|
|
// To represent a point pass a 0 as the end pos.
|
|
func NewRange(fset *token.FileSet, start, end token.Pos) Range {
|
|
return Range{
|
|
FileSet: fset,
|
|
Start: start,
|
|
End: end,
|
|
}
|
|
}
|
|
|
|
// NewTokenConverter returns an implementation of Coords and Offsets backed by a
|
|
// token.File.
|
|
func NewTokenConverter(fset *token.FileSet, f *token.File) *TokenConverter {
|
|
return &TokenConverter{fset: fset, file: f}
|
|
}
|
|
|
|
// NewContentConverter returns an implementation of Coords and Offsets for the
|
|
// given file content.
|
|
func NewContentConverter(filename string, content []byte) *TokenConverter {
|
|
fset := token.NewFileSet()
|
|
f := fset.AddFile(filename, -1, len(content))
|
|
f.SetLinesForContent(content)
|
|
return &TokenConverter{fset: fset, file: f}
|
|
}
|
|
|
|
// IsPoint returns true if the range represents a single point.
|
|
func (r Range) IsPoint() bool {
|
|
return r.Start == r.End
|
|
}
|
|
|
|
// Span converts a Range to a Span that represents the Range.
|
|
// It will fill in all the members of the Span, calculating the line and column
|
|
// information.
|
|
func (r Range) Span() Span {
|
|
f := r.FileSet.File(r.Start)
|
|
s := Span{URI: FileURI(f.Name())}
|
|
s.Start.Offset = f.Offset(r.Start)
|
|
if r.End.IsValid() {
|
|
s.End.Offset = f.Offset(r.End)
|
|
}
|
|
converter := NewTokenConverter(r.FileSet, f)
|
|
return s.CleanCoords(converter)
|
|
}
|
|
|
|
// Range converts a Span to a Range that represents the Span for the supplied
|
|
// File.
|
|
func (s Span) Range(converter *TokenConverter) Range {
|
|
s = s.CleanOffset(converter)
|
|
return Range{
|
|
FileSet: converter.fset,
|
|
Start: converter.file.Pos(s.Start.Offset),
|
|
End: converter.file.Pos(s.End.Offset),
|
|
}
|
|
}
|
|
|
|
func (l *TokenConverter) ToCoord(offset int) (int, int) {
|
|
pos := l.file.Pos(offset)
|
|
p := l.fset.Position(pos)
|
|
return p.Line, p.Column
|
|
}
|
|
|
|
func (l *TokenConverter) ToOffset(line, col int) int {
|
|
if line < 0 {
|
|
// before the start of the file
|
|
return -1
|
|
}
|
|
lineMax := l.file.LineCount() + 1
|
|
if line > lineMax {
|
|
// after the end of the file
|
|
return -1
|
|
} else if line == lineMax {
|
|
if col > 1 {
|
|
// after the end of the file
|
|
return -1
|
|
}
|
|
// at the end of the file, allowing for a trailing eol
|
|
return l.file.Size()
|
|
}
|
|
pos := lineStart(l.file, line)
|
|
if !pos.IsValid() {
|
|
return -1
|
|
}
|
|
// we assume that column is in bytes here, and that the first byte of a
|
|
// line is at column 1
|
|
pos += token.Pos(col - 1)
|
|
return l.file.Offset(pos)
|
|
}
|