Refactoring CSharpCodeWriter

* Using CodeGeneratorContext to pass around metadata.
* Tweaks in CSharpCodeWriter to use the right type name
* Adding a sample unit test
This commit is contained in:
Pranav K 2014-02-03 11:26:12 -08:00
parent 68bb44d73e
commit 57854b2cc0
15 changed files with 154 additions and 111 deletions

View File

@ -2,6 +2,8 @@
using System; using System;
using Microsoft.AspNet.Razor.Generator; using Microsoft.AspNet.Razor.Generator;
using Microsoft.AspNet.Razor.Generator.Compiler;
using Microsoft.AspNet.Razor.Generator.Compiler.CSharp;
using Microsoft.AspNet.Razor.Parser; using Microsoft.AspNet.Razor.Parser;
using Microsoft.CSharp; using Microsoft.CSharp;
@ -47,5 +49,10 @@ namespace Microsoft.AspNet.Razor
{ {
return new CSharpRazorCodeGenerator(className, rootNamespaceName, sourceFileName, host); return new CSharpRazorCodeGenerator(className, rootNamespaceName, sourceFileName, host);
} }
public override CodeBuilder CreateBuilder(CodeGeneratorContext codeGeneratorContext)
{
return new CSharpCodeBuilder(codeGeneratorContext);
}
} }
} }

View File

@ -29,6 +29,8 @@ namespace Microsoft.AspNet.Razor.Generator
// It's way safer to make them internal for now, especially with the code generator stuff in a bit of flux. // 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; } 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 ClassName { get; private set; }
#region deletable #region deletable
#if NET45 #if NET45
@ -46,6 +48,7 @@ namespace Microsoft.AspNet.Razor.Generator
public CodeCompileUnit CompileUnit { get; internal set; } public CodeCompileUnit CompileUnit { get; internal set; }
public CodeNamespace Namespace { get; internal set; } public CodeNamespace Namespace { get; internal set; }
public CodeTypeDeclaration GeneratedClass { get; internal set; } public CodeTypeDeclaration GeneratedClass { get; internal set; }
public CodeMemberMethod TargetMethod { get; set; } public CodeMemberMethod TargetMethod { get; set; }
public IDictionary<int, GeneratedCodeMapping> CodeMappings { get; private set; } public IDictionary<int, GeneratedCodeMapping> CodeMappings { get; private set; }
@ -320,6 +323,8 @@ namespace Microsoft.AspNet.Razor.Generator
CodeTreeBuilder = new CodeTreeBuilder(), CodeTreeBuilder = new CodeTreeBuilder(),
Host = host, Host = host,
SourceFile = shouldGenerateLinePragmas ? sourceFile : null, SourceFile = shouldGenerateLinePragmas ? sourceFile : null,
RootNamespace = rootNamespace,
ClassName = className,
#if NET45 #if NET45
// This section is #if'd because it contains SOME incompatible pieces but also will not be needed once we transition over // This section is #if'd because it contains SOME incompatible pieces but also will not be needed once we transition over
// to using the CodeTree // to using the CodeTree

View File

