Files
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

273 lines
7.6 KiB
C#

// RichTextKit
// Copyright © 2019-2020 Topten Software. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this product except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
using System;
using System.IO;
using System.Xml.Schema;
namespace Topten.RichTextKit
{
/// <summary>
/// Represents a range of code points in a text document
/// </summary>
public struct TextRange
{
/// <summary>
/// Initializes a TextRange
/// </summary>
/// <param name="start">The code point index of the start of the range</param>
/// <param name="end">The code point index of the end of the range</param>
/// <param name="altPosition">Whether the caret at the end of the range should be displayed in its alternative position</param>
public TextRange( int start, int end, bool altPosition = false )
{
Start = start;
End = end;
AltPosition = altPosition;
}
/// <summary>
/// Initializes a TextRange with a non-range position
/// </summary>
/// <param name="position">The code point index of the position</param>
/// <param name="altPosition">Whether the caret should be displayed in its alternative position</param>
public TextRange( int position, bool altPosition = false )
{
Start = position;
End = position;
AltPosition = altPosition;
}
/// <summary>
/// Initializes a TextRange from a caret position
/// </summary>
/// <param name="position">The code point index of the position</param>
public TextRange( CaretPosition position )
{
Start = position.CodePointIndex;
End = position.CodePointIndex;
AltPosition = position.AltPosition;
}
/// <summary>
/// The code point index of the start of the range
/// </summary>
public int Start;
/// <summary>
/// The code point index of the end of the range
/// </summary>
public int End;
/// <summary>
/// True if the end of the range should be displayed
/// with the caret in the alt position
/// </summary>
public bool AltPosition;
/// <summary>
/// Get the length of this range
/// </summary>
/// <remarks>
/// Will return negative if the range isn't normalized
/// </remarks>
public int Length => End - Start;
/// <summary>
/// Offset this text range by the specified amount
/// </summary>
/// <param name="delta">The number of code points to offset the range by</param>
/// <returns>A new TextRange</returns>
public TextRange Offset( int delta )
{
return new TextRange( Start + delta, End + delta, AltPosition );
}
/// <summary>
/// Returns the reversed text range
/// </summary>
public TextRange Reversed
{
get
{
return new TextRange( End, Start, false );
}
}
/// <summary>
/// Returns the normalized version of the text range
/// </summary>
public TextRange Normalized
{
get
{
if ( Start > End )
return Reversed;
else
return this;
}
}
/// <summary>
/// Compare this text range to another for equality
/// </summary>
/// <param name="other">The text range to compare to</param>
/// <returns>True if the text ranges are equal</returns>
public bool IsEqual( TextRange other )
{
return other.Start == Start &&
other.End == End &&
other.AltPosition == AltPosition;
}
/// <summary>
/// Check if this is actually a range
/// </summary>
public bool IsRange => Start != End;
/// <summary>
/// Get the end of the range closer to the start of the document
/// </summary>
public int Minimum => Math.Min( Start, End );
/// <summary>
/// Get the end of the range closer to the end of the document
/// </summary>
public int Maximum => Math.Max( Start, End );
/// <summary>
/// Gets the end of the range as a caret position
/// </summary>
public CaretPosition CaretPosition
{
get
{
return new CaretPosition( End, AltPosition );
}
}
/// <summary>
/// Clamp the text range to a document length
/// </summary>
/// <param name="maxCodePointIndex">The max code point index</param>
/// <returns>A clamped TextRange</returns>
public TextRange Clamp( int maxCodePointIndex )
{
var newStart = Start;
if ( newStart < 0 )
newStart = 0;
if ( newStart > maxCodePointIndex )
newStart = maxCodePointIndex;
var newEnd = End;
if ( newEnd < 0 )
newEnd = 0;
if ( newEnd > maxCodePointIndex )
newEnd = maxCodePointIndex;
return new TextRange( newStart, newEnd, AltPosition );
}
/// <summary>
/// Create an updated text range that tries to represent the same
/// piece of text from before an edit to after the edit.
/// </summary>
/// <param name="codePointIndex">The position of the edit</param>
/// <param name="oldLength">The length of text deleted</param>
/// <param name="newLength">The length of text inserted</param>
/// <returns>An updated text range</returns>
public TextRange UpdateForEdit( int codePointIndex, int oldLength, int newLength )
{
int delta = newLength - oldLength;
// After this range?
if ( codePointIndex > Maximum )
return this;
// Before this range?
if ( codePointIndex + oldLength <= Minimum )
return new TextRange( Start + delta, End + delta, AltPosition );
// Entire range?
if ( codePointIndex <= Minimum && codePointIndex + oldLength >= Maximum )
return new TextRange( codePointIndex, codePointIndex, AltPosition );
// Inside this range?
if ( codePointIndex >= Minimum && codePointIndex + oldLength <= Maximum )
{
if ( Start < End )
return new TextRange( Start, End + delta, AltPosition );
else
return new TextRange( End, Start + delta, AltPosition );
}
// Overlap start of this range?
if ( codePointIndex < Minimum && codePointIndex + oldLength >= Minimum )
{
if ( Start < End )
return new TextRange( codePointIndex + newLength, End + delta, AltPosition );
else
return new TextRange( Start + delta, codePointIndex + newLength, AltPosition );
}
// Overlap end of this range?
if ( codePointIndex >= Minimum && codePointIndex + oldLength > Maximum )
{
if ( Start < End )
return new TextRange( Start, codePointIndex, AltPosition );
else
return new TextRange( codePointIndex, End, AltPosition );
}
// Should never get here.
return new TextRange();
}
/// <summary>
/// Create a new range that is the union of two other ranges. The
/// union is the smallest range that contains the other two other
/// ranges.
/// </summary>
/// <remarks>
/// The returned range is configured such that the 'b' range
/// is used for the end position (ie: the caret)
/// </remarks>
/// <param name="a">The first text range</param>
/// <param name="b">The second text range</param>
/// <returns>A range that encompasses both ranges</returns>
public static TextRange Union( TextRange a, TextRange b )
{
if ( a.Minimum <= b.Minimum )
{
return new TextRange(
Math.Min( a.Minimum, b.Minimum ),
Math.Max( a.Maximum, b.Maximum ), b.AltPosition );
}
else
{
return new TextRange(
Math.Max( a.Maximum, b.Maximum ),
Math.Min( a.Minimum, b.Minimum ), b.AltPosition );
}
}
/// <inheritdoc />
public override string ToString()
{
return $"{Start} → {End} (len: {Length})";
}
}
}