vsintegration/src/FSharp.LanguageService.Base/PatternMatcher/TextSpan.cs (107 lines of code) (raw):
using System;
namespace Roslyn.Utilities
{
/// <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 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 => this.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 <= this.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)
{
int overlapStart = Math.Max(Start, span.Start);
int overlapEnd = Math.Min(this.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)
{
int overlapStart = Math.Max(Start, span.Start);
int overlapEnd = Math.Min(this.End, span.End);
return overlapStart < overlapEnd
? TextSpan.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 <= this.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)
{
int intersectStart = Math.Max(Start, span.Start);
int intersectEnd = Math.Min(this.End, span.End);
return intersectStart <= intersectEnd
? TextSpan.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 ne ngative");
}
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()
{
return Hash.Combine(Start, Length);
}
/// <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;
}
}
}