Moved ExecuteTagHelpersIRNode from renderer to writer

This commit is contained in:
Ajay Bhargav Baaskaran 2017-04-05 17:13:18 -07:00
parent 0481cc6569
commit 0228fd2770
10 changed files with 273 additions and 46 deletions

View File

@ -96,7 +96,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
throw new ArgumentNullException(nameof(writer));
}
var scope = new TagHelperWriterScope(this, BasicWriter);
var scope = new TagHelperWriterScope(this, TagHelperWriter);
TagHelperWriter = writer;
return scope;
}
@ -131,9 +131,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
public struct TagHelperWriterScope : IDisposable
{
private readonly CSharpRenderingContext _context;
private readonly BasicWriter _writer;
private readonly TagHelperWriter _writer;
public TagHelperWriterScope(CSharpRenderingContext context, BasicWriter writer)
public TagHelperWriterScope(CSharpRenderingContext context, TagHelperWriter writer)
{
if (context == null)
{
@ -151,7 +151,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
public void Dispose()
{
_context.BasicWriter = _writer;
_context.TagHelperWriter = _writer;
}
}
}

View File

@ -211,6 +211,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
Context.TagHelperWriter.WriteCreateTagHelper(Context, node);
}
public override void VisitExecuteTagHelpers(ExecuteTagHelpersIRNode node)
{
Context.TagHelperWriter.WriteExecuteTagHelpers(Context, node);
}
public override void VisitDefault(RazorIRNode node)
{
// This is a temporary bridge to the renderer, which allows us to move functionality piecemeal

View File

@ -43,7 +43,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
public override void WriteExecuteTagHelpers(CSharpRenderingContext context, ExecuteTagHelpersIRNode node)
{
throw new NotImplementedException();
// Do nothing
}
public override void WriteInitializeTagHelperStructure(CSharpRenderingContext context, InitializeTagHelperStructureIRNode node)

View File

@ -0,0 +1,108 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
{
internal class RedirectedTagHelperWriter : TagHelperWriter
{
private readonly TagHelperWriter _previous;
private readonly string _textWriter;
public RedirectedTagHelperWriter(TagHelperWriter previous, string textWriter)
{
_previous = previous;
_textWriter = textWriter;
}
public string ExecutionContextVariableName { get; set; } = "__tagHelperExecutionContext";
public string ExecutionContextOutputPropertyName { get; set; } = "Output";
public string ExecutionContextSetOutputContentAsyncMethodName { get; set; } = "SetOutputContentAsync";
public string RunnerVariableName { get; set; } = "__tagHelperRunner";
public string RunnerRunAsyncMethodName { get; set; } = "RunAsync";
public string ScopeManagerVariableName { get; set; } = "__tagHelperScopeManager";
public string ScopeManagerEndMethodName { get; set; } = "End";
public string TagHelperOutputIsContentModifiedPropertyName { get; set; } = "IsContentModified";
public string WriteTagHelperOutputMethod { get; set; } = "WriteTo";
public override void WriteAddTagHelperHtmlAttribute(CSharpRenderingContext context, AddTagHelperHtmlAttributeIRNode node)
{
_previous.WriteAddTagHelperHtmlAttribute(context, node);
}
public override void WriteCreateTagHelper(CSharpRenderingContext context, CreateTagHelperIRNode node)
{
_previous.WriteCreateTagHelper(context, node);
}
public override void WriteDeclareTagHelperFields(CSharpRenderingContext context, DeclareTagHelperFieldsIRNode node)
{
_previous.WriteDeclareTagHelperFields(context, node);
}
public override void WriteExecuteTagHelpers(CSharpRenderingContext context, ExecuteTagHelpersIRNode node)
{
if (context.Options.DesignTimeMode)
{
_previous.WriteExecuteTagHelpers(context, node);
return;
}
context.Writer
.Write("await ")
.WriteStartInstanceMethodInvocation(
RunnerVariableName,
RunnerRunAsyncMethodName)
.Write(ExecutionContextVariableName)
.WriteEndMethodInvocation();
var tagHelperOutputAccessor = $"{ExecutionContextVariableName}.{ExecutionContextOutputPropertyName}";
context.Writer
.Write("if (!")
.Write(tagHelperOutputAccessor)
.Write(".")
.Write(TagHelperOutputIsContentModifiedPropertyName)
.WriteLine(")");
using (context.Writer.BuildScope())
{
context.Writer
.Write("await ")
.WriteInstanceMethodInvocation(
ExecutionContextVariableName,
ExecutionContextSetOutputContentAsyncMethodName);
}
context.Writer
.WriteStartMethodInvocation(WriteTagHelperOutputMethod)
.Write(_textWriter)
.WriteParameterSeparator()
.Write(tagHelperOutputAccessor)
.WriteEndMethodInvocation()
.WriteStartAssignment(ExecutionContextVariableName)
.WriteInstanceMethodInvocation(
ScopeManagerVariableName,
ScopeManagerEndMethodName);
}
public override void WriteInitializeTagHelperStructure(CSharpRenderingContext context, InitializeTagHelperStructureIRNode node)
{
_previous.WriteInitializeTagHelperStructure(context, node);
}
public override void WriteSetTagHelperProperty(CSharpRenderingContext context, SetTagHelperPropertyIRNode node)
{
_previous.WriteSetTagHelperProperty(context, node);
}
}
}

View File

@ -456,46 +456,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
.WriteEndMethodInvocation();
}
public override void VisitExecuteTagHelpers(ExecuteTagHelpersIRNode node)
{
Context.Writer
.Write("await ")
.WriteStartInstanceMethodInvocation(
"__tagHelperRunner" /* ORIGINAL: RunnerVariableName */,
"RunAsync" /* ORIGINAL: RunnerRunAsyncMethodName */)
.Write("__tagHelperExecutionContext" /* ORIGINAL: ExecutionContextVariableName */)
.WriteEndMethodInvocation();
var executionContextVariableName = "__tagHelperExecutionContext" /* ORIGINAL: ExecutionContextVariableName */;
var executionContextOutputPropertyName = "Output" /* ORIGINAL: ExecutionContextOutputPropertyName */;
var tagHelperOutputAccessor = $"{executionContextVariableName}.{executionContextOutputPropertyName}";
Context.Writer
.Write("if (!")
.Write(tagHelperOutputAccessor)
.Write(".")
.Write("IsContentModified" /* ORIGINAL: TagHelperOutputIsContentModifiedPropertyName */)
.WriteLine(")");
using (Context.Writer.BuildScope())
{
Context.Writer
.Write("await ")
.WriteInstanceMethodInvocation(
executionContextVariableName,
"SetOutputContentAsync" /* ORIGINAL: ExecutionContextSetOutputContentAsyncMethodName */);
}
Context.Writer
.Write(Context.RenderingConventions.StartWriteMethod)
.Write(tagHelperOutputAccessor)
.WriteEndMethodInvocation()
.WriteStartAssignment(executionContextVariableName)
.WriteInstanceMethodInvocation(
"__tagHelperScopeManager" /* ORIGINAL: ScopeManagerVariableName */,
"End" /* ORIGINAL: ScopeManagerEndMethodName */);
}
public override void VisitDeclarePreallocatedTagHelperHtmlAttribute(DeclarePreallocatedTagHelperHtmlAttributeIRNode node)
{
Context.Writer

View File

@ -16,16 +16,24 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
public string ExecutionContextAddMethodName { get; set; } = "Add";
public string ExecutionContextOutputPropertyName { get; set; } = "Output";
public string ExecutionContextSetOutputContentAsyncMethodName { get; set; } = "SetOutputContentAsync";
public string RunnerTypeName { get; set; } = "global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner";
public string RunnerVariableName { get; set; } = "__tagHelperRunner";
public string RunnerRunAsyncMethodName { get; set; } = "RunAsync";
public string ScopeManagerTypeName { get; set; } = "global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperScopeManager";
public string ScopeManagerVariableName { get; set; } = "__tagHelperScopeManager";
public string ScopeManagerBeginMethodName { get; set; } = "Begin";
public string ScopeManagerEndMethodName { get; set; } = "End";
public string StartTagHelperWritingScopeMethodName { get; set; } = "StartTagHelperWritingScope";
public string EndTagHelperWritingScopeMethodName { get; set; } = "EndTagHelperWritingScope";
@ -34,6 +42,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
public string CreateTagHelperMethodName { get; set; } = "CreateTagHelper";
public string TagHelperOutputIsContentModifiedPropertyName { get; set; } = "IsContentModified";
public string WriteTagHelperOutputMethod { get; set; } = "Write";
public override void WriteDeclareTagHelperFields(CSharpRenderingContext context, DeclareTagHelperFieldsIRNode node)
{
context.Writer.WriteLineHiddenDirective();
@ -137,7 +149,40 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
public override void WriteExecuteTagHelpers(CSharpRenderingContext context, ExecuteTagHelpersIRNode node)
{
throw new NotImplementedException();
context.Writer
.Write("await ")
.WriteStartInstanceMethodInvocation(
RunnerVariableName,
RunnerRunAsyncMethodName)
.Write(ExecutionContextVariableName)
.WriteEndMethodInvocation();
var tagHelperOutputAccessor = $"{ExecutionContextVariableName}.{ExecutionContextOutputPropertyName}";
context.Writer
.Write("if (!")
.Write(tagHelperOutputAccessor)
.Write(".")
.Write(TagHelperOutputIsContentModifiedPropertyName)
.WriteLine(")");
using (context.Writer.BuildScope())
{
context.Writer
.Write("await ")
.WriteInstanceMethodInvocation(
ExecutionContextVariableName,
ExecutionContextSetOutputContentAsyncMethodName);
}
context.Writer
.WriteStartMethodInvocation(WriteTagHelperOutputMethod)
.Write(tagHelperOutputAccessor)
.WriteEndMethodInvocation()
.WriteStartAssignment(ExecutionContextVariableName)
.WriteInstanceMethodInvocation(
ScopeManagerVariableName,
ScopeManagerEndMethodName);
}
public override void WriteInitializeTagHelperStructure(CSharpRenderingContext context, InitializeTagHelperStructureIRNode node)
@ -167,6 +212,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
context.RenderingConventions = new CSharpRenderingConventions(context.Writer);
using (context.Push(new RuntimeBasicWriter()))
using (context.Push(new RuntimeTagHelperWriter()))
{
using (context.Writer.BuildAsyncLambda(endLine: false))
{

View File

@ -24,6 +24,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
context.RenderingConventions = new CSharpRedirectRenderingConventions(TemplateWriterName, context.Writer);
using (context.Push(new RedirectedBasicWriter(context.BasicWriter, TemplateWriterName)))
using (context.Push(new RedirectedTagHelperWriter(context.TagHelperWriter, TemplateWriterName)))
{
using (context.Writer.BuildAsyncLambda(endLine: false, parameterNames: TemplateWriterName))
{

View File

@ -0,0 +1,76 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
using Xunit;
namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
{
public class RedirectedTagHelperWriterTest
{
// In design time this will not include the 'text writer' parameter.
[Fact]
public void WriteExecuteTagHelpers_DesignTime_DoesNormalWrite()
{
// Arrange
var writer = new RedirectedTagHelperWriter(new DesignTimeTagHelperWriter(), "test_writer")
{
WriteTagHelperOutputMethod = "Test",
};
var context = new CSharpRenderingContext()
{
Options = RazorParserOptions.CreateDefaultOptions(),
Writer = new Legacy.CSharpCodeWriter(),
};
context.Options.DesignTimeMode = true;
var node = new ExecuteTagHelpersIRNode();
// Act
writer.WriteExecuteTagHelpers(context, node);
// Assert
var csharp = context.Writer.Builder.ToString();
Assert.Empty(csharp);
}
[Fact]
public void WriteExecuteTagHelpers_Runtime_RendersWithRedirectWriter()
{
// Arrange
var writer = new RedirectedTagHelperWriter(new RuntimeTagHelperWriter(), "test_writer")
{
WriteTagHelperOutputMethod = "Test",
};
var context = new CSharpRenderingContext()
{
Options = RazorParserOptions.CreateDefaultOptions(),
Writer = new Legacy.CSharpCodeWriter(),
};
var node = new ExecuteTagHelpersIRNode();
// Act
writer.WriteExecuteTagHelpers(context, node);
// Assert
var csharp = context.Writer.Builder.ToString();
Assert.Equal(
@"await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Test(test_writer, __tagHelperExecutionContext.Output);
__tagHelperExecutionContext = __tagHelperScopeManager.End();
",
csharp,
ignoreLineEndingDifferences: true);
}
}
}

View File

@ -101,6 +101,7 @@ private global::MyTagHelper __MyTagHelper = null;
{
Writer = new Legacy.CSharpCodeWriter(),
BasicWriter = new RuntimeBasicWriter(),
TagHelperWriter = new RuntimeTagHelperWriter(),
IdGenerator = () => "test",
RenderChildren = n => { }
};
@ -146,6 +147,35 @@ private global::MyTagHelper __MyTagHelper = null;
Assert.Equal(
@"__TestNamespace_MyTagHelper = CreateTagHelper<global::TestNamespace.MyTagHelper>();
__tagHelperExecutionContext.Add(__TestNamespace_MyTagHelper);
",
csharp,
ignoreLineEndingDifferences: true);
}
[Fact]
public void WriteExecuteTagHelpers_RendersCorrectly()
{
// Arrange
var writer = new RuntimeTagHelperWriter();
var context = new CSharpRenderingContext()
{
Writer = new Legacy.CSharpCodeWriter(),
};
var node = new ExecuteTagHelpersIRNode();
// Act
writer.WriteExecuteTagHelpers(context, node);
// Assert
var csharp = context.Writer.Builder.ToString();
Assert.Equal(
@"await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
if (!__tagHelperExecutionContext.Output.IsContentModified)
{
await __tagHelperExecutionContext.SetOutputContentAsync();
}
Write(__tagHelperExecutionContext.Output);
__tagHelperExecutionContext = __tagHelperScopeManager.End();
",
csharp,
ignoreLineEndingDifferences: true);

View File

@ -23,6 +23,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
var context = new CSharpRenderingContext()
{
BasicWriter = new RuntimeBasicWriter(),
TagHelperWriter = new RuntimeTagHelperWriter(),
Writer = new CSharpCodeWriter(),
};