Refactored CodeGeneratorContext to CodeBuilderContext.

- Needed to separate the context's of "generation" and "building" to enable the communication of the TagHelperProvider.
This commit is contained in:
N. Taylor Mullen 2014-09-08 17:49:21 -07:00
parent 20824f37e0
commit 950828cbab
23 changed files with 145 additions and 68 deletions

View File

@ -42,7 +42,7 @@ namespace Microsoft.AspNet.Razor
return new CSharpRazorCodeGenerator(className, rootNamespaceName, sourceFileName, host); return new CSharpRazorCodeGenerator(className, rootNamespaceName, sourceFileName, host);
} }
public override CodeBuilder CreateCodeBuilder(CodeGeneratorContext context) public override CodeBuilder CreateCodeBuilder(CodeBuilderContext context)
{ {
return new CSharpCodeBuilder(context); return new CSharpCodeBuilder(context);
} }

View File

@ -0,0 +1,54 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Razor.Generator
{
/// <summary>
/// Context object with information used to generate a Razor page.
/// </summary>
public class CodeBuilderContext : CodeGeneratorContext
{
/// <summary>
/// Instantiates a new instance of the <see cref="CodeBuilderContext"/> object.
/// </summary>
/// <param name="generatorContext">A <see cref="CodeGeneratorContext"/> to copy information from.</param>
public CodeBuilderContext(CodeGeneratorContext generatorContext)
: base(generatorContext)
{
ExpressionRenderingMode = ExpressionRenderingMode.WriteToOutput;
}
// Internal for testing.
internal CodeBuilderContext(RazorEngineHost host,
string className,
string rootNamespace,
string sourceFile,
bool shouldGenerateLinePragmas)
: base(host, className, rootNamespace, sourceFile, shouldGenerateLinePragmas)
{
ExpressionRenderingMode = ExpressionRenderingMode.WriteToOutput;
}
/// <summary>
/// The current C# rendering mode.
/// </summary>
/// <remarks>
/// <see cref="ExpressionRenderingMode.WriteToOutput"/> forces C# generation to write
/// <see cref="Compiler.Chunk"/>s to the output page, i.e. WriteLiteral("Hello World").
/// <see cref="ExpressionRenderingMode.InjectCode"/> writes <see cref="Compiler.Chunk"/> values in their
/// rawest form, i.g. "Hello World".
/// </remarks>
public ExpressionRenderingMode ExpressionRenderingMode { get; set; }
/// <summary>
/// The C# writer to write <see cref="Compiler.Chunk"/> information to.
/// </summary>
/// <remarks>
/// If <see cref="TargetWriterName"/> is <c>null</c> values will be written using a default write method
/// i.e. WriteLiteral("Hello World").
/// If <see cref="TargetWriterName"/> is not <c>null</c> values will be written to the given
/// <see cref="TargetWriterName"/>, i.e. WriteLiteralTo(myWriter, "Hello World").
/// </remarks>
public string TargetWriterName { get; set; }
}
}

View File

