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