Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

162 lines
4.7 KiB
C#

// 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;
}
}
}
}