Imported Upstream version 3.6.0

Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
This commit is contained in:
Jo Shields
2014-08-13 10:39:27 +01:00
commit a575963da9
50588 changed files with 8155799 additions and 0 deletions

View File

@ -0,0 +1,200 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Web.Razor.Resources;
using System.Web.Razor.Utils;
namespace System.Web.Razor.Text
{
public class BufferingTextReader : LookaheadTextReader
{
private Stack<BacktrackContext> _backtrackStack = new Stack<BacktrackContext>();
private int _currentBufferPosition;
private int _currentCharacter;
private SourceLocationTracker _locationTracker;
public BufferingTextReader(TextReader source)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
InnerReader = source;
_locationTracker = new SourceLocationTracker();
UpdateCurrentCharacter();
}
internal StringBuilder Buffer { get; set; }
internal bool Buffering { get; set; }
internal TextReader InnerReader { get; private set; }
public override SourceLocation CurrentLocation
{
get { return _locationTracker.CurrentLocation; }
}
protected virtual int CurrentCharacter
{
get { return _currentCharacter; }
}
public override int Read()
{
int ch = CurrentCharacter;
NextCharacter();
return ch;
}
// TODO: Optimize Read(char[],int,int) to copy direct from the buffer where possible
public override int Peek()
{
return CurrentCharacter;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
InnerReader.Dispose();
}
base.Dispose(disposing);
}
public override IDisposable BeginLookahead()
{
// Is this our first lookahead?
if (Buffer == null)
{
// Yes, setup the backtrack buffer
Buffer = new StringBuilder();
}
if (!Buffering)
{
// We're not already buffering, so we need to expand the buffer to hold the first character
ExpandBuffer();
Buffering = true;
}
// Mark the position to return to when we backtrack
// Use the closures and the "using" statement rather than an explicit stack
BacktrackContext context = new BacktrackContext()
{
BufferIndex = _currentBufferPosition,
Location = CurrentLocation
};
_backtrackStack.Push(context);
return new DisposableAction(() =>
{
EndLookahead(context);
});
}
// REVIEW: This really doesn't sound like the best name for this...
public override void CancelBacktrack()
{
if (_backtrackStack.Count == 0)
{
throw new InvalidOperationException(RazorResources.CancelBacktrack_Must_Be_Called_Within_Lookahead);
}
// Just pop the current backtrack context so that when the lookahead ends, it won't be backtracked
_backtrackStack.Pop();
}
private void EndLookahead(BacktrackContext context)
{
// If the specified context is not the one on the stack, it was popped by a call to DoNotBacktrack
if (_backtrackStack.Count > 0 && ReferenceEquals(_backtrackStack.Peek(), context))
{
_backtrackStack.Pop();
_currentBufferPosition = context.BufferIndex;
_locationTracker.CurrentLocation = context.Location;
UpdateCurrentCharacter();
}
}
protected virtual void NextCharacter()
{
int prevChar = CurrentCharacter;
if (prevChar == -1)
{
return; // We're at the end of the source
}
if (Buffering)
{
if (_currentBufferPosition >= Buffer.Length - 1)
{
// If there are no more lookaheads (thus no need to continue with the buffer) we can just clean up the buffer
if (_backtrackStack.Count == 0)
{
// Reset the buffer
Buffer.Length = 0;
_currentBufferPosition = 0;
Buffering = false;
}
else if (!ExpandBuffer())
{
// Failed to expand the buffer, because we're at the end of the source
_currentBufferPosition = Buffer.Length; // Force the position past the end of the buffer
}
}
else
{
// Not at the end yet, just advance the buffer pointer
_currentBufferPosition++;
}
}
else
{
// Just act like normal
InnerReader.Read(); // Don't care about the return value, Peek() is used to get characters from the source
}
UpdateCurrentCharacter();
_locationTracker.UpdateLocation((char)prevChar, (char)CurrentCharacter);
}
protected bool ExpandBuffer()
{
// Pull another character into the buffer and update the position
int ch = InnerReader.Read();
// Only append the character to the buffer if there actually is one
if (ch != -1)
{
Buffer.Append((char)ch);
_currentBufferPosition = Buffer.Length - 1;
return true;
}
return false;
}
private void UpdateCurrentCharacter()
{
if (Buffering && _currentBufferPosition < Buffer.Length)
{
// Read from the buffer
_currentCharacter = (int)Buffer[_currentBufferPosition];
}
else
{
// No buffer? Peek from the source
_currentCharacter = InnerReader.Peek();
}
}
private class BacktrackContext
{
public int BufferIndex { get; set; }
public SourceLocation Location { get; set; }
}
}
}

