1
1
package scip
2
2
3
- // Range represents a range between two offset positions.
3
+ import "fmt"
4
+
5
+ // Range represents [start, end) between two offset positions.
6
+ //
4
7
// NOTE: the github.com/sourcegraph/sourcegraph/lib/codeintel/lsif/protocol package
5
8
// contains similarly shaped structs but this one exists primarily to make it
6
9
// easier to work with SCIP encoded positions, which have the type []int32
@@ -16,18 +19,109 @@ type Position struct {
16
19
Character int32
17
20
}
18
21
19
- // NewRange converts an SCIP range into `Range`
20
- func NewRange (scipRange []int32 ) * Range {
21
- var endLine int32
22
- var endCharacter int32
23
- if len (scipRange ) == 3 { // single line
24
- endLine = scipRange [0 ]
25
- endCharacter = scipRange [2 ]
26
- } else if len (scipRange ) == 4 { // multi-line
22
+ func (p Position ) Compare (other Position ) int {
23
+ if p .Line < other .Line {
24
+ return - 1
25
+ }
26
+ if p .Line > other .Line {
27
+ return 1
28
+ }
29
+ if p .Character < other .Character {
30
+ return - 1
31
+ }
32
+ if p .Character > other .Character {
33
+ return 1
34
+ }
35
+ return 0
36
+ }
37
+
38
+ func (p Position ) Less (other Position ) bool {
39
+ if p .Line < other .Line {
40
+ return true
41
+ }
42
+ if p .Line > other .Line {
43
+ return false
44
+ }
45
+ return p .Character < other .Character
46
+ }
47
+
48
+ //go:noinline
49
+ func makeNewRangeError (startLine , endLine , startChar , endChar int32 ) (Range , error ) {
50
+ if startLine < 0 || endLine < 0 || startChar < 0 || endChar < 0 {
51
+ return Range {}, NegativeOffsetsRangeError
52
+ }
53
+ if startLine > endLine || (startLine == endLine && startChar > endChar ) {
54
+ return Range {}, EndBeforeStartRangeError
55
+ }
56
+ panic ("unreachable" )
57
+ }
58
+
59
+ // NewRange constructs a Range while checking if the input is valid.
60
+ func NewRange (scipRange []int32 ) (Range , error ) {
61
+ // N.B. This function is kept small so that it can be inlined easily.
62
+ // See also: https://github.com/golang/go/issues/17566
63
+ var startLine , endLine , startChar , endChar int32
64
+ switch len (scipRange ) {
65
+ case 3 :
66
+ startLine = scipRange [0 ]
67
+ endLine = startLine
68
+ startChar = scipRange [1 ]
69
+ endChar = scipRange [2 ]
70
+ if startLine >= 0 && startChar >= 0 && endChar >= startChar {
71
+ break
72
+ }
73
+ return makeNewRangeError (startLine , endLine , startChar , endChar )
74
+ case 4 :
75
+ startLine = scipRange [0 ]
76
+ startChar = scipRange [1 ]
27
77
endLine = scipRange [2 ]
78
+ endChar = scipRange [3 ]
79
+ if startLine >= 0 && startChar >= 0 &&
80
+ ((endLine > startLine && endChar >= 0 ) || (endLine == startLine && endChar >= startChar )) {
81
+ break
82
+ }
83
+ return makeNewRangeError (startLine , endLine , startChar , endChar )
84
+ default :
85
+ return Range {}, IncorrectLengthRangeError
86
+ }
87
+ return Range {Start : Position {Line : startLine , Character : startChar }, End : Position {Line : endLine , Character : endChar }}, nil
88
+ }
89
+
90
+ type RangeError int32
91
+
92
+ const (
93
+ IncorrectLengthRangeError RangeError = iota
94
+ NegativeOffsetsRangeError
95
+ EndBeforeStartRangeError
96
+ )
97
+
98
+ var _ error = RangeError (0 )
99
+
100
+ func (e RangeError ) Error () string {
101
+ switch e {
102
+ case IncorrectLengthRangeError :
103
+ return "incorrect length"
104
+ case NegativeOffsetsRangeError :
105
+ return "negative offsets"
106
+ case EndBeforeStartRangeError :
107
+ return "end before start"
108
+ }
109
+ panic ("unhandled range error" )
110
+ }
111
+
112
+ // NewRangeUnchecked converts an SCIP range into `Range`
113
+ //
114
+ // Pre-condition: The input slice must follow the SCIP range encoding.
115
+ // https://sourcegraph.com/github.com/sourcegraph/scip/-/blob/scip.proto?L646:18-646:23
116
+ func NewRangeUnchecked (scipRange []int32 ) Range {
117
+ // Single-line case is most common
118
+ endCharacter := scipRange [2 ]
119
+ endLine := scipRange [0 ]
120
+ if len (scipRange ) == 4 { // multi-line
28
121
endCharacter = scipRange [3 ]
122
+ endLine = scipRange [2 ]
29
123
}
30
- return & Range {
124
+ return Range {
31
125
Start : Position {
32
126
Line : scipRange [0 ],
33
127
Character : scipRange [1 ],
@@ -49,3 +143,57 @@ func (r Range) SCIPRange() []int32 {
49
143
}
50
144
return []int32 {r .Start .Line , r .Start .Character , r .End .Line , r .End .Character }
51
145
}
146
+
147
+ // Contains checks if position is within the range
148
+ func (r Range ) Contains (position Position ) bool {
149
+ return ! position .Less (r .Start ) && position .Less (r .End )
150
+ }
151
+
152
+ // Intersects checks if two ranges intersect.
153
+ //
154
+ // case 1: r1.Start >= other.Start && r1.Start < other.End
155
+ // case 2: r2.Start >= r1.Start && r2.Start < r1.End
156
+ func (r Range ) Intersects (other Range ) bool {
157
+ return r .Start .Less (other .End ) && other .Start .Less (r .End )
158
+ }
159
+
160
+ // Compare compares two ranges.
161
+ //
162
+ // Returns 0 if the ranges intersect (not just if they're equal).
163
+ func (r Range ) Compare (other Range ) int {
164
+ if r .Intersects (other ) {
165
+ return 0
166
+ }
167
+ return r .Start .Compare (other .Start )
168
+ }
169
+
170
+ // Less compares two ranges, consistent with Compare.
171
+ //
172
+ // r.Compare(other) < 0 iff r.Less(other).
173
+ func (r Range ) Less (other Range ) bool {
174
+ return r .End .Compare (other .Start ) <= 0
175
+ }
176
+
177
+ // CompareStrict compares two ranges.
178
+ //
179
+ // Returns 0 iff the ranges are exactly equal.
180
+ func (r Range ) CompareStrict (other Range ) int {
181
+ if ret := r .Start .Compare (other .Start ); ret != 0 {
182
+ return ret
183
+ }
184
+ return r .End .Compare (other .End )
185
+ }
186
+
187
+ // LessStrict compares two ranges, consistent with CompareStrict.
188
+ //
189
+ // r.CompareStrict(other) < 0 iff r.LessStrict(other).
190
+ func (r Range ) LessStrict (other Range ) bool {
191
+ if ret := r .Start .Compare (other .Start ); ret != 0 {
192
+ return ret < 0
193
+ }
194
+ return r .End .Less (other .End )
195
+ }
196
+
197
+ func (r Range ) String () string {
198
+ return fmt .Sprintf ("%d:%d-%d:%d" , r .Start .Line , r .Start .Character , r .End .Line , r .End .Character )
199
+ }
0 commit comments