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

Unified 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 side-by-side diff with in-line comments
Download patch
« 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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/source_maps/lib/span.dart
diff --git a/pkg/source_maps/lib/span.dart b/pkg/source_maps/lib/span.dart
new file mode 100644
index 0000000000000000000000000000000000000000..19829941f3b07c962f1a159979df29c6dd80181e
--- /dev/null
+++ b/pkg/source_maps/lib/span.dart
@@ -0,0 +1,330 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// Dart classes representing the souce spans and source files.
+library source_maps.span;
+
+import 'dart:utf' show stringToCodepoints, codepointsToString;
+import 'dart:math' show min;
+
+import 'src/utils.dart';
+
+/// A simple class that describe a segment of source text.
+abstract class Span implements Comparable {
+ /// The start location of this span.
+ final Location start;
+
+ /// The end location of this span, exclusive.
+ final Location end;
+
+ /// Url of the source (typically a file) containing this span.
+ String get sourceUrl => start.sourceUrl;
+
+ /// The length of this span, in characters.
+ int get length => end.offset - start.offset;
+
+ /// The source text for this span, if available.
+ String get text;
+
+ /// Whether [text] corresponds to an identifier symbol.
+ final bool isIdentifier;
+
+ Span(this.start, this.end, bool isIdentifier)
+ : isIdentifier = isIdentifier != null ? isIdentifier : false {
+ _checkRange();
+ }
+
+ /// Creates a new span that is the union of two existing spans [start] and
+ /// [end]. Note that the resulting span might contain some positions that were
+ /// not in either of the original spans if [start] and [end] are disjoint.
+ Span.union(Span start, Span end)
+ : start = start.start, end = end.end, isIdentifier = false {
+ _checkRange();
+ }
+
+ void _checkRange() {
+ if (start.offset < 0) throw new ArgumentError('start $start must be >= 0');
+ if (end.offset < start.offset) {
+ throw new ArgumentError('end $end must be >= start $start');
+ }
+ }
+
+ /// Compares two spans. If the spans are not in the same source, this method
+ /// generates an error.
+ int compareTo(Span other) {
+ int d = start.compareTo(other.start);
+ return d == 0 ? end.compareTo(other.end) : d;
+ }
+
+ /// Gets the location in standard printed form `filename:line:column`, where
+ /// line and column are adjusted by 1 to match the convention in editors.
+ String get formatLocation => start.formatString;
+
+ String getLocationMessage(String message,
+ {bool useColors: false, String color}) {
+ return '$formatLocation: $message';
+ }
+
+ bool operator ==(Span other) =>
+ sourceUrl == other.sourceUrl && start == other.start && end == other.end;
+
+ String toString() => '<$runtimeType: $start $end $formatLocation $text>';
+}
+
+/// A location in the source text
+abstract class Location implements Comparable {
+ /// Url of the source containing this span.
+ String get sourceUrl;
+
+ /// The offset of this location, 0-based.
+ final int offset;
+
+ /// The 0-based line in the source of this location.
+ int get line;
+
+ /// The 0-based column in the source of this location.
+ int get column;
+
+ Location(this.offset);
+
+ /// Compares two locations. If the locations are not in the same source, this
+ /// method generates an error.
+ int compareTo(Location other) {
+ if (sourceUrl != other.sourceUrl) {
+ throw new ArgumentError('can only compare locations of the same source');
+ }
+ return offset - other.offset;
+ }
+
+ String toString() => '(Location $offset)';
+ String get formatString => '$sourceUrl:${line + 1}:${column + 1}';
+}
+
+/// Implementation of [Location] with fixed values given at allocation time.
+class FixedLocation extends Location {
+ final String sourceUrl;
+ final int line;
+ final int column;
+
+ FixedLocation(int offset, this.sourceUrl, this.line, this.column)
+ : super(offset);
+}
+
+/// Implementation of [Span] where all the values are given at allocation time.
+class FixedSpan extends Span {
+ /// The source text for this span, if available.
+ final String text;
+
+ /// Creates a span which starts and end in the same line.
+ FixedSpan(String sourceUrl, int start, int line, int column,
+ {String text: '', bool isIdentifier: false})
+ : text = text, super(new FixedLocation(start, sourceUrl, line, column),
+ new FixedLocation(start + text.length, sourceUrl, line,
+ column + text.length),
+ isIdentifier);
+}
+
+/// [Location] with values computed from an underling [SourceFile].
+class FileLocation extends Location {
+ /// The source file containing this location.
+ final SourceFile file;
+
+ String get sourceUrl => file.url;
+ int get line => file.getLine(offset);
+ int get column => file.getColumn(line, offset);
+
+ FileLocation(this.file, int offset): super(offset);
+}
+
+/// [Span] where values are computed from an underling [SourceFile].
+class FileSpan extends Span {
+ /// The source file containing this span.
+ final SourceFile file;
+
+ /// The source text for this span, if available.
+ String get text => file.getText(start.offset, end.offset);
+
+ factory FileSpan(SourceFile file, int start,
+ [int end, bool isIdentifier = false]) {
+ var startLoc = new FileLocation(file, start);
+ var endLoc = end == null ? startLoc : new FileLocation(file, end);
+ return new FileSpan.locations(startLoc, endLoc, isIdentifier);
+ }
+
+ FileSpan.locations(FileLocation start, FileLocation end,
+ bool isIdentifier)
+ : file = start.file, super(start, end, isIdentifier);
+
+ /// Creates a new span that is the union of two existing spans [start] and
+ /// [end]. Note that the resulting span might contain some positions that were
+ /// not in either of the original spans if [start] and [end] are disjoint.
+ FileSpan.union(FileSpan start, FileSpan end)
+ : file = start.file, super.union(start, end) {
+ if (start.file != end.file) {
+ throw new ArgumentError('start and end must be from the same file');
+ }
+ }
+
+ String getLocationMessage(String message,
+ {bool useColors: false, String color}) {
+ return file.getLocationMessage(message, start.offset, end.offset,
+ useColors: useColors, color: color);
+ }
+}
+
+// Constants to determine end-of-lines.
+const int _LF = 10;
+const int _CR = 13;
+
+// Color constants used for generating messages.
+const String _RED_COLOR = '\u001b[31m';
+const String _NO_COLOR = '\u001b[0m';
+
+/// Stores information about a source file, to permit computation of the line
+/// and column. Also contains a nice default error message highlighting the code
+/// location.
+class SourceFile {
+ /// Url where the source file is located.
+ final String url;
+ final List<int> _lineStarts;
+ final List<int> _decodedChars;
+
+ SourceFile(this.url, this._lineStarts, this._decodedChars);
+
+ SourceFile.text(this.url, String text)
+ : _lineStarts = <int>[0],
+ _decodedChars = stringToCodepoints(text) {
+ for (int i = 0; i < _decodedChars.length; i++) {
+ var c = _decodedChars[i];
+ if (c == _CR) {
+ // Return not followed by newline is treated as a newline
+ int j = i + 1;
+ if (j >= _decodedChars.length || _decodedChars[j] != _LF) {
+ c = _LF;
+ }
+ }
+ if (c == _LF) _lineStarts.add(i + 1);
+ }
+ }
+
+ /// Returns a span in this [SourceFile] with the given offsets.
+ Span span(int start, [int end, bool isIdentifier = false]) =>
+ new FileSpan(this, start, end, isIdentifier);
+
+ /// Returns a location in this [SourceFile] with the given offset.
+ Location location(int offset) => new FileLocation(this, offset);
+
+ /// Gets the 0-based line corresponding to an offset.
+ int getLine(int offset) {
+ return binarySearch(_lineStarts, (o) => o > offset) - 1;
+ }
+
+ /// Gets the 0-based column corresponding to an offset.
+ int getColumn(int line, int offset) {
+ return offset - _lineStarts[line];
+ }
+
+ /// Get the offset for a given line and column
+ int getOffset(int line, int column) {
+ return _lineStarts[min(line, _lineStarts.length - 1)] + column;
+ }
+
+ /// Gets the text at the given offsets.
+ String getText(int start, [int end]) {
+ return codepointsToString(_decodedChars.sublist(start, end));
+ }
+
+ /// Create a pretty string representation from a span.
+ String getLocationMessage(String message, int start, int end,
+ {bool useColors: false, String color}) {
+ // TODO(jmesserly): it would be more useful to pass in an object that
+ // controls how the errors are printed. This method is a bit too smart.
+ var line = getLine(start);
+ var column = getColumn(line, start);
+
+ var src = url == null ? '' : url;
+ var msg = '$src:${line + 1}:${column + 1}: $message';
+
+ if (_decodedChars == null) {
+ // We don't have any text to include, so exit.
+ return msg;
+ }
+
+ var buf = new StringBuffer(msg);
+ buf.write('\n');
+ var textLine;
+
+ // +1 for 0-indexing, +1 again to avoid the last line
+ if ((line + 2) < _lineStarts.length) {
+ textLine = getText(_lineStarts[line], _lineStarts[line + 1]);
+ } else {
+ textLine = getText(_lineStarts[line]);
+ textLine = '$textLine\n';
+ }
+
+ int toColumn = min(column + end - start, textLine.length);
+ if (useColors) {
+ if (color == null) {
+ color = _RED_COLOR;
+ }
+ buf.write(textLine.substring(0, column));
+ buf.write(color);
+ buf.write(textLine.substring(column, toColumn));
+ buf.write(_NO_COLOR);
+ buf.write(textLine.substring(toColumn));
+ } else {
+ buf.write(textLine);
+ }
+
+ int i = 0;
+ for (; i < column; i++) {
+ buf.write(' ');
+ }
+
+ if (useColors) buf.write(color);
+ for (; i < toColumn; i++) {
+ buf.write('^');
+ }
+ if (useColors) buf.write(_NO_COLOR);
+ return buf.toString();
+ }
+}
+
+/// A convenience type to treat a code segment as if it were a separate
+/// [SourceFile]. A [SourceFileSegment] shifts all locations by an offset, which
+/// allows you to set source-map locations based on the locations relative to
+/// the start of the segment, but that get translated to absolute locations in
+/// the original source file.
+class SourceFileSegment extends SourceFile {
+ final int _baseOffset;
+ final int _baseLine;
+ final int _baseColumn;
+
+ SourceFileSegment(String url, String textSegment, Location startOffset)
+ : _baseOffset = startOffset.offset,
+ _baseLine = startOffset.line,
+ _baseColumn = startOffset.column,
+ super.text(url, textSegment);
+
+ Span span(int start, [int end, bool isIdentifier = false]) =>
+ super.span(start + _baseOffset,
+ end == null ? null : end + _baseOffset, isIdentifier);
+
+ Location location(int offset) => super.location(offset + _baseOffset);
+
+ int getLine(int offset) =>
+ super.getLine(offset - _baseOffset) + _baseLine;
+
+ int getColumn(int line, int offset) {
+ var col = super.getColumn(line - _baseLine, offset - _baseOffset);
+ return line == _baseLine ? col + _baseColumn : col;
+ }
+
+ int getOffset(int line, int column) =>
+ super.getOffset(line - _baseLine,
+ line == _baseLine ? column - _baseColumn : column) + _baseOffset;
+
+ String getText(int start, [int end]) =>
+ super.getText(start - _baseOffset, end - _baseOffset);
+}
« 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