File: position.go

package info (click to toggle)
golang-golang-x-tools 1%3A0.0~git20190125.d66bd3c%2Bds-4
  • links: PTS, VCS
  • area: main
  • in suites: buster, buster-backports
  • size: 8,912 kB
  • sloc: asm: 1,394; yacc: 155; makefile: 109; sh: 108; ansic: 17; xml: 11
file content (133 lines) | stat: -rw-r--r-- 4,118 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// 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 (
	"context"
	"go/token"

	"golang.org/x/tools/internal/lsp/cache"
	"golang.org/x/tools/internal/lsp/protocol"
	"golang.org/x/tools/internal/lsp/source"
)

// fromProtocolURI converts a protocol.DocumentURI to a source.URI.
// TODO(rstambler): Add logic here to support Windows.
func fromProtocolURI(uri protocol.DocumentURI) source.URI {
	return source.URI(uri)
}

// 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(ctx context.Context, v *cache.View, loc protocol.Location) (source.Range, error) {
	f, err := v.GetFile(ctx, fromProtocolURI(loc.URI))
	if err != nil {
		return source.Range{}, err
	}
	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(fset *token.FileSet, r source.Range) protocol.Location {
	tok := fset.File(r.Start)
	uri := source.ToURI(tok.Name())
	return protocol.Location{
		URI:   protocol.DocumentURI(uri),
		Range: toProtocolRange(tok, 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),
	}
}

// fromTokenPosition converts a token.Position (1-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 fromTokenPosition(f *token.File, pos token.Position) token.Pos {
	line := lineStart(f, pos.Line)
	return line + token.Pos(pos.Column-1) // TODO: this is wrong, bytes not characters
}

// 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
		}
	}
}