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

View File

@ -176,8 +176,11 @@ namespace Microsoft.AspNet.Razor.Test.Generator
BuildLineMapping(1, 0, 1, 51, 3, 0, 15), BuildLineMapping(1, 0, 1, 51, 3, 0, 15),
BuildLineMapping(19, 1, 1, 132, 9, 0, 32), BuildLineMapping(19, 1, 1, 132, 9, 0, 32),
BuildLineMapping(54, 2, 1, 230, 15, 0, 12), BuildLineMapping(54, 2, 1, 230, 15, 0, 12),
BuildLineMapping(99, 4, 772, 39, 29, 21), BuildLineMapping(71, 4, 1, 308, 21, 0, 19),
BuildLineMapping(161, 5, 888, 44, 35, 20) 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. // 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. // 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.Generator;
using Microsoft.AspNet.Razor.Parser; using Microsoft.AspNet.Razor.Parser;
using Microsoft.AspNet.Razor.Parser.SyntaxTree; 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] [Fact]
public void UsingStatement() public void UsingStatement()
{ {

View File

@ -15,6 +15,24 @@ using Foo = System.Text.Encoding
#line 3 "Imports.cshtml" #line 3 "Imports.cshtml"
using System 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 default
#line hidden #line hidden
; ;
@ -36,12 +54,12 @@ using System
#pragma warning disable 1998 #pragma warning disable 1998
public override async Task ExecuteAsync() public override async Task ExecuteAsync()
{ {
#line 5 "Imports.cshtml" #line 9 "Imports.cshtml"
__o = typeof(Path).FullName; __o = typeof(Path).FullName;
#line default #line default
#line hidden #line hidden
#line 6 "Imports.cshtml" #line 10 "Imports.cshtml"
__o = typeof(Foo).FullName; __o = typeof(Foo).FullName;
#line default #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 namespace TestOutput
{ {
#line 1 "Imports.cshtml" #line 1 "Imports.cshtml"
@ -16,6 +16,24 @@ using Foo = System.Text.Encoding
#line 3 "Imports.cshtml" #line 3 "Imports.cshtml"
using System 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 default
#line hidden #line hidden
; ;
@ -31,27 +49,30 @@ using System
#pragma warning disable 1998 #pragma warning disable 1998
public override async Task ExecuteAsync() 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 "); WriteLiteral("\r\n<p>Path\'s full type name is ");
Instrumentation.EndContext(); Instrumentation.EndContext();
Instrumentation.BeginContext(99, 21, false); Instrumentation.BeginContext(197, 21, false);
#line 5 "Imports.cshtml" #line 9 "Imports.cshtml"
Write(typeof(Path).FullName); Write(typeof(Path).FullName);
#line default #line default
#line hidden #line hidden
Instrumentation.EndContext(); Instrumentation.EndContext();
Instrumentation.BeginContext(120, 40, true); Instrumentation.BeginContext(218, 40, true);
WriteLiteral("</p>\r\n<p>Foo\'s actual full type name is "); WriteLiteral("</p>\r\n<p>Foo\'s actual full type name is ");
Instrumentation.EndContext(); Instrumentation.EndContext();
Instrumentation.BeginContext(161, 20, false); Instrumentation.BeginContext(259, 20, false);
#line 6 "Imports.cshtml" #line 10 "Imports.cshtml"
Write(typeof(Foo).FullName); Write(typeof(Foo).FullName);
#line default #line default
#line hidden #line hidden
Instrumentation.EndContext(); Instrumentation.EndContext();
Instrumentation.BeginContext(181, 4, true); Instrumentation.BeginContext(279, 4, true);
WriteLiteral("</p>"); WriteLiteral("</p>");
Instrumentation.EndContext(); Instrumentation.EndContext();
} }

View File

@ -2,5 +2,9 @@
@using Foo = System.Text.Encoding @using Foo = System.Text.Encoding
@using System @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>Path's full type name is @typeof(Path).FullName</p>
<p>Foo's actual full type name is @typeof(Foo).FullName</p> <p>Foo's actual full type name is @typeof(Foo).FullName</p>