| 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; | |
| 11 | |
| 12 import 'colors.dart' as colors; | |
| 13 import 'location.dart'; | 10 import 'location.dart'; |
| 14 import 'span.dart'; | 11 import 'span.dart'; |
| 15 import 'span_mixin.dart'; | 12 import 'span_mixin.dart'; |
| 16 import 'span_with_context.dart'; | 13 import 'span_with_context.dart'; |
| 17 import 'utils.dart'; | 14 import 'utils.dart'; |
| 18 | 15 |
| 19 // Constants to determine end-of-lines. | 16 // Constants to determine end-of-lines. |
| 20 const int _LF = 10; | 17 const int _LF = 10; |
| 21 const int _CR = 13; | 18 const int _CR = 13; |
| 22 | 19 |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 67 } | 64 } |
| 68 if (c == _LF) _lineStarts.add(i + 1); | 65 if (c == _LF) _lineStarts.add(i + 1); |
| 69 } | 66 } |
| 70 } | 67 } |
| 71 | 68 |
| 72 /// Returns a span in [this] from [start] to [end] (exclusive). | 69 /// Returns a span in [this] from [start] to [end] (exclusive). |
| 73 /// | 70 /// |
| 74 /// If [end] isn't passed, it defaults to the end of the file. | 71 /// If [end] isn't passed, it defaults to the end of the file. |
| 75 FileSpan span(int start, [int end]) { | 72 FileSpan span(int start, [int end]) { |
| 76 if (end == null) end = length - 1; | 73 if (end == null) end = length - 1; |
| 77 return new FileSpan._(this, start, end); | 74 return new _FileSpan(this, start, end); |
| 78 } | 75 } |
| 79 | 76 |
| 80 /// Returns a location in [this] at [offset]. | 77 /// Returns a location in [this] at [offset]. |
| 81 FileLocation location(int offset) => new FileLocation._(this, offset); | 78 FileLocation location(int offset) => new FileLocation._(this, offset); |
| 82 | 79 |
| 83 /// Gets the 0-based line corresponding to [offset]. | 80 /// Gets the 0-based line corresponding to [offset]. |
| 84 int getLine(int offset) { | 81 int getLine(int offset) { |
| 85 if (offset < 0) { | 82 if (offset < 0) { |
| 86 throw new RangeError("Offset may not be negative, was $offset."); | 83 throw new RangeError("Offset may not be negative, was $offset."); |
| 87 } else if (offset > length) { | 84 } else if (offset > length) { |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 166 int get column => file.getColumn(offset); | 163 int get column => file.getColumn(offset); |
| 167 | 164 |
| 168 FileLocation._(this.file, int offset) | 165 FileLocation._(this.file, int offset) |
| 169 : super(offset) { | 166 : super(offset) { |
| 170 if (offset > file.length) { | 167 if (offset > file.length) { |
| 171 throw new RangeError("Offset $offset must not be greater than the number " | 168 throw new RangeError("Offset $offset must not be greater than the number " |
| 172 "of characters in the file, ${file.length}."); | 169 "of characters in the file, ${file.length}."); |
| 173 } | 170 } |
| 174 } | 171 } |
| 175 | 172 |
| 176 FileSpan pointSpan() => new FileSpan._(file, offset, offset); | 173 FileSpan pointSpan() => new _FileSpan(file, offset, offset); |
| 177 } | 174 } |
| 178 | 175 |
| 179 /// A [SourceSpan] within a [SourceFile]. | 176 /// A [SourceSpan] within a [SourceFile]. |
| 180 /// | 177 /// |
| 181 /// Unlike the base [SourceSpan], [FileSpan] lazily computes its line and column | 178 /// Unlike the base [SourceSpan], [FileSpan] lazily computes its line and column |
| 182 /// values based on its offset and the contents of [file]. [FileSpan.message] is | 179 /// values based on its offset and the contents of [file]. [FileSpan.message] is |
| 183 /// also able to provide more context then [SourceSpan.message], and | 180 /// also able to provide more context then [SourceSpan.message], and |
| 184 /// [FileSpan.union] will return a [FileSpan] if possible. | 181 /// [FileSpan.union] will return a [FileSpan] if possible. |
| 185 /// | 182 /// |
| 186 /// A [FileSpan] can be created using [SourceFile.span]. | 183 /// A [FileSpan] can be created using [SourceFile.span]. |
| 187 class FileSpan extends SourceSpanMixin implements SourceSpanWithContext { | 184 abstract class FileSpan implements SourceSpanWithContext { |
| 188 /// The [file] that [this] belongs to. | 185 /// The [file] that [this] belongs to. |
| 186 SourceFile get file; |
| 187 |
| 188 /// Returns a new span that covers both [this] and [other]. |
| 189 /// |
| 190 /// Unlike [union], [other] may be disjoint from [this]. If it is, the text |
| 191 /// between the two will be covered by the returned span. |
| 192 FileSpan expand(FileSpan other); |
| 193 } |
| 194 |
| 195 /// The implementation of [FileSpan]. |
| 196 /// |
| 197 /// This is split into a separate class so that `is _FileSpan` checks can be run |
| 198 /// to make certain operations more efficient. If we used `is FileSpan`, that |
| 199 /// would break if external classes implemented the interface. |
| 200 class _FileSpan extends SourceSpanMixin implements FileSpan { |
| 189 final SourceFile file; | 201 final SourceFile file; |
| 190 | 202 |
| 191 /// The offset of the beginning of the span. | 203 /// The offset of the beginning of the span. |
| 192 /// | 204 /// |
| 193 /// [start] is lazily generated from this to avoid allocating unnecessary | 205 /// [start] is lazily generated from this to avoid allocating unnecessary |
| 194 /// objects. | 206 /// objects. |
| 195 final int _start; | 207 final int _start; |
| 196 | 208 |
| 197 /// The offset of the end of the span. | 209 /// The offset of the end of the span. |
| 198 /// | 210 /// |
| 199 /// [end] is lazily generated from this to avoid allocating unnecessary | 211 /// [end] is lazily generated from this to avoid allocating unnecessary |
| 200 /// objects. | 212 /// objects. |
| 201 final int _end; | 213 final int _end; |
| 202 | 214 |
| 203 Uri get sourceUrl => file.url; | 215 Uri get sourceUrl => file.url; |
| 204 int get length => _end - _start; | 216 int get length => _end - _start; |
| 205 FileLocation get start => new FileLocation._(file, _start); | 217 FileLocation get start => new FileLocation._(file, _start); |
| 206 FileLocation get end => new FileLocation._(file, _end); | 218 FileLocation get end => new FileLocation._(file, _end); |
| 207 String get text => file.getText(_start, _end); | 219 String get text => file.getText(_start, _end); |
| 208 String get context => file.getText(file.getOffset(start.line), | 220 String get context => file.getText(file.getOffset(start.line), |
| 209 end.line == file.lines - 1 ? null : file.getOffset(end.line + 1)); | 221 end.line == file.lines - 1 ? null : file.getOffset(end.line + 1)); |
| 210 | 222 |
| 211 FileSpan._(this.file, this._start, this._end) { | 223 _FileSpan(this.file, this._start, this._end) { |
| 212 if (_end < _start) { | 224 if (_end < _start) { |
| 213 throw new ArgumentError('End $_end must come after start $_start.'); | 225 throw new ArgumentError('End $_end must come after start $_start.'); |
| 214 } else if (_end > file.length) { | 226 } else if (_end > file.length) { |
| 215 throw new RangeError("End $_end must not be greater than the number " | 227 throw new RangeError("End $_end must not be greater than the number " |
| 216 "of characters in the file, ${file.length}."); | 228 "of characters in the file, ${file.length}."); |
| 217 } else if (_start < 0) { | 229 } else if (_start < 0) { |
| 218 throw new RangeError("Start may not be negative, was $_start."); | 230 throw new RangeError("Start may not be negative, was $_start."); |
| 219 } | 231 } |
| 220 } | 232 } |
| 221 | 233 |
| 222 int compareTo(SourceSpan other) { | 234 int compareTo(SourceSpan other) { |
| 223 if (other is! FileSpan) return super.compareTo(other); | 235 if (other is! _FileSpan) return super.compareTo(other); |
| 224 | 236 |
| 225 FileSpan otherFile = other; | 237 _FileSpan otherFile = other; |
| 226 var result = _start.compareTo(otherFile._start); | 238 var result = _start.compareTo(otherFile._start); |
| 227 return result == 0 ? _end.compareTo(otherFile._end) : result; | 239 return result == 0 ? _end.compareTo(otherFile._end) : result; |
| 228 } | 240 } |
| 229 | 241 |
| 230 SourceSpan union(SourceSpan other) { | 242 SourceSpan union(SourceSpan other) { |
| 231 if (other is! FileSpan) return super.union(other); | 243 if (other is! FileSpan) return super.union(other); |
| 232 | 244 |
| 233 var span = expand(other); | 245 _FileSpan span = expand(other); |
| 234 var beginSpan = span._start == _start ? this : other; | 246 var beginSpan = span._start == _start ? this : other; |
| 235 var endSpan = span._end == _end ? this : other; | 247 var endSpan = span._end == _end ? this : other; |
| 236 | 248 |
| 237 if (beginSpan._end < endSpan._start) { | 249 if (beginSpan._end < endSpan._start) { |
| 238 throw new ArgumentError("Spans $this and $other are disjoint."); | 250 throw new ArgumentError("Spans $this and $other are disjoint."); |
| 239 } | 251 } |
| 240 | 252 |
| 241 return span; | 253 return span; |
| 242 } | 254 } |
| 243 | 255 |
| 244 bool operator ==(other) { | 256 bool operator ==(other) { |
| 245 if (other is! FileSpan) return super == other; | 257 if (other is! FileSpan) return super == other; |
| 258 if (other is! _FileSpan) { |
| 259 return super == other && sourceUrl == other.sourceUrl; |
| 260 } |
| 261 |
| 246 return _start == other._start && _end == other._end && | 262 return _start == other._start && _end == other._end && |
| 247 sourceUrl == other.sourceUrl; | 263 sourceUrl == other.sourceUrl; |
| 248 } | 264 } |
| 249 | 265 |
| 250 int get hashCode => _start.hashCode + 5 * _end.hashCode + | |
| 251 7 * sourceUrl.hashCode; | |
| 252 | |
| 253 /// Returns a new span that covers both [this] and [other]. | 266 /// Returns a new span that covers both [this] and [other]. |
| 254 /// | 267 /// |
| 255 /// Unlike [union], [other] may be disjoint from [this]. If it is, the text | 268 /// Unlike [union], [other] may be disjoint from [this]. If it is, the text |
| 256 /// between the two will be covered by the returned span. | 269 /// between the two will be covered by the returned span. |
| 257 FileSpan expand(FileSpan other) { | 270 FileSpan expand(FileSpan other) { |
| 258 if (sourceUrl != other.sourceUrl) { | 271 if (sourceUrl != other.sourceUrl) { |
| 259 throw new ArgumentError("Source URLs \"${sourceUrl}\" and " | 272 throw new ArgumentError("Source URLs \"${sourceUrl}\" and " |
| 260 " \"${other.sourceUrl}\" don't match."); | 273 " \"${other.sourceUrl}\" don't match."); |
| 261 } | 274 } |
| 262 | 275 |
| 263 var start = math.min(this._start, other._start); | 276 if (other is _FileSpan) { |
| 264 var end = math.max(this._end, other._end); | 277 var start = math.min(this._start, other._start); |
| 265 return new FileSpan._(file, start, end); | 278 var end = math.max(this._end, other._end); |
| 279 return new _FileSpan(file, start, end); |
| 280 } else { |
| 281 var start = math.min(this._start, other.start.offset); |
| 282 var end = math.max(this._end, other.end.offset); |
| 283 return new _FileSpan(file, start, end); |
| 284 } |
| 266 } | 285 } |
| 267 } | 286 } |
| OLD | NEW |