Add support for static usings.

- Added special cases to the using statement parser to understand `@using static Foo`.
- Added several unit tests to validate the static using structure.
- Modified existing import code generation tests to validate several static usings.

#44
This commit is contained in:
N. Taylor Mullen 2015-05-18 17:05:45 -07:00
parent aaa14cd36b
commit 1879ac6427
6 changed files with 133 additions and 30 deletions

View File

@ -101,7 +101,7 @@ namespace Microsoft.AspNet.Razor.Parser
// using ( ==> Using Statement
UsingStatement(block);
}
else if (At(CSharpSymbolType.Identifier))
else if (At(CSharpSymbolType.Identifier) || At(CSharpKeyword.Static))
{
// using Identifier ==> Using Declaration
if (!topLevel)
@ -126,31 +126,40 @@ namespace Microsoft.AspNet.Razor.Parser
// Set block type to directive
Context.CurrentBlock.Type = BlockType.Directive;
// Parse a type name
Assert(CSharpSymbolType.Identifier);
NamespaceOrTypeName();
IEnumerable<CSharpSymbol> ws = ReadWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
if (At(CSharpSymbolType.Assign))
if (At(CSharpSymbolType.Identifier))
{
// Alias
Accept(ws);
Assert(CSharpSymbolType.Assign);
AcceptAndMoveNext();
AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
// One more namespace or type name
// non-static using
NamespaceOrTypeName();
var whitespace = ReadWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
if (At(CSharpSymbolType.Assign))
{
// Alias
Accept(whitespace);
Assert(CSharpSymbolType.Assign);
AcceptAndMoveNext();
AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
// One more namespace or type name
NamespaceOrTypeName();
}
else
{
PutCurrentBack();
PutBack(whitespace);
}
}
else
else if (At(CSharpKeyword.Static))
{
PutCurrentBack();
PutBack(ws);
// static using
AcceptAndMoveNext();
AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true));
NamespaceOrTypeName();
}
Span.EditHandler.AcceptedCharacters = AcceptedCharacters.AnyExceptNewline;
Span.CodeGenerator = new AddImportCodeGenerator(
Span.GetContent(syms => syms.Skip(1)));
Span.GetContent(symbols => symbols.Skip(1)));
// Optional ";"
if (EnsureCurrent())

View File

@ -176,8 +176,11 @@ namespace Microsoft.AspNet.Razor.Test.Generator
BuildLineMapping(1, 0, 1, 51, 3, 0, 15),
BuildLineMapping(19, 1, 1, 132, 9, 0, 32),
BuildLineMapping(54, 2, 1, 230, 15, 0, 12),
BuildLineMapping(99, 4, 772, 39, 29, 21),
BuildLineMapping(161, 5, 888, 44, 35, 20)
BuildLineMapping(71, 4, 1, 308, 21, 0, 19),
BuildLineMapping(93, 5, 1, 393, 27, 0, 27),
BuildLineMapping(123, 6, 1, 486, 33, 0, 41),
BuildLineMapping(197, 8, 1057, 57, 29, 21),
BuildLineMapping(259, 9, 1174, 62, 35, 20)
});
}

View File

