4
4
5
5
import 'package:charcode/charcode.dart' ;
6
6
import 'package:meta/meta.dart' ;
7
+ import 'package:source_span/source_span.dart' ;
7
8
8
9
/// A [Mustache] (https://mustache.github.io/mustache.5.html) parser for use by a
9
10
/// generated Mustachio renderer.
@@ -14,10 +15,14 @@ class MustachioParser {
14
15
/// The length of the template, in code units.
15
16
final int _templateLength;
16
17
18
+ final SourceFile _sourceFile;
19
+
17
20
/// The index of the character currently being parsed.
18
21
int _index = 0 ;
19
22
20
- MustachioParser (this .template) : _templateLength = template.length;
23
+ MustachioParser (this .template, Uri url)
24
+ : _templateLength = template.length,
25
+ _sourceFile = SourceFile .fromString (template, url: url);
21
26
22
27
/// Parses [template] into a sequence of [MustachioNode] s.
23
28
///
@@ -46,7 +51,8 @@ class MustachioParser {
46
51
47
52
void addTextNode (int startIndex, int endIndex) {
48
53
if (endIndex > startIndex) {
49
- children.add (Text (template.substring (startIndex, endIndex)));
54
+ children.add (Text (template.substring (startIndex, endIndex),
55
+ span: _sourceFile.span (startIndex, endIndex)));
50
56
}
51
57
}
52
58
@@ -96,28 +102,29 @@ class MustachioParser {
96
102
/// [_index] should be at the character immediately following the open
97
103
/// delimiter `{{` .
98
104
_TagParseResult _parseTag () {
105
+ var tagStartIndex = _index - 2 ;
99
106
_walkPastWhitespace ();
100
107
if (_atEnd) {
101
108
return _TagParseResult .endOfFile;
102
109
}
103
110
var char = _thisChar;
104
111
if (char == $hash) {
105
112
_index++ ;
106
- return _parseSection (invert: false );
113
+ return _parseSection (invert: false , tagStartIndex : tagStartIndex );
107
114
} else if (char == $caret) {
108
115
_index++ ;
109
- return _parseSection (invert: true );
116
+ return _parseSection (invert: true , tagStartIndex : tagStartIndex );
110
117
} else if (char == $slash) {
111
118
_index++ ;
112
119
return _parseEndSection ();
113
120
} else if (char == $gt) {
114
121
_index++ ;
115
- return _parsePartial ();
122
+ return _parsePartial (tagStartIndex : tagStartIndex );
116
123
} else if (char == $exclamation) {
117
124
_index++ ;
118
125
return _parseComment ();
119
126
} else {
120
- return _parseVariable ();
127
+ return _parseVariable (tagStartIndex : tagStartIndex );
121
128
}
122
129
}
123
130
@@ -144,7 +151,7 @@ class MustachioParser {
144
151
///
145
152
/// [_index] should be at the character immediately following the `>`
146
153
/// character which opens a possible partial tag.
147
- _TagParseResult _parsePartial () {
154
+ _TagParseResult _parsePartial ({ @required int tagStartIndex} ) {
148
155
var startIndex = _index;
149
156
int endIndex;
150
157
while (true ) {
@@ -170,14 +177,16 @@ class MustachioParser {
170
177
_index += 2 ;
171
178
172
179
var key = template.substring (startIndex, endIndex);
173
- return _TagParseResult .ok (Partial (key));
180
+ return _TagParseResult .ok (
181
+ Partial (key, span: _sourceFile.span (tagStartIndex, _index)));
174
182
}
175
183
176
184
/// Tries to parse a section tag at [_index] .
177
185
///
178
186
/// [_index] should be at the character immediately following the `#`
179
187
/// character which opens a possible section tag.
180
- _TagParseResult _parseSection ({@required bool invert}) {
188
+ _TagParseResult _parseSection (
189
+ {@required bool invert, @required int tagStartIndex}) {
181
190
var parsedKey = _parseKey ();
182
191
if (parsedKey.type == _KeyParseResultType .notKey) {
183
192
return _TagParseResult .notTag;
@@ -186,6 +195,7 @@ class MustachioParser {
186
195
}
187
196
188
197
var children = _parseBlock (sectionKey: parsedKey.joinedNames);
198
+ var span = _sourceFile.span (tagStartIndex, _index);
189
199
190
200
if (parsedKey.names.length > 1 ) {
191
201
// Desugar section with dots into nested sections.
@@ -198,15 +208,20 @@ class MustachioParser {
198
208
// inside the section, are the children of the [three] section. The
199
209
// [three] section is the singular child node of the [two] section, and
200
210
// the [two] section is the singular child of the [one] section.
201
- var section = Section ([parsedKey.names.last], children, invert: invert);
211
+ var section =
212
+ Section ([parsedKey.names.last], children, invert: invert, span: span);
202
213
for (var sectionKey in parsedKey.names.reversed.skip (1 )) {
203
- section = Section ([sectionKey], [section], invert: false );
214
+ section = Section ([sectionKey], [section],
215
+ invert: false ,
216
+ // TODO(srawlins): It may not make sense to use [span] here; we
217
+ // might want to do the work to find the span of [sectionKey].
218
+ span: span);
204
219
}
205
220
return _TagParseResult .ok (section);
206
221
}
207
222
208
223
return _TagParseResult .ok (
209
- Section (parsedKey.names, children, invert: invert));
224
+ Section (parsedKey.names, children, invert: invert, span : span ));
210
225
}
211
226
212
227
/// Tries to parse an end tag at [_index] .
@@ -228,7 +243,7 @@ class MustachioParser {
228
243
///
229
244
/// [_index] should be at the character immediately following the `{{`
230
245
/// characters which open a possible variable tag.
231
- _TagParseResult _parseVariable () {
246
+ _TagParseResult _parseVariable ({ @required int tagStartIndex} ) {
232
247
var escape = true ;
233
248
if (_thisChar == $lbrace) {
234
249
escape = false ;
@@ -242,7 +257,8 @@ class MustachioParser {
242
257
return _TagParseResult .endOfFile;
243
258
}
244
259
245
- return _TagParseResult .ok (Variable (parsedKey.names, escape: escape));
260
+ return _TagParseResult .ok (Variable (parsedKey.names,
261
+ escape: escape, span: _sourceFile.span (tagStartIndex, _index)));
246
262
}
247
263
248
264
/// Tries to parse a key at [_index] .
@@ -347,14 +363,19 @@ class MustachioParser {
347
363
348
364
/// An interface for various types of node in a Mustache template.
349
365
@sealed
350
- abstract class MustachioNode {}
366
+ abstract class MustachioNode {
367
+ SourceSpan get span;
368
+ }
351
369
352
370
/// A Text node, representing literal text.
353
371
@immutable
354
372
class Text implements MustachioNode {
355
373
final String content;
356
374
357
- Text (this .content);
375
+ @override
376
+ final SourceSpan span;
377
+
378
+ Text (this .content, {@required this .span});
358
379
359
380
@override
360
381
String toString () => 'Text["$content "]' ;
@@ -367,7 +388,10 @@ class Variable implements MustachioNode {
367
388
368
389
final bool escape;
369
390
370
- Variable (this .key, {@required this .escape});
391
+ @override
392
+ final SourceSpan span;
393
+
394
+ Variable (this .key, {@required this .escape, @required this .span});
371
395
372
396
@override
373
397
String toString () => 'Variable[$key , escape=$escape ]' ;
@@ -383,7 +407,11 @@ class Section implements MustachioNode {
383
407
384
408
final List <MustachioNode > children;
385
409
386
- Section (this .key, this .children, {@required this .invert});
410
+ @override
411
+ final SourceSpan span;
412
+
413
+ Section (this .key, this .children,
414
+ {@required this .invert, @required this .span});
387
415
388
416
@override
389
417
String toString () => 'Section[$key , invert=$invert ]' ;
@@ -394,7 +422,10 @@ class Section implements MustachioNode {
394
422
class Partial implements MustachioNode {
395
423
final String key;
396
424
397
- Partial (this .key);
425
+ @override
426
+ final SourceSpan span;
427
+
428
+ Partial (this .key, {@required this .span});
398
429
}
399
430
400
431
/// An enumeration of types of tag parse results.
0 commit comments