3
3
4
4
using System ;
5
5
using System . IO ;
6
+ using System . Text ;
7
+ using Microsoft . AspNetCore . Razor . Language . Syntax ;
6
8
7
9
namespace Microsoft . AspNetCore . Razor . Language . Legacy
8
10
{
9
11
internal class SeekableTextReader : TextReader , ITextDocument
10
12
{
11
- private readonly LineTrackingStringBuffer _buffer ;
13
+ private readonly RazorSourceDocument _sourceDocument ;
12
14
private int _position = 0 ;
13
15
private int _current ;
14
16
private SourceLocation _location ;
17
+ private ( TextSpan Span , int LineIndex ) _cachedLineInfo ;
15
18
16
- public SeekableTextReader ( string source , string filePath ) : this ( source . ToCharArray ( ) , filePath ) { }
19
+ public SeekableTextReader ( string source , string filePath ) : this ( new StringSourceDocument ( source , Encoding . UTF8 , new RazorSourceDocumentProperties ( filePath , relativePath : null ) ) ) { }
17
20
18
- public SeekableTextReader ( char [ ] source , string filePath )
21
+ public SeekableTextReader ( RazorSourceDocument source )
19
22
{
20
23
if ( source == null )
21
24
{
22
25
throw new ArgumentNullException ( nameof ( source ) ) ;
23
26
}
24
27
25
- _buffer = new LineTrackingStringBuffer ( source , filePath ) ;
28
+ _sourceDocument = source ;
29
+ _cachedLineInfo = ( new TextSpan ( 0 , _sourceDocument . Lines . GetLineLength ( 0 ) ) , 0 ) ;
26
30
UpdateState ( ) ;
27
31
}
28
32
29
33
public SourceLocation Location => _location ;
30
34
31
- public int Length => _buffer . Length ;
35
+ public int Length => _sourceDocument . Length ;
32
36
33
37
public int Position
34
38
{
@@ -55,22 +59,73 @@ public override int Read()
55
59
56
60
private void UpdateState ( )
57
61
{
58
- if ( _position < _buffer . Length )
62
+ if ( _cachedLineInfo . Span . Contains ( _position ) )
59
63
{
60
- var chr = _buffer . CharAt ( _position ) ;
61
- _current = chr . Character ;
62
- _location = chr . Location ;
64
+ _location = new SourceLocation ( _sourceDocument . FilePath , _position , _cachedLineInfo . LineIndex , _position - _cachedLineInfo . Span . Start ) ;
65
+ _current = _sourceDocument [ _location . AbsoluteIndex ] ;
66
+
67
+ return ;
63
68
}
64
- else if ( _buffer . Length == 0 )
69
+
70
+ if ( _position < _sourceDocument . Length )
65
71
{
66
- _current = - 1 ;
67
- _location = SourceLocation . Zero ;
72
+ if ( _position >= _cachedLineInfo . Span . End )
73
+ {
74
+ // Try to avoid the GetLocation call by checking if the next line contains the position
75
+ int nextLineIndex = _cachedLineInfo . LineIndex + 1 ;
76
+ int nextLineLength = _sourceDocument . Lines . GetLineLength ( nextLineIndex ) ;
77
+ TextSpan nextLineSpan = new TextSpan ( _cachedLineInfo . Span . End , nextLineLength ) ;
78
+
79
+ if ( nextLineSpan . Contains ( _position ) )
80
+ {
81
+ _cachedLineInfo = ( nextLineSpan , nextLineIndex ) ;
82
+ _location = new SourceLocation ( _sourceDocument . FilePath , _position , nextLineIndex , _position - nextLineSpan . Start ) ;
83
+ _current = _sourceDocument [ _location . AbsoluteIndex ] ;
84
+
85
+ return ;
86
+ }
87
+ }
88
+ else
89
+ {
90
+ // Try to avoid the GetLocation call by checking if the previous line contains the position
91
+ int prevLineIndex = _cachedLineInfo . LineIndex - 1 ;
92
+ int prevLineLength = _sourceDocument . Lines . GetLineLength ( prevLineIndex ) ;
93
+ TextSpan prevLineSpan = new TextSpan ( _cachedLineInfo . Span . Start - prevLineLength , prevLineLength ) ;
94
+
95
+ if ( prevLineSpan . Contains ( _position ) )
96
+ {
97
+ _cachedLineInfo = ( prevLineSpan , prevLineIndex ) ;
98
+ _location = new SourceLocation ( _sourceDocument . FilePath , _position , prevLineIndex , _position - prevLineSpan . Start ) ;
99
+ _current = _sourceDocument [ _location . AbsoluteIndex ] ;
100
+
101
+ return ;
102
+ }
103
+ }
104
+
105
+ // The call to GetLocation is expensive
106
+ _location = _sourceDocument . Lines . GetLocation ( _position ) ;
107
+
108
+ int lineLength = _sourceDocument . Lines . GetLineLength ( _location . LineIndex ) ;
109
+ TextSpan lineSpan = new TextSpan ( _position - _location . CharacterIndex , lineLength ) ;
110
+ _cachedLineInfo = ( lineSpan , _location . LineIndex ) ;
111
+
112
+ _current = _sourceDocument [ _location . AbsoluteIndex ] ;
113
+
114
+ return ;
68
115
}
69
- else
116
+
117
+ if ( _sourceDocument . Length == 0 )
70
118
{
119
+ _location = SourceLocation . Zero ;
71
120
_current = - 1 ;
72
- _location = _buffer . EndLocation ;
121
+
122
+ return ;
73
123
}
124
+
125
+ var lineNumber = _sourceDocument . Lines . Count - 1 ;
126
+ _location = new SourceLocation ( _sourceDocument . FilePath , Length , lineNumber , _sourceDocument . Lines . GetLineLength ( lineNumber ) ) ;
127
+
128
+ _current = - 1 ;
74
129
}
75
130
}
76
131
}
0 commit comments