@ -7,37 +7,43 @@ namespace Microsoft.AspNet.Razor.Generator
{ {
public class CodeGeneratorContext public class CodeGeneratorContext
{ {
private CodeGeneratorContext() protected CodeGeneratorContext(CodeGeneratorContext context)
: this(context.Host,
context.ClassName,
context.RootNamespace,
context.SourceFile,
// True because we're pulling from the provided context's source file.
shouldGenerateLinePragmas: true)
{ {
ExpressionRenderingMode = ExpressionRenderingMode.WriteToOutput; CodeTreeBuilder = context.CodeTreeBuilder;
}
public CodeGeneratorContext(RazorEngineHost host,
string className,
string rootNamespace,
string sourceFile,
bool shouldGenerateLinePragmas)
{
CodeTreeBuilder = new CodeTreeBuilder();
Host = host;
SourceFile = shouldGenerateLinePragmas ? sourceFile : null;
RootNamespace = rootNamespace;
ClassName = className;
} }
// Internal/Private state. Technically consumers might want to use some of these but they can implement them independently if necessary.
// It's way safer to make them internal for now, especially with the code generator stuff in a bit of flux.
internal ExpressionRenderingMode ExpressionRenderingMode { get; set; }
public string SourceFile { get; internal set; } public string SourceFile { get; internal set; }
public string RootNamespace { get; private set; } public string RootNamespace { get; private set; }
public string ClassName { get; private set; } public string ClassName { get; private set; }
public RazorEngineHost Host { get; private set; } public RazorEngineHost Host { get; private set; }
public string TargetWriterName { get; set; }
public CodeTreeBuilder CodeTreeBuilder { get; set; } public CodeTreeBuilder CodeTreeBuilder { get; set; }
/// <summary> /// <summary>
/// Gets or sets the <c>SHA1</c> based checksum for the file whose location is defined by <see cref="SourceFile"/>. /// Gets or sets the <c>SHA1</c> based checksum for the file whose location is defined by <see cref="SourceFile"/>.
/// </summary> /// </summary>
public string Checksum { get; set; } public string Checksum { get; set; }
public static CodeGeneratorContext Create(RazorEngineHost host, string className, string rootNamespace, string sourceFile, bool shouldGenerateLinePragmas)
{
return new CodeGeneratorContext()
{
CodeTreeBuilder = new CodeTreeBuilder(),
Host = host,
SourceFile = shouldGenerateLinePragmas ? sourceFile : null,
RootNamespace = rootNamespace,
ClassName = className
};
}
} }
} }

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
private const string Sha1AlgorithmId = "{ff1816ec-aa5e-4d10-87f7-6f4963833460}"; private const string Sha1AlgorithmId = "{ff1816ec-aa5e-4d10-87f7-6f4963833460}";
private const int DisableAsyncWarning = 1998; private const int DisableAsyncWarning = 1998;
public CSharpCodeBuilder(CodeGeneratorContext context) public CSharpCodeBuilder(CodeBuilderContext context)
: base(context) : base(context)
{ {
} }

View File