@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNet.Razor.Generator;
using Microsoft.AspNet.Razor.Parser;
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
@ -148,6 +149,53 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
));
}
public static TheoryData StaticUsingData
{
get
{
var factory = SpanFactory.CreateCsHtml();
Func<string, string, DirectiveBlock> createUsing = (code, import) =>
new DirectiveBlock(
factory.CodeTransition(),
factory.Code(code)
.AsNamespaceImport(import)
.Accepts(AcceptedCharacters.AnyExceptNewline));
// document, expectedResult
return new TheoryData<string, DirectiveBlock>
{
{ "@using static", createUsing("using static", " static") },
{ "@using static ", createUsing("using static ", " static ") },
{ "@using static ", createUsing("using static ", " static ") },
{ "@using static System", createUsing("using static System", " static System") },
{
"@using static System",
createUsing("using static System", " static System")
},
{
"@using static System.Console",
createUsing("using static System.Console", " static System.Console")
},
{
"@using static global::System.Console",
createUsing("using static global::System.Console", " static global::System.Console")
},
{
"@using static global::System.Console ",
createUsing("using static global::System.Console", " static global::System.Console")
},
};
}
}
[Theory]
[MemberData(nameof(StaticUsingData))]
public void StaticUsingImport(string document, DirectiveBlock expectedResult)
{
// Act & Assert
ParseBlockTest(document, expectedResult);
}
[Fact]
public void UsingStatement()
{

View File

@ -15,6 +15,24 @@ using Foo = System.Text.Encoding
#line 3 "Imports.cshtml"
using System
#line default
#line hidden
;
#line 5 "Imports.cshtml"
using static System
#line default
#line hidden
;
#line 6 "Imports.cshtml"
using static System.Console
#line default
#line hidden
;
#line 7 "Imports.cshtml"
using static global::System.Text.Encoding
#line default
#line hidden
;
@ -36,12 +54,12 @@ using System
#pragma warning disable 1998
public override async Task ExecuteAsync()
{
#line 5 "Imports.cshtml"
#line 9 "Imports.cshtml"
__o = typeof(Path).FullName;
#line default
#line hidden
#line 6 "Imports.cshtml"
#line 10 "Imports.cshtml"
__o = typeof(Foo).FullName;
#line default

View File

@ -1,4 +1,4 @@
#pragma checksum "Imports.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "1b57def0ce2632d0f50d63dbdc5e0e719c202863"
#pragma checksum "Imports.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "f452adb7c255f6d9d6d2573c6add7cb28022b151"
namespace TestOutput
{
#line 1 "Imports.cshtml"
@ -16,6 +16,24 @@ using Foo = System.Text.Encoding
#line 3 "Imports.cshtml"
using System
#line default
#line hidden
;
#line 5 "Imports.cshtml"
using static System
#line default
#line hidden
;
#line 6 "Imports.cshtml"
using static System.Console
#line default
#line hidden
;
#line 7 "Imports.cshtml"
using static global::System.Text.Encoding
#line default
#line hidden
;
@ -31,27 +49,30 @@ using System
#pragma warning disable 1998
public override async Task ExecuteAsync()
{
Instrumentation.BeginContext(68, 30, true);
Instrumentation.BeginContext(68, 2, true);
WriteLiteral("\r\n");
Instrumentation.EndContext();
Instrumentation.BeginContext(166, 30, true);
WriteLiteral("\r\n<p>Path\'s full type name is ");
Instrumentation.EndContext();
Instrumentation.BeginContext(99, 21, false);
#line 5 "Imports.cshtml"
Instrumentation.BeginContext(197, 21, false);
#line 9 "Imports.cshtml"
Write(typeof(Path).FullName);
#line default
#line hidden
Instrumentation.EndContext();
Instrumentation.BeginContext(120, 40, true);
Instrumentation.BeginContext(218, 40, true);
WriteLiteral("</p>\r\n<p>Foo\'s actual full type name is ");
Instrumentation.EndContext();
Instrumentation.BeginContext(161, 20, false);
#line 6 "Imports.cshtml"
Instrumentation.BeginContext(259, 20, false);
#line 10 "Imports.cshtml"
Write(typeof(Foo).FullName);
#line default
#line hidden
Instrumentation.EndContext();
Instrumentation.BeginContext(181, 4, true);
Instrumentation.BeginContext(279, 4, true);
WriteLiteral("</p>");
Instrumentation.EndContext();
}

View File

@ -2,5 +2,9 @@
@using Foo = System.Text.Encoding
@using System
@using static System
@using static System.Console
@using static global::System.Text.Encoding
<p>Path's full type name is @typeof(Path).FullName</p>
<p>Foo's actual full type name is @typeof(Foo).FullName</p>