1
0
mirror of https://github.com/golang/go synced 2024-10-01 03:28:32 -06:00
go/internal/lsp/position.go
Ian Cottrell 4b1f3b6b16 internal/lsp: make format work on the ast not the source
This makes the format code use the AST that is already cached on the file to do
the formatting. It also moves the core format code into the source directory.

Change-Id: Iaa79169708e92525cce326ea094ab98144fe1011
Reviewed-on: https://go-review.googlesource.com/c/148198
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2018-11-12 21:02:38 +00:00

115 lines
3.3 KiB
Go

// Copyright 2018 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 lsp
import (
"go/token"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/lsp/source"
)
// fromProtocolLocation converts from a protocol location to a source range.
// It will return an error if the file of the location was not valid.
// It uses fromProtocolRange to convert the start and end positions.
func fromProtocolLocation(v *source.View, loc protocol.Location) (source.Range, error) {
f := v.GetFile(source.URI(loc.URI))
tok, err := f.GetToken()
if err != nil {
return source.Range{}, err
}
return fromProtocolRange(tok, loc.Range), nil
}
// toProtocolLocation converts from a source range back to a protocol location.
func toProtocolLocation(v *source.View, r source.Range) protocol.Location {
tokFile := v.Config.Fset.File(r.Start)
file := v.GetFile(source.ToURI(tokFile.Name()))
return protocol.Location{
URI: protocol.DocumentURI(file.URI),
Range: toProtocolRange(tokFile, r),
}
}
// fromProtocolRange converts a protocol range to a source range.
// It uses fromProtocolPosition to convert the start and end positions, which
// requires the token file the positions belongs to.
func fromProtocolRange(f *token.File, r protocol.Range) source.Range {
start := fromProtocolPosition(f, r.Start)
var end token.Pos
switch {
case r.End == r.Start:
end = start
case r.End.Line < 0:
end = token.NoPos
default:
end = fromProtocolPosition(f, r.End)
}
return source.Range{
Start: start,
End: end,
}
}
// toProtocolRange converts from a source range back to a protocol range.
func toProtocolRange(f *token.File, r source.Range) protocol.Range {
return protocol.Range{
Start: toProtocolPosition(f, r.Start),
End: toProtocolPosition(f, r.End),
}
}
// fromProtocolPosition converts a protocol position (0-based line and column
// number) to a token.Pos (byte offset value).
// It requires the token file the pos belongs to in order to do this.
func fromProtocolPosition(f *token.File, pos protocol.Position) token.Pos {
line := lineStart(f, int(pos.Line)+1)
return line + token.Pos(pos.Character) // TODO: this is wrong, bytes not characters
}
// toProtocolPosition converts from a token pos (byte offset) to a protocol
// position (0-based line and column number)
// It requires the token file the pos belongs to in order to do this.
func toProtocolPosition(f *token.File, pos token.Pos) protocol.Position {
if !pos.IsValid() {
return protocol.Position{Line: -1.0, Character: -1.0}
}
p := f.Position(pos)
return protocol.Position{
Line: float64(p.Line - 1),
Character: float64(p.Column - 1),
}
}
// this functionality was borrowed from the analysisutil package
func lineStart(f *token.File, line int) token.Pos {
// Use binary search to find the start offset of this line.
//
// TODO(adonovan): eventually replace this function with the
// simpler and more efficient (*go/token.File).LineStart, added
// in go1.12.
min := 0 // inclusive
max := f.Size() // exclusive
for {
offset := (min + max) / 2
pos := f.Pos(offset)
posn := f.Position(pos)
if posn.Line == line {
return pos - (token.Pos(posn.Column) - 1)
}
if min+1 >= max {
return token.NoPos
}
if posn.Line < line {
min = offset
} else {
max = offset
}
}
}