Generate line mappings for empty expressions.

- At design time we weren't generating line mappings when a user would type `@` or `@(`. This results in no C# IntelliSense being provided to the user because the editor hasn't mapped any of Razor to the C# buffer.
- Updated the design time renderer and design time writer to account for empty expressions.
- Modified the `DefaultIRLoweringPhase` to set source locations on empty expression nodes.
- Re-generated test files to account for 0 length line mappings on empty expression nodes.

#1155
This commit is contained in:
N. Taylor Mullen 2017-04-02 22:17:48 -07:00
parent 660db5b733
commit 4c0afbad86
28 changed files with 136 additions and 43 deletions

View File

@ -21,11 +21,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
throw new ArgumentNullException(nameof(node)); throw new ArgumentNullException(nameof(node));
} }
if (node.Children.Count == 0)
{
return;
}
if (node.Source != null) if (node.Source != null)
{ {
using (context.Writer.BuildLinePragma(node.Source.Value)) using (context.Writer.BuildLinePragma(node.Source.Value))
@ -34,19 +29,29 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
context.Writer.WritePadding(offset, node.Source, context); context.Writer.WritePadding(offset, node.Source, context);
context.Writer.WriteStartAssignment(RazorDesignTimeIRPass.DesignTimeVariable); context.Writer.WriteStartAssignment(RazorDesignTimeIRPass.DesignTimeVariable);
for (var i = 0; i < node.Children.Count; i++) if (node.Children.Count > 0)
{ {
if (node.Children[i] is RazorIRToken token && token.IsCSharp) for (var i = 0; i < node.Children.Count; i++)
{ {
context.AddLineMappingFor(token); if (node.Children[i] is RazorIRToken token && token.IsCSharp)
context.Writer.Write(token.Content); {
} context.AddLineMappingFor(token);
else context.Writer.Write(token.Content);
{ }
// There may be something else inside the expression like a Template or another extension node. else
context.RenderNode(node.Children[i]); {
// There may be something else inside the expression like a Template or another extension node.
context.RenderNode(node.Children[i]);
}
} }
} }
else
{
// When typing "@" / "@(" we still need to provide IntelliSense. This is taken care of by creating a 0 length
// line mapping. It's also important that this 0 length line mapping exists in a line pragma so when a user
// starts typing additional characters following their "@" they get appropriately located errors.
context.AddLineMappingFor(node);
}
context.Writer.WriteLine(";"); context.Writer.WriteLine(";");
} }

View File

@ -19,10 +19,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
public override void VisitCSharpExpression(CSharpExpressionIRNode node) public override void VisitCSharpExpression(CSharpExpressionIRNode node)
{ {
// We can't remove this yet, because it's still used recursively in a few places. // We can't remove this yet, because it's still used recursively in a few places.
if (node.Children.Count == 0)
{
return;
}
if (node.Source != null) if (node.Source != null)
{ {
@ -35,20 +31,30 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
.Write(padding) .Write(padding)
.WriteStartAssignment(RazorDesignTimeIRPass.DesignTimeVariable); .WriteStartAssignment(RazorDesignTimeIRPass.DesignTimeVariable);
for (var i = 0; i < node.Children.Count; i++) if (node.Children.Count > 0)
{ {
for (var i = 0; i < node.Children.Count; i++)
{
var token = node.Children[i] as RazorIRToken; var token = node.Children[i] as RazorIRToken;
if (token != null && token.IsCSharp) if (token != null && token.IsCSharp)
{ {
Context.AddLineMappingFor(token); Context.AddLineMappingFor(token);
Context.Writer.Write(token.Content); Context.Writer.Write(token.Content);
} }
else else
{ {
// There may be something else inside the expression like a Template or another extension node. // There may be something else inside the expression like a Template or another extension node.
Visit(node.Children[i]); Visit(node.Children[i]);
}
} }
} }
else
{
// When typing "@" / "@(" we still need to provide IntelliSense. This is taken care of by creating a 0 length
// line mapping. It's also important that this 0 length line mapping exists in a line pragma so when a user
// starts typing additional characters following their "@" they get appropriately located errors.
Context.AddLineMappingFor(node);
}
Context.Writer.WriteLine(";"); Context.Writer.WriteLine(";");
} }

View File

@ -344,6 +344,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
_builder.Pop(); _builder.Pop();
var emptyExpression = true;
if (expressionNode.Children.Count > 0) if (expressionNode.Children.Count > 0)
{ {
var sourceRangeStart = expressionNode var sourceRangeStart = expressionNode
@ -361,8 +362,19 @@ namespace Microsoft.AspNetCore.Razor.Evolution
sourceRangeStart.Value.LineIndex, sourceRangeStart.Value.LineIndex,
sourceRangeStart.Value.CharacterIndex, sourceRangeStart.Value.CharacterIndex,
contentLength); contentLength);
emptyExpression = false;
} }
} }
if (emptyExpression)
{
expressionNode.Source = new SourceSpan(
block.Start.FilePath ?? FileName,
block.Start.AbsoluteIndex,
block.Start.LineIndex,
block.Start.CharacterIndex,
length: 0);
}
} }
public override void VisitExpressionSpan(ExpressionChunkGenerator chunkGenerator, Span span) public override void VisitExpressionSpan(ExpressionChunkGenerator chunkGenerator, Span span)

