Files
BookRPG/Library/PackageCache/com.unity.collab-proxy@0b1559bcd34e/Editor/CodeEditor/Document/TextSourceVersionProvider.cs
2026-07-16 21:49:59 +01:00

128 lines
5.0 KiB
C#

// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Unity.CodeEditor.Utils;
namespace Unity.CodeEditor.Document
{
/// <summary>
/// Provides ITextSourceVersion instances.
/// </summary>
internal class TextSourceVersionProvider
{
private Version _currentVersion;
/// <summary>
/// Creates a new TextSourceVersionProvider instance.
/// </summary>
internal TextSourceVersionProvider()
{
_currentVersion = new Version(this);
}
/// <summary>
/// Gets the current version.
/// </summary>
internal ITextSourceVersion CurrentVersion => _currentVersion;
/// <summary>
/// Replaces the current version with a new version.
/// </summary>
/// <param name="change">Change from current version to new version</param>
internal void AppendChange(TextChangeEventArgs change)
{
_currentVersion.Change = change ?? throw new ArgumentNullException(nameof(change));
_currentVersion.Next = new Version(_currentVersion);
_currentVersion = _currentVersion.Next;
}
[DebuggerDisplay("Version #{" + nameof(_id) + "}")]
private sealed class Version : ITextSourceVersion
{
// Reference back to the provider.
// Used to determine if two checkpoints belong to the same document.
private readonly TextSourceVersionProvider _provider;
// ID used for CompareAge()
private readonly int _id;
// the change from this version to the next version
internal TextChangeEventArgs Change;
public Version Next;
public Version(TextSourceVersionProvider provider)
{
_provider = provider;
}
public Version(Version prev)
{
_provider = prev._provider;
_id = unchecked(prev._id + 1);
}
public bool BelongsToSameDocumentAs(ITextSourceVersion other)
{
var o = other as Version;
return o != null && _provider == o._provider;
}
public int CompareAge(ITextSourceVersion other)
{
if (other == null)
throw new ArgumentNullException(nameof(other));
var o = other as Version;
if (o == null || _provider != o._provider)
throw new ArgumentException("Versions do not belong to the same document.");
// We will allow overflows, but assume that the maximum distance between checkpoints is 2^31-1.
// This is guaranteed on x86 because so many checkpoints don't fit into memory.
return Math.Sign(unchecked(_id - o._id));
}
public IEnumerable<TextChangeEventArgs> GetChangesTo(ITextSourceVersion other)
{
var result = CompareAge(other);
var o = (Version)other;
if (result < 0)
return GetForwardChanges(o);
if (result > 0)
return o.GetForwardChanges(this).Reverse().Select(c => c.Invert());
return Array.Empty<TextChangeEventArgs>();
}
private IEnumerable<TextChangeEventArgs> GetForwardChanges(Version other)
{
// Return changes from this(inclusive) to other(exclusive).
for (var node = this; node != other; node = node.Next)
{
yield return node.Change;
}
}
public int MoveOffsetTo(ITextSourceVersion other, int oldOffset, AnchorMovementType movement)
{
return GetChangesTo(other)
.Aggregate(oldOffset, (current, e) => e.GetNewOffset(current, movement));
}
}
}
}