2018-12-14 13:46:12 -07:00
|
|
|
// 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 cmd
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"go/token"
|
|
|
|
"regexp"
|
|
|
|
"strconv"
|
|
|
|
|
|
|
|
"golang.org/x/tools/internal/lsp/source"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Location struct {
|
|
|
|
Filename string `json:"file"`
|
|
|
|
Start Position `json:"start"`
|
|
|
|
End Position `json:"end"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Position struct {
|
|
|
|
Line int `json:"line"`
|
|
|
|
Column int `json:"column"`
|
|
|
|
Offset int `json:"offset"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func newLocation(fset *token.FileSet, r source.Range) Location {
|
|
|
|
start := fset.Position(r.Start)
|
|
|
|
end := fset.Position(r.End)
|
|
|
|
// it should not be possible the following line to fail
|
|
|
|
filename, _ := source.ToURI(start.Filename).Filename()
|
|
|
|
return Location{
|
|
|
|
Filename: filename,
|
|
|
|
Start: Position{
|
|
|
|
Line: start.Line,
|
|
|
|
Column: start.Column,
|
|
|
|
Offset: fset.File(r.Start).Offset(r.Start),
|
|
|
|
},
|
|
|
|
End: Position{
|
|
|
|
Line: end.Line,
|
|
|
|
Column: end.Column,
|
|
|
|
Offset: fset.File(r.End).Offset(r.End),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var posRe = regexp.MustCompile(
|
|
|
|
`(?P<file>.*):(?P<start>(?P<sline>\d+):(?P<scol>\d)+|#(?P<soff>\d+))(?P<end>:(?P<eline>\d+):(?P<ecol>\d+)|#(?P<eoff>\d+))?$`)
|
|
|
|
|
|
|
|
const (
|
|
|
|
posReAll = iota
|
|
|
|
posReFile
|
|
|
|
posReStart
|
|
|
|
posReSLine
|
|
|
|
posReSCol
|
|
|
|
posReSOff
|
|
|
|
posReEnd
|
|
|
|
posReELine
|
|
|
|
posReECol
|
|
|
|
posReEOff
|
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
names := posRe.SubexpNames()
|
|
|
|
// verify all our submatch offsets are correct
|
|
|
|
for name, index := range map[string]int{
|
|
|
|
"file": posReFile,
|
|
|
|
"start": posReStart,
|
|
|
|
"sline": posReSLine,
|
|
|
|
"scol": posReSCol,
|
|
|
|
"soff": posReSOff,
|
|
|
|
"end": posReEnd,
|
|
|
|
"eline": posReELine,
|
|
|
|
"ecol": posReECol,
|
|
|
|
"eoff": posReEOff,
|
|
|
|
} {
|
|
|
|
if names[index] == name {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// try to find it
|
|
|
|
for test := range names {
|
|
|
|
if names[test] == name {
|
|
|
|
panic(fmt.Errorf("Index for %s incorrect, wanted %v have %v", name, index, test))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
panic(fmt.Errorf("Subexp %s does not exist", name))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// parseLocation parses a string of the form "file:pos" or
|
|
|
|
// file:start,end" where pos, start, end match either a byte offset in the
|
|
|
|
// form #%d or a line and column in the form %d,%d.
|
|
|
|
func parseLocation(value string) (Location, error) {
|
|
|
|
var loc Location
|
|
|
|
m := posRe.FindStringSubmatch(value)
|
|
|
|
if m == nil {
|
|
|
|
return loc, fmt.Errorf("bad location syntax %q", value)
|
|
|
|
}
|
|
|
|
loc.Filename = m[posReFile]
|
|
|
|
if m[posReSLine] != "" {
|
2019-02-05 15:27:35 -07:00
|
|
|
v, err := strconv.ParseInt(m[posReSLine], 10, 32)
|
|
|
|
if err != nil {
|
2018-12-14 13:46:12 -07:00
|
|
|
return loc, err
|
|
|
|
}
|
2019-02-05 15:27:35 -07:00
|
|
|
loc.Start.Line = int(v)
|
|
|
|
v, err = strconv.ParseInt(m[posReSCol], 10, 32)
|
|
|
|
if err != nil {
|
2018-12-14 13:46:12 -07:00
|
|
|
return loc, err
|
|
|
|
}
|
2019-02-05 15:27:35 -07:00
|
|
|
loc.Start.Column = int(v)
|
2018-12-14 13:46:12 -07:00
|
|
|
} else {
|
2019-02-05 15:27:35 -07:00
|
|
|
v, err := strconv.ParseInt(m[posReSOff], 10, 32)
|
|
|
|
if err != nil {
|
2018-12-14 13:46:12 -07:00
|
|
|
return loc, err
|
|
|
|
}
|
2019-02-05 15:27:35 -07:00
|
|
|
loc.Start.Offset = int(v)
|
2018-12-14 13:46:12 -07:00
|
|
|
}
|
|
|
|
if m[posReEnd] == "" {
|
|
|
|
loc.End = loc.Start
|
|
|
|
} else {
|
|
|
|
if m[posReELine] != "" {
|
2019-02-05 15:27:35 -07:00
|
|
|
v, err := strconv.ParseInt(m[posReELine], 10, 32)
|
|
|
|
if err != nil {
|
2018-12-14 13:46:12 -07:00
|
|
|
return loc, err
|
|
|
|
}
|
2019-02-05 15:27:35 -07:00
|
|
|
loc.End.Line = int(v)
|
|
|
|
v, err = strconv.ParseInt(m[posReECol], 10, 32)
|
|
|
|
if err != nil {
|
2018-12-14 13:46:12 -07:00
|
|
|
return loc, err
|
|
|
|
}
|
2019-02-05 15:27:35 -07:00
|
|
|
loc.End.Column = int(v)
|
2018-12-14 13:46:12 -07:00
|
|
|
} else {
|
2019-02-05 15:27:35 -07:00
|
|
|
v, err := strconv.ParseInt(m[posReEOff], 10, 32)
|
|
|
|
if err != nil {
|
2018-12-14 13:46:12 -07:00
|
|
|
return loc, err
|
|
|
|
}
|
2019-02-05 15:27:35 -07:00
|
|
|
loc.End.Offset = int(v)
|
2018-12-14 13:46:12 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return loc, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l Location) Format(f fmt.State, c rune) {
|
|
|
|
// we should always have a filename
|
|
|
|
fmt.Fprint(f, l.Filename)
|
|
|
|
// are we in line:column format or #offset format
|
|
|
|
fmt.Fprintf(f, ":%v", l.Start)
|
|
|
|
if l.End != l.Start {
|
|
|
|
fmt.Fprintf(f, ",%v", l.End)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p Position) Format(f fmt.State, c rune) {
|
|
|
|
// are we in line:column format or #offset format
|
|
|
|
if p.Line > 0 {
|
|
|
|
fmt.Fprintf(f, "%d:%d", p.Line, p.Column)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
fmt.Fprintf(f, "#%d", p.Offset)
|
|
|
|
}
|