OLD | NEW |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library source_span.file; | 5 library source_span.file; |
6 | 6 |
7 import 'dart:math' as math; | 7 import 'dart:math' as math; |
8 import 'dart:typed_data'; | 8 import 'dart:typed_data'; |
9 | 9 |
10 import 'package:path/path.dart' as p; | 10 import 'package:path/path.dart' as p; |
11 | 11 |
12 import 'colors.dart' as colors; | 12 import 'colors.dart' as colors; |
13 import 'location.dart'; | 13 import 'location.dart'; |
14 import 'span.dart'; | 14 import 'span.dart'; |
15 import 'span_mixin.dart'; | 15 import 'span_mixin.dart'; |
| 16 import 'span_with_context.dart'; |
16 import 'utils.dart'; | 17 import 'utils.dart'; |
17 | 18 |
18 // Constants to determine end-of-lines. | 19 // Constants to determine end-of-lines. |
19 const int _LF = 10; | 20 const int _LF = 10; |
20 const int _CR = 13; | 21 const int _CR = 13; |
21 | 22 |
22 /// A class representing a source file. | 23 /// A class representing a source file. |
23 /// | 24 /// |
24 /// This doesn't necessarily have to correspond to a file on disk, just a chunk | 25 /// This doesn't necessarily have to correspond to a file on disk, just a chunk |
25 /// of text usually with a URL associated with it. | 26 /// of text usually with a URL associated with it. |
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
176 } | 177 } |
177 | 178 |
178 /// A [SourceSpan] within a [SourceFile]. | 179 /// A [SourceSpan] within a [SourceFile]. |
179 /// | 180 /// |
180 /// Unlike the base [SourceSpan], [FileSpan] lazily computes its line and column | 181 /// Unlike the base [SourceSpan], [FileSpan] lazily computes its line and column |
181 /// values based on its offset and the contents of [file]. [FileSpan.message] is | 182 /// values based on its offset and the contents of [file]. [FileSpan.message] is |
182 /// also able to provide more context then [SourceSpan.message], and | 183 /// also able to provide more context then [SourceSpan.message], and |
183 /// [FileSpan.union] will return a [FileSpan] if possible. | 184 /// [FileSpan.union] will return a [FileSpan] if possible. |
184 /// | 185 /// |
185 /// A [FileSpan] can be created using [SourceFile.span]. | 186 /// A [FileSpan] can be created using [SourceFile.span]. |
186 class FileSpan extends SourceSpanMixin { | 187 class FileSpan extends SourceSpanMixin implements SourceSpanWithContext { |
187 /// The [file] that [this] belongs to. | 188 /// The [file] that [this] belongs to. |
188 final SourceFile file; | 189 final SourceFile file; |
189 | 190 |
190 /// The offset of the beginning of the span. | 191 /// The offset of the beginning of the span. |
191 /// | 192 /// |
192 /// [start] is lazily generated from this to avoid allocating unnecessary | 193 /// [start] is lazily generated from this to avoid allocating unnecessary |
193 /// objects. | 194 /// objects. |
194 final int _start; | 195 final int _start; |
195 | 196 |
196 /// The offset of the end of the span. | 197 /// The offset of the end of the span. |
197 /// | 198 /// |
198 /// [end] is lazily generated from this to avoid allocating unnecessary | 199 /// [end] is lazily generated from this to avoid allocating unnecessary |
199 /// objects. | 200 /// objects. |
200 final int _end; | 201 final int _end; |
201 | 202 |
202 Uri get sourceUrl => file.url; | 203 Uri get sourceUrl => file.url; |
203 int get length => _end - _start; | 204 int get length => _end - _start; |
204 FileLocation get start => new FileLocation._(file, _start); | 205 FileLocation get start => new FileLocation._(file, _start); |
205 FileLocation get end => new FileLocation._(file, _end); | 206 FileLocation get end => new FileLocation._(file, _end); |
206 String get text => file.getText(_start, _end); | 207 String get text => file.getText(_start, _end); |
207 | 208 |
| 209 String get context { |
| 210 var line = start.line; |
| 211 return file.getText(file.getOffset(line), |
| 212 line == file.lines - 1 ? null : file.getOffset(line + 1)); |
| 213 } |
| 214 |
208 FileSpan._(this.file, this._start, this._end) { | 215 FileSpan._(this.file, this._start, this._end) { |
209 if (_end < _start) { | 216 if (_end < _start) { |
210 throw new ArgumentError('End $_end must come after start $_start.'); | 217 throw new ArgumentError('End $_end must come after start $_start.'); |
211 } else if (_end > file.length) { | 218 } else if (_end > file.length) { |
212 throw new RangeError("End $_end must not be greater than the number " | 219 throw new RangeError("End $_end must not be greater than the number " |
213 "of characters in the file, ${file.length}."); | 220 "of characters in the file, ${file.length}."); |
214 } else if (_start < 0) { | 221 } else if (_start < 0) { |
215 throw new RangeError("Start may not be negative, was $_start."); | 222 throw new RangeError("Start may not be negative, was $_start."); |
216 } | 223 } |
217 } | 224 } |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
254 FileSpan expand(FileSpan other) { | 261 FileSpan expand(FileSpan other) { |
255 if (sourceUrl != other.sourceUrl) { | 262 if (sourceUrl != other.sourceUrl) { |
256 throw new ArgumentError("Source URLs \"${sourceUrl}\" and " | 263 throw new ArgumentError("Source URLs \"${sourceUrl}\" and " |
257 " \"${other.sourceUrl}\" don't match."); | 264 " \"${other.sourceUrl}\" don't match."); |
258 } | 265 } |
259 | 266 |
260 var start = math.min(this._start, other._start); | 267 var start = math.min(this._start, other._start); |
261 var end = math.max(this._end, other._end); | 268 var end = math.max(this._end, other._end); |
262 return new FileSpan._(file, start, end); | 269 return new FileSpan._(file, start, end); |
263 } | 270 } |
264 | |
265 String message(String message, {color}) { | |
266 if (color == true) color = colors.RED; | |
267 if (color == false) color = null; | |
268 | |
269 var line = start.line; | |
270 var column = start.column; | |
271 | |
272 var buffer = new StringBuffer(); | |
273 buffer.write('line ${start.line + 1}, column ${start.column + 1}'); | |
274 if (sourceUrl != null) buffer.write(' of ${p.prettyUri(sourceUrl)}'); | |
275 buffer.write(': $message\n'); | |
276 | |
277 var textLine = file.getText(file.getOffset(line), | |
278 line == file.lines - 1 ? null : file.getOffset(line + 1)); | |
279 | |
280 column = math.min(column, textLine.length - 1); | |
281 var toColumn = | |
282 math.min(column + end.offset - start.offset, textLine.length); | |
283 | |
284 if (color != null) { | |
285 buffer.write(textLine.substring(0, column)); | |
286 buffer.write(color); | |
287 buffer.write(textLine.substring(column, toColumn)); | |
288 buffer.write(colors.NONE); | |
289 buffer.write(textLine.substring(toColumn)); | |
290 } else { | |
291 buffer.write(textLine); | |
292 } | |
293 if (!textLine.endsWith('\n')) buffer.write('\n'); | |
294 | |
295 buffer.write(' ' * column); | |
296 if (color != null) buffer.write(color); | |
297 buffer.write('^' * math.max(toColumn - column, 1)); | |
298 if (color != null) buffer.write(colors.NONE); | |
299 return buffer.toString(); | |
300 } | |
301 } | 271 } |
OLD | NEW |