View File

@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Razor.Text
{
public interface ITextBuffer
{
int Length { get; }
int Position { get; set; }
int Read();
int Peek();
}
// TextBuffer with Location tracking
public interface ITextDocument : ITextBuffer
{
SourceLocation Location { get; }
}
}

View File

@ -0,0 +1,161 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Web.Razor.Parser;
namespace System.Web.Razor.Text
{
internal class LineTrackingStringBuffer
{
private TextLine _currentLine;
private TextLine _endLine;
private IList<TextLine> _lines;
public LineTrackingStringBuffer()
{
_endLine = new TextLine(0, 0);
_lines = new List<TextLine>() { _endLine };
}
public int Length
{
get { return _endLine.End; }
}
public SourceLocation EndLocation
{
get { return new SourceLocation(Length, _lines.Count - 1, _lines[_lines.Count - 1].Length); }
}
public void Append(string content)
{
for (int i = 0; i < content.Length; i++)
{
AppendCore(content[i]);
// \r on it's own: Start a new line, otherwise wait for \n
// Other Newline: Start a new line
if ((content[i] == '\r' && (i + 1 == content.Length || content[i + 1] != '\n')) || (content[i] != '\r' && ParserHelpers.IsNewLine(content[i])))
{
PushNewLine();
}
}
}
public CharacterReference CharAt(int absoluteIndex)
{
TextLine line = FindLine(absoluteIndex);
if (line == null)
{
throw new ArgumentOutOfRangeException("absoluteIndex");
}
int idx = absoluteIndex - line.Start;
return new CharacterReference(line.Content[idx], new SourceLocation(absoluteIndex, line.Index, idx));
}
private void PushNewLine()
{
_endLine = new TextLine(_endLine.End, _endLine.Index + 1);
_lines.Add(_endLine);
}
private void AppendCore(char chr)
{
Debug.Assert(_lines.Count > 0);
_lines[_lines.Count - 1].Content.Append(chr);
}
private TextLine FindLine(int absoluteIndex)
{
TextLine selected = null;
if (_currentLine != null)
{
if (_currentLine.Contains(absoluteIndex))
{
// This index is on the last read line
selected = _currentLine;
}
else if (absoluteIndex > _currentLine.Index && _currentLine.Index + 1 < _lines.Count)
{
// This index is ahead of the last read line
selected = ScanLines(absoluteIndex, _currentLine.Index);
}
}
// Have we found a line yet?
if (selected == null)
{
// Scan from line 0
selected = ScanLines(absoluteIndex, 0);
}
Debug.Assert(selected == null || selected.Contains(absoluteIndex));
_currentLine = selected;
return selected;
}
private TextLine ScanLines(int absoluteIndex, int startPos)
{
for (int i = 0; i < _lines.Count; i++)
{
int idx = (i + startPos) % _lines.Count;
Debug.Assert(idx >= 0 && idx < _lines.Count);
if (_lines[idx].Contains(absoluteIndex))
{
return _lines[idx];
}
}
return null;
}
internal class CharacterReference
{
public CharacterReference(char character, SourceLocation location)
{
Character = character;
Location = location;
}
public char Character { get; private set; }
public SourceLocation Location { get; private set; }
}
private class TextLine
{
private StringBuilder _content = new StringBuilder();
public TextLine(int start, int index)
{
Start = start;
Index = index;
}
public StringBuilder Content
{
get { return _content; }
}
public int Length
{
get { return Content.Length; }
}
public int Start { get; set; }
public int Index { get; set; }
public int End
{
get { return Start + Length; }
}
public bool Contains(int index)
{
return index < End && index >= Start;
}
}
}
}

View File

