blob: 2d23db1a3d7b67cb6d0146c285a6ef245ce71bef [file] [log] [blame]
// Copyright (c) 2014, 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.
import 'span.dart';
// TODO(nweiz): Use SourceLocationMixin once we decide to cut a release with
// breaking changes. See SourceLocationMixin for details.
/// A class that describes a single location within a source file.
///
/// This class should not be extended. Instead, [SourceLocationBase] should be
/// extended instead.
class SourceLocation implements Comparable<SourceLocation> {
/// URL of the source containing this location.
///
/// This may be null, indicating that the source URL is unknown or
/// unavailable.
final Uri sourceUrl;
/// The 0-based offset of this location in the source.
final int offset;
/// The 0-based line of this location in the source.
final int line;
/// The 0-based column of this location in the source
final int column;
/// Returns a representation of this location in the `source:line:column`
/// format used by text editors.
///
/// This prints 1-based lines and columns.
String get toolString {
var source = sourceUrl == null ? 'unknown source' : sourceUrl;
return '$source:${line + 1}:${column + 1}';
}
/// Creates a new location indicating [offset] within [sourceUrl].
///
/// [line] and [column] default to assuming the source is a single line. This
/// means that [line] defaults to 0 and [column] defaults to [offset].
///
/// [sourceUrl] may be either a [String], a [Uri], or `null`.
SourceLocation(int offset, {sourceUrl, int line, int column})
: sourceUrl = sourceUrl is String ? Uri.parse(sourceUrl) : sourceUrl,
offset = offset,
line = line == null ? 0 : line,
column = column == null ? offset : column {
if (offset < 0) {
throw new RangeError("Offset may not be negative, was $offset.");
} else if (line != null && line < 0) {
throw new RangeError("Line may not be negative, was $line.");
} else if (column != null && column < 0) {
throw new RangeError("Column may not be negative, was $column.");
}
}
/// Returns the distance in characters between [this] and [other].
///
/// This always returns a non-negative value.
int distance(SourceLocation other) {
if (sourceUrl != other.sourceUrl) {
throw new ArgumentError("Source URLs \"${sourceUrl}\" and "
"\"${other.sourceUrl}\" don't match.");
}
return (offset - other.offset).abs();
}
/// Returns a span that covers only a single point: this location.
SourceSpan pointSpan() => new SourceSpan(this, this, "");
/// Compares two locations.
///
/// [other] must have the same source URL as [this].
int compareTo(SourceLocation other) {
if (sourceUrl != other.sourceUrl) {
throw new ArgumentError("Source URLs \"${sourceUrl}\" and "
"\"${other.sourceUrl}\" don't match.");
}
return offset - other.offset;
}
bool operator ==(other) =>
other is SourceLocation && sourceUrl == other.sourceUrl &&
offset == other.offset;
int get hashCode => sourceUrl.hashCode + offset;
String toString() => '<$runtimeType: $offset $toolString>';
}
/// A base class for source locations with [offset], [line], and [column] known
/// at construction time.
class SourceLocationBase extends SourceLocation {
SourceLocationBase(int offset, {sourceUrl, int line, int column})
: super(offset, sourceUrl: sourceUrl, line: line, column: column);
}