Line data Source code
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 : import 'span.dart';
6 :
7 : // TODO(nweiz): Use SourceLocationMixin once we decide to cut a release with
8 : // breaking changes. See SourceLocationMixin for details.
9 :
10 : /// A class that describes a single location within a source file.
11 : ///
12 : /// This class should not be extended. Instead, [SourceLocationBase] should be
13 : /// extended instead.
14 : class SourceLocation implements Comparable<SourceLocation> {
15 : /// URL of the source containing this location.
16 : ///
17 : /// This may be null, indicating that the source URL is unknown or
18 : /// unavailable.
19 : final Uri sourceUrl;
20 :
21 : /// The 0-based offset of this location in the source.
22 : final int offset;
23 :
24 : /// The 0-based line of this location in the source.
25 : final int line;
26 :
27 : /// The 0-based column of this location in the source
28 : final int column;
29 :
30 : /// Returns a representation of this location in the `source:line:column`
31 : /// format used by text editors.
32 : ///
33 : /// This prints 1-based lines and columns.
34 : String get toolString {
35 0 : var source = sourceUrl == null ? 'unknown source' : sourceUrl;
36 0 : return '$source:${line + 1}:${column + 1}';
37 : }
38 :
39 : /// Creates a new location indicating [offset] within [sourceUrl].
40 : ///
41 : /// [line] and [column] default to assuming the source is a single line. This
42 : /// means that [line] defaults to 0 and [column] defaults to [offset].
43 : ///
44 : /// [sourceUrl] may be either a [String], a [Uri], or `null`.
45 : SourceLocation(int offset, {sourceUrl, int line, int column})
46 0 : : sourceUrl = sourceUrl is String ? Uri.parse(sourceUrl) : sourceUrl,
47 : offset = offset,
48 : line = line == null ? 0 : line,
49 0 : column = column == null ? offset : column {
50 0 : if (offset < 0) {
51 0 : throw new RangeError("Offset may not be negative, was $offset.");
52 0 : } else if (line != null && line < 0) {
53 0 : throw new RangeError("Line may not be negative, was $line.");
54 0 : } else if (column != null && column < 0) {
55 0 : throw new RangeError("Column may not be negative, was $column.");
56 : }
57 : }
58 :
59 : /// Returns the distance in characters between [this] and [other].
60 : ///
61 : /// This always returns a non-negative value.
62 : int distance(SourceLocation other) {
63 0 : if (sourceUrl != other.sourceUrl) {
64 0 : throw new ArgumentError("Source URLs \"${sourceUrl}\" and "
65 0 : "\"${other.sourceUrl}\" don't match.");
66 : }
67 0 : return (offset - other.offset).abs();
68 : }
69 :
70 : /// Returns a span that covers only a single point: this location.
71 0 : SourceSpan pointSpan() => new SourceSpan(this, this, "");
72 :
73 : /// Compares two locations.
74 : ///
75 : /// [other] must have the same source URL as [this].
76 : int compareTo(SourceLocation other) {
77 0 : if (sourceUrl != other.sourceUrl) {
78 0 : throw new ArgumentError("Source URLs \"${sourceUrl}\" and "
79 0 : "\"${other.sourceUrl}\" don't match.");
80 : }
81 0 : return offset - other.offset;
82 : }
83 :
84 : bool operator ==(other) =>
85 0 : other is SourceLocation && sourceUrl == other.sourceUrl &&
86 0 : offset == other.offset;
87 :
88 0 : int get hashCode => sourceUrl.hashCode + offset;
89 :
90 0 : String toString() => '<$runtimeType: $offset $toolString>';
91 : }
92 :
93 : /// A base class for source locations with [offset], [line], and [column] known
94 : /// at construction time.
95 : class SourceLocationBase extends SourceLocation {
96 : SourceLocationBase(int offset, {sourceUrl, int line, int column})
97 0 : : super(offset, sourceUrl: sourceUrl, line: line, column: column);
98 : }
|