Fix CodeGeneration process to format correctly within cshtml.
Added newlines inbetween ending line pragma's and code. Without the extra line the document does not format correctly. Separated expression and statement padding functionality. Statements need to have 1 less padding to account for the transition. Changed how runtime and design time code generates to enable accurate debugging experiences in runtime and functional formatting experiences during design time.
This commit is contained in:
parent
b6082d1523
commit
549e36b803
|
|
@ -25,6 +25,11 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
|
|||
return (CSharpCodeWriter)base.Indent(size);
|
||||
}
|
||||
|
||||
public CSharpCodeWriter ResetIndent()
|
||||
{
|
||||
return (CSharpCodeWriter)base.ResetIndent();
|
||||
}
|
||||
|
||||
public CSharpCodeWriter SetIndent(int size)
|
||||
{
|
||||
return (CSharpCodeWriter)base.SetIndent(size);
|
||||
|
|
|
|||
|
|
@ -11,8 +11,9 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
|
|||
private SourceLocation _generatedLocation;
|
||||
private int _startIndent;
|
||||
private int _generatedContentLength;
|
||||
private bool _writePragmas;
|
||||
|
||||
public CSharpLineMappingWriter(CSharpCodeWriter writer, SourceLocation documentLocation, int contentLength, string sourceFilename)
|
||||
public CSharpLineMappingWriter(CSharpCodeWriter writer, SourceLocation documentLocation, int contentLength)
|
||||
{
|
||||
_writer = writer;
|
||||
_documentMapping = new MappingLocation(documentLocation, contentLength);
|
||||
|
|
@ -20,6 +21,14 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
|
|||
_startIndent = _writer.CurrentIndent;
|
||||
_generatedContentLength = 0;
|
||||
_writer.ResetIndent();
|
||||
|
||||
_generatedLocation = _writer.GetCurrentSourceLocation();
|
||||
}
|
||||
|
||||
public CSharpLineMappingWriter(CSharpCodeWriter writer, SourceLocation documentLocation, int contentLength, string sourceFilename)
|
||||
: this(writer, documentLocation, contentLength)
|
||||
{
|
||||
_writePragmas = true;
|
||||
|
||||
// TODO: Should this just be '\n'?
|
||||
if (_writer.LastWrite.Last() != '\n')
|
||||
|
|
@ -49,9 +58,9 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
|
|||
{
|
||||
_generatedContentLength = _writer.ToString().Length - _generatedLocation.AbsoluteIndex;
|
||||
}
|
||||
|
||||
|
||||
var generatedLocation = new MappingLocation(_generatedLocation, _generatedContentLength);
|
||||
if(_documentMapping.ContentLength == -1)
|
||||
if (_documentMapping.ContentLength == -1)
|
||||
{
|
||||
_documentMapping.ContentLength = generatedLocation.ContentLength;
|
||||
}
|
||||
|
|
@ -60,13 +69,24 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
|
|||
documentLocation: _documentMapping,
|
||||
generatedLocation: new MappingLocation(_generatedLocation, _generatedContentLength));
|
||||
|
||||
if (_writer.ToString().Last() != '\n')
|
||||
if (_writePragmas)
|
||||
{
|
||||
_writer.WriteLine();
|
||||
}
|
||||
// Need to add an additional line at the end IF there wasn't one already written.
|
||||
// This is needed to work with the C# editor's handling of #line ...
|
||||
bool writeExtraLine = _writer.ToString().Last() != '\n';
|
||||
|
||||
_writer.WriteLineDefaultDirective();
|
||||
_writer.WriteLineHiddenDirective();
|
||||
// Always write at least 1 empty line to potentially separate code from pragmas.
|
||||
_writer.WriteLine();
|
||||
|
||||
// Check if the previous empty line wasn't enough to separate code from pragmas.
|
||||
if (writeExtraLine)
|
||||
{
|
||||
_writer.WriteLine();
|
||||
}
|
||||
|
||||
_writer.WriteLineDefaultDirective()
|
||||
.WriteLineHiddenDirective();
|
||||
}
|
||||
|
||||
// Reset indent back to when it was started
|
||||
_writer.SetIndent(_startIndent);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
|
|||
throw new ArgumentNullException("target");
|
||||
}
|
||||
|
||||
int padding = CalculatePadding(target);
|
||||
int padding = CalculatePadding(target, 0);
|
||||
|
||||
// We treat statement padding specially so for brace positioning, so that in the following example:
|
||||
// @if (foo > 0)
|
||||
|
|
@ -44,21 +44,38 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
|
|||
return generatedCode;
|
||||
}
|
||||
|
||||
public string BuildExpressionPadding(Span target)
|
||||
public string BuildExpressionPadding(Span target, int generatedStart = 0)
|
||||
{
|
||||
int padding = CalculatePadding(target);
|
||||
int padding = CalculatePadding(target, generatedStart);
|
||||
|
||||
return BuildPaddingInternal(padding);
|
||||
}
|
||||
|
||||
internal int CalculatePadding(Span target)
|
||||
internal int CalculatePadding(Span target, int generatedStart)
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
throw new ArgumentNullException("target");
|
||||
}
|
||||
|
||||
return CollectSpacesAndTabs(target, _host.TabSize);
|
||||
int padding;
|
||||
|
||||
padding = CollectSpacesAndTabs(target, _host.TabSize) - generatedStart;
|
||||
|
||||
// if we add generated text that is longer than the padding we wanted to insert we have no recourse and we have to skip padding
|
||||
// example:
|
||||
// Razor code at column zero: @somecode()
|
||||
// Generated code will be:
|
||||
// In design time: __o = somecode();
|
||||
// In Run time: Write(somecode());
|
||||
//
|
||||
// In both cases the padding would have been 1 space to remote the space the @ symbol takes, which will be smaller than the 6 chars the hidden generated code takes.
|
||||
if (padding < 0)
|
||||
{
|
||||
padding = 0;
|
||||
}
|
||||
|
||||
return padding;
|
||||
}
|
||||
|
||||
private string BuildPaddingInternal(int padding)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
||||
using Microsoft.AspNet.Razor.Text;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
|
||||
{
|
||||
|
|
@ -115,53 +116,25 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
|
|||
protected override void Visit(ExpressionBlockChunk chunk)
|
||||
{
|
||||
// TODO: Handle instrumentation
|
||||
// TODO: Refactor
|
||||
|
||||
if (!Context.Host.DesignTimeMode && Context.ExpressionRenderingMode == ExpressionRenderingMode.InjectCode)
|
||||
if (Context.Host.DesignTimeMode)
|
||||
{
|
||||
Visit((ChunkBlock)chunk);
|
||||
RenderDesignTimeExpressionBlockChunk(chunk);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Context.Host.DesignTimeMode)
|
||||
{
|
||||
Writer.WriteStartAssignment("__o");
|
||||
}
|
||||
else if (Context.ExpressionRenderingMode == ExpressionRenderingMode.WriteToOutput)
|
||||
{
|
||||
if (!String.IsNullOrEmpty(Context.TargetWriterName))
|
||||
{
|
||||
Writer.WriteStartMethodInvocation(Context.Host.GeneratedClassContext.WriteToMethodName)
|
||||
.Write(Context.TargetWriterName)
|
||||
.WriteParameterSeparator();
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.WriteStartMethodInvocation(Context.Host.GeneratedClassContext.WriteMethodName);
|
||||
}
|
||||
}
|
||||
|
||||
Visit((ChunkBlock)chunk);
|
||||
|
||||
if (Context.Host.DesignTimeMode)
|
||||
{
|
||||
Writer.WriteLine(";");
|
||||
}
|
||||
else if (Context.ExpressionRenderingMode == ExpressionRenderingMode.WriteToOutput)
|
||||
{
|
||||
Writer.WriteEndMethodInvocation();
|
||||
}
|
||||
RenderRuntimeExpressionBlockChunk(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Visit(ExpressionChunk chunk)
|
||||
{
|
||||
CreateCodeMapping(chunk.Code, chunk);
|
||||
CreateExpressionCodeMapping(chunk.Code, chunk);
|
||||
}
|
||||
|
||||
protected override void Visit(StatementChunk chunk)
|
||||
{
|
||||
CreateCodeMapping(chunk.Code, chunk);
|
||||
CreateStatementCodeMapping(chunk.Code, chunk);
|
||||
Writer.WriteLine();
|
||||
}
|
||||
|
||||
|
|
@ -308,16 +281,101 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
|
|||
Writer.WriteEndMethodInvocation();
|
||||
}
|
||||
|
||||
public void CreateCodeMapping(string code, Chunk chunk)
|
||||
public void RenderDesignTimeExpressionBlockChunk(ExpressionBlockChunk chunk)
|
||||
{
|
||||
// TODO: Handle instrumentation
|
||||
|
||||
int currentIndent = Writer.CurrentIndent;
|
||||
string designTimeAssignment = "__o = ";
|
||||
|
||||
// The first child should never be null, it should always be a transition span, that's what
|
||||
// defines an expression block chunk.
|
||||
var firstChild = (ExpressionChunk)chunk.Children.FirstOrDefault();
|
||||
|
||||
Writer.ResetIndent()
|
||||
.WriteLineNumberDirective(1, "This is here only for document formatting.")
|
||||
// We build the padding with an offset of the design time assignment statement.
|
||||
.Write(_paddingBuilder.BuildExpressionPadding((Span)firstChild.Association, designTimeAssignment.Length))
|
||||
.Write(designTimeAssignment);
|
||||
|
||||
// We map the first line of code but do not write the line pragmas associated with it.
|
||||
CreateRawCodeMapping(firstChild.Code, firstChild.Association.Start);
|
||||
|
||||
// This is a temporary block that is indentical to the current one with the exception of its children.
|
||||
var subBlock = new ChunkBlock
|
||||
{
|
||||
Start = chunk.Start,
|
||||
Association = chunk.Association,
|
||||
Children = chunk.Children.Skip(1).ToList()
|
||||
};
|
||||
|
||||
// Render all children (except for the first one)
|
||||
Visit(subBlock);
|
||||
|
||||
Writer.WriteLine(";")
|
||||
.WriteLine()
|
||||
.WriteLineDefaultDirective()
|
||||
.WriteLineHiddenDirective()
|
||||
.SetIndent(currentIndent);
|
||||
}
|
||||
|
||||
public void RenderRuntimeExpressionBlockChunk(ExpressionBlockChunk chunk)
|
||||
{
|
||||
// TODO: Handle instrumentation
|
||||
|
||||
if (Context.ExpressionRenderingMode == ExpressionRenderingMode.InjectCode)
|
||||
{
|
||||
Visit((ChunkBlock)chunk);
|
||||
}
|
||||
else if (Context.ExpressionRenderingMode == ExpressionRenderingMode.WriteToOutput)
|
||||
{
|
||||
if (!String.IsNullOrEmpty(Context.TargetWriterName))
|
||||
{
|
||||
Writer.WriteStartMethodInvocation(Context.Host.GeneratedClassContext.WriteToMethodName)
|
||||
.Write(Context.TargetWriterName)
|
||||
.WriteParameterSeparator();
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.WriteStartMethodInvocation(Context.Host.GeneratedClassContext.WriteMethodName);
|
||||
}
|
||||
|
||||
Visit((ChunkBlock)chunk);
|
||||
|
||||
Writer.WriteEndMethodInvocation()
|
||||
.WriteLine();
|
||||
}
|
||||
}
|
||||
|
||||
public void CreateExpressionCodeMapping(string code, Chunk chunk)
|
||||
{
|
||||
CreateCodeMapping(_paddingBuilder.BuildExpressionPadding((Span)chunk.Association), code, chunk);
|
||||
}
|
||||
|
||||
public void CreateStatementCodeMapping(string code, Chunk chunk)
|
||||
{
|
||||
CreateCodeMapping(_paddingBuilder.BuildStatementPadding((Span)chunk.Association), code, chunk);
|
||||
}
|
||||
|
||||
public void CreateCodeMapping(string padding, string code, Chunk chunk)
|
||||
{
|
||||
using (CSharpLineMappingWriter mappingWriter = Writer.BuildLineMapping(chunk.Start, code.Length, Context.SourceFile))
|
||||
{
|
||||
Writer.Write(_paddingBuilder.BuildExpressionPadding((Span)chunk.Association));
|
||||
Writer.Write(padding);
|
||||
|
||||
mappingWriter.MarkLineMappingStart();
|
||||
Writer.Write(code);
|
||||
mappingWriter.MarkLineMappingEnd();
|
||||
}
|
||||
}
|
||||
|
||||
// Raw CodeMapping's do not write out line pragmas, they just map code.
|
||||
public void CreateRawCodeMapping(string code, SourceLocation documentLocation)
|
||||
{
|
||||
using (new CSharpLineMappingWriter(Writer, documentLocation, code.Length))
|
||||
{
|
||||
Writer.Write(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
|
|||
{
|
||||
if (!String.IsNullOrEmpty(chunk.Code))
|
||||
{
|
||||
_csharpCodeVisitor.CreateCodeMapping(chunk.Code, chunk);
|
||||
_csharpCodeVisitor.CreateCodeMapping(String.Empty, chunk.Code, chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue