262 lines
8.6 KiB
C#
262 lines
8.6 KiB
C#
// Copyright (c) .NET Foundation. All rights reserved.
|
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
|
|
using System;
|
|
using Microsoft.Extensions.Internal;
|
|
|
|
namespace Microsoft.AspNetCore.Razor.Language.Syntax
|
|
{
|
|
/// <summary>
|
|
/// Immutable abstract representation of a span of text. For example, in an error diagnostic that reports a
|
|
/// location, it could come from a parsed string, text from a tool editor buffer, etc.
|
|
/// </summary>
|
|
internal readonly struct TextSpan : IEquatable<TextSpan>, IComparable<TextSpan>
|
|
{
|
|
/// <summary>
|
|
/// Creates a TextSpan instance beginning with the position Start and having the Length
|
|
/// specified with <paramref name="length" />.
|
|
/// </summary>
|
|
public TextSpan(int start, int length)
|
|
{
|
|
if (start < 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(start));
|
|
}
|
|
|
|
if (start + length < start)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(length));
|
|
}
|
|
|
|
Start = start;
|
|
Length = length;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Start point of the span.
|
|
/// </summary>
|
|
public int Start { get; }
|
|
|
|
/// <summary>
|
|
/// End of the span.
|
|
/// </summary>
|
|
public int End => Start + Length;
|
|
|
|
/// <summary>
|
|
/// Length of the span.
|
|
/// </summary>
|
|
public int Length { get; }
|
|
|
|
/// <summary>
|
|
/// Determines whether or not the span is empty.
|
|
/// </summary>
|
|
public bool IsEmpty => Length == 0;
|
|
|
|
/// <summary>
|
|
/// Determines whether the position lies within the span.
|
|
/// </summary>
|
|
/// <param name="position">
|
|
/// The position to check.
|
|
/// </param>
|
|
/// <returns>
|
|
/// <c>true</c> if the position is greater than or equal to Start and strictly less
|
|
/// than End, otherwise <c>false</c>.
|
|
/// </returns>
|
|
public bool Contains(int position)
|
|
{
|
|
return unchecked((uint)(position - Start) < (uint)Length);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines whether <paramref name="span"/> falls completely within this span.
|
|
/// </summary>
|
|
/// <param name="span">
|
|
/// The span to check.
|
|
/// </param>
|
|
/// <returns>
|
|
/// <c>true</c> if the specified span falls completely within this span, otherwise <c>false</c>.
|
|
/// </returns>
|
|
public bool Contains(TextSpan span)
|
|
{
|
|
return span.Start >= Start && span.End <= End;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines whether <paramref name="span"/> overlaps this span. Two spans are considered to overlap
|
|
/// if they have positions in common and neither is empty. Empty spans do not overlap with any
|
|
/// other span.
|
|
/// </summary>
|
|
/// <param name="span">
|
|
/// The span to check.
|
|
/// </param>
|
|
/// <returns>
|
|
/// <c>true</c> if the spans overlap, otherwise <c>false</c>.
|
|
/// </returns>
|
|
public bool OverlapsWith(TextSpan span)
|
|
{
|
|
var overlapStart = Math.Max(Start, span.Start);
|
|
var overlapEnd = Math.Min(End, span.End);
|
|
|
|
return overlapStart < overlapEnd;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the overlap with the given span, or null if there is no overlap.
|
|
/// </summary>
|
|
/// <param name="span">
|
|
/// The span to check.
|
|
/// </param>
|
|
/// <returns>
|
|
/// The overlap of the spans, or null if the overlap is empty.
|
|
/// </returns>
|
|
public TextSpan? Overlap(TextSpan span)
|
|
{
|
|
var overlapStart = Math.Max(Start, span.Start);
|
|
var overlapEnd = Math.Min(End, span.End);
|
|
|
|
return overlapStart < overlapEnd
|
|
? FromBounds(overlapStart, overlapEnd)
|
|
: (TextSpan?)null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines whether <paramref name="span"/> intersects this span. Two spans are considered to
|
|
/// intersect if they have positions in common or the end of one span
|
|
/// coincides with the start of the other span.
|
|
/// </summary>
|
|
/// <param name="span">
|
|
/// The span to check.
|
|
/// </param>
|
|
/// <returns>
|
|
/// <c>true</c> if the spans intersect, otherwise <c>false</c>.
|
|
/// </returns>
|
|
public bool IntersectsWith(TextSpan span)
|
|
{
|
|
return span.Start <= End && span.End >= Start;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines whether <paramref name="position"/> intersects this span.
|
|
/// A position is considered to intersect if it is between the start and
|
|
/// end positions (inclusive) of this span.
|
|
/// </summary>
|
|
/// <param name="position">
|
|
/// The position to check.
|
|
/// </param>
|
|
/// <returns>
|
|
/// <c>true</c> if the position intersects, otherwise <c>false</c>.
|
|
/// </returns>
|
|
public bool IntersectsWith(int position)
|
|
{
|
|
return unchecked((uint)(position - Start) <= (uint)Length);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the intersection with the given span, or null if there is no intersection.
|
|
/// </summary>
|
|
/// <param name="span">
|
|
/// The span to check.
|
|
/// </param>
|
|
/// <returns>
|
|
/// The intersection of the spans, or null if the intersection is empty.
|
|
/// </returns>
|
|
public TextSpan? Intersection(TextSpan span)
|
|
{
|
|
var intersectStart = Math.Max(Start, span.Start);
|
|
var intersectEnd = Math.Min(End, span.End);
|
|
|
|
return intersectStart <= intersectEnd
|
|
? FromBounds(intersectStart, intersectEnd)
|
|
: (TextSpan?)null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new <see cref="TextSpan"/> from <paramref name="start" /> and <paramref
|
|
/// name="end"/> positions as opposed to a position and length.
|
|
///
|
|
/// The returned TextSpan contains the range with <paramref name="start"/> inclusive,
|
|
/// and <paramref name="end"/> exclusive.
|
|
/// </summary>
|
|
public static TextSpan FromBounds(int start, int end)
|
|
{
|
|
if (start < 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(start), "start must not be negative");
|
|
}
|
|
|
|
if (end < start)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(end), "end must not be less than start");
|
|
}
|
|
|
|
return new TextSpan(start, end - start);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if two instances of <see cref="TextSpan"/> are the same.
|
|
/// </summary>
|
|
public static bool operator ==(TextSpan left, TextSpan right)
|
|
{
|
|
return left.Equals(right);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if two instances of <see cref="TextSpan"/> are different.
|
|
/// </summary>
|
|
public static bool operator !=(TextSpan left, TextSpan right)
|
|
{
|
|
return !left.Equals(right);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if current instance of <see cref="TextSpan"/> is equal to another.
|
|
/// </summary>
|
|
public bool Equals(TextSpan other)
|
|
{
|
|
return Start == other.Start && Length == other.Length;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if current instance of <see cref="TextSpan"/> is equal to another.
|
|
/// </summary>
|
|
public override bool Equals(object obj)
|
|
{
|
|
return obj is TextSpan && Equals((TextSpan)obj);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Produces a hash code for <see cref="TextSpan"/>.
|
|
/// </summary>
|
|
public override int GetHashCode()
|
|
{
|
|
var combiner = new HashCodeCombiner();
|
|
combiner.Add(Start);
|
|
combiner.Add(Length);
|
|
|
|
return combiner.CombinedHash;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Provides a string representation for <see cref="TextSpan"/>.
|
|
/// </summary>
|
|
public override string ToString()
|
|
{
|
|
return $"[{Start}..{End})";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compares current instance of <see cref="TextSpan"/> with another.
|
|
/// </summary>
|
|
public int CompareTo(TextSpan other)
|
|
{
|
|
var diff = Start - other.Start;
|
|
if (diff != 0)
|
|
{
|
|
return diff;
|
|
}
|
|
|
|
return Length - other.Length;
|
|
}
|
|
}
|
|
}
|