]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/hcl2/hcl/pos.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / hcl2 / hcl / pos.go
1 package hcl
2
3 import "fmt"
4
5 // Pos represents a single position in a source file, by addressing the
6 // start byte of a unicode character encoded in UTF-8.
7 //
8 // Pos is generally used only in the context of a Range, which then defines
9 // which source file the position is within.
10 type Pos struct {
11 // Line is the source code line where this position points. Lines are
12 // counted starting at 1 and incremented for each newline character
13 // encountered.
14 Line int
15
16 // Column is the source code column where this position points, in
17 // unicode characters, with counting starting at 1.
18 //
19 // Column counts characters as they appear visually, so for example a
20 // latin letter with a combining diacritic mark counts as one character.
21 // This is intended for rendering visual markers against source code in
22 // contexts where these diacritics would be rendered in a single character
23 // cell. Technically speaking, Column is counting grapheme clusters as
24 // used in unicode normalization.
25 Column int
26
27 // Byte is the byte offset into the file where the indicated character
28 // begins. This is a zero-based offset to the first byte of the first
29 // UTF-8 codepoint sequence in the character, and thus gives a position
30 // that can be resolved _without_ awareness of Unicode characters.
31 Byte int
32 }
33
34 // InitialPos is a suitable position to use to mark the start of a file.
35 var InitialPos = Pos{Byte: 0, Line: 1, Column: 1}
36
37 // Range represents a span of characters between two positions in a source
38 // file.
39 //
40 // This struct is usually used by value in types that represent AST nodes,
41 // but by pointer in types that refer to the positions of other objects,
42 // such as in diagnostics.
43 type Range struct {
44 // Filename is the name of the file into which this range's positions
45 // point.
46 Filename string
47
48 // Start and End represent the bounds of this range. Start is inclusive
49 // and End is exclusive.
50 Start, End Pos
51 }
52
53 // RangeBetween returns a new range that spans from the beginning of the
54 // start range to the end of the end range.
55 //
56 // The result is meaningless if the two ranges do not belong to the same
57 // source file or if the end range appears before the start range.
58 func RangeBetween(start, end Range) Range {
59 return Range{
60 Filename: start.Filename,
61 Start: start.Start,
62 End: end.End,
63 }
64 }
65
66 // RangeOver returns a new range that covers both of the given ranges and
67 // possibly additional content between them if the two ranges do not overlap.
68 //
69 // If either range is empty then it is ignored. The result is empty if both
70 // given ranges are empty.
71 //
72 // The result is meaningless if the two ranges to not belong to the same
73 // source file.
74 func RangeOver(a, b Range) Range {
75 if a.Empty() {
76 return b
77 }
78 if b.Empty() {
79 return a
80 }
81
82 var start, end Pos
83 if a.Start.Byte < b.Start.Byte {
84 start = a.Start
85 } else {
86 start = b.Start
87 }
88 if a.End.Byte > b.End.Byte {
89 end = a.End
90 } else {
91 end = b.End
92 }
93 return Range{
94 Filename: a.Filename,
95 Start: start,
96 End: end,
97 }
98 }
99
100 // ContainsPos returns true if and only if the given position is contained within
101 // the receiving range.
102 //
103 // In the unlikely case that the line/column information disagree with the byte
104 // offset information in the given position or receiving range, the byte
105 // offsets are given priority.
106 func (r Range) ContainsPos(pos Pos) bool {
107 return r.ContainsOffset(pos.Byte)
108 }
109
110 // ContainsOffset returns true if and only if the given byte offset is within
111 // the receiving Range.
112 func (r Range) ContainsOffset(offset int) bool {
113 return offset >= r.Start.Byte && offset < r.End.Byte
114 }
115
116 // Ptr returns a pointer to a copy of the receiver. This is a convenience when
117 // ranges in places where pointers are required, such as in Diagnostic, but
118 // the range in question is returned from a method. Go would otherwise not
119 // allow one to take the address of a function call.
120 func (r Range) Ptr() *Range {
121 return &r
122 }
123
124 // String returns a compact string representation of the receiver.
125 // Callers should generally prefer to present a range more visually,
126 // e.g. via markers directly on the relevant portion of source code.
127 func (r Range) String() string {
128 if r.Start.Line == r.End.Line {
129 return fmt.Sprintf(
130 "%s:%d,%d-%d",
131 r.Filename,
132 r.Start.Line, r.Start.Column,
133 r.End.Column,
134 )
135 } else {
136 return fmt.Sprintf(
137 "%s:%d,%d-%d,%d",
138 r.Filename,
139 r.Start.Line, r.Start.Column,
140 r.End.Line, r.End.Column,
141 )
142 }
143 }
144
145 func (r Range) Empty() bool {
146 return r.Start.Byte == r.End.Byte
147 }
148
149 // CanSliceBytes returns true if SliceBytes could return an accurate
150 // sub-slice of the given slice.
151 //
152 // This effectively tests whether the start and end offsets of the range
153 // are within the bounds of the slice, and thus whether SliceBytes can be
154 // trusted to produce an accurate start and end position within that slice.
155 func (r Range) CanSliceBytes(b []byte) bool {
156 switch {
157 case r.Start.Byte < 0 || r.Start.Byte > len(b):
158 return false
159 case r.End.Byte < 0 || r.End.Byte > len(b):
160 return false
161 case r.End.Byte < r.Start.Byte:
162 return false
163 default:
164 return true
165 }
166 }
167
168 // SliceBytes returns a sub-slice of the given slice that is covered by the
169 // receiving range, assuming that the given slice is the source code of the
170 // file indicated by r.Filename.
171 //
172 // If the receiver refers to any byte offsets that are outside of the slice
173 // then the result is constrained to the overlapping portion only, to avoid
174 // a panic. Use CanSliceBytes to determine if the result is guaranteed to
175 // be an accurate span of the requested range.
176 func (r Range) SliceBytes(b []byte) []byte {
177 start := r.Start.Byte
178 end := r.End.Byte
179 if start < 0 {
180 start = 0
181 } else if start > len(b) {
182 start = len(b)
183 }
184 if end < 0 {
185 end = 0
186 } else if end > len(b) {
187 end = len(b)
188 }
189 if end < start {
190 end = start
191 }
192 return b[start:end]
193 }
194
195 // Overlaps returns true if the receiver and the other given range share any
196 // characters in common.
197 func (r Range) Overlaps(other Range) bool {
198 switch {
199 case r.Filename != other.Filename:
200 // If the ranges are in different files then they can't possibly overlap
201 return false
202 case r.Empty() || other.Empty():
203 // Empty ranges can never overlap
204 return false
205 case r.ContainsOffset(other.Start.Byte) || r.ContainsOffset(other.End.Byte):
206 return true
207 case other.ContainsOffset(r.Start.Byte) || other.ContainsOffset(r.End.Byte):
208 return true
209 default:
210 return false
211 }
212 }
213
214 // Overlap finds a range that is either identical to or a sub-range of both
215 // the receiver and the other given range. It returns an empty range
216 // within the receiver if there is no overlap between the two ranges.
217 //
218 // A non-empty result is either identical to or a subset of the receiver.
219 func (r Range) Overlap(other Range) Range {
220 if !r.Overlaps(other) {
221 // Start == End indicates an empty range
222 return Range{
223 Filename: r.Filename,
224 Start: r.Start,
225 End: r.Start,
226 }
227 }
228
229 var start, end Pos
230 if r.Start.Byte > other.Start.Byte {
231 start = r.Start
232 } else {
233 start = other.Start
234 }
235 if r.End.Byte < other.End.Byte {
236 end = r.End
237 } else {
238 end = other.End
239 }
240
241 return Range{
242 Filename: r.Filename,
243 Start: start,
244 End: end,
245 }
246 }
247
248 // PartitionAround finds the portion of the given range that overlaps with
249 // the reciever and returns three ranges: the portion of the reciever that
250 // precedes the overlap, the overlap itself, and then the portion of the
251 // reciever that comes after the overlap.
252 //
253 // If the two ranges do not overlap then all three returned ranges are empty.
254 //
255 // If the given range aligns with or extends beyond either extent of the
256 // reciever then the corresponding outer range will be empty.
257 func (r Range) PartitionAround(other Range) (before, overlap, after Range) {
258 overlap = r.Overlap(other)
259 if overlap.Empty() {
260 return overlap, overlap, overlap
261 }
262
263 before = Range{
264 Filename: r.Filename,
265 Start: r.Start,
266 End: overlap.Start,
267 }
268 after = Range{
269 Filename: r.Filename,
270 Start: overlap.End,
271 End: r.End,
272 }
273
274 return before, overlap, after
275 }