1
0
mirror of https://github.com/golang/go synced 2024-10-01 10:18:32 -06:00
go/internal/span/token.go
Ian Cottrell b40df0fb21 internal/span: adding a source location package to unify the pos conversions
This implements a standard way of describing source locations along with
printing and parsing of those locations and conversion to and from the token.Pos
forms.

Change-Id: Ibb3df0a282bc2effb0fa8bd3a51bb0d281b2ffb1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/163778
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2019-03-08 14:21:31 +00:00

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) Converter {
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)
}