Skip to content
This repository was archived by the owner on Dec 19, 2018. It is now read-only.

Added the ability for users to await expressions. #19

Merged
merged 2 commits into from
Mar 21, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.Expressions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using Microsoft.AspNet.Razor.Tokenizer.Symbols;

namespace Microsoft.AspNet.Razor.Parser
{
public partial class CSharpCodeParser
{
private void SetUpExpressions()
{
MapKeywords(AwaitExpression, CSharpKeyword.Await);
}

private void AwaitExpression(bool topLevel)
{
// Ensure that we're on the await statement (only runs in debug)
Assert(CSharpKeyword.Await);

// Accept the "await" and move on
AcceptAndMoveNext();

// Accept 1 or more spaces between the await and the following code.
AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true));

// Accept a single code piece to await. This will accept up until a method "call" signature.
// Ex: "@await |Foo|()" Inbetween the pipes is what is accepted. The Statement/ImplicitExpression
// handling capture method calls and the parameters passed in.
AcceptWhile(CSharpSymbolType.Identifier);

// Top level basically indicates if we're within an expression or statement.
// Ex: topLevel true = @await Foo() | topLevel false = @{ await Foo(); }
// Note that in this case @{ <b>@await Foo()</b> } top level is true for await.
// Therefore, if we're top level then we want to act like an implicit expression,
// otherwise just act as whatever we're contained in.
if (topLevel)
{
// Setup the Span to be an async implicit expression (an implicit expresison that allows spaces).
// Spaces are allowed because of "@await Foo()".
AsyncImplicitExpression();
}
}
}
}
26 changes: 21 additions & 5 deletions src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public partial class CSharpCodeParser : TokenizerBackedParser<CSharpTokenizer, C

internal static ISet<string> DefaultKeywords = new HashSet<string>()
{
"await",
"if",
"do",
"try",
Expand Down Expand Up @@ -45,6 +46,7 @@ public CSharpCodeParser()
Keywords = new HashSet<string>();
SetUpKeywords();
SetupDirectives();
SetUpExpressions();
}

protected internal ISet<string> Keywords { get; private set; }
Expand Down Expand Up @@ -307,14 +309,26 @@ private void VerbatimBlock()
}

private void ImplicitExpression()
{
ImplicitExpression(AcceptedCharacters.NonWhiteSpace);
}

// Async implicit expressions include the "await" keyword and therefore need to allow spaces to
// separate the "await" and the following code.
private void AsyncImplicitExpression()
{
ImplicitExpression(AcceptedCharacters.AnyExceptNewline);
}

private void ImplicitExpression(AcceptedCharacters acceptedCharacters)
{
Context.CurrentBlock.Type = BlockType.Expression;
Context.CurrentBlock.CodeGenerator = new ExpressionCodeGenerator();

using (PushSpanConfig(span =>
{
span.EditHandler = new ImplicitExpressionEditHandler(Language.TokenizeString, Keywords, acceptTrailingDot: IsNested);
span.EditHandler.AcceptedCharacters = AcceptedCharacters.NonWhiteSpace;
span.EditHandler.AcceptedCharacters = acceptedCharacters;
span.CodeGenerator = new ExpressionCodeGenerator();
}))
{
Expand All @@ -325,14 +339,14 @@ private void ImplicitExpression()
AcceptAndMoveNext();
}
}
while (MethodCallOrArrayIndex());
while (MethodCallOrArrayIndex(acceptedCharacters));

PutCurrentBack();
Output(SpanKind.Code);
}
}

