Skip to content

Commit f8d62e0

Browse files
authored
Mustachio: avoid calling Iterable getter multiple times (#2546)
Mustachio: avoid calling Iterable getter multiple times Take this template, where `widgets` is an Iterable property: Text {{#widgets}} {{name}} {{/widgets}}. A mustache renderer should assume that the `widgets` getter may have side effects, and it should only be called one time. Currently, Mustachio will call this getter 2 or 3 times, as the behavior differs if the section is inverted, and if the Iterable is empty. This change is actually quite readable, and simplifies the code. The magic sauce is in the lazy Iterable returned by `renderIterable`. If Mustachio does not wish to render the elements, then the lazy map will not be evaluated.
1 parent 4e901e1 commit f8d62e0

File tree

6 files changed

+55
-178
lines changed

6 files changed

+55
-178
lines changed

lib/src/generator/templates.renderers.dart

Lines changed: 42 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -89,14 +89,10 @@ class _Renderer_PackageTemplateData extends RendererBase<PackageTemplateData> {
8989
(CT_ c, Property<CT_> self, List<String> remainingNames) =>
9090
self.renderSimpleVariable(
9191
c, remainingNames, 'List<Documentable>'),
92-
isEmptyIterable: (CT_ c) => c.navLinks?.isEmpty ?? true,
9392
renderIterable:
9493
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
95-
var buffer = StringBuffer();
96-
for (var e in c.navLinks) {
97-
buffer.write(renderSimple(e, ast, r.template, parent: r));
98-
}
99-
return buffer.toString();
94+
return c.navLinks
95+
.map((e) => renderSimple(e, ast, r.template, parent: r));
10096
},
10197
),
10298
'package': Property(
@@ -175,14 +171,10 @@ class _Renderer_Package extends RendererBase<Package> {
175171
renderVariable:
176172
(CT_ c, Property<CT_> self, List<String> remainingNames) =>
177173
self.renderSimpleVariable(c, remainingNames, 'Set<Library>'),
178-
isEmptyIterable: (CT_ c) => c.allLibraries?.isEmpty ?? true,
179174
renderIterable:
180175
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
181-
var buffer = StringBuffer();
182-
for (var e in c.allLibraries) {
183-
buffer.write(renderSimple(e, ast, r.template, parent: r));
184-
}
185-
return buffer.toString();
176+
return c.allLibraries
177+
.map((e) => renderSimple(e, ast, r.template, parent: r));
186178
},
187179
),
188180
'baseHref': Property(
@@ -210,14 +202,10 @@ class _Renderer_Package extends RendererBase<Package> {
210202
renderVariable: (CT_ c, Property<CT_> self,
211203
List<String> remainingNames) =>
212204
self.renderSimpleVariable(c, remainingNames, 'List<Category>'),
213-
isEmptyIterable: (CT_ c) => c.categories?.isEmpty ?? true,
214205
renderIterable:
215206
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
216-
var buffer = StringBuffer();
217-
for (var e in c.categories) {
218-
buffer.write(renderSimple(e, ast, r.template, parent: r));
219-
}
220-
return buffer.toString();
207+
return c.categories
208+
.map((e) => renderSimple(e, ast, r.template, parent: r));
221209
},
222210
),
223211
'categoriesWithPublicLibraries': Property(
@@ -226,16 +214,10 @@ class _Renderer_Package extends RendererBase<Package> {
226214
(CT_ c, Property<CT_> self, List<String> remainingNames) =>
227215
self.renderSimpleVariable(
228216
c, remainingNames, 'Iterable<LibraryContainer>'),
229-
isEmptyIterable: (CT_ c) =>
230-
c.categoriesWithPublicLibraries?.isEmpty ?? true,
231217
renderIterable:
232218
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
233-
var buffer = StringBuffer();
234-
for (var e in c.categoriesWithPublicLibraries) {
235-
buffer.write(
236-
_render_LibraryContainer(e, ast, r.template, parent: r));
237-
}
238-
return buffer.toString();
219+
return c.categoriesWithPublicLibraries.map(
220+
(e) => _render_LibraryContainer(e, ast, r.template, parent: r));
239221
},
240222
),
241223
'config': Property(
@@ -254,14 +236,10 @@ class _Renderer_Package extends RendererBase<Package> {
254236
renderVariable:
255237
(CT_ c, Property<CT_> self, List<String> remainingNames) =>
256238
self.renderSimpleVariable(c, remainingNames, 'List<String>'),
257-
isEmptyIterable: (CT_ c) => c.containerOrder?.isEmpty ?? true,
258239
renderIterable:
259240
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
260-
var buffer = StringBuffer();
261-
for (var e in c.containerOrder) {
262-
buffer.write(renderSimple(e, ast, r.template, parent: r));
263-
}
264-
return buffer.toString();
241+
return c.containerOrder
242+
.map((e) => renderSimple(e, ast, r.template, parent: r));
265243
},
266244
),
267245
'defaultCategory': Property(
@@ -318,14 +296,10 @@ class _Renderer_Package extends RendererBase<Package> {
318296
renderVariable: (CT_ c, Property<CT_> self,
319297
List<String> remainingNames) =>
320298
self.renderSimpleVariable(c, remainingNames, 'List<Locatable>'),
321-
isEmptyIterable: (CT_ c) => c.documentationFrom?.isEmpty ?? true,
322299
renderIterable:
323300
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
324-
var buffer = StringBuffer();
325-
for (var e in c.documentationFrom) {
326-
buffer.write(_render_Locatable(e, ast, r.template, parent: r));
327-
}
328-
return buffer.toString();
301+
return c.documentationFrom
302+
.map((e) => _render_Locatable(e, ast, r.template, parent: r));
329303
},
330304
),
331305
'documentedCategories': Property(
@@ -334,14 +308,10 @@ class _Renderer_Package extends RendererBase<Package> {
334308
(CT_ c, Property<CT_> self, List<String> remainingNames) =>
335309
self.renderSimpleVariable(
336310
c, remainingNames, 'Iterable<Category>'),
337-
isEmptyIterable: (CT_ c) => c.documentedCategories?.isEmpty ?? true,
338311
renderIterable:
339312
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
340-
var buffer = StringBuffer();
341-
for (var e in c.documentedCategories) {
342-
buffer.write(renderSimple(e, ast, r.template, parent: r));
343-
}
344-
return buffer.toString();
313+
return c.documentedCategories
314+
.map((e) => renderSimple(e, ast, r.template, parent: r));
345315
},
346316
),
347317
'documentedCategoriesSorted': Property(
@@ -350,15 +320,10 @@ class _Renderer_Package extends RendererBase<Package> {
350320
(CT_ c, Property<CT_> self, List<String> remainingNames) =>
351321
self.renderSimpleVariable(
352322
c, remainingNames, 'Iterable<Category>'),
353-
isEmptyIterable: (CT_ c) =>
354-
c.documentedCategoriesSorted?.isEmpty ?? true,
355323
renderIterable:
356324
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
357-
var buffer = StringBuffer();
358-
for (var e in c.documentedCategoriesSorted) {
359-
buffer.write(renderSimple(e, ast, r.template, parent: r));
360-
}
361-
return buffer.toString();
325+
return c.documentedCategoriesSorted
326+
.map((e) => renderSimple(e, ast, r.template, parent: r));
362327
},
363328
),
364329
'documentedWhere': Property(
@@ -567,14 +532,10 @@ class _Renderer_Package extends RendererBase<Package> {
567532
renderVariable:
568533
(CT_ c, Property<CT_> self, List<String> remainingNames) =>
569534
self.renderSimpleVariable(c, remainingNames, 'Set<String>'),
570-
isEmptyIterable: (CT_ c) => c.locationPieces?.isEmpty ?? true,
571535
renderIterable:
572536
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
573-
var buffer = StringBuffer();
574-
for (var e in c.locationPieces) {
575-
buffer.write(renderSimple(e, ast, r.template, parent: r));
576-
}
577-
return buffer.toString();
537+
return c.locationPieces
538+
.map((e) => renderSimple(e, ast, r.template, parent: r));
578539
},
579540
),
580541
'name': Property(
@@ -658,14 +619,10 @@ class _Renderer_Package extends RendererBase<Package> {
658619
renderVariable: (CT_ c, Property<CT_> self,
659620
List<String> remainingNames) =>
660621
self.renderSimpleVariable(c, remainingNames, 'Iterable<Library>'),
661-
isEmptyIterable: (CT_ c) => c.publicLibraries?.isEmpty ?? true,
662622
renderIterable:
663623
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
664-
var buffer = StringBuffer();
665-
for (var e in c.publicLibraries) {
666-
buffer.write(renderSimple(e, ast, r.template, parent: r));
667-
}
668-
return buffer.toString();
624+
return c.publicLibraries
625+
.map((e) => renderSimple(e, ast, r.template, parent: r));
669626
},
670627
),
671628
'toolInvocationIndex': Property(
@@ -733,14 +690,10 @@ class _Renderer_Locatable extends RendererBase<Locatable> {
733690
renderVariable: (CT_ c, Property<CT_> self,
734691
List<String> remainingNames) =>
735692
self.renderSimpleVariable(c, remainingNames, 'List<Locatable>'),
736-
isEmptyIterable: (CT_ c) => c.documentationFrom?.isEmpty ?? true,
737693
renderIterable:
738694
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
739-
var buffer = StringBuffer();
740-
for (var e in c.documentationFrom) {
741-
buffer.write(_render_Locatable(e, ast, r.template, parent: r));
742-
}
743-
return buffer.toString();
695+
return c.documentationFrom
696+
.map((e) => _render_Locatable(e, ast, r.template, parent: r));
744697
},
745698
),
746699
'documentationIsLocal': Property(
@@ -815,14 +768,10 @@ class _Renderer_LibraryContainer extends RendererBase<LibraryContainer> {
815768
renderVariable:
816769
(CT_ c, Property<CT_> self, List<String> remainingNames) =>
817770
self.renderSimpleVariable(c, remainingNames, 'List<String>'),
818-
isEmptyIterable: (CT_ c) => c.containerOrder?.isEmpty ?? true,
819771
renderIterable:
820772
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
821-
var buffer = StringBuffer();
822-
for (var e in c.containerOrder) {
823-
buffer.write(renderSimple(e, ast, r.template, parent: r));
824-
}
825-
return buffer.toString();
773+
return c.containerOrder
774+
.map((e) => renderSimple(e, ast, r.template, parent: r));
826775
},
827776
),
828777
'enclosingName': Property(
@@ -854,14 +803,10 @@ class _Renderer_LibraryContainer extends RendererBase<LibraryContainer> {
854803
renderVariable:
855804
(CT_ c, Property<CT_> self, List<String> remainingNames) =>
856805
self.renderSimpleVariable(c, remainingNames, 'List<Library>'),
857-
isEmptyIterable: (CT_ c) => c.libraries?.isEmpty ?? true,
858806
renderIterable:
859807
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
860-
var buffer = StringBuffer();
861-
for (var e in c.libraries) {
862-
buffer.write(renderSimple(e, ast, r.template, parent: r));
863-
}
864-
return buffer.toString();
808+
return c.libraries
809+
.map((e) => renderSimple(e, ast, r.template, parent: r));
865810
},
866811
),
867812
'packageGraph': Property(
@@ -879,29 +824,21 @@ class _Renderer_LibraryContainer extends RendererBase<LibraryContainer> {
879824
renderVariable: (CT_ c, Property<CT_> self,
880825
List<String> remainingNames) =>
881826
self.renderSimpleVariable(c, remainingNames, 'Iterable<Library>'),
882-
isEmptyIterable: (CT_ c) => c.publicLibraries?.isEmpty ?? true,
883827
renderIterable:
884828
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
885-
var buffer = StringBuffer();
886-
for (var e in c.publicLibraries) {
887-
buffer.write(renderSimple(e, ast, r.template, parent: r));
888-
}
889-
return buffer.toString();
829+
return c.publicLibraries
830+
.map((e) => renderSimple(e, ast, r.template, parent: r));
890831
},
891832
),
892833
'publicLibrariesSorted': Property(
893834
getValue: (CT_ c) => c.publicLibrariesSorted,
894835
renderVariable: (CT_ c, Property<CT_> self,
895836
List<String> remainingNames) =>
896837
self.renderSimpleVariable(c, remainingNames, 'Iterable<Library>'),
897-
isEmptyIterable: (CT_ c) => c.publicLibrariesSorted?.isEmpty ?? true,
898838
renderIterable:
899839
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
900-
var buffer = StringBuffer();
901-
for (var e in c.publicLibrariesSorted) {
902-
buffer.write(renderSimple(e, ast, r.template, parent: r));
903-
}
904-
return buffer.toString();
840+
return c.publicLibrariesSorted
841+
.map((e) => renderSimple(e, ast, r.template, parent: r));
905842
},
906843
),
907844
'sortKey': Property(
@@ -1044,14 +981,10 @@ class _Renderer_Nameable extends RendererBase<Nameable> {
1044981
renderVariable:
1045982
(CT_ c, Property<CT_> self, List<String> remainingNames) =>
1046983
self.renderSimpleVariable(c, remainingNames, 'Set<String>'),
1047-
isEmptyIterable: (CT_ c) => c.namePieces?.isEmpty ?? true,
1048984
renderIterable:
1049985
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
1050-
var buffer = StringBuffer();
1051-
for (var e in c.namePieces) {
1052-
buffer.write(renderSimple(e, ast, r.template, parent: r));
1053-
}
1054-
return buffer.toString();
986+
return c.namePieces
987+
.map((e) => renderSimple(e, ast, r.template, parent: r));
1055988
},
1056989
),
1057990
};
@@ -1099,14 +1032,10 @@ class _Renderer_Canonicalization extends RendererBase<Canonicalization> {
10991032
(CT_ c, Property<CT_> self, List<String> remainingNames) =>
11001033
self.renderSimpleVariable(
11011034
c, remainingNames, 'List<ModelCommentReference>'),
1102-
isEmptyIterable: (CT_ c) => c.commentRefs?.isEmpty ?? true,
11031035
renderIterable:
11041036
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
1105-
var buffer = StringBuffer();
1106-
for (var e in c.commentRefs) {
1107-
buffer.write(renderSimple(e, ast, r.template, parent: r));
1108-
}
1109-
return buffer.toString();
1037+
return c.commentRefs
1038+
.map((e) => renderSimple(e, ast, r.template, parent: r));
11101039
},
11111040
),
11121041
'isCanonical': Property(
@@ -1121,14 +1050,10 @@ class _Renderer_Canonicalization extends RendererBase<Canonicalization> {
11211050
renderVariable:
11221051
(CT_ c, Property<CT_> self, List<String> remainingNames) =>
11231052
self.renderSimpleVariable(c, remainingNames, 'Set<String>'),
1124-
isEmptyIterable: (CT_ c) => c.locationPieces?.isEmpty ?? true,
11251053
renderIterable:
11261054
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
1127-
var buffer = StringBuffer();
1128-
for (var e in c.locationPieces) {
1129-
buffer.write(renderSimple(e, ast, r.template, parent: r));
1130-
}
1131-
return buffer.toString();
1055+
return c.locationPieces
1056+
.map((e) => renderSimple(e, ast, r.template, parent: r));
11321057
},
11331058
),
11341059
};
@@ -1299,14 +1224,10 @@ class _Renderer_TemplateData<T extends Documentable>
12991224
renderVariable:
13001225
(CT_ c, Property<CT_> self, List<String> remainingNames) =>
13011226
self.renderSimpleVariable(c, remainingNames, 'List<Package>'),
1302-
isEmptyIterable: (CT_ c) => c.localPackages?.isEmpty ?? true,
13031227
renderIterable:
13041228
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
1305-
var buffer = StringBuffer();
1306-
for (var e in c.localPackages) {
1307-
buffer.write(_render_Package(e, ast, r.template, parent: r));
1308-
}
1309-
return buffer.toString();
1229+
return c.localPackages
1230+
.map((e) => _render_Package(e, ast, r.template, parent: r));
13101231
},
13111232
),
13121233
'metaDescription': Property(
@@ -1325,29 +1246,21 @@ class _Renderer_TemplateData<T extends Documentable>
13251246
(CT_ c, Property<CT_> self, List<String> remainingNames) =>
13261247
self.renderSimpleVariable(
13271248
c, remainingNames, 'List<Documentable>'),
1328-
isEmptyIterable: (CT_ c) => c.navLinks?.isEmpty ?? true,
13291249
renderIterable:
13301250
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
1331-
var buffer = StringBuffer();
1332-
for (var e in c.navLinks) {
1333-
buffer.write(renderSimple(e, ast, r.template, parent: r));
1334-
}
1335-
return buffer.toString();
1251+
return c.navLinks
1252+
.map((e) => renderSimple(e, ast, r.template, parent: r));
13361253
},
13371254
),
13381255
'navLinksWithGenerics': Property(
13391256
getValue: (CT_ c) => c.navLinksWithGenerics,
13401257
renderVariable: (CT_ c, Property<CT_> self,
13411258
List<String> remainingNames) =>
13421259
self.renderSimpleVariable(c, remainingNames, 'List<Container>'),
1343-
isEmptyIterable: (CT_ c) => c.navLinksWithGenerics?.isEmpty ?? true,
13441260
renderIterable:
13451261
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
1346-
var buffer = StringBuffer();
1347-
for (var e in c.navLinksWithGenerics) {
1348-
buffer.write(renderSimple(e, ast, r.template, parent: r));
1349-
}
1350-
return buffer.toString();
1262+
return c.navLinksWithGenerics
1263+
.map((e) => renderSimple(e, ast, r.template, parent: r));
13511264
},
13521265
),
13531266
'parent': Property(

0 commit comments

Comments
 (0)