Introducing BasicWriter and TagHelperWriter

These are the replacements for CSharpRenderingConventions
This commit is contained in:
Ryan Nowak 2017-03-03 15:00:21 -08:00
parent 7730d33482
commit 966cd4a68d
13 changed files with 559 additions and 95 deletions

View File

@ -0,0 +1,18 @@
// 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
{
public abstract class BasicWriter
{
public abstract void WriteCSharpExpression(CSharpRenderingContext context, CSharpExpressionIRNode node);
public abstract void WriteCSharpStatement(CSharpRenderingContext context, CSharpStatementIRNode node);
public abstract void WriteHtmlContent(CSharpRenderingContext context, HtmlContentIRNode node);
public abstract void WriteHtmlAttribute(CSharpRenderingContext context, HtmlAttributeIRNode node);
}
}

View File

@ -46,5 +46,104 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
internal TagHelperRenderingContext TagHelperRenderingContext { get; set; }
internal Action<RazorIRNode> RenderChildren { get; set; }
internal Action<RazorIRNode> RenderNode { get; set; }
public BasicWriter BasicWriter { get; set; }
public TagHelperWriter TagHelperWriter { get; set; }
public void AddLineMappingFor(RazorIRNode node)
{
if (node.Source == null)
{
return;
}
var source = node.Source.Value;
var generatedLocation = new SourceSpan(Writer.GetCurrentSourceLocation(), source.Length);
var lineMapping = new LineMapping(source, generatedLocation);
LineMappings.Add(lineMapping);
}
public BasicWriterScope Push(BasicWriter writer)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
var scope = new BasicWriterScope(this, BasicWriter);
BasicWriter = writer;
return scope;
}
public TagHelperWriterScope Push(TagHelperWriter writer)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
var scope = new TagHelperWriterScope(this, BasicWriter);
TagHelperWriter = writer;
return scope;
}
public struct BasicWriterScope : IDisposable
{
private readonly CSharpRenderingContext _context;
private readonly BasicWriter _writer;
public BasicWriterScope(CSharpRenderingContext context, BasicWriter writer)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
_context = context;
_writer = writer;
}
public void Dispose()
{
_context.BasicWriter = _writer;
}
}
public struct TagHelperWriterScope : IDisposable
{
private readonly CSharpRenderingContext _context;
private readonly BasicWriter _writer;
public TagHelperWriterScope(CSharpRenderingContext context, BasicWriter writer)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
_context = context;
_writer = writer;
}
public void Dispose()
{
_context.BasicWriter = _writer;
}
}
}
}

View File

@ -0,0 +1,129 @@
// 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 Microsoft.AspNetCore.Razor.Evolution.Intermediate;
namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
{
public class DefaultBasicWriter : BasicWriter
{
public string WriteCSharpExpressionMethod { get; set; } = "Write";
public override void WriteCSharpExpression(CSharpRenderingContext context, CSharpExpressionIRNode node)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (node == null)
{
throw new ArgumentNullException(nameof(node));
}
if (context.Options.DesignTimeMode)
{
WriteCSharpExpressionDesignTime(context, node);
}
else
{
WriteCSharpExpressionRuntime(context, node);
}
}
public override void WriteCSharpStatement(CSharpRenderingContext context, CSharpStatementIRNode node)
{
throw new NotImplementedException();
}
public override void WriteHtmlAttribute(CSharpRenderingContext context, HtmlAttributeIRNode node)
{
throw new NotImplementedException();
}
public override void WriteHtmlContent(CSharpRenderingContext context, HtmlContentIRNode node)
{
throw new NotImplementedException();
}
protected void WriteCSharpExpressionRuntime(CSharpRenderingContext context, CSharpExpressionIRNode node)
{
IDisposable linePragmaScope = null;
if (node.Source != null)
{
linePragmaScope = context.Writer.BuildLinePragma(node.Source.Value);
context.Writer.WritePadding(WriteCSharpExpressionMethod.Length + 1, node.Source, context);
}
context.Writer.WriteStartMethodInvocation(WriteCSharpExpressionMethod);
for (var i = 0; i < node.Children.Count; i++)
{
if (node.Children[i] is RazorIRToken token && token.IsCSharp)
{
context.Writer.Write(token.Content);
}
else
{
// There may be something else inside the expression like a Template or another extension node.
context.RenderNode(node.Children[i]);
}
}
context.Writer.WriteEndMethodInvocation();
linePragmaScope?.Dispose();
}
protected void WriteCSharpExpressionDesignTime(CSharpRenderingContext context, CSharpExpressionIRNode node)
{
if (node.Children.Count == 0)
{
return;
}
if (node.Source != null)
{
using (context.Writer.BuildLinePragma(node.Source.Value))
{
context.Writer.WritePadding(RazorDesignTimeIRPass.DesignTimeVariable.Length, node.Source, context);
context.Writer.WriteStartAssignment(RazorDesignTimeIRPass.DesignTimeVariable);
for (var i = 0; i < node.Children.Count; i++)
{
if (node.Children[i] is RazorIRToken token && token.IsCSharp)
{
context.AddLineMappingFor(token);
context.Writer.Write(token.Content);
}
else
{
// There may be something else inside the expression like a Template or another extension node.
context.RenderNode(node.Children[i]);
}
}
context.Writer.WriteLine(";");
}
}
else
{
context.Writer.WriteStartAssignment(RazorDesignTimeIRPass.DesignTimeVariable);
for (var i = 0; i < node.Children.Count; i++)
{
if (node.Children[i] is RazorIRToken token && token.IsCSharp)
{
context.Writer.Write(token.Content);
}
else
{
// There may be something else inside the expression like a Template or another extension node.
context.RenderNode(node.Children[i]);
}
}
context.Writer.WriteLine(";");
}
}
}
}

