Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(556)

Side by Side Diff: observatory_pub_packages/source_span/src/file.dart

Issue 816693004: Add observatory_pub_packages snapshot to third_party (Closed) Base URL: http://dart.googlecode.com/svn/third_party/
Patch Set: Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library source_span.file;
6
7 import 'dart:math' as math;
8 import 'dart:typed_data';
9
10 import 'package:path/path.dart' as p;
11
12 import 'colors.dart' as colors;
13 import 'location.dart';
14 import 'span.dart';
15 import 'span_mixin.dart';
16 import 'utils.dart';
17
18 // Constants to determine end-of-lines.
19 const int _LF = 10;
20 const int _CR = 13;
21
22 /// A class representing a source file.
23 ///
24 /// 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 class SourceFile {
27 /// The URL where the source file is located.
28 ///
29 /// This may be null, indicating that the URL is unknown or unavailable.
30 final Uri url;
31
32 /// An array of offsets for each line beginning in the file.
33 ///
34 /// Each offset refers to the first character *after* the newline. If the
35 /// source file has a trailing newline, the final offset won't actually be in
36 /// the file.
37 final _lineStarts = <int>[0];
38
39 /// The code points of the characters in the file.
40 final Uint32List _decodedChars;
41
42 /// The length of the file in characters.
43 int get length => _decodedChars.length;
44
45 /// The number of lines in the file.
46 int get lines => _lineStarts.length;
47
48 /// Creates a new source file from [text].
49 ///
50 /// [url] may be either a [String], a [Uri], or `null`.
51 SourceFile(String text, {url})
52 : this.decoded(text.runes, url: url);
53
54 /// Creates a new source file from a list of decoded characters.
55 ///
56 /// [url] may be either a [String], a [Uri], or `null`.
57 SourceFile.decoded(Iterable<int> decodedChars, {url})
58 : url = url is String ? Uri.parse(url) : url,
59 _decodedChars = new Uint32List.fromList(decodedChars.toList()) {
60 for (var i = 0; i < _decodedChars.length; i++) {
61 var c = _decodedChars[i];
62 if (c == _CR) {
63 // Return not followed by newline is treated as a newline
64 var j = i + 1;
65 if (j >= _decodedChars.length || _decodedChars[j] != _LF) c = _LF;
66 }
67 if (c == _LF) _lineStarts.add(i + 1);
68 }
69 }
70
71 /// Returns a span in [this] from [start] to [end] (exclusive).
72 ///
73 /// If [end] isn't passed, it defaults to the end of the file.
74 FileSpan span(int start, [int end]) {
75 if (end == null) end = length - 1;
76 return new FileSpan._(this, start, end);
77 }
78
79 /// Returns a location in [this] at [offset].
80 FileLocation location(int offset) => new FileLocation._(this, offset);
81
82 /// Gets the 0-based line corresponding to [offset].
83 int getLine(int offset) {
84 if (offset < 0) {
85 throw new RangeError("Offset may not be negative, was $offset.");
86 } else if (offset > length) {
87 throw new RangeError("Offset $offset must not be greater than the number "
88 "of characters in the file, $length.");
89 }
90 return binarySearch(_lineStarts, (o) => o > offset) - 1;
91 }
92
93 /// Gets the 0-based column corresponding to [offset].
94 ///
95 /// If [line] is passed, it's assumed to be the line containing [offset] and
96 /// is used to more efficiently compute the column.
97 int getColumn(int offset, {int line}) {
98 if (offset < 0) {
99 throw new RangeError("Offset may not be negative, was $offset.");
100 } else if (offset > length) {
101 throw new RangeError("Offset $offset must be not be greater than the "
102 "number of characters in the file, $length.");
103 }
104
105 if (line == null) {
106 line = getLine(offset);
107 } else if (line < 0) {
108 throw new RangeError("Line may not be negative, was $line.");
109 } else if (line >= lines) {
110 throw new RangeError("Line $line must be less than the number of "
111 "lines in the file, $lines.");
112 }
113
114 var lineStart = _lineStarts[line];
115 if (lineStart > offset) {
116 throw new RangeError("Line $line comes after offset $offset.");
117 }
118
119 return offset - lineStart;
120 }
121
122 /// Gets the offset for a [line] and [column].
123 ///
124 /// [column] defaults to 0.
125 int getOffset(int line, [int column]) {
126 if (column == null) column = 0;
127
128 if (line < 0) {
129 throw new RangeError("Line may not be negative, was $line.");
130 } else if (line >= lines) {
131 throw new RangeError("Line $line must be less than the number of "
132 "lines in the file, $lines.");
133 } else if (column < 0) {
134 throw new RangeError("Column may not be negative, was $column.");
135 }
136
137 var result = _lineStarts[line] + column;
138 if (result > length ||
139 (line + 1 < lines && result >= _lineStarts[line + 1])) {
140 throw new RangeError("Line $line doesn't have $column columns.");
141 }
142
143 return result;
144 }
145
146 /// Returns the text of the file from [start] to [end] (exclusive).
147 ///
148 /// If [end] isn't passed, it defaults to the end of the file.
149 String getText(int start, [int end]) =>
150 new String.fromCharCodes(_decodedChars.sublist(start, end));
151 }
152
153 /// A [SourceLocation] within a [SourceFile].
154 ///
155 /// Unlike the base [SourceLocation], [FileLocation] lazily computes its line
156 /// and column values based on its offset and the contents of [file].
157 ///
158 /// A [FileLocation] can be created using [SourceFile.location].
159 class FileLocation extends SourceLocation {
160 /// The [file] that [this] belongs to.
161 final SourceFile file;
162
163 Uri get sourceUrl => file.url;
164 int get line => file.getLine(offset);
165 int get column => file.getColumn(offset);
166
167 FileLocation._(this.file, int offset)
168 : super(offset) {
169 if (offset > file.length) {
170 throw new RangeError("Offset $offset must not be greater than the number "
171 "of characters in the file, ${file.length}.");
172 }
173 }
174
175 FileSpan pointSpan() => new FileSpan._(file, offset, offset);
176 }
177
178 /// A [SourceSpan] within a [SourceFile].
179 ///
180 /// 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 /// also able to provide more context then [SourceSpan.message], and
183 /// [FileSpan.union] will return a [FileSpan] if possible.
184 ///
185 /// A [FileSpan] can be created using [SourceFile.span].
186 class FileSpan extends SourceSpanMixin {
187 /// The [file] that [this] belongs to.
188 final SourceFile file;
189
190 /// The offset of the beginning of the span.
191 ///
192 /// [start] is lazily generated from this to avoid allocating unnecessary
193 /// objects.
194 final int _start;
195
196 /// The offset of the end of the span.
197 ///
198 /// [end] is lazily generated from this to avoid allocating unnecessary
199 /// objects.
200 final int _end;
201
202 Uri get sourceUrl => file.url;
203 int get length => _end - _start;
204 FileLocation get start => new FileLocation._(file, _start);
205 FileLocation get end => new FileLocation._(file, _end);
206 String get text => file.getText(_start, _end);
207
208 FileSpan._(this.file, this._start, this._end) {
209 if (_end < _start) {
210 throw new ArgumentError('End $_end must come after start $_start.');
211 } else if (_end > file.length) {
212 throw new RangeError("End $_end must not be greater than the number "
213 "of characters in the file, ${file.length}.");
214 } else if (_start < 0) {
215 throw new RangeError("Start may not be negative, was $_start.");
216 }
217 }
218
219 int compareTo(SourceSpan other) {
220 if (other is! FileSpan) return super.compareTo(other);
221
222 FileSpan otherFile = other;
223 var result = _start.compareTo(otherFile._start);
224 return result == 0 ? _end.compareTo(otherFile._end) : result;
225 }
226
227 SourceSpan union(SourceSpan other) {
228 if (other is! FileSpan) return super.union(other);
229
230 var span = expand(other);
231 var beginSpan = span._start == _start ? this : other;
232 var endSpan = span._end == _end ? this : other;
233
234 if (beginSpan._end < endSpan._start) {
235 throw new ArgumentError("Spans $this and $other are disjoint.");
236 }
237
238 return span;
239 }
240
241 bool operator ==(other) {
242 if (other is! FileSpan) return super == other;
243 return _start == other._start && _end == other._end &&
244 sourceUrl == other.sourceUrl;
245 }
246
247 int get hashCode => _start.hashCode + 5 * _end.hashCode +
248 7 * sourceUrl.hashCode;
249
250 /// Returns a new span that covers both [this] and [other].
251 ///
252 /// Unlike [union], [other] may be disjoint from [this]. If it is, the text
253 /// between the two will be covered by the returned span.
254 FileSpan expand(FileSpan other) {
255 if (sourceUrl != other.sourceUrl) {
256 throw new ArgumentError("Source URLs \"${sourceUrl}\" and "
257 " \"${other.sourceUrl}\" don't match.");
258 }
259
260 var start = math.min(this._start, other._start);
261 var end = math.max(this._end, other._end);
262 return new FileSpan._(file, start, end);
263 }
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 }
OLDNEW
« no previous file with comments | « observatory_pub_packages/source_span/src/colors.dart ('k') | observatory_pub_packages/source_span/src/location.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698