private bool MethodCallOrArrayIndex()
private bool MethodCallOrArrayIndex(AcceptedCharacters acceptedCharacters)
{
if (!EndOfFile)
{
Expand Down Expand Up @@ -361,9 +375,11 @@ private bool MethodCallOrArrayIndex()
if (At(right))
{
AcceptAndMoveNext();
Span.EditHandler.AcceptedCharacters = AcceptedCharacters.NonWhiteSpace;

// At the ending brace, restore the initial accepted characters.
Span.EditHandler.AcceptedCharacters = acceptedCharacters;
}
return MethodCallOrArrayIndex();
return MethodCallOrArrayIndex(acceptedCharacters);
}
if (CurrentSymbol.Type == CSharpSymbolType.Dot)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ internal static class CSharpKeywordDetector
{
private static readonly Dictionary<string, CSharpKeyword> _keywords = new Dictionary<string, CSharpKeyword>(StringComparer.Ordinal)
{
{ "await", CSharpKeyword.Await },
{ "abstract", CSharpKeyword.Abstract },
{ "byte", CSharpKeyword.Byte },
{ "class", CSharpKeyword.Class },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ namespace Microsoft.AspNet.Razor.Tokenizer.Symbols
{
public enum CSharpKeyword
{
Await,
Abstract,
Byte,
Class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,39 @@ public void ConstructorRequiresNonNullHost()
[InlineData("LayoutDirective")]
[InlineData("ConditionalAttributes")]
[InlineData("ResolveUrl")]
[InlineData("Await")]
public void CSharpCodeGeneratorCorrectlyGeneratesRunTimeCode(string testType)
{
RunTest(testType);
}

[Fact]
public void CSharpCodeGeneratorCorrectlyGeneratesMappingsForAwait()
{
RunTest("Await",
"Await.DesignTime",
designTimeMode: true,
tabTest: TabTest.Tabs,
expectedDesignTimePragmas: new List<LineMapping>()
{
BuildLineMapping(12, 0, 12, 173, 9, 0, 76),
BuildLineMapping(192, 9, 39, 637, 30, 15, 11),
BuildLineMapping(247, 10, 38, 750, 35, 14, 11),
BuildLineMapping(304, 11, 39, 832, 40, 12, 14),
BuildLineMapping(371, 12, 46, 919, 46, 13, 1),
BuildLineMapping(376, 12, 51, 1027, 52, 18, 11),
BuildLineMapping(391, 12, 66, 1115, 57, 18, 1),
BuildLineMapping(448, 13, 49, 1224, 63, 19, 5),
BuildLineMapping(578, 18, 42, 1332, 68, 15, 15),
BuildLineMapping(640, 19, 41, 1452, 73, 17, 22),
BuildLineMapping(711, 20, 42, 1545, 78, 12, 39),
BuildLineMapping(806, 21, 49, 1657, 84, 13, 1),
BuildLineMapping(811, 21, 54, 1765, 90, 18, 27),
BuildLineMapping(842, 21, 85, 1873, 95, 22, 1),
BuildLineMapping(902, 22, 52, 1982, 101, 19, 19)
});
}

[Fact]
public void CSharpCodeGeneratorCorrectlyGeneratesMappingsForSimpleUnspacedIf()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@
<Compile Include="RazorTemplateEngineTest.cs" />
<Compile Include="StringTextBuffer.cs" />
<EmbeddedResource Include="TestFiles\CodeGenerator\CS\Output\CodeTree.cs" />
<EmbeddedResource Include="TestFiles\CodeGenerator\CS\Output\Await.cs" />
<EmbeddedResource Include="TestFiles\CodeGenerator\CS\Output\Await.DesignTime.cs" />
<Compile Include="Text\BufferingTextReaderTest.cs" />
<Compile Include="Text\LineTrackingStringBufferTest.cs" />
<Compile Include="Text\LookaheadTextReaderTestBase.cs" />
Expand Down Expand Up @@ -317,6 +319,7 @@
<ItemGroup>
<None Include="packages.config" />
<EmbeddedResource Include="TestFiles\CodeGenerator\CS\Source\CodeTree.cshtml" />
<EmbeddedResource Include="TestFiles\CodeGenerator\CS\Source\Await.cshtml" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
namespace TestOutput
{
using System;
using System.Threading.Tasks;

public class Await
{
private static object @__o;
#line 1 "Await.cshtml"

public async Task<string> Foo()
{
return "Bar";
}

#line default
#line hidden
private void @__RazorDesignTimeHelpers__()
{
#pragma warning disable 219
#pragma warning restore 219
}
#line hidden
public Await()
{
}

public override async Task ExecuteAsync()
{
#line 1 "------------------------------------------"
__o = await Foo();

#line default
#line hidden
#line 1 "------------------------------------------"
__o = await Foo();

#line default
#line hidden
#line 12 "Await.cshtml"
await Foo();

#line default
#line hidden

#line 13 "Await.cshtml"


#line default
#line hidden

#line 1 "------------------------------------------"
__o = await Foo();

#line default
#line hidden
#line 13 "Await.cshtml"


#line default
#line hidden

#line 1 "------------------------------------------"
__o = await;

#line default
#line hidden
#line 1 "------------------------------------------"
__o = await Foo(1, 2);

#line default
#line hidden
#line 1 "------------------------------------------"
__o = await Foo("bob", true);

#line default
#line hidden
#line 21 "Await.cshtml"
await Foo(something, hello: "world");

#line default
#line hidden

#line 22 "Await.cshtml"


#line default
#line hidden

#line 1 "------------------------------------------"
__o = await Foo(boolValue: false);

#line default
#line hidden
#line 22 "Await.cshtml"


#line default
#line hidden

#line 1 "------------------------------------------"
__o = await ("wrrronggg");

#line default
#line hidden
}
}
}
Loading