Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions source/Handlebars.Test/InlinePartialTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,18 @@ public void BasicBlockInlinePartial()

var result2 = template(data);
Assert.Equal("Hello, Pete Jones!", result2);

source = "Hello, {{#>personInline}}{{/personInline}}!";
template = Handlebars.Compile(source);

var result3 = template(data);
Assert.Equal("Hello, !", result3);

source = "{{#*inline \"personInline\"}}{{firstName}} {{lastName}}{{/inline}}" + source;
template = Handlebars.Compile(source);

var result4 = template(data);
Assert.Equal("Hello, Pete Jones!", result4);
}

[Fact]
Expand Down Expand Up @@ -413,6 +425,42 @@ public void RecursionBoundedAboveLimitInlinePartial()
ex = Assert.IsType<HandlebarsRuntimeException>(ex.InnerException);
Assert.Equal("Runtime error while rendering partial 'list', exceeded recursion depth limit of 100", ex.Message);
}

[Fact]
public void BlockInlinePartialWithInlinePartials()
{
string partialSource = "{{#*inline \"greeting\"}}{{#>salutation}}Dear{{/salutation}} {{#>name}}{{firstName}} {{lastName}}{{/name}}{{/inline}}";

var data = new
{
firstName = "Pete",
lastName = "Jones"
};

string source = partialSource + "{{#>greeting}}{{/greeting}}";
var template = Handlebars.Compile(source);

var result1 = template(data);
Assert.Equal("Dear Pete Jones", result1);

source = partialSource + "{{#>greeting}}{{#*inline \"salutation\"}}Hello{{/inline}}{{/greeting}}";
template = Handlebars.Compile(source);

var result2 = template(data);
Assert.Equal("Hello Pete Jones", result2);

source = partialSource + "{{#>greeting}}{{#*inline \"name\"}}Mr. {{lastName}}{{/inline}}{{/greeting}}";
template = Handlebars.Compile(source);

var result3 = template(data);
Assert.Equal("Dear Mr. Jones", result3);

source = partialSource + "{{#>greeting}}{{#*inline \"salutation\"}}Hello{{/inline}}{{#*inline \"name\"}}{{firstName}}{{/inline}}{{/greeting}}";
template = Handlebars.Compile(source);

var result4 = template(data);
Assert.Equal("Hello Pete", result4);
}
}
}

