1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
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;
}
}
}
}
|