View File

@ -44,6 +44,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
var visitor = new Visitor(_target, _context, _renderer);
_context.RenderChildren = visitor.RenderChildren;
_context.RenderNode = visitor.Visit;
_context.BasicWriter = new DefaultBasicWriter();
_context.TagHelperWriter = new DefaultTagHelperWriter();
visitor.VisitDocument(node);
_context.RenderChildren = null;
@ -63,6 +67,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
_renderer = renderer;
}
private CSharpRenderingContext Context => _context;
public void RenderChildren(RazorIRNode node)
{
for (var i = 0; i < node.Children.Count; i++)
@ -77,6 +83,108 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
RenderChildren(node);
}
public override void VisitNamespace(NamespaceDeclarationIRNode node)
{
Context.Writer
.Write("namespace ")
.WriteLine(node.Content);
using (Context.Writer.BuildScope())
{
Context.Writer.WriteLineHiddenDirective();
RenderChildren(node);
}
}
public override void VisitClass(ClassDeclarationIRNode node)
{
Context.Writer
.Write(node.AccessModifier)
.Write(" class ")
.Write(node.Name);
if (node.BaseType != null || node.Interfaces != null)
{
Context.Writer.Write(" : ");
}
if (node.BaseType != null)
{
Context.Writer.Write(node.BaseType);
if (node.Interfaces != null)
{
Context.Writer.WriteParameterSeparator();
}
}
if (node.Interfaces != null)
{
for (var i = 0; i < node.Interfaces.Count; i++)
{
Context.Writer.Write(node.Interfaces[i]);
if (i + 1 < node.Interfaces.Count)
{
Context.Writer.WriteParameterSeparator();
}
}
}
Context.Writer.WriteLine();
using (Context.Writer.BuildScope())
{
RenderChildren(node);
}
}
public override void VisitRazorMethodDeclaration(RazorMethodDeclarationIRNode node)
{
Context.Writer.WriteLine("#pragma warning disable 1998");
Context.Writer
.Write(node.AccessModifier)
.Write(" ");
if (node.Modifiers != null)
{
for (var i = 0; i < node.Modifiers.Count; i++)
{
Context.Writer.Write(node.Modifiers[i]);
if (i + 1 < node.Modifiers.Count)
{
Context.Writer.Write(" ");
}
}
}
Context.Writer
.Write(" ")
.Write(node.ReturnType)
.Write(" ")
.Write(node.Name)
.WriteLine("()");
using (Context.Writer.BuildScope())
{
RenderChildren(node);
}
Context.Writer.WriteLine("#pragma warning restore 1998");
}
public override void VisitExtension(ExtensionIRNode node)
{
node.WriteNode(_target, Context);
}
public override void VisitCSharpExpression(CSharpExpressionIRNode node)
{
Context.BasicWriter.WriteCSharpExpression(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

@ -0,0 +1,36 @@
// 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 Microsoft.AspNetCore.Razor.Evolution.Intermediate;
namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
{
public class DefaultTagHelperWriter : TagHelperWriter
{
public override void WriteAddTagHelperHtmlAttribute(CSharpRenderingContext context, AddTagHelperHtmlAttributeIRNode node)
{
throw new NotImplementedException();
}
public override void WriteCreateTagHelper(CSharpRenderingContext context, CreateTagHelperIRNode node)
{
throw new NotImplementedException();
}
public override void WriteExecuteTagHelpers(CSharpRenderingContext context, ExecuteTagHelpersIRNode node)
{
throw new NotImplementedException();
}
public override void WriteInitializeTagHelperStructure(CSharpRenderingContext context, InitializeTagHelperStructureIRNode node)
{
throw new NotImplementedException();
}
public override void WriteSetTagHelperProperty(CSharpRenderingContext context, SetTagHelperPropertyIRNode node)
{
throw new NotImplementedException();
}
}
}

View File

@ -17,6 +17,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
public override void VisitCSharpExpression(CSharpExpressionIRNode node)
{
// We can't remove this yet, because it's still used recursively in a few places.
if (node.Children.Count == 0)
{
return;

View File

@ -17,100 +17,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
Target = target;
}
public override void VisitNamespace(NamespaceDeclarationIRNode node)
{
Context.Writer
.Write("namespace ")
.WriteLine(node.Content);
using (Context.Writer.BuildScope())
{
Context.Writer.WriteLineHiddenDirective();
VisitDefault(node);
}
}
public override void VisitRazorMethodDeclaration(RazorMethodDeclarationIRNode node)
{
Context.Writer.WriteLine("#pragma warning disable 1998");
Context.Writer
.Write(node.AccessModifier)
.Write(" ");
if (node.Modifiers != null)
{
for (var i = 0; i < node.Modifiers.Count; i++)
{
Context.Writer.Write(node.Modifiers[i]);
if (i + 1 < node.Modifiers.Count)
{
Context.Writer.Write(" ");
}
}
}
Context.Writer
.Write(" ")
.Write(node.ReturnType)
.Write(" ")
.Write(node.Name)
.WriteLine("()");
using (Context.Writer.BuildScope())
{
VisitDefault(node);
}
Context.Writer.WriteLine("#pragma warning restore 1998");
}
public override void VisitClass(ClassDeclarationIRNode node)
{
Context.Writer
.Write(node.AccessModifier)
.Write(" class ")
.Write(node.Name);
if (node.BaseType != null || node.Interfaces != null)
{
Context.Writer.Write(" : ");
}
if (node.BaseType != null)
{
Context.Writer.Write(node.BaseType);
if (node.Interfaces != null)
{
Context.Writer.WriteParameterSeparator();
}
}
if (node.Interfaces != null)
{
for (var i = 0; i < node.Interfaces.Count; i++)
{
Context.Writer.Write(node.Interfaces[i]);
if (i + 1 < node.Interfaces.Count)
{
Context.Writer.WriteParameterSeparator();
}
}
}
Context.Writer.WriteLine();
using (Context.Writer.BuildScope())
{
VisitDefault(node);
}
}
public override void VisitExtension(ExtensionIRNode node)
{
// This needs to stay here until the rest of the code in the renderers is rewritten because
// and extension can occur at any level.
node.WriteNode(Target, Context);
}

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 Microsoft.AspNetCore.Razor.Evolution.Intermediate;
namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
{
internal class RedirectedBasicWriter : BasicWriter
{
private readonly BasicWriter _previous;
private readonly string _textWriter;
public RedirectedBasicWriter(BasicWriter previous, string textWriter)
{
_previous = previous;
_textWriter = textWriter;
}
public string WriteCSharpExpressionMethod { get; set; } = "WriteTo";
public override void WriteCSharpExpression(CSharpRenderingContext context, CSharpExpressionIRNode node)
{
if (context.Options.DesignTimeMode)
{
_previous.WriteCSharpExpression(context, node);
return;
}
IDisposable linePragmaScope = null;
if (node.Source != null)
{
linePragmaScope = context.Writer.BuildLinePragma(node.Source.Value);
var offset = WriteCSharpExpressionMethod.Length + "(".Length + _textWriter.Length + ", ".Length;
context.Writer.WritePadding(offset, node.Source, context);
}
context.Writer.WriteStartMethodInvocation(WriteCSharpExpressionMethod);
context.Writer.Write(_textWriter);
context.Writer.WriteParameterSeparator();
for (var i = 0; i < node.Children.Count; i++)
{
if (node.Children[i] is RazorIRToken token && token.IsCSharp)
{
context.Writer.Write(token.Content);
}
else
{
// There may be something else inside the expression like a Template or another extension node.
context.RenderNode(node.Children[i]);
}
}
context.Writer.WriteEndMethodInvocation();
linePragmaScope?.Dispose();
}
public override void WriteCSharpStatement(CSharpRenderingContext context, CSharpStatementIRNode node)
{
_previous.WriteCSharpStatement(context, node);
}
public override void WriteHtmlAttribute(CSharpRenderingContext context, HtmlAttributeIRNode node)
{
_previous.WriteHtmlAttribute(context, node);
}
public override void WriteHtmlContent(CSharpRenderingContext context, HtmlContentIRNode node)
{
_previous.WriteHtmlContent(context, node);
}
}
}

View File

@ -63,6 +63,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
public override void VisitCSharpExpression(CSharpExpressionIRNode node)
{
// We can't remove this yet, because it's still used recursively in a few places.
IDisposable linePragmaScope = null;
if (node.Source != null)
{
@ -70,7 +71,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
var padding = BuildOffsetPadding(Context.RenderingConventions.StartWriteMethod.Length, node.Source.Value, Context);
Context.Writer.Write(padding);
}
Context.Writer.Write(Context.RenderingConventions.StartWriteMethod);
for (var i = 0; i < node.Children.Count; i++)

View File

@ -0,0 +1,20 @@
// 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
{
public abstract class TagHelperWriter
{
public abstract void WriteInitializeTagHelperStructure(CSharpRenderingContext context, InitializeTagHelperStructureIRNode node);
public abstract void WriteSetTagHelperProperty(CSharpRenderingContext context, SetTagHelperPropertyIRNode node);
public abstract void WriteAddTagHelperHtmlAttribute(CSharpRenderingContext context, AddTagHelperHtmlAttributeIRNode node);
public abstract void WriteCreateTagHelper(CSharpRenderingContext context, CreateTagHelperIRNode node);
public abstract void WriteExecuteTagHelpers(CSharpRenderingContext context, ExecuteTagHelpersIRNode node);
}
}

View File

@ -22,10 +22,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
var initialRenderingConventions = context.RenderingConventions;
context.RenderingConventions = new CSharpRedirectRenderingConventions(TemplateWriterName, context.Writer);
using (context.Writer.BuildAsyncLambda(endLine: false, parameterNames: TemplateWriterName))
using (context.Push(new RedirectedBasicWriter(context.BasicWriter, TemplateWriterName)))
{
context.RenderChildren(node);
using (context.Writer.BuildAsyncLambda(endLine: false, parameterNames: TemplateWriterName))
{
context.RenderChildren(node);
}
}
context.RenderingConventions = initialRenderingConventions;
context.Writer.WriteEndMethodInvocation(endLine: false);

View File

@ -66,6 +66,66 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
return (CSharpCodeWriter)base.WriteLine();
}
public CSharpCodeWriter WritePadding(int offset, SourceSpan? span, CSharpRenderingContext context)
{
if (span == null)
{
return this;
}
var basePadding = CalculatePadding();
var resolvedPadding = Math.Max(basePadding - offset, 0);
if (context.Options.IsIndentingWithTabs)
{
// Avoid writing directly to the StringBuilder here, that will throw off the manual indexing
// done by the base class.
var tabs = resolvedPadding / context.Options.TabSize;
for (var i = 0; i < tabs; i++)
{
Write("\t");
}
var spaces = resolvedPadding % context.Options.TabSize;
for (var i = 0; i < spaces; i++)
{
Write(" ");
}
}
else
{
for (var i = 0; i < resolvedPadding; i++)
{
Write(" ");
}
}
return this;
int CalculatePadding()
{
var spaceCount = 0;
for (var i = span.Value.AbsoluteIndex - 1; i >= 0; i--)
{
var @char = context.SourceDocument[i];
if (@char == '\n' || @char == '\r')
{
break;
}
else if (@char == '\t')
{
spaceCount += context.Options.TabSize;
}
else
{
spaceCount++;
}
}
return spaceCount;
}
}
public CSharpCodeWriter WriteVariableDeclaration(string type, string name, string value)
{
Write(type).Write(" ").Write(name);

View File

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