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

Side by Side Diff: pkg/source_maps/lib/span.dart

Issue 12207008: Move source maps package to the dart repo, so it can be used by other internal (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: updates Created 7 years, 9 months 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
« no previous file with comments | « pkg/source_maps/lib/source_maps.dart ('k') | pkg/source_maps/lib/src/utils.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2013, 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 /// Dart classes representing the souce spans and source files.
6 library source_maps.span;
7
8 import 'dart:utf' show stringToCodepoints, codepointsToString;
9 import 'dart:math' show min;
10
11 import 'src/utils.dart';
12
13 /// A simple class that describe a segment of source text.
14 abstract class Span implements Comparable {
15 /// The start location of this span.
16 final Location start;
17
18 /// The end location of this span, exclusive.
19 final Location end;
20
21 /// Url of the source (typically a file) containing this span.
22 String get sourceUrl => start.sourceUrl;
23
24 /// The length of this span, in characters.
25 int get length => end.offset - start.offset;
26
27 /// The source text for this span, if available.
28 String get text;
29
30 /// Whether [text] corresponds to an identifier symbol.
31 final bool isIdentifier;
32
33 Span(this.start, this.end, bool isIdentifier)
34 : isIdentifier = isIdentifier != null ? isIdentifier : false {
35 _checkRange();
36 }
37
38 /// Creates a new span that is the union of two existing spans [start] and
39 /// [end]. Note that the resulting span might contain some positions that were
40 /// not in either of the original spans if [start] and [end] are disjoint.
41 Span.union(Span start, Span end)
42 : start = start.start, end = end.end, isIdentifier = false {
43 _checkRange();
44 }
45
46 void _checkRange() {
47 if (start.offset < 0) throw new ArgumentError('start $start must be >= 0');
48 if (end.offset < start.offset) {
49 throw new ArgumentError('end $end must be >= start $start');
50 }
51 }
52
53 /// Compares two spans. If the spans are not in the same source, this method
54 /// generates an error.
55 int compareTo(Span other) {
56 int d = start.compareTo(other.start);
57 return d == 0 ? end.compareTo(other.end) : d;
58 }
59
60 /// Gets the location in standard printed form `filename:line:column`, where
61 /// line and column are adjusted by 1 to match the convention in editors.
62 String get formatLocation => start.formatString;
63
64 String getLocationMessage(String message,
65 {bool useColors: false, String color}) {
66 return '$formatLocation: $message';
67 }
68
69 bool operator ==(Span other) =>
70 sourceUrl == other.sourceUrl && start == other.start && end == other.end;
71
72 String toString() => '<$runtimeType: $start $end $formatLocation $text>';
73 }
74
75 /// A location in the source text
76 abstract class Location implements Comparable {
77 /// Url of the source containing this span.
78 String get sourceUrl;
79
80 /// The offset of this location, 0-based.
81 final int offset;
82
83 /// The 0-based line in the source of this location.
84 int get line;
85
86 /// The 0-based column in the source of this location.
87 int get column;
88
89 Location(this.offset);
90
91 /// Compares two locations. If the locations are not in the same source, this
92 /// method generates an error.
93 int compareTo(Location other) {
94 if (sourceUrl != other.sourceUrl) {
95 throw new ArgumentError('can only compare locations of the same source');
96 }
97 return offset - other.offset;
98 }
99
100 String toString() => '(Location $offset)';
101 String get formatString => '$sourceUrl:${line + 1}:${column + 1}';
102 }
103
104 /// Implementation of [Location] with fixed values given at allocation time.
105 class FixedLocation extends Location {
106 final String sourceUrl;
107 final int line;
108 final int column;
109
110 FixedLocation(int offset, this.sourceUrl, this.line, this.column)
111 : super(offset);
112 }
113
114 /// Implementation of [Span] where all the values are given at allocation time.
115 class FixedSpan extends Span {
116 /// The source text for this span, if available.
117 final String text;
118
119 /// Creates a span which starts and end in the same line.
120 FixedSpan(String sourceUrl, int start, int line, int column,
121 {String text: '', bool isIdentifier: false})
122 : text = text, super(new FixedLocation(start, sourceUrl, line, column),
123 new FixedLocation(start + text.length, sourceUrl, line,
124 column + text.length),
125 isIdentifier);
126 }
127
128 /// [Location] with values computed from an underling [SourceFile].
129 class FileLocation extends Location {
130 /// The source file containing this location.
131 final SourceFile file;
132
133 String get sourceUrl => file.url;
134 int get line => file.getLine(offset);
135 int get column => file.getColumn(line, offset);
136
137 FileLocation(this.file, int offset): super(offset);
138 }
139
140 /// [Span] where values are computed from an underling [SourceFile].
141 class FileSpan extends Span {
142 /// The source file containing this span.
143 final SourceFile file;
144
145 /// The source text for this span, if available.
146 String get text => file.getText(start.offset, end.offset);
147
148 factory FileSpan(SourceFile file, int start,
149 [int end, bool isIdentifier = false]) {
150 var startLoc = new FileLocation(file, start);
151 var endLoc = end == null ? startLoc : new FileLocation(file, end);
152 return new FileSpan.locations(startLoc, endLoc, isIdentifier);
153 }
154
155 FileSpan.locations(FileLocation start, FileLocation end,
156 bool isIdentifier)
157 : file = start.file, super(start, end, isIdentifier);
158
159 /// Creates a new span that is the union of two existing spans [start] and
160 /// [end]. Note that the resulting span might contain some positions that were
161 /// not in either of the original spans if [start] and [end] are disjoint.
162 FileSpan.union(FileSpan start, FileSpan end)
163 : file = start.file, super.union(start, end) {
164 if (start.file != end.file) {
165 throw new ArgumentError('start and end must be from the same file');
166 }
167 }
168
169 String getLocationMessage(String message,
170 {bool useColors: false, String color}) {
171 return file.getLocationMessage(message, start.offset, end.offset,
172 useColors: useColors, color: color);
173 }
174 }
175
176 // Constants to determine end-of-lines.
177 const int _LF = 10;
178 const int _CR = 13;
179
180 // Color constants used for generating messages.
181 const String _RED_COLOR = '\u001b[31m';
182 const String _NO_COLOR = '\u001b[0m';
183
184 /// Stores information about a source file, to permit computation of the line
185 /// and column. Also contains a nice default error message highlighting the code
186 /// location.
187 class SourceFile {
188 /// Url where the source file is located.
189 final String url;
190 final List<int> _lineStarts;
191 final List<int> _decodedChars;
192
193 SourceFile(this.url, this._lineStarts, this._decodedChars);
194
195 SourceFile.text(this.url, String text)
196 : _lineStarts = <int>[0],
197 _decodedChars = stringToCodepoints(text) {
198 for (int i = 0; i < _decodedChars.length; i++) {
199 var c = _decodedChars[i];
200 if (c == _CR) {
201 // Return not followed by newline is treated as a newline
202 int j = i + 1;
203 if (j >= _decodedChars.length || _decodedChars[j] != _LF) {
204 c = _LF;
205 }
206 }
207 if (c == _LF) _lineStarts.add(i + 1);
208 }
209 }
210
211 /// Returns a span in this [SourceFile] with the given offsets.
212 Span span(int start, [int end, bool isIdentifier = false]) =>
213 new FileSpan(this, start, end, isIdentifier);
214
215 /// Returns a location in this [SourceFile] with the given offset.
216 Location location(int offset) => new FileLocation(this, offset);
217
218 /// Gets the 0-based line corresponding to an offset.
219 int getLine(int offset) {
220 return binarySearch(_lineStarts, (o) => o > offset) - 1;
221 }
222
223 /// Gets the 0-based column corresponding to an offset.
224 int getColumn(int line, int offset) {
225 return offset - _lineStarts[line];
226 }
227
228 /// Get the offset for a given line and column
229 int getOffset(int line, int column) {
230 return _lineStarts[min(line, _lineStarts.length - 1)] + column;
231 }
232
233 /// Gets the text at the given offsets.
234 String getText(int start, [int end]) {
235 return codepointsToString(_decodedChars.sublist(start, end));
236 }
237
238 /// Create a pretty string representation from a span.
239 String getLocationMessage(String message, int start, int end,
240 {bool useColors: false, String color}) {
241 // TODO(jmesserly): it would be more useful to pass in an object that
242 // controls how the errors are printed. This method is a bit too smart.
243 var line = getLine(start);
244 var column = getColumn(line, start);
245
246 var src = url == null ? '' : url;
247 var msg = '$src:${line + 1}:${column + 1}: $message';
248
249 if (_decodedChars == null) {
250 // We don't have any text to include, so exit.
251 return msg;
252 }
253
254 var buf = new StringBuffer(msg);
255 buf.write('\n');
256 var textLine;
257
258 // +1 for 0-indexing, +1 again to avoid the last line
259 if ((line + 2) < _lineStarts.length) {
260 textLine = getText(_lineStarts[line], _lineStarts[line + 1]);
261 } else {
262 textLine = getText(_lineStarts[line]);
263 textLine = '$textLine\n';
264 }
265
266 int toColumn = min(column + end - start, textLine.length);
267 if (useColors) {
268 if (color == null) {
269 color = _RED_COLOR;
270 }
271 buf.write(textLine.substring(0, column));
272 buf.write(color);
273 buf.write(textLine.substring(column, toColumn));
274 buf.write(_NO_COLOR);
275 buf.write(textLine.substring(toColumn));
276 } else {
277 buf.write(textLine);
278 }
279
280 int i = 0;
281 for (; i < column; i++) {
282 buf.write(' ');
283 }
284
285 if (useColors) buf.write(color);
286 for (; i < toColumn; i++) {
287 buf.write('^');
288 }
289 if (useColors) buf.write(_NO_COLOR);
290 return buf.toString();
291 }
292 }
293
294 /// A convenience type to treat a code segment as if it were a separate
295 /// [SourceFile]. A [SourceFileSegment] shifts all locations by an offset, which
296 /// allows you to set source-map locations based on the locations relative to
297 /// the start of the segment, but that get translated to absolute locations in
298 /// the original source file.
299 class SourceFileSegment extends SourceFile {
300 final int _baseOffset;
301 final int _baseLine;
302 final int _baseColumn;
303
304 SourceFileSegment(String url, String textSegment, Location startOffset)
305 : _baseOffset = startOffset.offset,
306 _baseLine = startOffset.line,
307 _baseColumn = startOffset.column,
308 super.text(url, textSegment);
309
310 Span span(int start, [int end, bool isIdentifier = false]) =>
311 super.span(start + _baseOffset,
312 end == null ? null : end + _baseOffset, isIdentifier);
313
314 Location location(int offset) => super.location(offset + _baseOffset);
315
316 int getLine(int offset) =>
317 super.getLine(offset - _baseOffset) + _baseLine;
318
319 int getColumn(int line, int offset) {
320 var col = super.getColumn(line - _baseLine, offset - _baseOffset);
321 return line == _baseLine ? col + _baseColumn : col;
322 }
323
324 int getOffset(int line, int column) =>
325 super.getOffset(line - _baseLine,
326 line == _baseLine ? column - _baseColumn : column) + _baseOffset;
327
328 String getText(int start, [int end]) =>
329 super.getText(start - _baseOffset, end - _baseOffset);
330 }
OLDNEW
« no previous file with comments | « pkg/source_maps/lib/source_maps.dart ('k') | pkg/source_maps/lib/src/utils.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698