diff --git a/lib/src/mustachio/parser.dart b/lib/src/mustachio/parser.dart index f31b2d0414..296f9861e1 100644 --- a/lib/src/mustachio/parser.dart +++ b/lib/src/mustachio/parser.dart @@ -255,7 +255,6 @@ class MustachioParser { keySpanEndOffset - lastName.length, keySpanEndOffset); var section = Section([lastName], children, invert: invert, span: span, keySpan: lastNameSpan); - //for (var sectionKey in parsedKey.names.reversed.skip(1)) { for (var i = parsedKey.names.length - 2; i >= 0; i--) { var sectionKey = parsedKey.names[i]; // To find the start offset of the ith name, take the length of all of diff --git a/lib/src/mustachio/renderer_base.dart b/lib/src/mustachio/renderer_base.dart index 13af3517e6..b187f6b8de 100644 --- a/lib/src/mustachio/renderer_base.dart +++ b/lib/src/mustachio/renderer_base.dart @@ -145,7 +145,9 @@ abstract class RendererBase { Template _template; /// The output buffer into which [context] is rendered, using a template. - final buffer = StringBuffer(); + // TODO(srawlins): Pass around a single [StringBuffer], and make this field + // `final`. + StringBuffer buffer = StringBuffer(); final Set _invisibleGetters; @@ -234,7 +236,7 @@ abstract class RendererBase { "Failed to resolve '$key' as a property on any types in the " 'current context')); } else { - return parent.section(node); + return parent.withBuffer(buffer, () => parent.section(node)); } } @@ -279,6 +281,16 @@ abstract class RendererBase { renderBlock(partialTemplate.ast); _template = outerTemplate; } + + /// Executes [fn] after replacing [buffer] with [newBuffer]. + /// + /// Replaces the previous buffer as [buffer]. + void withBuffer(StringBuffer newBuffer, void Function() fn) { + var previousBuffer = buffer; + buffer = newBuffer; + fn(); + buffer = previousBuffer; + } } String renderSimple(Object context, List ast, Template template, diff --git a/test/mustachio/aot_compiler_render_test.dart b/test/mustachio/aot_compiler_render_test.dart index dc771950ea..9cb9060424 100644 --- a/test/mustachio/aot_compiler_render_test.dart +++ b/test/mustachio/aot_compiler_render_test.dart @@ -259,6 +259,15 @@ void main() { expect(output, equals('Text Foo: hello')); }); + test('Renderer renders a value section node keyed lower in the stack', + () async { + var output = await renderBar({ + 'foo|lib/templates/html/bar.html': + 'Text {{#foo}}One {{#s2}}Two{{/s2}}{{/foo}}', + }, '_i1.Bar()..foo = _i1.Foo()..s2 = "hello"'); + expect(output, equals('Text One Two')); + }); + test('Renderer renders a null value section node as blank', () async { var output = await renderFoo({ 'foo|lib/templates/html/foo.html': diff --git a/test/mustachio/runtime_renderer_render_test.dart b/test/mustachio/runtime_renderer_render_test.dart index 0c93c7a74f..2829e71083 100644 --- a/test/mustachio/runtime_renderer_render_test.dart +++ b/test/mustachio/runtime_renderer_render_test.dart @@ -202,6 +202,17 @@ void main() { expect(renderBar(bar, barTemplate), equals('Text Foo: hello')); }); + test('Renderer renders a value section node keyed lower in the stack', + () async { + var barTemplateFile = getFile('/project/bar.mustache') + ..writeAsStringSync('Text {{#foo}}One {{#s2}}Two{{/s2}}{{/foo}}'); + var barTemplate = await Template.parse(barTemplateFile); + var bar = Bar() + ..foo = Foo() + ..s2 = 'hello'; + expect(renderBar(bar, barTemplate), equals('Text One Two')); + }); + test('Renderer renders a null value section node as blank', () async { var fooTemplateFile = getFile('/project/foo.mustache') ..writeAsStringSync('Text {{#s1}}"{{.}}" ({{length}}){{/s1}}');