forked from sebastienros/fluid
-
Notifications
You must be signed in to change notification settings - Fork 0
/
FluidViewParser.cs
129 lines (103 loc) · 4.9 KB
/
FluidViewParser.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
using Fluid.Ast;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using static Parlot.Fluent.Parsers;
namespace Fluid.ViewEngine
{
public class FluidViewParser : FluidParser
{
public FluidViewParser() : this (new())
{
}
public FluidViewParser(FluidParserOptions parserOptions) : base(parserOptions)
{
RegisterIdentifierTag("rendersection", static async (identifier, writer, encoder, context) =>
{
if (context.AmbientValues.TryGetValue(Constants.SectionsIndex, out var sections))
{
var dictionary = sections as Dictionary<string, IReadOnlyList<Statement>>;
// dictionary can be null if no "section" tag was invoked
if (dictionary != null && dictionary.TryGetValue(identifier, out var section))
{
foreach (var statement in section)
{
await statement.WriteToAsync(writer, encoder, context);
}
}
}
return Completion.Normal;
});
RegisterEmptyTag("renderbody", static async (writer, encoder, context) =>
{
if (context.AmbientValues.TryGetValue(Constants.BodyIndex, out var body))
{
await writer.WriteAsync((string)body);
}
else
{
throw new ParseException("Could not render body, Layouts can't be evaluated directly.");
}
return Completion.Normal;
});
RegisterIdentifierBlock("section", static (identifier, statements, writer, encoder, context) =>
{
if (context.AmbientValues.TryGetValue(Constants.SectionsIndex, out var sections))
{
var dictionary = sections as Dictionary<string, IReadOnlyList<Statement>>;
if (dictionary == null)
{
// Lazily initialize the sections dictionary
dictionary = new Dictionary<string, IReadOnlyList<Statement>>();
context.AmbientValues[Constants.SectionsIndex] = dictionary;
}
dictionary[identifier] = statements;
}
return new ValueTask<Completion>(Completion.Normal);
});
RegisterExpressionTag("layout", static async (pathExpression, writer, encoder, context) =>
{
var layoutPath = (await pathExpression.EvaluateAsync(context)).ToStringValue();
// If '' is assigned, remove any Layout, for instance to override one defined in a _viewstart
if (string.IsNullOrEmpty(layoutPath))
{
context.AmbientValues[Constants.LayoutIndex] = null;
return Completion.Normal;
}
context.AmbientValues[Constants.LayoutIndex] = layoutPath;
return Completion.Normal;
});
var partialExpression = OneOf(
Primary.AndSkip(Comma).And(Separated(Comma, Identifier.AndSkip(Colon).And(Primary).Then(static x => new AssignStatement(x.Item1, x.Item2)))).Then(x => new { Expression = x.Item1, Assignments = x.Item2 }),
Primary.Then(x => new { Expression = x, Assignments = new List<AssignStatement>() })
).ElseError("Invalid 'partial' tag");
RegisterParserTag("partial", partialExpression, static async (partialStatement, writer, encoder, context) =>
{
var relativePartialPath = (await partialStatement.Expression.EvaluateAsync(context)).ToStringValue();
context.IncrementSteps();
try
{
context.EnterChildScope();
if (!relativePartialPath.EndsWith(Constants.ViewExtension, StringComparison.OrdinalIgnoreCase))
{
relativePartialPath += Constants.ViewExtension;
}
var renderer = context.AmbientValues[Constants.RendererIndex] as IFluidViewRenderer;
if (partialStatement.Assignments != null)
{
foreach (var assignStatement in partialStatement.Assignments)
{
await assignStatement.WriteToAsync(writer, encoder, context);
}
}
await renderer.RenderPartialAsync(writer, relativePartialPath, context);
}
finally
{
context.ReleaseScope();
}
return Completion.Normal;
});
}
}
}