19 changes: 13 additions & 6 deletions source/Handlebars.Test/IssueTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1049,15 +1049,22 @@ public void Issue519_PartialBlockUsableAsBlockAndInIf()
{
var handlebars = Handlebars.Create();
handlebars.RegisterTemplate("myPartial",
@"Conditional:{{#if @partial-block}} {{> @partial-block}}{{/if}}
Plain: {{> @partial-block}}
Block:{{#> @partial-block }}{{/@partial-block}}");
"""
Conditional: {{#if @partial-block}}{{> @partial-block}}{{/if}}
Plain: {{> @partial-block}}
Block with empty fallback: {{#> @partial-block }}{{/@partial-block}}
Block with non-empty fallback: {{#> @partial-block }}...{{/@partial-block}}
""");

var render = handlebars.Compile("{{#> myPartial}}Block content{{/myPartial}}");
var actual = render(new { });
Assert.Contains("Conditional: Block content", actual);
Assert.Contains("Plain: Block content", actual);
Assert.Contains("Block:Block content", actual);
Assert.Equal("""
Conditional: Block content
Plain: Block content
Block with empty fallback: Block content
Block with non-empty fallback: Block content
""".ReplaceLineEndings("\n"),
actual);
}

// Issue: https://github.com/Handlebars-Net/Handlebars.Net/issues/458
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ public override void HandleElement(Expression item)

public override Expression GetAccumulatedBlock()
{
var fallback = _body.Count == 0 ? null : _body.Count == 1 ? _body.First() : Expression.Block(_body);
var fallback = _body.Count switch
{
0 => Expression.Empty(),
1 => _body[0],
_ => Expression.Block(_body)
};
return HandlebarsExpression.Partial(_startingNode.PartialName, _startingNode.Argument, fallback);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ private static void ProcessStatement(IList<object> list, int index, StatementExp
partialExpr.PartialName,
partialExpr.Argument,
partialExpr.Fallback,
partialExpr.IsBlock,
indent);
list[index] = HandlebarsExpression.Statement(
indentedPartial,
Expand Down
8 changes: 4 additions & 4 deletions source/Handlebars/Compiler/Structure/HandlebarsExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,17 +94,17 @@ public static PartialExpression Partial(Expression partialName)

public static PartialExpression Partial(Expression partialName, Expression argument)
{
return new PartialExpression(partialName, argument, null);
return new PartialExpression(partialName, argument, null, false);
}

public static PartialExpression Partial(Expression partialName, Expression argument, Expression fallback)
{
return new PartialExpression(partialName, argument, fallback);
return new PartialExpression(partialName, argument, fallback, true);
}

public static PartialExpression Partial(Expression partialName, Expression argument, Expression fallback, string indent)
public static PartialExpression Partial(Expression partialName, Expression argument, Expression fallback, bool isBlock, string indent)
{
return new PartialExpression(partialName, argument, fallback, indent);
return new PartialExpression(partialName, argument, fallback, isBlock, indent);
}

public static BoolishExpression Boolish(Expression condition, HashParametersExpression hashParameters)
Expand Down
5 changes: 4 additions & 1 deletion source/Handlebars/Compiler/Structure/PartialExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ namespace HandlebarsDotNet.Compiler
{
internal class PartialExpression : HandlebarsExpression
{
public PartialExpression(Expression partialName, Expression argument, Expression fallback, string indent = null)
public PartialExpression(Expression partialName, Expression argument, Expression fallback, bool isBlock = false, string indent = null)
{
PartialName = partialName;
Argument = argument;
Fallback = fallback;
IsBlock = isBlock;
Indent = indent;
}

Expand All @@ -20,6 +21,8 @@ public PartialExpression(Expression partialName, Expression argument, Expression

public Expression Fallback { get; }

public bool IsBlock { get; }

/// <summary>
/// The whitespace that preceded the partial tag on its line.
/// When non-null/non-empty, this indentation is prepended to every line of the rendered partial output,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ protected virtual Expression VisitPartialExpression(PartialExpression pex)
if (partialName != pex.PartialName
|| argument != pex.Argument)
{
return HandlebarsExpression.Partial(partialName, argument, pex.Fallback);
return HandlebarsExpression.Partial(partialName, argument, pex.Fallback, pex.IsBlock, pex.Indent);
}
return pex;
}
Expand Down
25 changes: 18 additions & 7 deletions source/Handlebars/Compiler/Translation/Expression/PartialBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,13 @@ protected override Expression VisitPartialExpression(PartialExpression pex)
var partialNameObj = Arg<object>(pex.PartialName);
var partialName = Call(() => ToPartialName(partialNameObj));
var configuration = Arg(CompilationContext.Configuration);
var isBlock = Arg(pex.IsBlock);
var indent = Arg(pex.Indent);
var templateDelegate = FunctionBuilder.Compile(
new []
{
Call(() =>
InvokePartialWithFallback(partialName, bindingContext, writer, (ICompiledHandlebarsConfiguration) configuration, indent) // NOSONAR S1944 — ExpressionShortcuts operator; not a runtime hierarchy cast
InvokePartialWithFallback(partialName, bindingContext, writer, (ICompiledHandlebarsConfiguration) configuration, isBlock, indent) // NOSONAR S1944 — ExpressionShortcuts operator; not a runtime hierarchy cast
).Expression
},
CompilationContext,
Expand Down Expand Up @@ -92,10 +93,11 @@ out _
var partialNameObj = Arg<object>(pex.PartialName);
var partialName = Call(() => ToPartialName(partialNameObj));
var configuration = Arg(CompilationContext.Configuration);
var isBlock = Arg(pex.IsBlock);
var indent = Arg(pex.Indent);

return Call(() =>
InvokePartialWithFallback(partialName, bindingContext, writer, (ICompiledHandlebarsConfiguration) configuration, indent)
InvokePartialWithFallback(partialName, bindingContext, writer, (ICompiledHandlebarsConfiguration) configuration, isBlock, indent)
);
}
}
Expand All @@ -105,10 +107,11 @@ private static void InvokePartialWithFallback(
BindingContext context,
EncodedTextWriter writer,
ICompiledHandlebarsConfiguration configuration,
string indent = null)
bool block,
string indent)
{
partialName = partialName != null ? ChainSegment.Create(partialName).TrimmedValue : null;
if (InvokePartial(partialName, context, writer, configuration, indent)) return;
if (InvokePartial(partialName, context, writer, configuration, block, indent)) return;
if (context.PartialBlockTemplate == null)
{
if (configuration.MissingPartialTemplateHandler == null)
Expand Down Expand Up @@ -173,16 +176,24 @@ private static bool InvokePartial(
BindingContext context,
EncodedTextWriter writer,
ICompiledHandlebarsConfiguration configuration,
string indent = null)
bool block,
string indent)
{
if (partialName.Equals(SpecialPartialBlockName))
{
if (context.PartialBlockTemplate == null)
var partialBlockTemplate = context.PartialBlockTemplate;

// If we are a block, our contents are the fallback and SpecialPartialBlockName refers to our parent
if (block)
{
partialBlockTemplate = context.ParentContext.PartialBlockTemplate;
}

if (partialBlockTemplate == null)
{
return false;
}

var partialBlockTemplate = context.PartialBlockTemplate;
try
{
context.PartialBlockTemplate = context.ParentContext.PartialBlockTemplate;
Expand Down
Loading