Skip to content

Commit 4e901e1

Browse files
authored
Mustachio: Trim whitespace preceding a tag (#2547)
1 parent 76206db commit 4e901e1

File tree

2 files changed

+52
-1
lines changed

2 files changed

+52
-1
lines changed

lib/src/mustachio/parser.dart

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ class MustachioParser {
4848
List<MustachioNode> _parseBlock({String /*?*/ sectionKey}) {
4949
var children = <MustachioNode>[];
5050
var textStartIndex = _index;
51+
var textEndIndex = _index;
5152

5253
void addTextNode(int startIndex, int endIndex) {
5354
if (endIndex > startIndex) {
@@ -56,13 +57,40 @@ class MustachioParser {
5657
}
5758
}
5859

60+
/// Trims [textEndIndex] if it marks the end of a blank line.
61+
///
62+
/// [textEndIndex] is reset back to the newline immediately preceding any
63+
/// whitespace preceding [textEndIndex].
64+
void trimTextRight() {
65+
var newEndIndex = textEndIndex;
66+
while (true) {
67+
if (newEndIndex == textStartIndex) {
68+
// We walked all the way to [textStartIndex] without finding a
69+
// newline; for example in `{{a}} {{b}}` we don't want to trim the
70+
// singular space.
71+
return;
72+
}
73+
var ch = template.codeUnitAt(newEndIndex - 1);
74+
if (ch == $space || ch == $tab) {
75+
newEndIndex--;
76+
} else if (ch == $cr || ch == $lf) {
77+
textEndIndex = newEndIndex - 1;
78+
return;
79+
} else {
80+
// We walked back to some other character; [textEndIndex] does not
81+
// mark the end of a blank line.
82+
return;
83+
}
84+
}
85+
}
86+
5987
while (true) {
6088
if (_nextAtEnd) {
6189
addTextNode(textStartIndex, _templateLength);
6290
break;
6391
}
6492
if (_thisChar == $lbrace && _nextChar == $lbrace) {
65-
var textEndIndex = _index;
93+
textEndIndex = _index;
6694
_index += 2;
6795
var result = _parseTag();
6896
if (result == _TagParseResult.endOfFile) {
@@ -77,6 +105,7 @@ class MustachioParser {
77105
continue;
78106
} else if (result.type == _TagParseResultType.parsedEndTag) {
79107
if (sectionKey != null && sectionKey == result.endTagKey) {
108+
trimTextRight();
80109
addTextNode(textStartIndex, textEndIndex);
81110
break;
82111
} else {
@@ -85,6 +114,8 @@ class MustachioParser {
85114
continue;
86115
}
87116
} else {
117+
assert(result.type == _TagParseResultType.parsedTag);
118+
trimTextRight();
88119
addTextNode(textStartIndex, textEndIndex);
89120
children.add(result.node);
90121
textStartIndex = _index;

test/mustachio/renderer_test.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,26 @@ void main() {
406406
expect(renderBar(bar, barTemplate), equals('Text Partial hello'));
407407
});
408408

409+
test('Parser removes whitespace preceding a tag on its own line', () async {
410+
var fooTemplateFile = getFile('/project/foo.mustache')
411+
..writeAsStringSync('''
412+
<ol>
413+
{{#l1}}
414+
<li>Num {{.}}</li>
415+
{{/l1}}
416+
</ol>
417+
''');
418+
var fooTemplate = await Template.parse(fooTemplateFile);
419+
var foo = Foo()..l1 = [1, 2, 3];
420+
expect(renderFoo(foo, fooTemplate), equals('''
421+
<ol>
422+
<li>Num 1</li>
423+
<li>Num 2</li>
424+
<li>Num 3</li>
425+
</ol>
426+
'''));
427+
});
428+
409429
test('Renderer throws when it cannot resolve a variable key', () async {
410430
var fooTemplateFile = getFile('/project/foo.mustache')
411431
..writeAsStringSync('Text {{s2}}');

0 commit comments

Comments
 (0)