View File

@ -13,6 +13,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests.TestFiles
#pragma warning disable 1998 #pragma warning disable 1998
public async System.Threading.Tasks.Task ExecuteAsync() public async System.Threading.Tasks.Task ExecuteAsync()
{ {
#line 3 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/EmptyExplicitExpression.cshtml"
__o = ;
#line default
#line hidden
} }
#pragma warning restore 1998 #pragma warning restore 1998
} }

View File

@ -17,4 +17,4 @@ Document -
RazorIRToken - - CSharp - private static System.Object __o = null; RazorIRToken - - CSharp - private static System.Object __o = null;
RazorMethodDeclaration - - public - async - System.Threading.Tasks.Task - ExecuteAsync RazorMethodDeclaration - - public - async - System.Threading.Tasks.Task - ExecuteAsync
HtmlContent - (0:0,0 [18] EmptyExplicitExpression.cshtml) - This is markup\n\n HtmlContent - (0:0,0 [18] EmptyExplicitExpression.cshtml) - This is markup\n\n
CSharpExpression - CSharpExpression - (18:2,0 [0] EmptyExplicitExpression.cshtml)

View File

@ -0,0 +1,5 @@
Source Location: (18:2,0 [0] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/EmptyExplicitExpression.cshtml)
||
Generated Location: (678:16,6 [0] )
||

View File

@ -10,7 +10,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests.TestFiles
public async System.Threading.Tasks.Task ExecuteAsync() public async System.Threading.Tasks.Task ExecuteAsync()
{ {
WriteLiteral("This is markup\r\n\r\n"); WriteLiteral("This is markup\r\n\r\n");
Write(); #line 3 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/EmptyExplicitExpression.cshtml"
Write();
#line default
#line hidden
} }
#pragma warning restore 1998 #pragma warning restore 1998
} }

View File

@ -6,4 +6,4 @@ Document -
ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_EmptyExplicitExpression_Runtime - - ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_EmptyExplicitExpression_Runtime - -
RazorMethodDeclaration - - public - async - System.Threading.Tasks.Task - ExecuteAsync RazorMethodDeclaration - - public - async - System.Threading.Tasks.Task - ExecuteAsync
HtmlContent - (0:0,0 [18] EmptyExplicitExpression.cshtml) - This is markup\n\n HtmlContent - (0:0,0 [18] EmptyExplicitExpression.cshtml) - This is markup\n\n
CSharpExpression - CSharpExpression - (18:2,0 [0] EmptyExplicitExpression.cshtml)

View File