@ -1,72 +1,53 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection;
namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
{ {
public class CSharpCodeBuilder : CodeBuilder public class CSharpCodeBuilder : CodeBuilder
{ {
public CSharpCodeBuilder(CodeTree codeTree, string rootNamespace, RazorEngineHost host, string sourceFile) public CSharpCodeBuilder(CodeGeneratorContext context)
: base(codeTree) : base(context)
{ {
Host = host;
RootNamespace = rootNamespace;
SourceFile = sourceFile;
} }
public RazorEngineHost Host { get; private set; } private CodeTree Tree { get { return Context.CodeTreeBuilder.CodeTree; } }
public string RootNamespace { get; private set; } public RazorEngineHost Host { get { return Context.Host; } }
public string SourceFile { get; private set; }
public override CodeBuilderResult Build() public override CodeBuilderResult Build()
{ {
var writer = new CSharpCodeWriter(); var writer = new CSharpCodeWriter();
// TODO: Combine into one string (perf) using (writer.BuildNamespace(Context.RootNamespace))
writer.WriteComment(new string('-', 78))
.WriteComment("<auto-generated>")
.WriteComment(" This code was generated by a tool.")
.WriteComment("")
.WriteComment(" Changes to this file may cause incorrect behavior and will be lost if")
.WriteComment(" the code is regenerated.")
.WriteComment("</auto-generated>")
.WriteComment(new string('-', 78))
.WriteLine();
using (writer.BuildNamespace(RootNamespace))
{ {
// Write out using directives // Write out using directives
AddImports(Tree, writer, Host.NamespaceImports); AddImports(Tree, writer, Host.NamespaceImports);
// TODO: Include current projects namespace? Does that happen to be included in the namespace imports?
var baseTypeVisitor = new CSharpBaseTypeVisitor(writer);
baseTypeVisitor.Accept(Tree.Chunks);
string baseType = baseTypeVisitor.CurrentBaseType ?? Host.DefaultBaseClass;
// Separate the usings and the class // Separate the usings and the class
writer.WriteLine(); writer.WriteLine();
var baseTypeVisitor = new CSharpBaseTypeVisitor(writer);
baseTypeVisitor.Accept(Tree.Chunks);
string baseType = baseTypeVisitor.CurrentBaseType ?? Host.DefaultBaseClass;
new CSharpClassAttributeVisitor(writer).Accept(Tree.Chunks); new CSharpClassAttributeVisitor(writer).Accept(Tree.Chunks);
using (writer.BuildClassDeclaration("public", Host.DefaultClassName, String.IsNullOrEmpty(baseType) ? new string[0] : new string[] { baseType })) IEnumerable<string> baseTypes = String.IsNullOrEmpty(baseType) ? Enumerable.Empty<string>() :
new string[] { baseType };
using (writer.BuildClassDeclaration("public", Context.ClassName, baseTypes))
{ {
if (Host.DesignTimeMode) if (Host.DesignTimeMode)
{ {
writer.WriteLine("private static object @__o;"); writer.WriteLine("private static object @__o;");
} }
new CSharpHelperVisitor(writer, Host, SourceFile).Accept(Tree.Chunks); new CSharpHelperVisitor(writer, Context).Accept(Tree.Chunks);
new CSharpTypeMemberVisitor(writer, SourceFile).Accept(Tree.Chunks); new CSharpTypeMemberVisitor(writer, Context).Accept(Tree.Chunks);
new CSharpDesignTimeHelpersVisitor(writer, Host, SourceFile).Accept(Tree); new CSharpDesignTimeHelpersVisitor(writer, Context).Accept(Tree.Chunks);
// TODO: resolve variable declarations // TODO: resolve variable declarations
writer.WriteLineHiddenDirective(); writer.WriteLineHiddenDirective();
using (writer.BuildConstructor(Host.DefaultClassName)) using (writer.BuildConstructor(Context.ClassName))
{ {
// Any constructor based logic that we need to add? // Any constructor based logic that we need to add?
}; };
@ -76,7 +57,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
using (writer.BuildMethodDeclaration("public override", "void", Host.GeneratedClassContext.ExecuteMethodName)) using (writer.BuildMethodDeclaration("public override", "void", Host.GeneratedClassContext.ExecuteMethodName))
{ {
new CSharpCodeVisitor(writer, Host, SourceFile).Accept(Tree.Chunks); new CSharpCodeVisitor(writer, Context).Accept(Tree.Chunks);
} }
} }
} }
@ -87,7 +68,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
private void AddImports(CodeTree codeTree, CSharpCodeWriter writer, IEnumerable<string> defaultImports) private void AddImports(CodeTree codeTree, CSharpCodeWriter writer, IEnumerable<string> defaultImports)
{ {
// Write out using directives // Write out using directives
var usingVisitor = new CSharpUsingVisitor(writer, SourceFile); var usingVisitor = new CSharpUsingVisitor(writer, Context.SourceFile);
foreach (Chunk chunk in Tree.Chunks) foreach (Chunk chunk in Tree.Chunks)
{ {
usingVisitor.Accept(chunk); usingVisitor.Accept(chunk);

View File

@ -8,23 +8,20 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
{ {
private const string ValueWriterName = "__razor_attribute_value_writer"; private const string ValueWriterName = "__razor_attribute_value_writer";
private CSharpCodeWriter _writer; private readonly CSharpCodeWriter _writer;
// TODO: No need for the entire host private readonly CodeGeneratorContext _context;
private RazorEngineHost _host;
private string _sourceFile;
public CSharpCodeVisitor(CSharpCodeWriter writer, RazorEngineHost host, string sourceFile) public CSharpCodeVisitor(CSharpCodeWriter writer, CodeGeneratorContext context)
{ {
_writer = writer; _writer = writer;
_host = host; _context = context;
_sourceFile = sourceFile;
} }
protected override void Visit(SetLayoutChunk chunk) protected override void Visit(SetLayoutChunk chunk)
{ {
if (!_host.DesignTimeMode && !String.IsNullOrEmpty(_host.GeneratedClassContext.LayoutPropertyName)) if (!_context.Host.DesignTimeMode && !String.IsNullOrEmpty(_context.Host.GeneratedClassContext.LayoutPropertyName))
{ {
_writer.Write(_host.GeneratedClassContext.LayoutPropertyName) _writer.Write(_context.Host.GeneratedClassContext.LayoutPropertyName)
.Write(" = ") .Write(" = ")
.WriteStringLiteral(chunk.Layout) .WriteStringLiteral(chunk.Layout)
.WriteLine(";"); .WriteLine(";");
@ -34,7 +31,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
protected override void Visit(TemplateChunk chunk) protected override void Visit(TemplateChunk chunk)
{ {
_writer.Write(TemplateBlockCodeGenerator.ItemParameterName).Write(" => ") _writer.Write(TemplateBlockCodeGenerator.ItemParameterName).Write(" => ")
.WriteStartNewObject(_host.GeneratedClassContext.TemplateTypeName); .WriteStartNewObject(_context.Host.GeneratedClassContext.TemplateTypeName);
using (_writer.BuildLambda(endLine: false, parameterNames: TemplateBlockCodeGenerator.TemplateWriterName)) using (_writer.BuildLambda(endLine: false, parameterNames: TemplateBlockCodeGenerator.TemplateWriterName))
{ {
@ -46,30 +43,30 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
protected override void Visit(ResolveUrlChunk chunk) protected override void Visit(ResolveUrlChunk chunk)
{ {
if (!_host.DesignTimeMode && String.IsNullOrEmpty(chunk.Url)) if (!_context.Host.DesignTimeMode && String.IsNullOrEmpty(chunk.Url))
{ {
return; return;
} }
// TODO: Add instrumentation // TODO: Add instrumentation
if (!String.IsNullOrEmpty(chunk.Url) && !_host.DesignTimeMode) if (!String.IsNullOrEmpty(chunk.Url) && !_context.Host.DesignTimeMode)
{ {
if (chunk.RenderingMode == ExpressionRenderingMode.WriteToOutput) if (chunk.RenderingMode == ExpressionRenderingMode.WriteToOutput)
{ {
if (!String.IsNullOrEmpty(chunk.WriterName)) if (!String.IsNullOrEmpty(chunk.WriterName))
{ {
_writer.WriteStartMethodInvocation(_host.GeneratedClassContext.WriteLiteralToMethodName) _writer.WriteStartMethodInvocation(_context.Host.GeneratedClassContext.WriteLiteralToMethodName)
.Write(chunk.WriterName) .Write(chunk.WriterName)
.WriteParameterSeparator(); .WriteParameterSeparator();
} }
else else
{ {
_writer.WriteStartMethodInvocation(_host.GeneratedClassContext.WriteLiteralMethodName); _writer.WriteStartMethodInvocation(_context.Host.GeneratedClassContext.WriteLiteralMethodName);
} }
} }
_writer.WriteStartMethodInvocation(_host.GeneratedClassContext.ResolveUrlMethodName) _writer.WriteStartMethodInvocation(_context.Host.GeneratedClassContext.ResolveUrlMethodName)
.WriteStringLiteral(chunk.Url) .WriteStringLiteral(chunk.Url)
.WriteEndMethodInvocation(endLine: false); .WriteEndMethodInvocation(endLine: false);
@ -82,24 +79,24 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
protected override void Visit(LiteralChunk chunk) protected override void Visit(LiteralChunk chunk)
{ {
if (!_host.DesignTimeMode && String.IsNullOrEmpty(chunk.Text)) if (!_context.Host.DesignTimeMode && String.IsNullOrEmpty(chunk.Text))
{ {
return; return;
} }
// TODO: Add instrumentation // TODO: Add instrumentation
if (!String.IsNullOrEmpty(chunk.Text) && !_host.DesignTimeMode) if (!String.IsNullOrEmpty(chunk.Text) && !_context.Host.DesignTimeMode)
{ {
if (!String.IsNullOrEmpty(chunk.WriterName)) if (!String.IsNullOrEmpty(chunk.WriterName))
{ {
_writer.WriteStartMethodInvocation(_host.GeneratedClassContext.WriteLiteralToMethodName) _writer.WriteStartMethodInvocation(_context.Host.GeneratedClassContext.WriteLiteralToMethodName)
.Write(chunk.WriterName) .Write(chunk.WriterName)
.WriteParameterSeparator(); .WriteParameterSeparator();
} }
else else
{ {
_writer.WriteStartMethodInvocation(_host.GeneratedClassContext.WriteLiteralMethodName); _writer.WriteStartMethodInvocation(_context.Host.GeneratedClassContext.WriteLiteralMethodName);
} }
_writer.WriteStringLiteral(chunk.Text) _writer.WriteStringLiteral(chunk.Text)
@ -114,13 +111,13 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
// TODO: Handle instrumentation // TODO: Handle instrumentation
// TODO: Refactor // TODO: Refactor
if (!_host.DesignTimeMode && chunk.RenderingMode == ExpressionRenderingMode.InjectCode) if (!_context.Host.DesignTimeMode && chunk.RenderingMode == ExpressionRenderingMode.InjectCode)
{ {
Visit((ChunkBlock)chunk); Visit((ChunkBlock)chunk);
} }
else else
{ {
if (_host.DesignTimeMode) if (_context.Host.DesignTimeMode)
{ {
_writer.WriteStartAssignment("__o"); _writer.WriteStartAssignment("__o");
} }
@ -130,19 +127,19 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
if (!String.IsNullOrEmpty(chunk.WriterName)) if (!String.IsNullOrEmpty(chunk.WriterName))
{ {
_writer.WriteStartMethodInvocation(_host.GeneratedClassContext.WriteToMethodName) _writer.WriteStartMethodInvocation(_context.Host.GeneratedClassContext.WriteToMethodName)
.Write(chunk.WriterName) .Write(chunk.WriterName)
.WriteParameterSeparator(); .WriteParameterSeparator();
} }
else else
{ {
_writer.WriteStartMethodInvocation(_host.GeneratedClassContext.WriteMethodName); _writer.WriteStartMethodInvocation(_context.Host.GeneratedClassContext.WriteMethodName);
} }
} }
Visit((ChunkBlock)chunk); Visit((ChunkBlock)chunk);
if (_host.DesignTimeMode) if (_context.Host.DesignTimeMode)
{ {
_writer.WriteLine(";"); _writer.WriteLine(";");
} }
@ -155,7 +152,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
protected override void Visit(ExpressionChunk chunk) protected override void Visit(ExpressionChunk chunk)
{ {
using (_writer.BuildLineMapping(chunk.Start, chunk.Code.Value.Length, _sourceFile)) using (_writer.BuildLineMapping(chunk.Start, chunk.Code.Value.Length, _context.SourceFile))
{ {
_writer.Indent(chunk.Start.CharacterIndex) _writer.Indent(chunk.Start.CharacterIndex)
.Write(chunk.Code.Value); .Write(chunk.Code.Value);
@ -166,7 +163,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
{ {
foreach (Snippet snippet in chunk.Code) foreach (Snippet snippet in chunk.Code)
{ {
using (_writer.BuildLineMapping(chunk.Start, snippet.Value.Length, _sourceFile)) using (_writer.BuildLineMapping(chunk.Start, snippet.Value.Length, _context.SourceFile))
{ {
_writer.Indent(chunk.Start.CharacterIndex); _writer.Indent(chunk.Start.CharacterIndex);
_writer.WriteLine(snippet.Value); _writer.WriteLine(snippet.Value);
@ -176,7 +173,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
protected override void Visit(DynamicCodeAttributeChunk chunk) protected override void Visit(DynamicCodeAttributeChunk chunk)
{ {
if (_host.DesignTimeMode) if (_context.Host.DesignTimeMode)
{ {
return; // Don't generate anything! return; // Don't generate anything!
} }
@ -208,7 +205,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
.WriteLocationTaggedString(chunk.Prefix) .WriteLocationTaggedString(chunk.Prefix)
.WriteParameterSeparator() .WriteParameterSeparator()
.WriteStartMethodInvocation("Tuple.Create", new string[] { "System.Object", "System.Int32" }) .WriteStartMethodInvocation("Tuple.Create", new string[] { "System.Object", "System.Int32" })
.WriteStartNewObject(_host.GeneratedClassContext.TemplateTypeName); .WriteStartNewObject(_context.Host.GeneratedClassContext.TemplateTypeName);
using (_writer.BuildLambda(endLine: false, parameterNames: ValueWriterName)) using (_writer.BuildLambda(endLine: false, parameterNames: ValueWriterName))
{ {
@ -227,7 +224,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
protected override void Visit(LiteralCodeAttributeChunk chunk) protected override void Visit(LiteralCodeAttributeChunk chunk)
{ {
if (_host.DesignTimeMode) if (_context.Host.DesignTimeMode)
{ {
return; // Don't generate anything! return; // Don't generate anything!
} }
@ -262,20 +259,20 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
protected override void Visit(CodeAttributeChunk chunk) protected override void Visit(CodeAttributeChunk chunk)
{ {
if (_host.DesignTimeMode) if (_context.Host.DesignTimeMode)
{ {
return; // Don't generate anything! return; // Don't generate anything!
} }
if (!String.IsNullOrEmpty(chunk.WriterName)) if (!String.IsNullOrEmpty(chunk.WriterName))
{ {
_writer.WriteStartMethodInvocation(_host.GeneratedClassContext.WriteAttributeToMethodName) _writer.WriteStartMethodInvocation(_context.Host.GeneratedClassContext.WriteAttributeToMethodName)
.Write(chunk.WriterName) .Write(chunk.WriterName)
.WriteParameterSeparator(); .WriteParameterSeparator();
} }
else else
{ {
_writer.WriteStartMethodInvocation(_host.GeneratedClassContext.WriteAttributeMethodName); _writer.WriteStartMethodInvocation(_context.Host.GeneratedClassContext.WriteAttributeMethodName);
} }
_writer.WriteStringLiteral(chunk.Attribute) _writer.WriteStringLiteral(chunk.Attribute)
@ -291,7 +288,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
protected override void Visit(SectionChunk chunk) protected override void Visit(SectionChunk chunk)
{ {
_writer.WriteStartMethodInvocation(_host.GeneratedClassContext.DefineSectionMethodName) _writer.WriteStartMethodInvocation(_context.Host.GeneratedClassContext.DefineSectionMethodName)
.WriteStringLiteral(chunk.Name) .WriteStringLiteral(chunk.Name)
.WriteParameterSeparator(); .WriteParameterSeparator();

View File

@ -5,27 +5,24 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
{ {
internal const string InheritsHelper = "__inheritsHelper"; internal const string InheritsHelper = "__inheritsHelper";
private CSharpCodeWriter _writer; private readonly CSharpCodeWriter _writer;
// TODO: No need for the entire host private readonly CodeGeneratorContext _context;
private RazorEngineHost _host;
private string _sourceFile;
public CSharpDesignTimeHelpersVisitor(CSharpCodeWriter writer, RazorEngineHost host, string sourceFile) public CSharpDesignTimeHelpersVisitor(CSharpCodeWriter writer, CodeGeneratorContext context)
{ {
_writer = writer; _writer = writer;
_host = host; _context = context;
_sourceFile = sourceFile;
} }
public void Accept(CodeTree tree) public override void Accept(System.Collections.Generic.IList<Chunk> chunks)
{ {
if(_host.DesignTimeMode) if (_context.Host.DesignTimeMode)
{ {
using(_writer.BuildMethodDeclaration("private","void", "@"+CodeGeneratorContext.DesignTimeHelperMethodName)) using (_writer.BuildMethodDeclaration("private", "void", "@" + CodeGeneratorContext.DesignTimeHelperMethodName))
{ {
using (_writer.BuildDisableWarningScope()) using (_writer.BuildDisableWarningScope())
{ {
Accept(tree.Chunks); Accept(chunks);
} }
} }
} }
@ -33,9 +30,9 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
protected override void Visit(SetBaseTypeChunk chunk) protected override void Visit(SetBaseTypeChunk chunk)
{ {
if (_host.DesignTimeMode) if (_context.Host.DesignTimeMode)
{ {
using (CSharpLineMappingWriter lineMappingWriter = _writer.BuildLineMapping(chunk.Start, chunk.TypeName.Length, _sourceFile)) using (CSharpLineMappingWriter lineMappingWriter = _writer.BuildLineMapping(chunk.Start, chunk.TypeName.Length, _context.SourceFile))
{ {
_writer.Indent(chunk.Start.CharacterIndex); _writer.Indent(chunk.Start.CharacterIndex);

View File

@ -6,28 +6,26 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
{ {
private const string HelperWriterName = "__razor_helper_writer"; private const string HelperWriterName = "__razor_helper_writer";
private CSharpCodeWriter _writer; private readonly CSharpCodeWriter _writer;
private string _sourceFile; private readonly CodeGeneratorContext _context;
private RazorEngineHost _host;
private CSharpCodeVisitor _codeVisitor; private CSharpCodeVisitor _codeVisitor;
public CSharpHelperVisitor(CSharpCodeWriter writer, RazorEngineHost host, string sourceFile) public CSharpHelperVisitor(CSharpCodeWriter writer, CodeGeneratorContext context)
{ {
_writer = writer; _writer = writer;
_sourceFile = sourceFile; _context = context;
_host = host; _codeVisitor = new CSharpCodeVisitor(writer, context);
_codeVisitor = new CSharpCodeVisitor(writer, host, sourceFile);
} }
protected override void Visit(HelperChunk chunk) protected override void Visit(HelperChunk chunk)
{ {
IDisposable lambdaScope = null; IDisposable lambdaScope = null;
using (CSharpLineMappingWriter mappingWriter = _writer.BuildLineMapping(chunk.Signature.Location, chunk.Signature.Value.Length, _sourceFile)) using (CSharpLineMappingWriter mappingWriter = _writer.BuildLineMapping(chunk.Signature.Location, chunk.Signature.Value.Length, _context.SourceFile))
{ {
string accessibility = "public " + (_host.StaticHelpers ? "static" : String.Empty); string accessibility = "public " + (_context.Host.StaticHelpers ? "static" : String.Empty);
_writer.Write(accessibility).Write(" ").Write(_host.GeneratedClassContext.TemplateTypeName).Write(" "); _writer.Write(accessibility).Write(" ").Write(_context.Host.GeneratedClassContext.TemplateTypeName).Write(" ");
mappingWriter.MarkLineMappingStart(); mappingWriter.MarkLineMappingStart();
_writer.Write(chunk.Signature); _writer.Write(chunk.Signature);
mappingWriter.MarkLineMappingEnd(); mappingWriter.MarkLineMappingEnd();
@ -36,7 +34,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
if(chunk.HeaderComplete) if(chunk.HeaderComplete)
{ {
_writer.WriteStartReturn() _writer.WriteStartReturn()
.WriteStartNewObject(_host.GeneratedClassContext.TemplateTypeName); .WriteStartNewObject(_context.Host.GeneratedClassContext.TemplateTypeName);
lambdaScope = _writer.BuildLambda(endLine: false, parameterNames: HelperWriterName); lambdaScope = _writer.BuildLambda(endLine: false, parameterNames: HelperWriterName);
} }
@ -52,7 +50,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
if(chunk.Footer != null && !String.IsNullOrEmpty(chunk.Footer.Value)) if(chunk.Footer != null && !String.IsNullOrEmpty(chunk.Footer.Value))
{ {
using(_writer.BuildLineMapping(chunk.Footer.Location, chunk.Footer.Value.Length, _sourceFile)) using(_writer.BuildLineMapping(chunk.Footer.Location, chunk.Footer.Value.Length, _context.SourceFile))
{ {
_writer.Write(chunk.Footer); _writer.Write(chunk.Footer);
} }

View File

@ -5,12 +5,12 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
public class CSharpTypeMemberVisitor : CodeVisitor public class CSharpTypeMemberVisitor : CodeVisitor
{ {
private CSharpCodeWriter _writer; private CSharpCodeWriter _writer;
private string _sourceFile; private CodeGeneratorContext _context;
public CSharpTypeMemberVisitor(CSharpCodeWriter writer, string sourceFile) public CSharpTypeMemberVisitor(CSharpCodeWriter writer, CodeGeneratorContext context)
{ {
_writer = writer; _writer = writer;
_sourceFile = sourceFile; _context = context;
} }
protected override void Visit(TypeMemberChunk chunk) protected override void Visit(TypeMemberChunk chunk)
@ -19,7 +19,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
if (code != null) if (code != null)
{ {
using (_writer.BuildLineMapping(chunk.Start, code.Value.Length, _sourceFile)) using (_writer.BuildLineMapping(chunk.Start, code.Value.Length, _context.SourceFile))
{ {
_writer.WriteLine(code.Value); _writer.WriteLine(code.Value);
} }

View File

@ -5,7 +5,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler
{ {
public abstract class ChunkVisitor : IChunkVisitor public abstract class ChunkVisitor : IChunkVisitor
{ {
public void Accept(IList<Chunk> chunks) public virtual void Accept(IList<Chunk> chunks)
{ {
if (chunks == null) if (chunks == null)
{ {

View File

@ -1,18 +1,20 @@
 
namespace Microsoft.AspNet.Razor.Generator.Compiler namespace Microsoft.AspNet.Razor.Generator.Compiler
{ {
public class CodeBuilder public abstract class CodeBuilder
{ {
protected CodeTree Tree; private readonly CodeGeneratorContext _context;
public CodeBuilder(CodeTree codeTree) public CodeBuilder(CodeGeneratorContext context)
{ {
Tree = codeTree; _context = context;
} }
public virtual CodeBuilderResult Build() protected CodeGeneratorContext Context
{ {
return null; get { return _context; }
} }
public abstract CodeBuilderResult Build();
} }
} }

View File

@ -6,8 +6,8 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler
{ {
public class CodeTreeBuilder public class CodeTreeBuilder
{ {
private readonly Stack<ChunkBlock> _blockChain;
private Chunk _lastChunk; private Chunk _lastChunk;
private Stack<ChunkBlock> _blockChain;
public CodeTreeBuilder() public CodeTreeBuilder()
{ {

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNet.Razor.Generator; using Microsoft.AspNet.Razor.Generator;
using Microsoft.AspNet.Razor.Generator.Compiler;
using Microsoft.AspNet.Razor.Parser; using Microsoft.AspNet.Razor.Parser;
namespace Microsoft.AspNet.Razor namespace Microsoft.AspNet.Razor
@ -59,5 +60,7 @@ namespace Microsoft.AspNet.Razor
/// Constructs the code generator. Must return a new instance on EVERY call to ensure thread-safety /// Constructs the code generator. Must return a new instance on EVERY call to ensure thread-safety
/// </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 CreateBuilder(CodeGeneratorContext codeGeneratorContext);
} }
} }

View File

@ -184,7 +184,7 @@ namespace Microsoft.AspNet.Razor
#endif #endif
} }
var builder = new CSharpCodeBuilder(generator.Context.CodeTreeBuilder.CodeTree, rootNamespace, Host, sourceFileName); var builder = new CSharpCodeBuilder(generator.Context);
CodeBuilderResult builderResult = builder.Build(); CodeBuilderResult builderResult = builder.Build();
// Collect results and return // Collect results and return

View File

@ -0,0 +1,52 @@
using Microsoft.AspNet.Razor.Generator;
using Microsoft.AspNet.Razor.Generator.Compiler;
using Microsoft.AspNet.Razor.Generator.Compiler.CSharp;
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
using Microsoft.TestCommon;
using Moq;
namespace Microsoft.AspNet.Razor.Test.Generator.CodeTree
{
public class CSharpCodeBuilderTests
{
[Fact]
public void CodeTreeWithUsings()
{
var syntaxTreeNode = Mock.Of<SyntaxTreeNode>();
var language = new CSharpRazorCodeLanguage();
RazorEngineHost host = new RazorEngineHost(language);
var context = CodeGeneratorContext.Create(host, "TestClass", "TestNamespace", "Foo.cs", shouldGenerateLinePragmas: false);
context.CodeTreeBuilder.AddUsingChunk("FakeNamespace1", syntaxTreeNode, context);
context.CodeTreeBuilder.AddUsingChunk("FakeNamespace2.SubNamespace", syntaxTreeNode, context);
CodeBuilder codeBuilder = language.CreateBuilder(context);
// Act
CodeBuilderResult result = codeBuilder.Build();
// Assert
Assert.Equal(@"namespace TestNamespace
{
#line 1 """"
using FakeNamespace1;
#line default
#line hidden
#line 1 """"
using FakeNamespace2.SubNamespace;
#line default
#line hidden
public class TestClass
{
#line hidden
public TestClass()
{
}
public override void Execute()
{
}
}
}", result.Code.TrimEnd());
}
}
}

View File

@ -51,6 +51,7 @@
<Compile Include="Framework\TestSpanBuilder.cs" /> <Compile Include="Framework\TestSpanBuilder.cs" />
<Compile Include="Generator\CodeTree\CodeTreeGenerationTest.cs" /> <Compile Include="Generator\CodeTree\CodeTreeGenerationTest.cs" />
<Compile Include="Generator\CodeTree\CodeTreeOutputValidator.cs" /> <Compile Include="Generator\CodeTree\CodeTreeOutputValidator.cs" />
<Compile Include="Generator\CodeTree\CSharpCodeBuilderTests.cs" />
<Compile Include="Generator\GeneratedCodeMappingTest.cs" /> <Compile Include="Generator\GeneratedCodeMappingTest.cs" />
<Compile Include="Generator\PaddingTest.cs" /> <Compile Include="Generator\PaddingTest.cs" />
<Compile Include="Generator\TabTest.cs" /> <Compile Include="Generator\TabTest.cs" />