// 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 { /// 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 { final source = sourceUrl ?? 'unknown source'; 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(this.offset, {Object? sourceUrl, int? line, int? column}) : sourceUrl = sourceUrl is String ? Uri.parse(sourceUrl) : sourceUrl as Uri?, line = line ?? 0, column = column ?? offset { if (offset < 0) { throw RangeError('Offset may not be negative, was $offset.'); } else if (line != null && line < 0) { throw RangeError('Line may not be negative, was $line.'); } else if (column != null && column < 0) { throw 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 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() => SourceSpan(this, this, ''); /// Compares two locations. /// /// [other] must have the same source URL as `this`. @override int compareTo(SourceLocation other) { if (sourceUrl != other.sourceUrl) { throw ArgumentError('Source URLs "$sourceUrl" and ' "\"${other.sourceUrl}\" don't match."); } return offset - other.offset; } @override bool operator ==(Object other) => other is SourceLocation && sourceUrl == other.sourceUrl && offset == other.offset; @override int get hashCode => (sourceUrl?.hashCode ?? 0) + offset; @override 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(super.offset, {super.sourceUrl, super.line, super.column}); }