@ -5,7 +5,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
{ {
public class CSharpBaseTypeVisitor : CodeVisitor<CSharpCodeWriter> public class CSharpBaseTypeVisitor : CodeVisitor<CSharpCodeWriter>
{ {
public CSharpBaseTypeVisitor(CSharpCodeWriter writer, CodeGeneratorContext context) public CSharpBaseTypeVisitor(CSharpCodeWriter writer, CodeBuilderContext context)
: base(writer, context) { } : base(writer, context) { }
public string CurrentBaseType { get; set; } public string CurrentBaseType { get; set; }

View File

@ -7,7 +7,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
{ {
public class CSharpClassAttributeVisitor : CodeVisitor<CSharpCodeWriter> public class CSharpClassAttributeVisitor : CodeVisitor<CSharpCodeWriter>
{ {
public CSharpClassAttributeVisitor(CSharpCodeWriter writer, CodeGeneratorContext context) public CSharpClassAttributeVisitor(CSharpCodeWriter writer, CodeBuilderContext context)
: base(writer, context) { } : base(writer, context) { }
protected override void Visit(SessionStateChunk chunk) protected override void Visit(SessionStateChunk chunk)

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
private CSharpPaddingBuilder _paddingBuilder; private CSharpPaddingBuilder _paddingBuilder;
public CSharpCodeVisitor(CSharpCodeWriter writer, CodeGeneratorContext context) public CSharpCodeVisitor(CSharpCodeWriter writer, CodeBuilderContext context)
: base(writer, context) : base(writer, context)
{ {
_paddingBuilder = new CSharpPaddingBuilder(context.Host); _paddingBuilder = new CSharpPaddingBuilder(context.Host);

View File

@ -10,7 +10,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
private const int DisableVariableNamingWarnings = 219; private const int DisableVariableNamingWarnings = 219;
public CSharpDesignTimeHelpersVisitor(CSharpCodeWriter writer, CodeGeneratorContext context) public CSharpDesignTimeHelpersVisitor(CSharpCodeWriter writer, CodeBuilderContext context)
: base(writer, context) { } : base(writer, context) { }
public void AcceptTree(CodeTree tree) public void AcceptTree(CodeTree tree)

View File

@ -11,7 +11,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
private CSharpCodeVisitor _codeVisitor; private CSharpCodeVisitor _codeVisitor;
public CSharpHelperVisitor(CSharpCodeWriter writer, CodeGeneratorContext context) public CSharpHelperVisitor(CSharpCodeWriter writer, CodeBuilderContext context)
: base(writer, context) : base(writer, context)
{ {
_codeVisitor = new CSharpCodeVisitor(writer, context); _codeVisitor = new CSharpCodeVisitor(writer, context);

View File

@ -9,7 +9,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
{ {
private CSharpCodeVisitor _csharpCodeVisitor; private CSharpCodeVisitor _csharpCodeVisitor;
public CSharpTypeMemberVisitor(CSharpCodeWriter writer, CodeGeneratorContext context) public CSharpTypeMemberVisitor(CSharpCodeWriter writer, CodeBuilderContext context)
: base(writer, context) : base(writer, context)
{ {
_csharpCodeVisitor = new CSharpCodeVisitor(writer, context); _csharpCodeVisitor = new CSharpCodeVisitor(writer, context);

View File

@ -9,7 +9,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
{ {
public class CSharpUsingVisitor : CodeVisitor<CSharpCodeWriter> public class CSharpUsingVisitor : CodeVisitor<CSharpCodeWriter>
{ {
public CSharpUsingVisitor(CSharpCodeWriter writer, CodeGeneratorContext context) public CSharpUsingVisitor(CSharpCodeWriter writer, CodeBuilderContext context)
: base(writer, context) : base(writer, context)
{ {
ImportedUsings = new List<string>(); ImportedUsings = new List<string>();

View File

@ -8,14 +8,14 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler
{ {
public abstract class ChunkVisitor<T> : IChunkVisitor where T : CodeWriter public abstract class ChunkVisitor<T> : IChunkVisitor where T : CodeWriter
{ {
public ChunkVisitor(T writer, CodeGeneratorContext context) public ChunkVisitor(T writer, CodeBuilderContext context)
{ {
Writer = writer; Writer = writer;
Context = context; Context = context;
} }
protected T Writer { get; private set; } protected T Writer { get; private set; }
protected CodeGeneratorContext Context { get; private set; } protected CodeBuilderContext Context { get; private set; }
public void Accept(IList<Chunk> chunks) public void Accept(IList<Chunk> chunks)
{ {

View File

@ -5,14 +5,14 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler
{ {
public abstract class CodeBuilder public abstract class CodeBuilder
{ {
private readonly CodeGeneratorContext _context; private readonly CodeBuilderContext _context;
public CodeBuilder(CodeGeneratorContext context) public CodeBuilder(CodeBuilderContext context)
{ {
_context = context; _context = context;
} }
protected CodeGeneratorContext Context protected CodeBuilderContext Context
{ {
get { return _context; } get { return _context; }
} }

View File

@ -5,7 +5,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler
{ {
public class CodeVisitor<T> : ChunkVisitor<T> where T : CodeWriter public class CodeVisitor<T> : ChunkVisitor<T> where T : CodeWriter
{ {
public CodeVisitor(T writer, CodeGeneratorContext context) public CodeVisitor(T writer, CodeBuilderContext context)
: base(writer, context) { } : base(writer, context) { }
protected override void Visit(LiteralChunk chunk) protected override void Visit(LiteralChunk chunk)

View File

@ -71,7 +71,11 @@ namespace Microsoft.AspNet.Razor.Generator
{ {
if (_context == null) if (_context == null)
{ {
_context = CodeGeneratorContext.Create(Host, ClassName, RootNamespaceName, SourceFileName, GenerateLinePragmas); _context = new CodeGeneratorContext(Host,
ClassName,
RootNamespaceName,
SourceFileName,
GenerateLinePragmas);
Initialize(_context); Initialize(_context);
} }
} }

View File

@ -9,9 +9,10 @@ namespace Microsoft.AspNet.Razor
{ {
public class GeneratorResults : ParserResults public class GeneratorResults : ParserResults
{ {
public GeneratorResults(ParserResults parserResults, public GeneratorResults(ParserResults parserResults, CodeBuilderResult codeBuilderResult)
CodeBuilderResult codeBuilderResult) : this(parserResults.Document,
: this(parserResults.Document, parserResults.ParserErrors, codeBuilderResult) parserResults.ParserErrors,
codeBuilderResult)
{ {
} }
@ -33,6 +34,7 @@ namespace Microsoft.AspNet.Razor
} }
public string GeneratedCode { get; private set; } public string GeneratedCode { get; private set; }
public IList<LineMapping> DesignTimeLineMappings { get; private set; } public IList<LineMapping> DesignTimeLineMappings { get; private set; }
} }
} }

View File

@ -54,6 +54,6 @@ namespace Microsoft.AspNet.Razor
/// </summary> /// </summary>
public abstract RazorCodeGenerator CreateCodeGenerator(string className, string rootNamespaceName, string sourceFileName, RazorEngineHost host); public abstract RazorCodeGenerator CreateCodeGenerator(string className, string rootNamespaceName, string sourceFileName, RazorEngineHost host);
public abstract CodeBuilder CreateCodeBuilder(CodeGeneratorContext codeGeneratorContext); public abstract CodeBuilder CreateCodeBuilder(CodeBuilderContext codeGeneratorContext);
} }
} }

View File

@ -207,7 +207,7 @@ namespace Microsoft.AspNet.Razor
/// </summary> /// </summary>
/// <param name="incomingBuilder">The code builder</param> /// <param name="incomingBuilder">The code builder</param>
/// <returns>Either the same code builder, after modifications, or a different code builder.</returns> /// <returns>Either the same code builder, after modifications, or a different code builder.</returns>
public virtual CodeBuilder DecorateCodeBuilder(CodeBuilder incomingBuilder, CodeGeneratorContext context) public virtual CodeBuilder DecorateCodeBuilder(CodeBuilder incomingBuilder, CodeBuilderContext context)
{ {
if (incomingBuilder == null) if (incomingBuilder == null)
{ {

View File

@ -252,8 +252,8 @@ namespace Microsoft.AspNet.Razor
generator.DesignTimeMode = Host.DesignTimeMode; generator.DesignTimeMode = Host.DesignTimeMode;
generator.Visit(results); generator.Visit(results);
var codeBuilderContext = new CodeBuilderContext(generator.Context);
var codeGenerationContext = generator.Context; var builder = CreateCodeBuilder(codeBuilderContext);
codeGenerationContext.Checksum = checksum; codeGenerationContext.Checksum = checksum;
var builder = CreateCodeBuilder(codeGenerationContext); var builder = CreateCodeBuilder(codeGenerationContext);
@ -281,7 +281,7 @@ namespace Microsoft.AspNet.Razor
}; };
} }
protected internal virtual CodeBuilder CreateCodeBuilder(CodeGeneratorContext context) protected internal virtual CodeBuilder CreateCodeBuilder(CodeBuilderContext context)
{ {
return Host.DecorateCodeBuilder(Host.CodeLanguage.CreateCodeBuilder(context), return Host.DecorateCodeBuilder(Host.CodeLanguage.CreateCodeBuilder(context),
context); context);

View File

@ -48,15 +48,16 @@ namespace Microsoft.AspNet.Razor.Test
{ {
// Arrange // Arrange
var language = new CSharpRazorCodeLanguage(); var language = new CSharpRazorCodeLanguage();
var host = new RazorEngineHost(language); var host = new RazorEngineHost(language);
var context = CodeGeneratorContext.Create(host, var codeBuilderContext = new CodeBuilderContext(
"myclass", host,
"myns", "myclass",
string.Empty, "myns",
shouldGenerateLinePragmas: false); string.Empty,
shouldGenerateLinePragmas: false);
// Act // Act
var generator = language.CreateCodeBuilder(context); var generator = language.CreateCodeBuilder(codeBuilderContext);
// Assert // Assert
Assert.IsType<CSharpCodeBuilder>(generator); Assert.IsType<CSharpCodeBuilder>(generator);

View File

@ -17,15 +17,22 @@ namespace Microsoft.AspNet.Razor.Test.Generator.CodeTree
var syntaxTreeNode = new Mock<Span>(new SpanBuilder()); var syntaxTreeNode = new Mock<Span>(new SpanBuilder());
var language = new CSharpRazorCodeLanguage(); var language = new CSharpRazorCodeLanguage();
var host = new RazorEngineHost(language); var host = new RazorEngineHost(language);
var context = CodeGeneratorContext.Create(host, "TestClass", "TestNamespace", "Foo.cs", shouldGenerateLinePragmas: false); var codeBuilderContext = new CodeBuilderContext(
context.CodeTreeBuilder.AddUsingChunk("FakeNamespace1", syntaxTreeNode.Object); host,
context.CodeTreeBuilder.AddUsingChunk("FakeNamespace2.SubNamespace", syntaxTreeNode.Object); "TestClass",
var codeBuilder = language.CreateCodeBuilder(context); "TestNamespace",
"Foo.cs",
shouldGenerateLinePragmas: false);
codeBuilderContext.CodeTreeBuilder.AddUsingChunk("FakeNamespace1", syntaxTreeNode.Object);
codeBuilderContext.CodeTreeBuilder.AddUsingChunk("FakeNamespace2.SubNamespace", syntaxTreeNode.Object);
var codeBuilder = language.CreateCodeBuilder(codeBuilderContext);
// Act // Act
var result = codeBuilder.Build(); var result = codeBuilder.Build();
BaselineWriter.WriteBaseline(@"test\Microsoft.AspNet.Razor.Test\TestFiles\CodeGenerator\CS\Output\CSharpCodeBuilder.cs", result.Code); BaselineWriter.WriteBaseline(
@"test\Microsoft.AspNet.Razor.Test\TestFiles\CodeGenerator\CS\Output\CSharpCodeBuilder.cs",
result.Code);
var expectedOutput = TestFile.Create("TestFiles/CodeGenerator/CS/Output/CSharpCodeBuilder.cs").ReadAllText(); var expectedOutput = TestFile.Create("TestFiles/CodeGenerator/CS/Output/CSharpCodeBuilder.cs").ReadAllText();

View File

@ -25,13 +25,14 @@ namespace Microsoft.AspNet.Razor
private static Mock<ChunkVisitor<CodeWriter>> CreateVisitor() private static Mock<ChunkVisitor<CodeWriter>> CreateVisitor()
{ {
var context = CodeGeneratorContext.Create(new RazorEngineHost(new CSharpRazorCodeLanguage()), var codeBuilderContext = new CodeBuilderContext(
"myclass", new RazorEngineHost(new CSharpRazorCodeLanguage()),
"myns", "myclass",
string.Empty, "myns",
shouldGenerateLinePragmas: false); string.Empty,
shouldGenerateLinePragmas: false);
var writer = Mock.Of<CodeWriter>(); var writer = Mock.Of<CodeWriter>();
return new Mock<ChunkVisitor<CodeWriter>>(writer, context); return new Mock<ChunkVisitor<CodeWriter>>(writer, codeBuilderContext);
} }
private class MyTestChunk : Chunk private class MyTestChunk : Chunk

View File

@ -114,19 +114,21 @@ namespace Microsoft.AspNet.Razor.Test
{ {
// Arrange // Arrange
var mockHost = new Mock<RazorEngineHost>(new CSharpRazorCodeLanguage()) { CallBase = true }; var mockHost = new Mock<RazorEngineHost>(new CSharpRazorCodeLanguage()) { CallBase = true };
var context = CodeGeneratorContext.Create(mockHost.Object, var codeBuilderContext = new CodeBuilderContext(
"different-class", mockHost.Object,
"different-ns", "different-class",
string.Empty, "different-ns",
shouldGenerateLinePragmas: true); string.Empty,
var expected = new CSharpCodeBuilder(context); shouldGenerateLinePragmas: true);
mockHost.Setup(h => h.DecorateCodeBuilder(It.IsAny<CSharpCodeBuilder>(), context)) var expected = new CSharpCodeBuilder(codeBuilderContext);
mockHost.Setup(h => h.DecorateCodeBuilder(It.IsAny<CSharpCodeBuilder>(), codeBuilderContext))
.Returns(expected); .Returns(expected);
var engine = new RazorTemplateEngine(mockHost.Object); var engine = new RazorTemplateEngine(mockHost.Object);
// Act // Act
var actual = engine.CreateCodeBuilder(context); var actual = engine.CreateCodeBuilder(codeBuilderContext);
// Assert // Assert
Assert.Equal(expected, actual); Assert.Equal(expected, actual);