@ -0,0 +1,92 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics;
using System.Globalization;
using Microsoft.Internal.Web.Utils;
namespace System.Web.Razor.Text
{
[DebuggerDisplay("({Location})\"{Value}\"")]
public class LocationTagged<T> : IFormattable
{
private LocationTagged()
{
Location = SourceLocation.Undefined;
Value = default(T);
}
public LocationTagged(T value, int offset, int line, int col)
: this(value, new SourceLocation(offset, line, col))
{
}
public LocationTagged(T value, SourceLocation location)
{
if (value == null)
{
throw new ArgumentNullException("value");
}
Location = location;
Value = value;
}
public SourceLocation Location { get; private set; }
public T Value { get; private set; }
public override bool Equals(object obj)
{
LocationTagged<T> other = obj as LocationTagged<T>;
return other != null &&
Equals(other.Location, Location) &&
Equals(other.Value, Value);
}
public override int GetHashCode()
{
return HashCodeCombiner.Start()
.Add(Location)
.Add(Value)
.CombinedHash;
}
public override string ToString()
{
return Value.ToString();
}
public string ToString(string format, IFormatProvider formatProvider)
{
if (String.IsNullOrEmpty(format))
{
format = "P";
}
if (formatProvider == null)
{
formatProvider = CultureInfo.CurrentCulture;
}
switch (format.ToUpperInvariant())
{
case "F":
return String.Format(formatProvider, "{0}@{1}", Value, Location);
default:
return Value.ToString();
}
}
public static implicit operator T(LocationTagged<T> value)
{
return value.Value;
}
public static bool operator ==(LocationTagged<T> left, LocationTagged<T> right)
{
return Equals(left, right);
}
public static bool operator !=(LocationTagged<T> left, LocationTagged<T> right)
{
return !Equals(left, right);
}
}
}

View File

@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.IO;
namespace System.Web.Razor.Text
{
public abstract class LookaheadTextReader : TextReader
{
public abstract SourceLocation CurrentLocation { get; }
public abstract IDisposable BeginLookahead();
public abstract void CancelBacktrack();
}
}

View File

@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Razor.Text
{
public class LookaheadToken : IDisposable
{
private Action _cancelAction;
private bool _accepted;
public LookaheadToken(Action cancelAction)
{
_cancelAction = cancelAction;
}
public void Accept()
{
_accepted = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_accepted)
{
_cancelAction();
}
}
}
}

View File

@ -0,0 +1,99 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.IO;
namespace System.Web.Razor.Text
{
public class SeekableTextReader : TextReader, ITextDocument
{
private int _position = 0;
private LineTrackingStringBuffer _buffer = new LineTrackingStringBuffer();
private SourceLocation _location = SourceLocation.Zero;
private char? _current;
public SeekableTextReader(string content)
{
_buffer.Append(content);
UpdateState();
}
public SeekableTextReader(TextReader source)
: this(source.ReadToEnd())
{
}
public SeekableTextReader(ITextBuffer buffer)
: this(buffer.ReadToEnd())
{
}
public SourceLocation Location
{
get { return _location; }
}
public int Length
{
get { return _buffer.Length; }
}
public int Position
{
get { return _position; }
set
{
if (_position != value)
{
_position = value;
UpdateState();
}
}
}
internal LineTrackingStringBuffer Buffer
{
get { return _buffer; }
}
public override int Read()
{
if (_current == null)
{
return -1;
}
char chr = _current.Value;
_position++;
UpdateState();
return chr;
}
public override int Peek()
{
if (_current == null)
{
return -1;
}
return _current.Value;
}
private void UpdateState()
{
if (_position < _buffer.Length)
{
LineTrackingStringBuffer.CharacterReference chr = _buffer.CharAt(_position);
_current = chr.Character;
_location = chr.Location;
}
else if (_buffer.Length == 0)
{
_current = null;
_location = SourceLocation.Zero;
}
else
{
_current = null;
_location = _buffer.EndLocation;
}
}
}
}

View File

