| // Copyright (c) 2025, 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 'dart:math' as math; | |
| import 'package:_fe_analyzer_shared/src/base/analyzer_public_api.dart'; | |
| /// A source range defines a range of characters within source code. | |
| @AnalyzerPublicApi( | |
| message: 'exported by package:analyzer/source/source_range.dart', | |
| ) | |
| class SourceRange { | |
| /// An empty source range (a range with offset `0` and length `0`). | |
| static const SourceRange EMPTY = const SourceRange(0, 0); | |
| /// The 0-based index of the first character of the source range. | |
| final int offset; | |
| /// The number of characters in the source range. | |
| final int length; | |
| /// Initialize a newly created source range using the given [offset] and | |
| /// [length]. | |
| const SourceRange(this.offset, this.length); | |
| /// Return the 0-based index of the character immediately after this source | |
| /// range. | |
| int get end => offset + length; | |
| @override | |
| int get hashCode => 31 * offset + length; | |
| @override | |
| bool operator ==(Object other) { | |
| return other is SourceRange && | |
| other.offset == offset && | |
| other.length == length; | |
| } | |
| /// Return `true` if [x] is in the interval `[offset, offset + length]`. | |
| bool contains(int x) => offset <= x && x <= offset + length; | |
| /// Return `true` if [x] is in the interval `(offset, offset + length)`. | |
| bool containsExclusive(int x) => offset < x && x < offset + length; | |
| /// Return `true` if the [otherRange] covers this source range. | |
| bool coveredBy(SourceRange otherRange) => otherRange.covers(this); | |
| /// Return `true` if this source range covers the [otherRange]. | |
| bool covers(SourceRange otherRange) => | |
| offset <= otherRange.offset && otherRange.end <= end; | |
| /// Return `true` if this source range ends inside the [otherRange]. | |
| bool endsIn(SourceRange otherRange) { | |
| int thisEnd = end; | |
| return otherRange.contains(thisEnd); | |
| } | |
| /// Return a source range covering [delta] characters before the start of this | |
| /// source range and [delta] characters after the end of this source range. | |
| SourceRange getExpanded(int delta) => | |
| new SourceRange(offset - delta, delta + length + delta); | |
| /// Return a source range with the same offset as this source range but whose | |
| /// length is [delta] characters longer than this source range. | |
| SourceRange getMoveEnd(int delta) => new SourceRange(offset, length + delta); | |
| /// Return a source range with the same length as this source range but whose | |
| /// offset is [delta] characters after the offset of this source range. | |
| SourceRange getTranslated(int delta) => | |
| new SourceRange(offset + delta, length); | |
| /// Return the minimal source range that covers both this and the | |
| /// [otherRange]. | |
| SourceRange getUnion(SourceRange otherRange) { | |
| int newOffset = math.min(offset, otherRange.offset); | |
| int newEnd = math.max( | |
| offset + length, | |
| otherRange.offset + otherRange.length, | |
| ); | |
| return new SourceRange(newOffset, newEnd - newOffset); | |
| } | |
| /// Return `true` if this source range intersects the [otherRange]. | |
| bool intersects(SourceRange? otherRange) { | |
| if (otherRange == null) { | |
| return false; | |
| } | |
| if (end <= otherRange.offset) { | |
| return false; | |
| } | |
| if (offset >= otherRange.end) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| /// Return `true` if this source range starts in the [otherRange]. | |
| bool startsIn(SourceRange otherRange) => otherRange.contains(offset); | |
| @override | |
| String toString() => '[offset=$offset, length=$length]'; | |
| } |