@ -15,6 +15,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests.TestFiles
{ {
#line 2 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/EmptyImplicitExpressionInCode.cshtml"
__o = ;
#line default
#line hidden
} }

View File

@ -18,6 +18,6 @@ Document -
RazorMethodDeclaration - - public - async - System.Threading.Tasks.Task - ExecuteAsync RazorMethodDeclaration - - public - async - System.Threading.Tasks.Task - ExecuteAsync
CSharpStatement - (2:0,2 [6] EmptyImplicitExpressionInCode.cshtml) CSharpStatement - (2:0,2 [6] EmptyImplicitExpressionInCode.cshtml)
RazorIRToken - (2:0,2 [6] EmptyImplicitExpressionInCode.cshtml) - CSharp - \n RazorIRToken - (2:0,2 [6] EmptyImplicitExpressionInCode.cshtml) - CSharp - \n
CSharpExpression - CSharpExpression - (8:1,4 [0] EmptyImplicitExpressionInCode.cshtml)
CSharpStatement - (9:1,5 [2] EmptyImplicitExpressionInCode.cshtml) CSharpStatement - (9:1,5 [2] EmptyImplicitExpressionInCode.cshtml)
RazorIRToken - (9:1,5 [2] EmptyImplicitExpressionInCode.cshtml) - CSharp - \n RazorIRToken - (9:1,5 [2] EmptyImplicitExpressionInCode.cshtml) - CSharp - \n

View File

@ -5,10 +5,15 @@ Generated Location: (593:15,14 [6] )
| |
| |
Source Location: (8:1,4 [0] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/EmptyImplicitExpressionInCode.cshtml)
||
Generated Location: (712:18,6 [0] )
||
Source Location: (9:1,5 [2] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/EmptyImplicitExpressionInCode.cshtml) Source Location: (9:1,5 [2] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/EmptyImplicitExpressionInCode.cshtml)
| |
| |
Generated Location: (618:17,17 [2] ) Generated Location: (763:22,17 [2] )
| |
| |

View File

@ -9,7 +9,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests.TestFiles
#pragma warning disable 1998 #pragma warning disable 1998
public async System.Threading.Tasks.Task ExecuteAsync() public async System.Threading.Tasks.Task ExecuteAsync()
{ {
Write(); #line 2 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/EmptyImplicitExpressionInCode.cshtml"
Write();
#line default
#line hidden
} }
#pragma warning restore 1998 #pragma warning restore 1998
} }

View File

@ -7,6 +7,6 @@ Document -
RazorMethodDeclaration - - public - async - System.Threading.Tasks.Task - ExecuteAsync RazorMethodDeclaration - - public - async - System.Threading.Tasks.Task - ExecuteAsync
CSharpStatement - (2:0,2 [6] EmptyImplicitExpressionInCode.cshtml) CSharpStatement - (2:0,2 [6] EmptyImplicitExpressionInCode.cshtml)
RazorIRToken - (2:0,2 [6] EmptyImplicitExpressionInCode.cshtml) - CSharp - \n RazorIRToken - (2:0,2 [6] EmptyImplicitExpressionInCode.cshtml) - CSharp - \n
CSharpExpression - CSharpExpression - (8:1,4 [0] EmptyImplicitExpressionInCode.cshtml)
CSharpStatement - (9:1,5 [2] EmptyImplicitExpressionInCode.cshtml) CSharpStatement - (9:1,5 [2] EmptyImplicitExpressionInCode.cshtml)
RazorIRToken - (9:1,5 [2] EmptyImplicitExpressionInCode.cshtml) - CSharp - \n RazorIRToken - (9:1,5 [2] EmptyImplicitExpressionInCode.cshtml) - CSharp - \n

View File

@ -13,6 +13,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests.TestFiles
#pragma warning disable 1998 #pragma warning disable 1998
public async System.Threading.Tasks.Task ExecuteAsync() public async System.Threading.Tasks.Task ExecuteAsync()
{ {
#line 3 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/EmptyImplicitExpression.cshtml"
__o = ;
#line default
#line hidden
} }
#pragma warning restore 1998 #pragma warning restore 1998
} }

