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);
}
public override CodeBuilder CreateCodeBuilder(CodeGeneratorContext context)
public override CodeBuilder CreateCodeBuilder(CodeBuilderContext 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
{
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 RootNamespace { get; private set; }
public string ClassName { get; private set; }
public RazorEngineHost Host { get; private set; }
public string TargetWriterName { get; set; }
public CodeTreeBuilder CodeTreeBuilder { get; set; }
/// <summary>
/// Gets or sets the <c>SHA1</c> based checksum for the file whose location is defined by <see cref="SourceFile"/>.
/// </summary>
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 int DisableAsyncWarning = 1998;
public CSharpCodeBuilder(CodeGeneratorContext context)
public CSharpCodeBuilder(CodeBuilderContext context)
: base(context)
{
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,7 +9,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
{
private CSharpCodeVisitor _csharpCodeVisitor;
public CSharpTypeMemberVisitor(CSharpCodeWriter writer, CodeGeneratorContext context)
public CSharpTypeMemberVisitor(CSharpCodeWriter writer, CodeBuilderContext context)
: base(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 CSharpUsingVisitor(CSharpCodeWriter writer, CodeGeneratorContext context)
public CSharpUsingVisitor(CSharpCodeWriter writer, CodeBuilderContext context)
: base(writer, context)
{
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 ChunkVisitor(T writer, CodeGeneratorContext context)
public ChunkVisitor(T writer, CodeBuilderContext context)
{
Writer = writer;
Context = context;
}
protected T Writer { get; private set; }
protected CodeGeneratorContext Context { get; private set; }
protected CodeBuilderContext Context { get; private set; }
public void Accept(IList<Chunk> chunks)
{

View File

@ -5,14 +5,14 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler
{
public abstract class CodeBuilder
{
private readonly CodeGeneratorContext _context;
private readonly CodeBuilderContext _context;
public CodeBuilder(CodeGeneratorContext context)
public CodeBuilder(CodeBuilderContext context)
{
_context = context;
}
protected CodeGeneratorContext Context
protected CodeBuilderContext 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 CodeVisitor(T writer, CodeGeneratorContext context)
public CodeVisitor(T writer, CodeBuilderContext context)
: base(writer, context) { }
protected override void Visit(LiteralChunk chunk)

View File

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

View File

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

View File

@ -54,6 +54,6 @@ namespace Microsoft.AspNet.Razor
/// </summary>
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>
/// <param name="incomingBuilder">The code builder</param>
/// <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)
{

View File

@ -252,8 +252,8 @@ namespace Microsoft.AspNet.Razor
generator.DesignTimeMode = Host.DesignTimeMode;
generator.Visit(results);
var codeGenerationContext = generator.Context;
var codeBuilderContext = new CodeBuilderContext(generator.Context);
var builder = CreateCodeBuilder(codeBuilderContext);
codeGenerationContext.Checksum = checksum;
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),
context);

View File

@ -48,15 +48,16 @@ namespace Microsoft.AspNet.Razor.Test
{
// Arrange
var language = new CSharpRazorCodeLanguage();
var host = new RazorEngineHost(language);
var context = CodeGeneratorContext.Create(host,
"myclass",
"myns",
string.Empty,
shouldGenerateLinePragmas: false);
var host = new RazorEngineHost(language);
var codeBuilderContext = new CodeBuilderContext(
host,
"myclass",
"myns",
string.Empty,
shouldGenerateLinePragmas: false);
// Act
var generator = language.CreateCodeBuilder(context);
var generator = language.CreateCodeBuilder(codeBuilderContext);
// Assert
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 language = new CSharpRazorCodeLanguage();
var host = new RazorEngineHost(language);
var context = CodeGeneratorContext.Create(host, "TestClass", "TestNamespace", "Foo.cs", shouldGenerateLinePragmas: false);
context.CodeTreeBuilder.AddUsingChunk("FakeNamespace1", syntaxTreeNode.Object);
context.CodeTreeBuilder.AddUsingChunk("FakeNamespace2.SubNamespace", syntaxTreeNode.Object);
var codeBuilder = language.CreateCodeBuilder(context);
var codeBuilderContext = new CodeBuilderContext(
host,
"TestClass",
"TestNamespace",
"Foo.cs",
shouldGenerateLinePragmas: false);
codeBuilderContext.CodeTreeBuilder.AddUsingChunk("FakeNamespace1", syntaxTreeNode.Object);
codeBuilderContext.CodeTreeBuilder.AddUsingChunk("FakeNamespace2.SubNamespace", syntaxTreeNode.Object);
var codeBuilder = language.CreateCodeBuilder(codeBuilderContext);
// Act
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();

View File

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

View File

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