@ -0,0 +1,138 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Globalization;
namespace System.Web.Razor.Text
{
[Serializable]
public struct SourceLocation : IEquatable<SourceLocation>, IComparable<SourceLocation>
{
public static readonly SourceLocation Undefined = CreateUndefined();
public static readonly SourceLocation Zero = new SourceLocation(0, 0, 0);
private int _absoluteIndex;
private int _lineIndex;
private int _characterIndex;
public SourceLocation(int absoluteIndex, int lineIndex, int characterIndex)
{
_absoluteIndex = absoluteIndex;
_lineIndex = lineIndex;
_characterIndex = characterIndex;
}
public int AbsoluteIndex
{
get { return _absoluteIndex; }
}
public int LineIndex
{
get { return _lineIndex; }
}
public int CharacterIndex
{
get { return _characterIndex; }
}
public override string ToString()
{
return String.Format(CultureInfo.CurrentCulture, "({0}:{1},{2})", AbsoluteIndex, LineIndex, CharacterIndex);
}
public override bool Equals(object obj)
{
return (obj is SourceLocation) && Equals((SourceLocation)obj);
}
public override int GetHashCode()
{
// LineIndex and CharacterIndex can be calculated from AbsoluteIndex and the document content.
return AbsoluteIndex;
}
public bool Equals(SourceLocation other)
{
if (other == null)
{
throw new ArgumentNullException("other");
}
return AbsoluteIndex == other.AbsoluteIndex &&
LineIndex == other.LineIndex &&
CharacterIndex == other.CharacterIndex;
}
public int CompareTo(SourceLocation other)
{
return AbsoluteIndex.CompareTo(other.AbsoluteIndex);
}
public static SourceLocation Advance(SourceLocation left, string text)
{
SourceLocationTracker tracker = new SourceLocationTracker(left);
tracker.UpdateLocation(text);
return tracker.CurrentLocation;
}
public static SourceLocation Add(SourceLocation left, SourceLocation right)
{
if (right.LineIndex > 0)
{
// Column index doesn't matter
return new SourceLocation(left.AbsoluteIndex + right.AbsoluteIndex, left.LineIndex + right.LineIndex, right.CharacterIndex);
}
else
{
return new SourceLocation(left.AbsoluteIndex + right.AbsoluteIndex, left.LineIndex + right.LineIndex, left.CharacterIndex + right.CharacterIndex);
}
}
public static SourceLocation Subtract(SourceLocation left, SourceLocation right)
{
return new SourceLocation(left.AbsoluteIndex - right.AbsoluteIndex,
left.LineIndex - right.LineIndex,
left.LineIndex != right.LineIndex ? left.CharacterIndex : left.CharacterIndex - right.CharacterIndex);
}
private static SourceLocation CreateUndefined()
{
SourceLocation sl = new SourceLocation();
sl._absoluteIndex = -1;
sl._lineIndex = -1;
sl._characterIndex = -1;
return sl;
}
public static bool operator <(SourceLocation left, SourceLocation right)
{
return left.CompareTo(right) < 0;
}
public static bool operator >(SourceLocation left, SourceLocation right)
{
return left.CompareTo(right) > 0;
}
public static bool operator ==(SourceLocation left, SourceLocation right)
{
return left.Equals(right);
}
public static bool operator !=(SourceLocation left, SourceLocation right)
{
return !left.Equals(right);
}
public static SourceLocation operator +(SourceLocation left, SourceLocation right)
{
return Add(left, right);
}
public static SourceLocation operator -(SourceLocation left, SourceLocation right)
{
return Subtract(left, right);
}
}
}

View File

@ -0,0 +1,87 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Web.Razor.Parser;
namespace System.Web.Razor.Text
{
public class SourceLocationTracker
{
private int _absoluteIndex = 0;
private int _characterIndex = 0;
private int _lineIndex = 0;
private SourceLocation _currentLocation;
public SourceLocationTracker()
: this(SourceLocation.Zero)
{
}
public SourceLocationTracker(SourceLocation currentLocation)
{
CurrentLocation = currentLocation;
UpdateInternalState();
}
public SourceLocation CurrentLocation
{
get { return _currentLocation; }
set
{
if (_currentLocation != value)
{
_currentLocation = value;
UpdateInternalState();
}
}
}
public void UpdateLocation(char characterRead, char nextCharacter)
{
_absoluteIndex++;
if (ParserHelpers.IsNewLine(characterRead) && (characterRead != '\r' || nextCharacter != '\n'))
{
_lineIndex++;
_characterIndex = 0;
}
else
{
_characterIndex++;
}
UpdateLocation();
}
public SourceLocationTracker UpdateLocation(string content)
{
for (int i = 0; i < content.Length; i++)
{
char nextCharacter = '\0';
if (i < content.Length - 1)
{
nextCharacter = content[i + 1];
}
UpdateLocation(content[i], nextCharacter);
}
return this;
}
private void UpdateInternalState()
{
_absoluteIndex = CurrentLocation.AbsoluteIndex;
_characterIndex = CurrentLocation.CharacterIndex;
_lineIndex = CurrentLocation.LineIndex;
}
private void UpdateLocation()
{
CurrentLocation = new SourceLocation(_absoluteIndex, _lineIndex, _characterIndex);
}
public static SourceLocation CalculateNewLocation(SourceLocation lastPosition, string newContent)
{
return new SourceLocationTracker(lastPosition).UpdateLocation(newContent).CurrentLocation;
}
}
}