View File

@ -17,5 +17,5 @@ Document -
RazorIRToken - - CSharp - private static System.Object __o = null; RazorIRToken - - CSharp - private static System.Object __o = null;
RazorMethodDeclaration - - public - async - System.Threading.Tasks.Task - ExecuteAsync RazorMethodDeclaration - - public - async - System.Threading.Tasks.Task - ExecuteAsync
HtmlContent - (0:0,0 [18] EmptyImplicitExpression.cshtml) - This is markup\n\n HtmlContent - (0:0,0 [18] EmptyImplicitExpression.cshtml) - This is markup\n\n
CSharpExpression - CSharpExpression - (18:2,0 [0] EmptyImplicitExpression.cshtml)
HtmlContent - (19:2,1 [1] EmptyImplicitExpression.cshtml) - ! HtmlContent - (19:2,1 [1] EmptyImplicitExpression.cshtml) - !

View File

@ -0,0 +1,5 @@
Source Location: (18:2,0 [0] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/EmptyImplicitExpression.cshtml)
||
Generated Location: (678:16,6 [0] )
||

View File

@ -10,7 +10,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests.TestFiles
public async System.Threading.Tasks.Task ExecuteAsync() public async System.Threading.Tasks.Task ExecuteAsync()
{ {
WriteLiteral("This is markup\r\n\r\n"); WriteLiteral("This is markup\r\n\r\n");
Write(); #line 3 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/EmptyImplicitExpression.cshtml"
Write();
#line default
#line hidden
WriteLiteral("!"); WriteLiteral("!");
} }
#pragma warning restore 1998 #pragma warning restore 1998

View File

@ -6,5 +6,5 @@ Document -
ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_EmptyImplicitExpression_Runtime - - ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_EmptyImplicitExpression_Runtime - -
RazorMethodDeclaration - - public - async - System.Threading.Tasks.Task - ExecuteAsync RazorMethodDeclaration - - public - async - System.Threading.Tasks.Task - ExecuteAsync
HtmlContent - (0:0,0 [18] EmptyImplicitExpression.cshtml) - This is markup\n\n HtmlContent - (0:0,0 [18] EmptyImplicitExpression.cshtml) - This is markup\n\n
CSharpExpression - CSharpExpression - (18:2,0 [0] EmptyImplicitExpression.cshtml)
HtmlContent - (19:2,1 [1] EmptyImplicitExpression.cshtml) - ! HtmlContent - (19:2,1 [1] EmptyImplicitExpression.cshtml) - !

View File

@ -13,6 +13,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests.TestFiles
#pragma warning disable 1998 #pragma warning disable 1998
public async System.Threading.Tasks.Task ExecuteAsync() public async System.Threading.Tasks.Task ExecuteAsync()
{ {
#line 3 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/ExplicitExpressionAtEOF.cshtml"
__o = ;
#line default
#line hidden
} }
#pragma warning restore 1998 #pragma warning restore 1998
} }

View File

@ -17,4 +17,4 @@ Document -
RazorIRToken - - CSharp - private static System.Object __o = null; RazorIRToken - - CSharp - private static System.Object __o = null;
RazorMethodDeclaration - - public - async - System.Threading.Tasks.Task - ExecuteAsync RazorMethodDeclaration - - public - async - System.Threading.Tasks.Task - ExecuteAsync
HtmlContent - (0:0,0 [18] ExplicitExpressionAtEOF.cshtml) - This is markup\n\n HtmlContent - (0:0,0 [18] ExplicitExpressionAtEOF.cshtml) - This is markup\n\n
CSharpExpression - CSharpExpression - (18:2,0 [0] ExplicitExpressionAtEOF.cshtml)

View File

