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 'location.dart'; | 10 import 'location.dart'; |
(...skipping 25 matching lines...) Expand all Loading... | |
36 | 36 |
37 /// The code points of the characters in the file. | 37 /// The code points of the characters in the file. |
38 final Uint32List _decodedChars; | 38 final Uint32List _decodedChars; |
39 | 39 |
40 /// The length of the file in characters. | 40 /// The length of the file in characters. |
41 int get length => _decodedChars.length; | 41 int get length => _decodedChars.length; |
42 | 42 |
43 /// The number of lines in the file. | 43 /// The number of lines in the file. |
44 int get lines => _lineStarts.length; | 44 int get lines => _lineStarts.length; |
45 | 45 |
46 /// The line that the offset fell on the last time [getLine] was called. | |
47 /// | |
48 /// In many cases, sequential calls to getLine() are for nearby, usually | |
49 /// increasing offsets. In that case, we can find the line for an offset | |
50 /// quickly by first checking to see if the offset is on the same line as the | |
51 /// previous result. | |
52 int _cachedLine; | |
53 | |
46 /// Creates a new source file from [text]. | 54 /// Creates a new source file from [text]. |
47 /// | 55 /// |
48 /// [url] may be either a [String], a [Uri], or `null`. | 56 /// [url] may be either a [String], a [Uri], or `null`. |
49 SourceFile(String text, {url}) | 57 SourceFile(String text, {url}) |
50 : this.decoded(text.runes, url: url); | 58 : this.decoded(text.runes, url: url); |
51 | 59 |
52 /// Creates a new source file from a list of decoded characters. | 60 /// Creates a new source file from a list of decoded characters. |
53 /// | 61 /// |
54 /// [url] may be either a [String], a [Uri], or `null`. | 62 /// [url] may be either a [String], a [Uri], or `null`. |
55 SourceFile.decoded(Iterable<int> decodedChars, {url}) | 63 SourceFile.decoded(Iterable<int> decodedChars, {url}) |
(...skipping 22 matching lines...) Expand all Loading... | |
78 FileLocation location(int offset) => new FileLocation._(this, offset); | 86 FileLocation location(int offset) => new FileLocation._(this, offset); |
79 | 87 |
80 /// Gets the 0-based line corresponding to [offset]. | 88 /// Gets the 0-based line corresponding to [offset]. |
81 int getLine(int offset) { | 89 int getLine(int offset) { |
82 if (offset < 0) { | 90 if (offset < 0) { |
83 throw new RangeError("Offset may not be negative, was $offset."); | 91 throw new RangeError("Offset may not be negative, was $offset."); |
84 } else if (offset > length) { | 92 } else if (offset > length) { |
85 throw new RangeError("Offset $offset must not be greater than the number " | 93 throw new RangeError("Offset $offset must not be greater than the number " |
86 "of characters in the file, $length."); | 94 "of characters in the file, $length."); |
87 } | 95 } |
88 return binarySearch(_lineStarts, (o) => o > offset) - 1; | 96 |
97 if (_isNearCachedLine(offset)) return _cachedLine; | |
98 | |
99 return _cachedLine = _binarySearch(offset) - 1; | |
nweiz
2015/09/02 00:06:30
This is cute, but I think it's clearer to assign s
| |
100 } | |
101 | |
102 /// Returns `true` if [offset] is within [_cachedLine]. | |
nweiz
2015/09/02 00:06:30
"within" -> "near"
| |
103 /// | |
104 /// This actually checks on [_cachedLine] and the next line. If it's on the | |
105 /// next line, it updates [_cachedLine] to point to that. | |
106 bool _isNearCachedLine(int offset) { | |
107 if (_cachedLine == null) return false; | |
108 | |
109 // See if it's before the cached line. | |
110 if (offset < _lineStarts[_cachedLine]) return false; | |
111 | |
112 // See if it's on the cached line. | |
113 if (_cachedLine >= _lineStarts.length - 1 || | |
114 offset < _lineStarts[_cachedLine + 1]) { | |
115 return true; | |
116 } | |
117 | |
118 // See if it's on the next line. | |
119 if (_cachedLine >= _lineStarts.length - 2 || | |
120 offset < _lineStarts[_cachedLine + 2]) { | |
121 _cachedLine++; | |
122 return true; | |
123 } | |
124 | |
125 return false; | |
126 } | |
127 | |
128 /// Binary search through [_lineStarts] to find the line containing [offset]. | |
129 /// | |
130 /// Given a result `n`, that all items before `n` will not match, `n` matches, | |
131 /// and all items after `n` match too. The result is -1 when there are no | |
132 /// items, 0 when all items match, and list.length when none does. | |
nweiz
2015/09/02 00:06:30
Since we can guarantee there will be at least one
| |
133 int _binarySearch(int offset) { | |
134 int min = 0; | |
135 int max = _lineStarts.length - 1; | |
136 while (min < max) { | |
137 var half = min + ((max - min) ~/ 2); | |
138 if (_lineStarts[half] > offset) { | |
139 max = half; | |
140 } else { | |
141 min = half + 1; | |
142 } | |
143 } | |
144 | |
145 return max; | |
89 } | 146 } |
90 | 147 |
91 /// Gets the 0-based column corresponding to [offset]. | 148 /// Gets the 0-based column corresponding to [offset]. |
92 /// | 149 /// |
93 /// If [line] is passed, it's assumed to be the line containing [offset] and | 150 /// If [line] is passed, it's assumed to be the line containing [offset] and |
94 /// is used to more efficiently compute the column. | 151 /// is used to more efficiently compute the column. |
95 int getColumn(int offset, {int line}) { | 152 int getColumn(int offset, {int line}) { |
96 if (offset < 0) { | 153 if (offset < 0) { |
97 throw new RangeError("Offset may not be negative, was $offset."); | 154 throw new RangeError("Offset may not be negative, was $offset."); |
98 } else if (offset > length) { | 155 } else if (offset > length) { |
(...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
280 var start = math.min(this._start, other._start); | 337 var start = math.min(this._start, other._start); |
281 var end = math.max(this._end, other._end); | 338 var end = math.max(this._end, other._end); |
282 return new _FileSpan(file, start, end); | 339 return new _FileSpan(file, start, end); |
283 } else { | 340 } else { |
284 var start = math.min(this._start, other.start.offset); | 341 var start = math.min(this._start, other.start.offset); |
285 var end = math.max(this._end, other.end.offset); | 342 var end = math.max(this._end, other.end.offset); |
286 return new _FileSpan(file, start, end); | 343 return new _FileSpan(file, start, end); |
287 } | 344 } |
288 } | 345 } |
289 } | 346 } |
OLD | NEW |