View File

@ -0,0 +1,105 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Web.Razor.Resources;
using System.Web.Razor.Utils;
namespace System.Web.Razor.Text
{
public class TextBufferReader : LookaheadTextReader
{
private Stack<BacktrackContext> _bookmarks = new Stack<BacktrackContext>();
private SourceLocationTracker _tracker = new SourceLocationTracker();
public TextBufferReader(ITextBuffer buffer)
{
if (buffer == null)
{
throw new ArgumentNullException("buffer");
}
InnerBuffer = buffer;
}
internal ITextBuffer InnerBuffer { get; private set; }
public override SourceLocation CurrentLocation
{
get { return _tracker.CurrentLocation; }
}
public override int Peek()
{
return InnerBuffer.Peek();
}
public override int Read()
{
int read = InnerBuffer.Read();
if (read != -1)
{
char nextChar = '\0';
int next = Peek();
if (next != -1)
{
nextChar = (char)next;
}
_tracker.UpdateLocation((char)read, nextChar);
}
return read;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
IDisposable disposable = InnerBuffer as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
base.Dispose(disposing);
}
public override IDisposable BeginLookahead()
{
BacktrackContext context = new BacktrackContext() { Location = CurrentLocation };
_bookmarks.Push(context);
return new DisposableAction(() =>
{
EndLookahead(context);
});
}
public override void CancelBacktrack()
{
if (_bookmarks.Count == 0)
{
throw new InvalidOperationException(RazorResources.CancelBacktrack_Must_Be_Called_Within_Lookahead);
}
_bookmarks.Pop();
}
private void EndLookahead(BacktrackContext context)
{
if (_bookmarks.Count > 0 && ReferenceEquals(_bookmarks.Peek(), context))
{
// Backtrack wasn't cancelled, so pop it
_bookmarks.Pop();
// Set the new current location
_tracker.CurrentLocation = context.Location;
InnerBuffer.Position = context.Location.AbsoluteIndex;
}
}
/// <summary>
/// Need a class for reference equality to support cancelling backtrack.
/// </summary>
private class BacktrackContext
{
public SourceLocation Location { get; set; }
}
}
}

View File

@ -0,0 +1,210 @@
// 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);
}
}
}

View File

@ -0,0 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Razor.Text
{
public enum TextChangeType
{
Insert,
Remove
}
}

View File

@ -0,0 +1,42 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.IO;
namespace System.Web.Razor.Text
{
public class TextDocumentReader : TextReader, ITextDocument
{
public TextDocumentReader(ITextDocument source)
{
Document = source;
}
internal ITextDocument Document { get; private set; }
public SourceLocation Location
{
get { return Document.Location; }
}
public int Length
{
get { return Document.Length; }
}
public int Position
{
get { return Document.Position; }
set { Document.Position = value; }
}
public override int Read()
{
return Document.Read();
}
public override int Peek()
{
return Document.Peek();
}
}
}

View File

@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics.CodeAnalysis;
using System.Text;
namespace System.Web.Razor.Text
{
internal static class TextExtensions
{
public static void Seek(this ITextBuffer self, int characters)
{
self.Position += characters;
}
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "The consumer is expected to dispose this object")]
public static ITextDocument ToDocument(this ITextBuffer self)
{
ITextDocument ret = self as ITextDocument;
if (ret == null)
{
ret = new SeekableTextReader(self);
}
return ret;
}
public static LookaheadToken BeginLookahead(this ITextBuffer self)
{
int start = self.Position;
return new LookaheadToken(() =>
{
self.Position = start;
});
}
public static string ReadToEnd(this ITextBuffer self)
{
StringBuilder builder = new StringBuilder();
int read;
while ((read = self.Read()) != -1)
{
builder.Append((char)read);
}
return builder.ToString();
}
}
}