@ -0,0 +1,5 @@
Source Location: (18:2,0 [0] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/ExplicitExpressionAtEOF.cshtml)
||
Generated Location: (678:16,6 [0] )
||

View File

@ -10,7 +10,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests.TestFiles
public async System.Threading.Tasks.Task ExecuteAsync() public async System.Threading.Tasks.Task ExecuteAsync()
{ {
WriteLiteral("This is markup\r\n\r\n"); WriteLiteral("This is markup\r\n\r\n");
Write(); #line 3 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/ExplicitExpressionAtEOF.cshtml"
Write();
#line default
#line hidden
} }
#pragma warning restore 1998 #pragma warning restore 1998
} }

View File

@ -6,4 +6,4 @@ Document -
ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_ExplicitExpressionAtEOF_Runtime - - ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_ExplicitExpressionAtEOF_Runtime - -
RazorMethodDeclaration - - public - async - System.Threading.Tasks.Task - ExecuteAsync RazorMethodDeclaration - - public - async - System.Threading.Tasks.Task - ExecuteAsync
HtmlContent - (0:0,0 [18] ExplicitExpressionAtEOF.cshtml) - This is markup\n\n HtmlContent - (0:0,0 [18] ExplicitExpressionAtEOF.cshtml) - This is markup\n\n
CSharpExpression - CSharpExpression - (18:2,0 [0] ExplicitExpressionAtEOF.cshtml)

View File

@ -13,6 +13,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests.TestFiles
#pragma warning disable 1998 #pragma warning disable 1998
public async System.Threading.Tasks.Task ExecuteAsync() public async System.Threading.Tasks.Task ExecuteAsync()
{ {
#line 3 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/ImplicitExpressionAtEOF.cshtml"
__o = ;
#line default
#line hidden
} }
#pragma warning restore 1998 #pragma warning restore 1998
} }

View File

@ -17,4 +17,4 @@ Document -
RazorIRToken - - CSharp - private static System.Object __o = null; RazorIRToken - - CSharp - private static System.Object __o = null;
RazorMethodDeclaration - - public - async - System.Threading.Tasks.Task - ExecuteAsync RazorMethodDeclaration - - public - async - System.Threading.Tasks.Task - ExecuteAsync
HtmlContent - (0:0,0 [18] ImplicitExpressionAtEOF.cshtml) - This is markup\n\n HtmlContent - (0:0,0 [18] ImplicitExpressionAtEOF.cshtml) - This is markup\n\n
CSharpExpression - CSharpExpression - (18:2,0 [0] ImplicitExpressionAtEOF.cshtml)

View File

@ -0,0 +1,5 @@
Source Location: (18:2,0 [0] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/ImplicitExpressionAtEOF.cshtml)
||
Generated Location: (678:16,6 [0] )
||

View File

@ -10,7 +10,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests.TestFiles
public async System.Threading.Tasks.Task ExecuteAsync() public async System.Threading.Tasks.Task ExecuteAsync()
{ {
WriteLiteral("This is markup\r\n\r\n"); WriteLiteral("This is markup\r\n\r\n");
Write(); #line 3 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/ImplicitExpressionAtEOF.cshtml"
Write();
#line default
#line hidden
} }
#pragma warning restore 1998 #pragma warning restore 1998
} }

View File

@ -6,4 +6,4 @@ Document -
ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_ImplicitExpressionAtEOF_Runtime - - ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_ImplicitExpressionAtEOF_Runtime - -
RazorMethodDeclaration - - public - async - System.Threading.Tasks.Task - ExecuteAsync RazorMethodDeclaration - - public - async - System.Threading.Tasks.Task - ExecuteAsync
HtmlContent - (0:0,0 [18] ImplicitExpressionAtEOF.cshtml) - This is markup\n\n HtmlContent - (0:0,0 [18] ImplicitExpressionAtEOF.cshtml) - This is markup\n\n
CSharpExpression - CSharpExpression - (18:2,0 [0] ImplicitExpressionAtEOF.cshtml)