Files
linux-packaging-mono/external/aspnetwebstack/src/System.Web.Razor/Text/TextChange.cs
Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

211 lines
7.4 KiB
C#

// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Web.Razor.Parser.SyntaxTree;
using Microsoft.Internal.Web.Utils;
namespace System.Web.Razor.Text
{
public struct TextChange
{
private string _newText;
private string _oldText;
/// <summary>
/// Constructor for changes where the position hasn't moved (primarily for tests)
/// </summary>
internal TextChange(int position, int oldLength, ITextBuffer oldBuffer, int newLength, ITextBuffer newBuffer)
: this(position, oldLength, oldBuffer, position, newLength, newBuffer)
{
}
public TextChange(int oldPosition, int oldLength, ITextBuffer oldBuffer, int newPosition, int newLength, ITextBuffer newBuffer)
: this()
{
if (oldPosition < 0)
{
throw new ArgumentOutOfRangeException("oldPosition", String.Format(CultureInfo.CurrentCulture, CommonResources.Argument_Must_Be_GreaterThanOrEqualTo, "0"));
}
if (newPosition < 0)
{
throw new ArgumentOutOfRangeException("newPosition", String.Format(CultureInfo.CurrentCulture, CommonResources.Argument_Must_Be_GreaterThanOrEqualTo, "0"));
}
if (oldLength < 0)
{
throw new ArgumentOutOfRangeException("oldLength", String.Format(CultureInfo.CurrentCulture, CommonResources.Argument_Must_Be_GreaterThanOrEqualTo, "0"));
}
if (newLength < 0)
{
throw new ArgumentOutOfRangeException("newLength", String.Format(CultureInfo.CurrentCulture, CommonResources.Argument_Must_Be_GreaterThanOrEqualTo, "0"));
}
if (oldBuffer == null)
{
throw new ArgumentNullException("oldBuffer");
}
if (newBuffer == null)
{
throw new ArgumentNullException("newBuffer");
}
OldPosition = oldPosition;
NewPosition = newPosition;
OldLength = oldLength;
NewLength = newLength;
NewBuffer = newBuffer;
OldBuffer = oldBuffer;
}
public int OldPosition { get; private set; }
public int NewPosition { get; private set; }
public int OldLength { get; private set; }
public int NewLength { get; private set; }
public ITextBuffer NewBuffer { get; private set; }
public ITextBuffer OldBuffer { get; private set; }
public string OldText
{
get
{
if (_oldText == null && OldBuffer != null)
{
_oldText = GetText(OldBuffer, OldPosition, OldLength);
}
return _oldText;
}
}
public string NewText
{
get
{
if (_newText == null)
{
_newText = GetText(NewBuffer, NewPosition, NewLength);
}
return _newText;
}
}
public bool IsInsert
{
get { return OldLength == 0 && NewLength > 0; }
}
public bool IsDelete
{
get { return OldLength > 0 && NewLength == 0; }
}
public bool IsReplace
{
get { return OldLength > 0 && NewLength > 0; }
}
public override bool Equals(object obj)
{
if (!(obj is TextChange))
{
return false;
}
TextChange change = (TextChange)obj;
return (change.OldPosition == OldPosition) &&
(change.NewPosition == NewPosition) &&
(change.OldLength == OldLength) &&
(change.NewLength == NewLength) &&
OldBuffer.Equals(change.OldBuffer) &&
NewBuffer.Equals(change.NewBuffer);
}
public string ApplyChange(string content, int changeOffset)
{
int changeRelativePosition = OldPosition - changeOffset;
Debug.Assert(changeRelativePosition >= 0);
return content.Remove(changeRelativePosition, OldLength)
.Insert(changeRelativePosition, NewText);
}
/// <summary>
/// Applies the text change to the content of the span and returns the new content.
/// This method doesn't update the span content.
/// </summary>
public string ApplyChange(Span span)
{
return ApplyChange(span.Content, span.Start.AbsoluteIndex);
}
public override int GetHashCode()
{
return OldPosition ^ NewPosition ^ OldLength ^ NewLength ^ NewBuffer.GetHashCode() ^ OldBuffer.GetHashCode();
}
public override string ToString()
{
return String.Format(CultureInfo.CurrentCulture, "({0}:{1}) \"{3}\" -> ({0}:{2}) \"{4}\"", OldPosition, OldLength, NewLength, OldText, NewText);
}
/// <summary>
/// Removes a common prefix from the edit to turn IntelliSense replacements into insertions where possible
/// </summary>
/// <returns>A normalized text change</returns>
public TextChange Normalize()
{
if (OldBuffer != null && IsReplace && NewLength > OldLength && NewText.StartsWith(OldText, StringComparison.Ordinal) && NewPosition == OldPosition)
{
// Normalize the change into an insertion of the uncommon suffix (i.e. strip out the common prefix)
return new TextChange(oldPosition: OldPosition + OldLength,
oldLength: 0,
oldBuffer: OldBuffer,
newPosition: OldPosition + OldLength,
newLength: NewLength - OldLength,
newBuffer: NewBuffer);
}
return this;
}
private string GetText(ITextBuffer buffer, int position, int length)
{
int oldPosition = buffer.Position;
try
{
buffer.Position = position;
// Optimization for the common case of one char inserts
if (NewLength == 1)
{
return ((char)buffer.Read()).ToString();
}
else
{
var builder = new StringBuilder();
for (int i = 0; i < length; i++)
{
char c = (char)buffer.Read();
builder.Append(c);
if (Char.IsHighSurrogate(c))
{
builder.Append((char)buffer.Read());
}
}
return builder.ToString();
}
}
finally
{
buffer.Position = oldPosition;
}
}
public static bool operator ==(TextChange left, TextChange right)
{
return left.Equals(right);
}
public static bool operator !=(TextChange left, TextChange right)
{
return !left.Equals(right);
}
}
}