1010using System . Globalization ;
1111using System . Linq ;
1212using System . Reflection ;
13- using System . Text . Encodings . Web ;
1413using System . Text . RegularExpressions ;
1514using Fluid ;
16- using Fluid . Ast ;
1715using Fluid . Values ;
18- using Parlot . Fluent ;
1916
2017namespace NJsonSchema . CodeGeneration
2118{
2219 /// <summary>The default template factory which loads templates from embedded resources.</summary>
2320 public partial class DefaultTemplateFactory : ITemplateFactory
2421 {
22+ internal const string TemplateTagName = "template" ;
23+
24+ private static readonly LiquidParser LiquidParser = new ( new FluidParserOptions ( ) ) ;
25+
2526 private readonly CodeGeneratorSettingsBase _settings ;
2627 private readonly Assembly [ ] _assemblies ;
2728 private readonly Func < string , string , string , string > _templateContentLoader ;
@@ -44,13 +45,15 @@ public DefaultTemplateFactory(CodeGeneratorSettingsBase settings, Assembly[] ass
4445 /// <exception cref="InvalidOperationException">Could not load template.</exception>
4546 public ITemplate CreateTemplate ( string language , string template , object model )
4647 {
47- return new LiquidTemplate (
48- language ,
49- template ,
50- _templateContentLoader ,
51- model ,
52- GetToolchainVersion ( ) ,
53- _settings ) ;
48+ return new LiquidTemplate ( CreateParser ( ) , language , template , _templateContentLoader , model , GetToolchainVersion ( ) , _settings ) ;
49+ }
50+
51+ /// <summary>
52+ /// Returns an instance of <see cref="FluidParser"/> that will be used when parsing template content.
53+ /// </summary>
54+ protected virtual FluidParser CreateParser ( )
55+ {
56+ return LiquidParser ;
5457 }
5558
5659 /// <summary>Gets the current toolchain version.</summary>
@@ -115,13 +118,10 @@ private sealed partial class LiquidTemplate : ITemplate
115118 {
116119 private readonly record struct TemplateKey ( string Language , string Template , string TemplateDirectory ) ;
117120
118- internal const string TemplateTagName = "template" ;
119121 private static readonly ConcurrentDictionary < TemplateKey , IFluidTemplate > Templates = new ( ) ;
120122
121123 static LiquidTemplate ( )
122124 {
123- // thread-safe
124- _parser = new LiquidParser ( ) ;
125125 _templateOptions = new TemplateOptions
126126 {
127127 MemberAccessStrategy = new UnsafeMemberAccessStrategy ( ) ,
@@ -135,14 +135,14 @@ static LiquidTemplate()
135135 _templateOptions . Filters . AddFilter ( "literal" , LiquidFilters . Literal ) ;
136136 }
137137
138+ private readonly FluidParser _parser ;
138139 private readonly string _language ;
139140 private readonly string _template ;
140141 private readonly Func < string , string , string , string > _templateContentLoader ;
141142 private readonly object _model ;
142143 private readonly string _toolchainVersion ;
143144 private readonly CodeGeneratorSettingsBase _settings ;
144145
145- private static readonly LiquidParser _parser ;
146146 private static readonly TemplateOptions _templateOptions ;
147147
148148 private const string TabCountRegexString = @"(\s*)?\{%(-)?\s+template\s+([a-zA-Z0-9_.]+)(\s*?.*?)\s(-)?%}" ;
@@ -163,13 +163,15 @@ static LiquidTemplate()
163163#endif
164164
165165 public LiquidTemplate (
166+ FluidParser parser ,
166167 string language ,
167168 string template ,
168169 Func < string , string , string , string > templateContentLoader ,
169170 object model ,
170171 string toolchainVersion ,
171172 CodeGeneratorSettingsBase settings )
172173 {
174+ _parser = parser ;
173175 _language = language ;
174176 _template = template ;
175177 _templateContentLoader = templateContentLoader ;
@@ -323,58 +325,6 @@ public static ValueTask<FluidValue> Literal(FluidValue input, FilterArguments ar
323325 }
324326 }
325327
326- private sealed class LiquidParser : FluidParser
327- {
328- internal const string LanguageKey = "__language" ;
329- internal const string TemplateKey = "__template" ;
330- internal const string SettingsKey = "__settings" ;
331-
332- public LiquidParser ( )
333- {
334- RegisterParserTag ( LiquidTemplate . TemplateTagName , Parsers . OneOrMany ( Primary ) , RenderTemplate ) ;
335- }
336-
337- private static ValueTask < Completion > RenderTemplate (
338- IReadOnlyList < Expression > arguments ,
339- TextWriter writer ,
340- TextEncoder encoder ,
341- TemplateContext context )
342- {
343- var templateName = ( ( LiteralExpression ) arguments [ 0 ] ) . Value . ToStringValue ( ) ;
344-
345- var tabCount = - 1 ;
346- if ( arguments . Count > 1 && arguments [ 1 ] is LiteralExpression literalExpression )
347- {
348- tabCount = ( int ) literalExpression . Value . ToNumberValue ( ) ;
349- }
350-
351- var settings = ( CodeGeneratorSettingsBase ) context . AmbientValues [ SettingsKey ] ;
352- var language = ( string ) context . AmbientValues [ LanguageKey ] ;
353- templateName = ! string . IsNullOrEmpty ( templateName )
354- ? templateName
355- : ( string ) context . AmbientValues [ TemplateKey ] + "!" ;
356-
357- var template = settings . TemplateFactory . CreateTemplate ( language , templateName , context ) ;
358- var output = template . Render ( ) ;
359-
360- if ( string . IsNullOrWhiteSpace ( output ) )
361- {
362- // signal cleanup
363- writer . Write ( "__EMPTY-TEMPLATE__" ) ;
364- }
365- else if ( tabCount > 0 )
366- {
367- ConversionUtilities . Tab ( output , tabCount , writer ) ;
368- }
369- else
370- {
371- writer . Write ( output ) ;
372- }
373-
374- return new ValueTask < Completion > ( Completion . Normal ) ;
375- }
376- }
377-
378328 /// <summary>
379329 /// Version that allows all access, safe as models are handled by NJsonSchema.
380330 /// </summary>
0 commit comments