Adding support for @Inject to Mvc
This commit is contained in:
parent
c98bc503e8
commit
59e419ba0a
13
Mvc.sln
13
Mvc.sln
|
|
@ -29,6 +29,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "MvcSample.Web", "samples\Mv
|
|||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.Razor.Host", "src\Microsoft.AspNet.Mvc.Razor.Host\Microsoft.AspNet.Mvc.Razor.Host.kproj", "{520B3AA4-363A-497C-8C15-80423C5AFC85}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.Razor.Host.Test", "test\Microsoft.AspNet.Mvc.Razor.Host.Test\Microsoft.AspNet.Mvc.Razor.Host.Test.kproj", "{7C4F5973-0491-4028-B1DC-A9BA73FF9F77}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebSites", "WebSites", "{16703B76-C9F7-4C75-AE6C-53D92E308E3C}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.FunctionalTests", "test\Microsoft.AspNet.Mvc.FunctionalTests\Microsoft.AspNet.Mvc.FunctionalTests.kproj", "{323D0C04-B518-4A8F-8A8E-3546AD153D34}"
|
||||
|
|
@ -145,6 +147,16 @@ Global
|
|||
{520B3AA4-363A-497C-8C15-80423C5AFC85}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{520B3AA4-363A-497C-8C15-80423C5AFC85}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{520B3AA4-363A-497C-8C15-80423C5AFC85}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{7C4F5973-0491-4028-B1DC-A9BA73FF9F77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7C4F5973-0491-4028-B1DC-A9BA73FF9F77}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7C4F5973-0491-4028-B1DC-A9BA73FF9F77}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{7C4F5973-0491-4028-B1DC-A9BA73FF9F77}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{7C4F5973-0491-4028-B1DC-A9BA73FF9F77}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{7C4F5973-0491-4028-B1DC-A9BA73FF9F77}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7C4F5973-0491-4028-B1DC-A9BA73FF9F77}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7C4F5973-0491-4028-B1DC-A9BA73FF9F77}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{7C4F5973-0491-4028-B1DC-A9BA73FF9F77}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{7C4F5973-0491-4028-B1DC-A9BA73FF9F77}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{323D0C04-B518-4A8F-8A8E-3546AD153D34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{323D0C04-B518-4A8F-8A8E-3546AD153D34}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{323D0C04-B518-4A8F-8A8E-3546AD153D34}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
|
|
@ -180,6 +192,7 @@ Global
|
|||
{A8AA326E-8EE8-4F11-B750-23028E0949D7} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
|
||||
{FBB2B86E-972B-4185-9FF2-62CAB5F8388F} = {DAAE4C74-D06F-4874-A166-33305D2643CE}
|
||||
{520B3AA4-363A-497C-8C15-80423C5AFC85} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
|
||||
{7C4F5973-0491-4028-B1DC-A9BA73FF9F77} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
|
||||
{16703B76-C9F7-4C75-AE6C-53D92E308E3C} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
|
||||
{323D0C04-B518-4A8F-8A8E-3546AD153D34} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
|
||||
{34DF1487-12C6-476C-BE0A-F31DF1939AE5} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,11 @@ namespace MvcSample.Web
|
|||
return View("ValidationSummary");
|
||||
}
|
||||
|
||||
public ActionResult InjectSample()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Action that shows metadata when model is <c>null</c>.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
<Content Include="project.json" />
|
||||
<Content Include="Views\Home\Create.cshtml" />
|
||||
<Content Include="Views\Home\EditorTemplates\IEnumerable`1.cshtml" />
|
||||
<Content Include="Views\Home\InjectSample.cshtml" />
|
||||
<Content Include="Views\Home\Test.cshtml" />
|
||||
<Content Include="Views\Home\ValidationSummary.cshtml" />
|
||||
<Content Include="Views\Link\Details.cshtml" />
|
||||
|
|
@ -60,6 +61,7 @@
|
|||
<Compile Include="LinkController.cs" />
|
||||
<Compile Include="Models\User.cs" />
|
||||
<Compile Include="OverloadController.cs" />
|
||||
<Compile Include="Services\TestService.cs" />
|
||||
<Compile Include="SimplePocoController.cs" />
|
||||
<Compile Include="SimpleRest.cs" />
|
||||
<Compile Include="Startup.cs" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace MvcSample.Web.Services
|
||||
{
|
||||
public interface ITestService
|
||||
{
|
||||
string GetFoo();
|
||||
}
|
||||
|
||||
|
||||
public class TestService : ITestService
|
||||
{
|
||||
public string GetFoo()
|
||||
{
|
||||
return "Hello world " + DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ using Microsoft.AspNet.Mvc;
|
|||
using Microsoft.AspNet.Routing;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using MvcSample.Web.Filters;
|
||||
using MvcSample.Web.Services;
|
||||
|
||||
namespace MvcSample.Web
|
||||
{
|
||||
|
|
@ -16,6 +17,7 @@ namespace MvcSample.Web
|
|||
services.AddMvc();
|
||||
services.AddSingleton<PassThroughAttribute>();
|
||||
services.AddSingleton<UserNameService>();
|
||||
services.AddTransient<ITestService, TestService>();
|
||||
});
|
||||
|
||||
app.UseMvc(routes =>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
@using MvcSample.Web.Services
|
||||
@inject MvcSample.Web.Services.ITestService MyService
|
||||
@inject ITestService MyService2
|
||||
|
||||
@MyService.GetFoo()
|
||||
@MyService2.GetFoo()
|
||||
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNet.Razor.Generator.Compiler;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
public class InjectChunk : Chunk
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the chunk for an @inject statement.
|
||||
/// </summary>
|
||||
/// <param name="typeName">The type of object that would be injected</param>
|
||||
/// <param name="propertyName">The member name the field is exposed to the page as.</param>
|
||||
public InjectChunk(string typeName,
|
||||
string propertyName)
|
||||
{
|
||||
TypeName = typeName;
|
||||
MemberName = propertyName;
|
||||
}
|
||||
|
||||
public string TypeName { get; private set; }
|
||||
|
||||
public string MemberName { get; private set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Microsoft.AspNet.Razor.Generator;
|
||||
using Microsoft.AspNet.Razor.Generator.Compiler.CSharp;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
public class InjectChunkVisitor : MvcCSharpCodeVisitor
|
||||
{
|
||||
private readonly List<InjectChunk> _injectChunks = new List<InjectChunk>();
|
||||
|
||||
public InjectChunkVisitor([NotNull] CSharpCodeWriter writer,
|
||||
[NotNull] CodeGeneratorContext context)
|
||||
: base(writer, context)
|
||||
{
|
||||
}
|
||||
|
||||
public List<InjectChunk> InjectChunks
|
||||
{
|
||||
get { return _injectChunks; }
|
||||
}
|
||||
|
||||
protected override void Visit([NotNull] InjectChunk chunk)
|
||||
{
|
||||
if (Context.Host.DesignTimeMode)
|
||||
{
|
||||
Writer.WriteLine("public");
|
||||
var code = string.Format(CultureInfo.InvariantCulture,
|
||||
"{0} {1}",
|
||||
chunk.TypeName,
|
||||
chunk.MemberName);
|
||||
var csharpVisitor = new CSharpCodeVisitor(Writer, Context);
|
||||
csharpVisitor.CreateExpressionCodeMapping(code, chunk);
|
||||
Writer.WriteLine("{ get; private set; }");
|
||||
}
|
||||
else
|
||||
{
|
||||
Writer.Write("public ")
|
||||
.Write(chunk.TypeName)
|
||||
.Write(" ")
|
||||
.Write(chunk.MemberName)
|
||||
.WriteLine(" { get; private set; }");
|
||||
}
|
||||
_injectChunks.Add(chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Microsoft.AspNet.Razor.Generator;
|
||||
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
public class InjectParameterGenerator : SpanCodeGenerator
|
||||
{
|
||||
public InjectParameterGenerator(string typeName, string propertyName)
|
||||
{
|
||||
TypeName = typeName;
|
||||
PropertyName = propertyName;
|
||||
}
|
||||
|
||||
public string TypeName { get; private set; }
|
||||
|
||||
public string PropertyName { get; private set; }
|
||||
|
||||
public override void GenerateCode(Span target, CodeGeneratorContext context)
|
||||
{
|
||||
var injectChunk = new InjectChunk(TypeName, PropertyName);
|
||||
context.CodeTreeBuilder.AddChunk(injectChunk, target);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "@inject {0} {1}", TypeName, PropertyName);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
var other = obj as InjectParameterGenerator;
|
||||
return other != null &&
|
||||
string.Equals(TypeName, other.TypeName, StringComparison.Ordinal) &&
|
||||
string.Equals(PropertyName, other.PropertyName, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return TypeName.GetHashCode() +
|
||||
(PropertyName.GetHashCode() * 13);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,6 +22,11 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="IMvcRazorHost.cs" />
|
||||
<Compile Include="InjectChunk.cs" />
|
||||
<Compile Include="InjectChunkVisitor.cs" />
|
||||
<Compile Include="MvcCSharpCodeBuilder.cs" />
|
||||
<Compile Include="MvcCSharpCodeVistor.cs" />
|
||||
<Compile Include="InjectParameterGenerator.cs" />
|
||||
<Compile Include="MvcRazorCodeParser.cs" />
|
||||
<Compile Include="MvcRazorHost.cs" />
|
||||
<Compile Include="Properties\Resources.Designer.cs" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Razor.Generator;
|
||||
using Microsoft.AspNet.Razor.Generator.Compiler.CSharp;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
public class MvcCSharpCodeBuilder : CSharpCodeBuilder
|
||||
{
|
||||
public MvcCSharpCodeBuilder([NotNull] CodeGeneratorContext context)
|
||||
: base(context)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void BuildConstructor([NotNull] CSharpCodeWriter writer)
|
||||
{
|
||||
writer.WriteLineHiddenDirective();
|
||||
|
||||
var injectVisitor = new InjectChunkVisitor(writer, Context);
|
||||
injectVisitor.Accept(Context.CodeTreeBuilder.CodeTree.Chunks);
|
||||
|
||||
writer.WriteLine();
|
||||
writer.WriteLineHiddenDirective();
|
||||
|
||||
var arguments = injectVisitor.InjectChunks
|
||||
.Select(chunk => new KeyValuePair<string, string>(chunk.TypeName,
|
||||
chunk.MemberName));
|
||||
using (writer.BuildConstructor("public", Context.ClassName, arguments))
|
||||
{
|
||||
foreach (var inject in injectVisitor.InjectChunks)
|
||||
{
|
||||
writer.WriteStartAssignment("this." + inject.MemberName)
|
||||
.Write(inject.MemberName)
|
||||
.WriteLine(";");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNet.Razor.Generator;
|
||||
using Microsoft.AspNet.Razor.Generator.Compiler;
|
||||
using Microsoft.AspNet.Razor.Generator.Compiler.CSharp;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
public abstract class MvcCSharpCodeVisitor : CodeVisitor<CSharpCodeWriter>
|
||||
{
|
||||
public MvcCSharpCodeVisitor([NotNull] CSharpCodeWriter writer,
|
||||
[NotNull] CodeGeneratorContext context)
|
||||
: base(writer, context)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Accept(Chunk chunk)
|
||||
{
|
||||
if (chunk is InjectChunk)
|
||||
{
|
||||
Visit((InjectChunk)chunk);
|
||||
}
|
||||
else
|
||||
{
|
||||
base.Accept(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void Visit(InjectChunk chunk);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Microsoft.AspNet.Mvc.Razor.Host;
|
||||
using Microsoft.AspNet.Razor.Generator;
|
||||
using Microsoft.AspNet.Razor.Parser;
|
||||
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
||||
using Microsoft.AspNet.Razor.Text;
|
||||
using Microsoft.AspNet.Mvc.Razor.Host;
|
||||
using Microsoft.AspNet.Razor.Tokenizer.Symbols;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
|
|
@ -14,6 +15,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
{
|
||||
private const string GenericTypeFormat = "{0}<{1}>";
|
||||
private const string ModelKeyword = "model";
|
||||
private const string InjectKeyword = "inject";
|
||||
private readonly string _baseType;
|
||||
private SourceLocation? _endInheritsLocation;
|
||||
private bool _modelStatementFound;
|
||||
|
|
@ -22,6 +24,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
{
|
||||
_baseType = baseType;
|
||||
MapDirectives(ModelDirective, ModelKeyword);
|
||||
MapDirectives(InjectDirective, InjectKeyword);
|
||||
}
|
||||
|
||||
protected override void InheritsDirective()
|
||||
|
|
@ -39,7 +42,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
{
|
||||
if (_modelStatementFound && _endInheritsLocation.HasValue)
|
||||
{
|
||||
Context.OnError(_endInheritsLocation.Value,
|
||||
Context.OnError(_endInheritsLocation.Value,
|
||||
Resources.FormatMvcRazorCodeParser_CannotHaveModelAndInheritsKeyword(ModelKeyword));
|
||||
}
|
||||
}
|
||||
|
|
@ -50,14 +53,14 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
AssertDirective(ModelKeyword);
|
||||
AcceptAndMoveNext();
|
||||
|
||||
SourceLocation endModelLocation = CurrentLocation;
|
||||
var endModelLocation = CurrentLocation;
|
||||
|
||||
BaseTypeDirective(Resources.FormatMvcRazorCodeParser_ModelKeywordMustBeFollowedByTypeName(ModelKeyword),
|
||||
BaseTypeDirective(Resources.FormatMvcRazorCodeParser_KeywordMustBeFollowedByTypeName(ModelKeyword),
|
||||
CreateModelCodeGenerator);
|
||||
|
||||
if (_modelStatementFound)
|
||||
{
|
||||
Context.OnError(endModelLocation,
|
||||
Context.OnError(endModelLocation,
|
||||
Resources.FormatMvcRazorCodeParser_OnlyOneModelStatementIsAllowed(ModelKeyword));
|
||||
}
|
||||
|
||||
|
|
@ -66,13 +69,80 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
CheckForInheritsAndModelStatements();
|
||||
}
|
||||
|
||||
protected virtual void InjectDirective()
|
||||
{
|
||||
// @inject MyApp.MyService MyServicePropertyName
|
||||
AssertDirective(InjectKeyword);
|
||||
AcceptAndMoveNext();
|
||||
|
||||
Context.CurrentBlock.Type = BlockType.Directive;
|
||||
|
||||
// Accept whitespace
|
||||
var remainingWs = AcceptSingleWhiteSpaceCharacter();
|
||||
if (Span.Symbols.Count > 1)
|
||||
{
|
||||
Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None;
|
||||
}
|
||||
Output(SpanKind.MetaCode);
|
||||
|
||||
if (remainingWs != null)
|
||||
{
|
||||
Accept(remainingWs);
|
||||
}
|
||||
|
||||
// Consume any other whitespace tokens.
|
||||
AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true));
|
||||
|
||||
var hasTypeError = !At(CSharpSymbolType.Identifier);
|
||||
if (hasTypeError)
|
||||
{
|
||||
Context.OnError(CurrentLocation,
|
||||
Resources.FormatMvcRazorCodeParser_KeywordMustBeFollowedByTypeName(InjectKeyword));
|
||||
}
|
||||
|
||||
// Accept 'MyApp.MyService'
|
||||
NamespaceOrTypeName();
|
||||
|
||||
// typeName now contains the token 'MyApp.MyService'
|
||||
var typeName = Span.GetContent().Value;
|
||||
|
||||
var propertyStartLocation = CurrentLocation;
|
||||
AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true));
|
||||
|
||||
if (!hasTypeError && At(CSharpSymbolType.NewLine))
|
||||
{
|
||||
// Add an error for the property name only if we successfully read the type name
|
||||
Context.OnError(propertyStartLocation,
|
||||
Resources.FormatMvcRazorCodeParser_InjectDirectivePropertyNameRequired(InjectKeyword));
|
||||
}
|
||||
|
||||
// Read until end of line. Span now contains 'MyApp.MyService MyServiceName'.
|
||||
AcceptUntil(CSharpSymbolType.NewLine);
|
||||
if (!Context.DesignTimeMode)
|
||||
{
|
||||
// We want the newline to be treated as code, but it causes issues at design-time.
|
||||
Optional(CSharpSymbolType.NewLine);
|
||||
}
|
||||
|
||||
// Parse out 'MyServicePropertyName' from the Span.
|
||||
var propertyName = Span.GetContent()
|
||||
.Value
|
||||
.Substring(typeName.Length);
|
||||
|
||||
Span.CodeGenerator = new InjectParameterGenerator(typeName.Trim(),
|
||||
propertyName.Trim());
|
||||
|
||||
// Output the span and finish the block
|
||||
Output(SpanKind.Code);
|
||||
}
|
||||
|
||||
private SpanCodeGenerator CreateModelCodeGenerator(string model)
|
||||
{
|
||||
// In the event we have an empty model, the name we generate does not matter since it's a parser error.
|
||||
// We'll use the non-generic version of the base type.
|
||||
string baseType = String.IsNullOrEmpty(model) ?
|
||||
_baseType :
|
||||
String.Format(CultureInfo.InvariantCulture, GenericTypeFormat, _baseType, model);
|
||||
var baseType = string.IsNullOrEmpty(model) ?
|
||||
_baseType :
|
||||
string.Format(CultureInfo.InvariantCulture, GenericTypeFormat, _baseType, model);
|
||||
return new SetBaseTypeCodeGenerator(baseType);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.IO;
|
||||
using Microsoft.AspNet.Razor;
|
||||
using Microsoft.AspNet.Razor.Generator;
|
||||
using Microsoft.AspNet.Razor.Generator.Compiler;
|
||||
using Microsoft.AspNet.Razor.Parser;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
|
|
@ -57,7 +58,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
public GeneratorResults GenerateCode(string rootRelativePath, Stream inputStream)
|
||||
{
|
||||
string className = ParserHelpers.SanitizeClassName(rootRelativePath);
|
||||
var className = ParserHelpers.SanitizeClassName(rootRelativePath);
|
||||
using (var reader = new StreamReader(inputStream))
|
||||
{
|
||||
var engine = new RazorTemplateEngine(this);
|
||||
|
|
@ -69,5 +70,10 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
{
|
||||
return new MvcRazorCodeParser(_baseType);
|
||||
}
|
||||
|
||||
public override CodeBuilder DecorateCodeBuilder(CodeBuilder incomingBuilder, CodeGeneratorContext context)
|
||||
{
|
||||
return new MvcCSharpCodeBuilder(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,19 +27,35 @@ namespace Microsoft.AspNet.Mvc.Razor.Host
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// The '{0}' keyword must be followed by a type name on the same line.
|
||||
/// A property name must be specified when using the '{0}' statement. Format for a '{0}' statement is '@{0} <TypeName> <PropertyName>'.
|
||||
/// </summary>
|
||||
internal static string MvcRazorCodeParser_ModelKeywordMustBeFollowedByTypeName
|
||||
internal static string MvcRazorCodeParser_InjectDirectivePropertyNameRequired
|
||||
{
|
||||
get { return GetString("MvcRazorCodeParser_ModelKeywordMustBeFollowedByTypeName"); }
|
||||
get { return GetString("MvcRazorCodeParser_InjectDirectivePropertyNameRequired"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A property name must be specified when using the '{0}' statement. Format for a '{0}' statement is '@{0} <TypeName> <PropertyName>'.
|
||||
/// </summary>
|
||||
internal static string FormatMvcRazorCodeParser_InjectDirectivePropertyNameRequired(object p0)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("MvcRazorCodeParser_InjectDirectivePropertyNameRequired"), p0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The '{0}' keyword must be followed by a type name on the same line.
|
||||
/// </summary>
|
||||
internal static string FormatMvcRazorCodeParser_ModelKeywordMustBeFollowedByTypeName(object p0)
|
||||
internal static string MvcRazorCodeParser_KeywordMustBeFollowedByTypeName
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("MvcRazorCodeParser_ModelKeywordMustBeFollowedByTypeName"), p0);
|
||||
get { return GetString("MvcRazorCodeParser_KeywordMustBeFollowedByTypeName"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The '{0}' keyword must be followed by a type name on the same line.
|
||||
/// </summary>
|
||||
internal static string FormatMvcRazorCodeParser_KeywordMustBeFollowedByTypeName(object p0)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("MvcRazorCodeParser_KeywordMustBeFollowedByTypeName"), p0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -120,7 +120,10 @@
|
|||
<data name="MvcRazorCodeParser_CannotHaveModelAndInheritsKeyword" xml:space="preserve">
|
||||
<value>The 'inherits' keyword is not allowed when a '{0}' keyword is used.</value>
|
||||
</data>
|
||||
<data name="MvcRazorCodeParser_ModelKeywordMustBeFollowedByTypeName" xml:space="preserve">
|
||||
<data name="MvcRazorCodeParser_InjectDirectivePropertyNameRequired" xml:space="preserve">
|
||||
<value>A property name must be specified when using the '{0}' statement. Format for a '{0}' statement is '@{0} <Type Name> <Property Name>'.</value>
|
||||
</data>
|
||||
<data name="MvcRazorCodeParser_KeywordMustBeFollowedByTypeName" xml:space="preserve">
|
||||
<value>The '{0}' keyword must be followed by a type name on the same line.</value>
|
||||
</data>
|
||||
<data name="MvcRazorCodeParser_OnlyOneModelStatementIsAllowed" xml:space="preserve">
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.FileSystems;
|
||||
using Microsoft.AspNet.Mvc.Rendering;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.Runtime;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
|
|
@ -13,22 +14,28 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
{
|
||||
private readonly PhysicalFileSystem _fileSystem;
|
||||
private readonly IRazorCompilationService _compilationService;
|
||||
private readonly ITypeActivator _activator;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public VirtualPathViewFactory(IApplicationEnvironment env,
|
||||
IRazorCompilationService compilationService)
|
||||
IRazorCompilationService compilationService,
|
||||
ITypeActivator typeActivator,
|
||||
IServiceProvider serviceProvider)
|
||||
{
|
||||
// TODO: Continue to inject the IFileSystem but only when we get it from the host
|
||||
_fileSystem = new PhysicalFileSystem(env.ApplicationBasePath);
|
||||
_compilationService = compilationService;
|
||||
_activator = typeActivator;
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public IView CreateInstance([NotNull]string virtualPath)
|
||||
public IView CreateInstance([NotNull] string virtualPath)
|
||||
{
|
||||
IFileInfo fileInfo;
|
||||
if (_fileSystem.TryGetFileInfo(virtualPath, out fileInfo))
|
||||
{
|
||||
CompilationResult result = _compilationService.Compile(fileInfo);
|
||||
return (IView)Activator.CreateInstance(result.CompiledType);
|
||||
return (IView)_activator.CreateInstance(_serviceProvider, result.CompiledType);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,181 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Microsoft.AspNet.Razor;
|
||||
using Microsoft.AspNet.Razor.Generator;
|
||||
using Microsoft.AspNet.Razor.Generator.Compiler;
|
||||
using Microsoft.AspNet.Razor.Generator.Compiler.CSharp;
|
||||
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
||||
using Microsoft.AspNet.Razor.Text;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
public class InjectChunkVisitorTest
|
||||
{
|
||||
[Fact]
|
||||
public void Visit_IgnoresNonInjectChunks()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new CSharpCodeWriter();
|
||||
var context = CreateContext();
|
||||
|
||||
var visitor = new InjectChunkVisitor(writer, context);
|
||||
|
||||
// Act
|
||||
visitor.Accept(new Chunk[]
|
||||
{
|
||||
new LiteralChunk(),
|
||||
new CodeAttributeChunk()
|
||||
});
|
||||
var code = writer.GenerateCode();
|
||||
|
||||
// Assert
|
||||
Assert.Empty(code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Visit_GeneratesProperties_ForInjectChunks()
|
||||
{
|
||||
// Arrange
|
||||
var expected =
|
||||
@"public MyType1 MyPropertyName1 { get; private set; }
|
||||
public MyType2 @MyPropertyName2 { get; private set; }
|
||||
";
|
||||
var writer = new CSharpCodeWriter();
|
||||
var context = CreateContext();
|
||||
|
||||
var visitor = new InjectChunkVisitor(writer, context);
|
||||
var factory = SpanFactory.CreateCsHtml();
|
||||
var node = (Span)factory.Code("Some code")
|
||||
.As(new InjectParameterGenerator("MyType", "MyPropertyName"));
|
||||
|
||||
// Act
|
||||
visitor.Accept(new Chunk[]
|
||||
{
|
||||
new LiteralChunk(),
|
||||
new InjectChunk("MyType1", "MyPropertyName1") { Association = node },
|
||||
new InjectChunk("MyType2", "@MyPropertyName2") { Association = node }
|
||||
});
|
||||
var code = writer.GenerateCode();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Visit_WithDesignTimeHost_GeneratesPropertiesAndLinePragmas_ForInjectChunks()
|
||||
{
|
||||
// Arrange
|
||||
var expected = @"public
|
||||
#line 1 """"
|
||||
MyType1 MyPropertyName1
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
{ get; private set; }
|
||||
public
|
||||
#line 1 """"
|
||||
MyType2 @MyPropertyName2
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
{ get; private set; }
|
||||
";
|
||||
var writer = new CSharpCodeWriter();
|
||||
var context = CreateContext();
|
||||
context.Host.DesignTimeMode = true;
|
||||
|
||||
var visitor = new InjectChunkVisitor(writer, context);
|
||||
var factory = SpanFactory.CreateCsHtml();
|
||||
var node = (Span)factory.Code("Some code")
|
||||
.As(new InjectParameterGenerator("MyType", "MyPropertyName"));
|
||||
|
||||
// Act
|
||||
visitor.Accept(new Chunk[]
|
||||
{
|
||||
new LiteralChunk(),
|
||||
new InjectChunk("MyType1", "MyPropertyName1") { Association = node },
|
||||
new InjectChunk("MyType2", "@MyPropertyName2") { Association = node }
|
||||
});
|
||||
var code = writer.GenerateCode();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InjectVisitor_GeneratesCorrectLineMappings()
|
||||
{
|
||||
// Arrange
|
||||
var host = new MvcRazorHost("RazorView")
|
||||
{
|
||||
DesignTimeMode = true
|
||||
};
|
||||
host.NamespaceImports.Clear();
|
||||
var engine = new RazorTemplateEngine(host);
|
||||
var source = ReadResource("Inject.cshtml");
|
||||
var expectedCode = ReadResource("Inject.cs");
|
||||
var expectedLineMappings = new List<LineMapping>
|
||||
{
|
||||
BuildLineMapping(1, 0, 1, 32, 3, 0, 17),
|
||||
BuildLineMapping(28, 1, 8, 442, 21, 8, 20)
|
||||
};
|
||||
|
||||
// Act
|
||||
GeneratorResults results;
|
||||
using (var buffer = new StringTextBuffer(source))
|
||||
{
|
||||
results = engine.GenerateCode(buffer);
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.True(results.Success);
|
||||
Assert.Equal(expectedCode, results.GeneratedCode);
|
||||
Assert.Empty(results.ParserErrors);
|
||||
Assert.Equal(expectedLineMappings, results.DesignTimeLineMappings);
|
||||
}
|
||||
|
||||
private string ReadResource(string resourceName)
|
||||
{
|
||||
var assembly = typeof(InjectChunkVisitorTest).Assembly;
|
||||
|
||||
using (var stream = assembly.GetManifestResourceStream(resourceName))
|
||||
using (var streamReader = new StreamReader(stream))
|
||||
{
|
||||
return streamReader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
|
||||
private static CodeGeneratorContext CreateContext()
|
||||
{
|
||||
return CodeGeneratorContext.Create(new MvcRazorHost("RazorView"),
|
||||
"MyClass",
|
||||
"MyNamespace",
|
||||
string.Empty,
|
||||
shouldGenerateLinePragmas: true);
|
||||
}
|
||||
|
||||
private static LineMapping BuildLineMapping(int documentAbsoluteIndex,
|
||||
int documentLineIndex,
|
||||
int documentCharacterIndex,
|
||||
int generatedAbsoluteIndex,
|
||||
int generatedLineIndex,
|
||||
int generatedCharacterIndex,
|
||||
int contentLength)
|
||||
{
|
||||
var documentLocation = new SourceLocation(documentAbsoluteIndex,
|
||||
documentLineIndex,
|
||||
documentCharacterIndex);
|
||||
var generatedLocation = new SourceLocation(generatedAbsoluteIndex,
|
||||
generatedLineIndex,
|
||||
generatedCharacterIndex);
|
||||
|
||||
return new LineMapping(
|
||||
documentLocation: new MappingLocation(documentLocation, contentLength),
|
||||
generatedLocation: new MappingLocation(generatedLocation, contentLength));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="__ToolsVersion__" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">12.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>7c4f5973-0491-4028-b1dc-a9ba73ff9f77</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(OutputType) == 'Console'">
|
||||
<DebuggerFlavor>ConsoleDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(OutputType) == 'Web'">
|
||||
<DebuggerFlavor>WebDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'" Label="Configuration">
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" Label="Configuration">
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Project.json" />
|
||||
<Content Include="TestFiles\Input\Inject.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="InjectChunkVisitorTest.cs" />
|
||||
<Compile Include="MvcCSharpRazorCodeParserTest.cs" />
|
||||
<Compile Include="SpanFactory\RawTextSymbol.cs" />
|
||||
<Compile Include="SpanFactory\SpanConstructor.cs" />
|
||||
<Compile Include="SpanFactory\SpanFactory.cs" />
|
||||
<Compile Include="SpanFactory\SpanFactoryExtensions.cs" />
|
||||
<Compile Include="SpanFactory\UnclassifiedSpanConstructor.cs" />
|
||||
<Compile Include="StringTextBuffer.cs" />
|
||||
<Compile Include="TestFiles\Output\Inject.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,406 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Razor.Generator;
|
||||
using Microsoft.AspNet.Razor.Generator.Compiler;
|
||||
using Microsoft.AspNet.Razor.Parser;
|
||||
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
||||
using Microsoft.AspNet.Razor.Text;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
public class MvcCSharpRazorCodeParserTest
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("model")]
|
||||
[InlineData("inject")]
|
||||
public void Constructor_AddsMvcSpecificKeywords(string keyword)
|
||||
{
|
||||
// Arrange
|
||||
var parser = new TestMvcCSharpRazorCodeParser();
|
||||
|
||||
// Act
|
||||
var hasDirective = parser.HasDirective(keyword);
|
||||
|
||||
// Assert
|
||||
Assert.True(hasDirective);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseModelKeyword_HandlesSingleInstance()
|
||||
{
|
||||
// Arrange
|
||||
var document = "@model Foo";
|
||||
var factory = SpanFactory.CreateCsHtml();
|
||||
var errors = new List<RazorError>();
|
||||
var expectedSpans = new Span[]
|
||||
{
|
||||
factory.EmptyHtml(),
|
||||
factory.CodeTransition(SyntaxConstants.TransitionString)
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
factory.MetaCode("model ")
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
factory.Code(" Foo")
|
||||
.As(new SetBaseTypeCodeGenerator("RazorView<Foo>"))
|
||||
};
|
||||
|
||||
// Act
|
||||
var spans = ParseDocument(document, errors);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedSpans, spans);
|
||||
Assert.Empty(errors);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Foo?", "RazorView<Foo?>")]
|
||||
[InlineData("Foo[[]][]", "RazorView<Foo[[]][]>")]
|
||||
[InlineData("$rootnamespace$.MyModel", "RazorView<$rootnamespace$.MyModel>")]
|
||||
public void ParseModelKeyword_InfersBaseType_FromModelName(string modelName,
|
||||
string expectedBaseType)
|
||||
{
|
||||
// Arrange
|
||||
var documentContent = "@model " + modelName + Environment.NewLine + "Bar";
|
||||
var factory = SpanFactory.CreateCsHtml();
|
||||
var errors = new List<RazorError>();
|
||||
var expectedSpans = new Span[]
|
||||
{
|
||||
factory.EmptyHtml(),
|
||||
factory.CodeTransition(SyntaxConstants.TransitionString)
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
factory.MetaCode("model ")
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
factory.Code(modelName + "\r\n")
|
||||
.As(new SetBaseTypeCodeGenerator(expectedBaseType)),
|
||||
factory.Markup("Bar")
|
||||
.With(new MarkupCodeGenerator())
|
||||
};
|
||||
|
||||
// Act
|
||||
var spans = ParseDocument(documentContent, errors);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedSpans, spans);
|
||||
Assert.Empty(errors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseModelKeyword_ErrorOnMissingModelType()
|
||||
{
|
||||
// Arrange + Act
|
||||
var errors = new List<RazorError>();
|
||||
var document = "@model ";
|
||||
var spans = ParseDocument(document, errors);
|
||||
|
||||
// Assert
|
||||
var factory = SpanFactory.CreateCsHtml();
|
||||
var expectedSpans = new Span[]
|
||||
{
|
||||
factory.EmptyHtml(),
|
||||
factory.CodeTransition(SyntaxConstants.TransitionString)
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
factory.MetaCode("model ")
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
factory.Code(" ")
|
||||
.As(new SetBaseTypeCodeGenerator("RazorView")),
|
||||
};
|
||||
var expectedErrors = new[]
|
||||
{
|
||||
new RazorError("The 'model' keyword must be followed by a type name on the same line.",
|
||||
new SourceLocation(9, 0, 9), 1)
|
||||
};
|
||||
Assert.Equal(expectedSpans, spans);
|
||||
Assert.Equal(expectedErrors, errors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseModelKeyword_ErrorOnMultipleModelStatements()
|
||||
{
|
||||
// Arrange + Act
|
||||
var errors = new List<RazorError>();
|
||||
var document =
|
||||
"@model Foo" + Environment.NewLine
|
||||
+ "@model Bar";
|
||||
|
||||
var factory = SpanFactory.CreateCsHtml();
|
||||
var expectedSpans = new Span[]
|
||||
{
|
||||
factory.EmptyHtml(),
|
||||
factory.CodeTransition(SyntaxConstants.TransitionString)
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
factory.MetaCode("model ")
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
factory.Code("Foo\r\n")
|
||||
.As(new SetBaseTypeCodeGenerator("RazorView<Foo>")),
|
||||
factory.CodeTransition(SyntaxConstants.TransitionString)
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
factory.MetaCode("model ")
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
factory.Code("Bar")
|
||||
.As(new SetBaseTypeCodeGenerator("RazorView<Bar>"))
|
||||
};
|
||||
|
||||
var expectedErrors = new[]
|
||||
{
|
||||
new RazorError("Only one 'model' statement is allowed in a file.",
|
||||
new SourceLocation(18, 1, 6), 1)
|
||||
};
|
||||
|
||||
// Act
|
||||
var spans = ParseDocument(document, errors);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedSpans, spans);
|
||||
Assert.Equal(expectedErrors, errors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseModelKeyword_ErrorOnModelFollowedByInherits()
|
||||
{
|
||||
// Arrange
|
||||
var errors = new List<RazorError>();
|
||||
var document =
|
||||
"@model Foo" + Environment.NewLine
|
||||
+ "@inherits Bar";
|
||||
|
||||
var factory = SpanFactory.CreateCsHtml();
|
||||
var expectedSpans = new Span[]
|
||||
{
|
||||
factory.EmptyHtml(),
|
||||
factory.CodeTransition(SyntaxConstants.TransitionString)
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
factory.MetaCode("model ")
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
factory.Code("Foo\r\n")
|
||||
.As(new SetBaseTypeCodeGenerator("RazorView<Foo>")),
|
||||
factory.CodeTransition(SyntaxConstants.TransitionString)
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
factory.MetaCode("inherits ")
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
factory.Code("Bar")
|
||||
.As(new SetBaseTypeCodeGenerator("Bar"))
|
||||
};
|
||||
|
||||
var expectedErrors = new[]
|
||||
{
|
||||
new RazorError("The 'inherits' keyword is not allowed when a 'model' keyword is used.",
|
||||
new SourceLocation(21, 1, 9), 1)
|
||||
};
|
||||
|
||||
// Act
|
||||
var spans = ParseDocument(document, errors);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedSpans, spans);
|
||||
Assert.Equal(expectedErrors, errors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseModelKeyword_ErrorOnInheritsFollowedByModel()
|
||||
{
|
||||
// Arrange
|
||||
var errors = new List<RazorError>();
|
||||
var document =
|
||||
"@inherits Bar" + Environment.NewLine
|
||||
+ "@model Foo";
|
||||
|
||||
var factory = SpanFactory.CreateCsHtml();
|
||||
var expectedSpans = new Span[]
|
||||
{
|
||||
factory.EmptyHtml(),
|
||||
factory.CodeTransition(SyntaxConstants.TransitionString)
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
factory.MetaCode("inherits ")
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
factory.Code("Bar" + Environment.NewLine)
|
||||
.As(new SetBaseTypeCodeGenerator("Bar")),
|
||||
factory.CodeTransition(SyntaxConstants.TransitionString)
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
factory.MetaCode("model ")
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
factory.Code("Foo")
|
||||
.As(new SetBaseTypeCodeGenerator("RazorView<Foo>"))
|
||||
};
|
||||
|
||||
var expectedErrors = new[]
|
||||
{
|
||||
new RazorError("The 'inherits' keyword is not allowed when a 'model' keyword is used.",
|
||||
new SourceLocation(9, 0, 9), 1)
|
||||
};
|
||||
|
||||
// Act
|
||||
var spans = ParseDocument(document, errors);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedSpans, spans.ToArray());
|
||||
Assert.Equal(expectedErrors, errors.ToArray());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("IMyService Service", "IMyService", "Service")]
|
||||
[InlineData(" Microsoft.AspNet.Mvc.IHtmlHelper<MyNullableModel[]?> MyHelper ",
|
||||
"Microsoft.AspNet.Mvc.IHtmlHelper<MyNullableModel[]?>", "MyHelper")]
|
||||
[InlineData(" TestService @class ", "TestService", "@class")]
|
||||
public void ParseInjectKeyword_InfersTypeAndPropertyName(string injectStatement,
|
||||
string expectedService,
|
||||
string expectedPropertyName)
|
||||
{
|
||||
// Arrange
|
||||
var documentContent = "@inject " + injectStatement;
|
||||
var factory = SpanFactory.CreateCsHtml();
|
||||
var errors = new List<RazorError>();
|
||||
var expectedSpans = new Span[]
|
||||
{
|
||||
factory.EmptyHtml(),
|
||||
factory.CodeTransition(SyntaxConstants.TransitionString)
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
factory.MetaCode("inject ")
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
factory.Code(injectStatement)
|
||||
.As(new InjectParameterGenerator(expectedService, expectedPropertyName))
|
||||
};
|
||||
|
||||
// Act
|
||||
var spans = ParseDocument(documentContent, errors);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedSpans, spans);
|
||||
Assert.Empty(errors);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("IMyService Service ", "IMyService", "Service")]
|
||||
[InlineData(" TestService @namespace ", "TestService", "@namespace")]
|
||||
public void ParseInjectKeyword_ParsesUpToNewLine(string injectStatement,
|
||||
string expectedService,
|
||||
string expectedPropertyName)
|
||||
{
|
||||
// Arrange
|
||||
var documentContent = "@inject " + injectStatement + Environment.NewLine + "Bar";
|
||||
var factory = SpanFactory.CreateCsHtml();
|
||||
var errors = new List<RazorError>();
|
||||
var expectedSpans = new Span[]
|
||||
{
|
||||
factory.EmptyHtml(),
|
||||
factory.CodeTransition(SyntaxConstants.TransitionString)
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
factory.MetaCode("inject ")
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
factory.Code(injectStatement + "\r\n")
|
||||
.As(new InjectParameterGenerator(expectedService, expectedPropertyName)),
|
||||
factory.Markup("Bar")
|
||||
.With(new MarkupCodeGenerator())
|
||||
};
|
||||
|
||||
// Act
|
||||
var spans = ParseDocument(documentContent, errors);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedSpans, spans);
|
||||
Assert.Empty(errors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseInjectKeyword_ErrorOnMissingTypeName()
|
||||
{
|
||||
// Arrange
|
||||
var errors = new List<RazorError>();
|
||||
var documentContent = "@inject \r\nBar";
|
||||
var factory = SpanFactory.CreateCsHtml();
|
||||
var expectedSpans = new Span[]
|
||||
{
|
||||
factory.EmptyHtml(),
|
||||
factory.CodeTransition(SyntaxConstants.TransitionString)
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
factory.MetaCode("inject ")
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
factory.Code(" \r\n")
|
||||
.As(new InjectParameterGenerator(string.Empty, string.Empty)),
|
||||
factory.Markup("Bar")
|
||||
.With(new MarkupCodeGenerator())
|
||||
};
|
||||
var expectedErrors = new[]
|
||||
{
|
||||
new RazorError("The 'inject' keyword must be followed by a type name on the same line.",
|
||||
new SourceLocation(11, 0, 11), 1)
|
||||
};
|
||||
|
||||
// Act
|
||||
var spans = ParseDocument(documentContent, errors);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedSpans, spans);
|
||||
Assert.Equal(expectedErrors, errors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseInjectKeyword_ErrorOnMissingPropertyName()
|
||||
{
|
||||
// Arrange
|
||||
var errors = new List<RazorError>();
|
||||
var documentContent = "@inject IMyService \r\nBar";
|
||||
var factory = SpanFactory.CreateCsHtml();
|
||||
var expectedSpans = new Span[]
|
||||
{
|
||||
factory.EmptyHtml(),
|
||||
factory.CodeTransition(SyntaxConstants.TransitionString)
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
factory.MetaCode("inject ")
|
||||
.Accepts(AcceptedCharacters.None),
|
||||
factory.Code(" IMyService \r\n")
|
||||
.As(new InjectParameterGenerator("IMyService", string.Empty)),
|
||||
factory.Markup("Bar")
|
||||
.With(new MarkupCodeGenerator())
|
||||
};
|
||||
var expectedErrors = new[]
|
||||
{
|
||||
new RazorError("A property name must be specified when using the 'inject' statement. " +
|
||||
"Format for a 'inject' statement is '@inject <Type Name> <Property Name>'.",
|
||||
new SourceLocation(20, 0, 20), 1)
|
||||
};
|
||||
|
||||
// Act
|
||||
var spans = ParseDocument(documentContent, errors);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedSpans, spans);
|
||||
Assert.Equal(expectedErrors, errors);
|
||||
}
|
||||
|
||||
private static List<Span> ParseDocument(string documentContents,
|
||||
List<RazorError> errors = null,
|
||||
List<LineMapping> lineMappings = null)
|
||||
{
|
||||
errors = errors ?? new List<RazorError>();
|
||||
var markupParser = new HtmlMarkupParser();
|
||||
var codeParser = new TestMvcCSharpRazorCodeParser();
|
||||
var reader = new SeekableTextReader(documentContents);
|
||||
var context = new ParserContext(reader, codeParser, markupParser, markupParser);
|
||||
codeParser.Context = context;
|
||||
markupParser.Context = context;
|
||||
markupParser.ParseDocument();
|
||||
|
||||
var results = context.CompleteParse();
|
||||
errors.AddRange(results.ParserErrors);
|
||||
return results.Document.Flatten().ToList();
|
||||
}
|
||||
|
||||
private sealed class TestMvcCSharpRazorCodeParser : MvcRazorCodeParser
|
||||
{
|
||||
public TestMvcCSharpRazorCodeParser()
|
||||
: base("RazorView")
|
||||
{
|
||||
}
|
||||
|
||||
public bool HasDirective(string directive)
|
||||
{
|
||||
Action handler;
|
||||
return TryGetDirectiveHandler(directive, out handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
||||
using Microsoft.AspNet.Razor.Text;
|
||||
using Microsoft.AspNet.Razor.Tokenizer.Symbols;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
internal class RawTextSymbol : ISymbol
|
||||
{
|
||||
public SourceLocation Start { get; private set; }
|
||||
public string Content { get; private set; }
|
||||
|
||||
public RawTextSymbol(SourceLocation start, string content)
|
||||
{
|
||||
if (content == null)
|
||||
{
|
||||
throw new ArgumentNullException("content");
|
||||
}
|
||||
|
||||
Start = start;
|
||||
Content = content;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
var other = obj as RawTextSymbol;
|
||||
return Equals(Start, other.Start) && Equals(Content, other.Content);
|
||||
}
|
||||
|
||||
internal bool EquivalentTo(ISymbol sym)
|
||||
{
|
||||
return Equals(Start, sym.Start) && Equals(Content, sym.Content);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Start.GetHashCode() +
|
||||
(13 * Content.GetHashCode());
|
||||
}
|
||||
|
||||
public void OffsetStart(SourceLocation documentStart)
|
||||
{
|
||||
Start = documentStart + Start;
|
||||
}
|
||||
|
||||
public void ChangeStart(SourceLocation newStart)
|
||||
{
|
||||
Start = newStart;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "{0} RAW - [{1}]", Start, Content);
|
||||
}
|
||||
|
||||
internal void CalculateStart(Span prev)
|
||||
{
|
||||
if (prev == null)
|
||||
{
|
||||
Start = SourceLocation.Zero;
|
||||
}
|
||||
else
|
||||
{
|
||||
Start = new SourceLocationTracker(prev.Start).UpdateLocation(prev.Content).CurrentLocation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNet.Razor.Editor;
|
||||
using Microsoft.AspNet.Razor.Generator;
|
||||
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
||||
using Microsoft.AspNet.Razor.Text;
|
||||
using Microsoft.AspNet.Razor.Tokenizer.Symbols;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
public class SpanConstructor
|
||||
{
|
||||
public SpanBuilder Builder { get; private set; }
|
||||
|
||||
internal static IEnumerable<ISymbol> TestTokenizer(string str)
|
||||
{
|
||||
yield return new RawTextSymbol(SourceLocation.Zero, str);
|
||||
}
|
||||
|
||||
public SpanConstructor(SpanKind kind, IEnumerable<ISymbol> symbols)
|
||||
{
|
||||
Builder = new SpanBuilder();
|
||||
Builder.Kind = kind;
|
||||
Builder.EditHandler = SpanEditHandler.CreateDefault(TestTokenizer);
|
||||
foreach (ISymbol sym in symbols)
|
||||
{
|
||||
Builder.Accept(sym);
|
||||
}
|
||||
}
|
||||
|
||||
private Span Build()
|
||||
{
|
||||
return Builder.Build();
|
||||
}
|
||||
|
||||
public SpanConstructor With(ISpanCodeGenerator generator)
|
||||
{
|
||||
Builder.CodeGenerator = generator;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SpanConstructor With(SpanEditHandler handler)
|
||||
{
|
||||
Builder.EditHandler = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SpanConstructor With(Action<ISpanCodeGenerator> generatorConfigurer)
|
||||
{
|
||||
generatorConfigurer(Builder.CodeGenerator);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SpanConstructor With(Action<SpanEditHandler> handlerConfigurer)
|
||||
{
|
||||
handlerConfigurer(Builder.EditHandler);
|
||||
return this;
|
||||
}
|
||||
|
||||
public static implicit operator Span(SpanConstructor self)
|
||||
{
|
||||
return self.Build();
|
||||
}
|
||||
|
||||
public SpanConstructor Hidden()
|
||||
{
|
||||
Builder.CodeGenerator = SpanCodeGenerator.Null;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SpanConstructor Accepts(AcceptedCharacters accepted)
|
||||
{
|
||||
return With(eh => eh.AcceptedCharacters = accepted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
||||
using Microsoft.AspNet.Razor.Text;
|
||||
using Microsoft.AspNet.Razor.Tokenizer;
|
||||
using Microsoft.AspNet.Razor.Tokenizer.Symbols;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
public class SpanFactory
|
||||
{
|
||||
public Func<ITextDocument, ITokenizer> MarkupTokenizerFactory { get; set; }
|
||||
public Func<ITextDocument, ITokenizer> CodeTokenizerFactory { get; set; }
|
||||
public SourceLocationTracker LocationTracker { get; private set; }
|
||||
|
||||
public static SpanFactory CreateCsHtml()
|
||||
{
|
||||
return new SpanFactory()
|
||||
{
|
||||
MarkupTokenizerFactory = doc => new HtmlTokenizer(doc),
|
||||
CodeTokenizerFactory = doc => new CSharpTokenizer(doc)
|
||||
};
|
||||
}
|
||||
|
||||
public SpanFactory()
|
||||
{
|
||||
LocationTracker = new SourceLocationTracker();
|
||||
}
|
||||
|
||||
public SpanConstructor Span(SpanKind kind, string content, CSharpSymbolType type)
|
||||
{
|
||||
return CreateSymbolSpan(kind, content, st => new CSharpSymbol(st, content, type));
|
||||
}
|
||||
|
||||
public SpanConstructor Span(SpanKind kind, string content, HtmlSymbolType type)
|
||||
{
|
||||
return CreateSymbolSpan(kind, content, st => new HtmlSymbol(st, content, type));
|
||||
}
|
||||
|
||||
public SpanConstructor Span(SpanKind kind, string content, bool markup)
|
||||
{
|
||||
return new SpanConstructor(kind, Tokenize(new[] { content }, markup));
|
||||
}
|
||||
|
||||
public SpanConstructor Span(SpanKind kind, string[] content, bool markup)
|
||||
{
|
||||
return new SpanConstructor(kind, Tokenize(content, markup));
|
||||
}
|
||||
|
||||
public SpanConstructor Span(SpanKind kind, params ISymbol[] symbols)
|
||||
{
|
||||
return new SpanConstructor(kind, symbols);
|
||||
}
|
||||
|
||||
private SpanConstructor CreateSymbolSpan(SpanKind kind, string content, Func<SourceLocation, ISymbol> ctor)
|
||||
{
|
||||
var start = LocationTracker.CurrentLocation;
|
||||
LocationTracker.UpdateLocation(content);
|
||||
return new SpanConstructor(kind, new[] { ctor(start) });
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
LocationTracker.CurrentLocation = SourceLocation.Zero;
|
||||
}
|
||||
|
||||
private IEnumerable<ISymbol> Tokenize(IEnumerable<string> contentFragments, bool markup)
|
||||
{
|
||||
return contentFragments.SelectMany(fragment => Tokenize(fragment, markup));
|
||||
}
|
||||
|
||||
private IEnumerable<ISymbol> Tokenize(string content, bool markup)
|
||||
{
|
||||
var tok = MakeTokenizer(markup, new SeekableTextReader(content));
|
||||
ISymbol sym;
|
||||
ISymbol last = null;
|
||||
while ((sym = tok.NextSymbol()) != null)
|
||||
{
|
||||
OffsetStart(sym, LocationTracker.CurrentLocation);
|
||||
last = sym;
|
||||
yield return sym;
|
||||
}
|
||||
LocationTracker.UpdateLocation(content);
|
||||
}
|
||||
|
||||
private ITokenizer MakeTokenizer(bool markup, SeekableTextReader seekableTextReader)
|
||||
{
|
||||
if (markup)
|
||||
{
|
||||
return MarkupTokenizerFactory(seekableTextReader);
|
||||
}
|
||||
else
|
||||
{
|
||||
return CodeTokenizerFactory(seekableTextReader);
|
||||
}
|
||||
}
|
||||
|
||||
private void OffsetStart(ISymbol sym, SourceLocation sourceLocation)
|
||||
{
|
||||
sym.OffsetStart(sourceLocation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNet.Razor.Generator;
|
||||
using Microsoft.AspNet.Razor.Parser;
|
||||
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
||||
using Microsoft.AspNet.Razor.Text;
|
||||
using Microsoft.AspNet.Razor.Tokenizer.Symbols;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
public static class SpanFactoryExtensions
|
||||
{
|
||||
public static UnclassifiedCodeSpanConstructor EmptyCSharp(this SpanFactory self)
|
||||
{
|
||||
var symbol = new CSharpSymbol(self.LocationTracker.CurrentLocation, string.Empty, CSharpSymbolType.Unknown);
|
||||
return new UnclassifiedCodeSpanConstructor(self.Span(SpanKind.Code, symbol));
|
||||
}
|
||||
|
||||
public static SpanConstructor EmptyHtml(this SpanFactory self)
|
||||
{
|
||||
var symbol = new HtmlSymbol(self.LocationTracker.CurrentLocation, string.Empty, HtmlSymbolType.Unknown);
|
||||
return self.Span(SpanKind.Markup, symbol)
|
||||
.With(new MarkupCodeGenerator());
|
||||
}
|
||||
|
||||
public static UnclassifiedCodeSpanConstructor Code(this SpanFactory self, string content)
|
||||
{
|
||||
return new UnclassifiedCodeSpanConstructor(
|
||||
self.Span(SpanKind.Code, content, markup: false));
|
||||
}
|
||||
|
||||
public static SpanConstructor CodeTransition(this SpanFactory self)
|
||||
{
|
||||
return self.Span(SpanKind.Transition, SyntaxConstants.TransitionString, markup: false)
|
||||
.Accepts(AcceptedCharacters.None);
|
||||
}
|
||||
|
||||
public static SpanConstructor CodeTransition(this SpanFactory self, string content)
|
||||
{
|
||||
return self.Span(SpanKind.Transition, content, markup: false).Accepts(AcceptedCharacters.None);
|
||||
}
|
||||
|
||||
public static SpanConstructor CodeTransition(this SpanFactory self, CSharpSymbolType type)
|
||||
{
|
||||
return self.Span(SpanKind.Transition, SyntaxConstants.TransitionString, type)
|
||||
.Accepts(AcceptedCharacters.None);
|
||||
}
|
||||
|
||||
public static SpanConstructor CodeTransition(this SpanFactory self, string content, CSharpSymbolType type)
|
||||
{
|
||||
return self.Span(SpanKind.Transition, content, type).Accepts(AcceptedCharacters.None);
|
||||
}
|
||||
|
||||
public static SpanConstructor MarkupTransition(this SpanFactory self)
|
||||
{
|
||||
return self.Span(SpanKind.Transition, SyntaxConstants.TransitionString, markup: true)
|
||||
.Accepts(AcceptedCharacters.None);
|
||||
}
|
||||
|
||||
public static SpanConstructor MarkupTransition(this SpanFactory self, string content)
|
||||
{
|
||||
return self.Span(SpanKind.Transition, content, markup: true).Accepts(AcceptedCharacters.None);
|
||||
}
|
||||
|
||||
public static SpanConstructor MarkupTransition(this SpanFactory self, HtmlSymbolType type)
|
||||
{
|
||||
return self.Span(SpanKind.Transition, SyntaxConstants.TransitionString, type)
|
||||
.Accepts(AcceptedCharacters.None);
|
||||
}
|
||||
|
||||
public static SpanConstructor MarkupTransition(this SpanFactory self, string content, HtmlSymbolType type)
|
||||
{
|
||||
return self.Span(SpanKind.Transition, content, type).Accepts(AcceptedCharacters.None);
|
||||
}
|
||||
|
||||
public static SpanConstructor MetaCode(this SpanFactory self, string content)
|
||||
{
|
||||
return self.Span(SpanKind.MetaCode, content, markup: false);
|
||||
}
|
||||
|
||||
public static SpanConstructor MetaCode(this SpanFactory self, string content, CSharpSymbolType type)
|
||||
{
|
||||
return self.Span(SpanKind.MetaCode, content, type);
|
||||
}
|
||||
|
||||
public static SpanConstructor MetaMarkup(this SpanFactory self, string content)
|
||||
{
|
||||
return self.Span(SpanKind.MetaCode, content, markup: true);
|
||||
}
|
||||
|
||||
public static SpanConstructor MetaMarkup(this SpanFactory self, string content, HtmlSymbolType type)
|
||||
{
|
||||
return self.Span(SpanKind.MetaCode, content, type);
|
||||
}
|
||||
|
||||
public static SpanConstructor Comment(this SpanFactory self, string content, CSharpSymbolType type)
|
||||
{
|
||||
return self.Span(SpanKind.Comment, content, type);
|
||||
}
|
||||
|
||||
public static SpanConstructor Comment(this SpanFactory self, string content, HtmlSymbolType type)
|
||||
{
|
||||
return self.Span(SpanKind.Comment, content, type);
|
||||
}
|
||||
|
||||
public static SpanConstructor Markup(this SpanFactory self, string content)
|
||||
{
|
||||
return self.Span(SpanKind.Markup, content, markup: true).With(new MarkupCodeGenerator());
|
||||
}
|
||||
|
||||
public static SpanConstructor Markup(this SpanFactory self, params string[] content)
|
||||
{
|
||||
return self.Span(SpanKind.Markup, content, markup: true).With(new MarkupCodeGenerator());
|
||||
}
|
||||
|
||||
public static SourceLocation GetLocationAndAdvance(this SourceLocationTracker self, string content)
|
||||
{
|
||||
var ret = self.CurrentLocation;
|
||||
self.UpdateLocation(content);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNet.Razor.Editor;
|
||||
using Microsoft.AspNet.Razor.Generator;
|
||||
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
public class UnclassifiedCodeSpanConstructor
|
||||
{
|
||||
private SpanConstructor _self;
|
||||
|
||||
public UnclassifiedCodeSpanConstructor(SpanConstructor self)
|
||||
{
|
||||
_self = self;
|
||||
}
|
||||
|
||||
public SpanConstructor AsMetaCode()
|
||||
{
|
||||
_self.Builder.Kind = SpanKind.MetaCode;
|
||||
return _self;
|
||||
}
|
||||
|
||||
public SpanConstructor AsStatement()
|
||||
{
|
||||
return _self.With(new StatementCodeGenerator());
|
||||
}
|
||||
|
||||
public SpanConstructor AsExpression()
|
||||
{
|
||||
return _self.With(new ExpressionCodeGenerator());
|
||||
}
|
||||
|
||||
public SpanConstructor AsImplicitExpression(ISet<string> keywords)
|
||||
{
|
||||
return AsImplicitExpression(keywords, acceptTrailingDot: false);
|
||||
}
|
||||
|
||||
public SpanConstructor AsImplicitExpression(ISet<string> keywords, bool acceptTrailingDot)
|
||||
{
|
||||
return _self.With(new ImplicitExpressionEditHandler(SpanConstructor.TestTokenizer,
|
||||
keywords,
|
||||
acceptTrailingDot))
|
||||
.With(new ExpressionCodeGenerator());
|
||||
}
|
||||
|
||||
public SpanConstructor AsFunctionsBody()
|
||||
{
|
||||
return _self.With(new TypeMemberCodeGenerator());
|
||||
}
|
||||
|
||||
public SpanConstructor AsNamespaceImport(string ns, int namespaceKeywordLength)
|
||||
{
|
||||
return _self.With(new AddImportCodeGenerator(ns, namespaceKeywordLength));
|
||||
}
|
||||
|
||||
public SpanConstructor Hidden()
|
||||
{
|
||||
return _self.With(SpanCodeGenerator.Null);
|
||||
}
|
||||
|
||||
public SpanConstructor AsBaseType(string baseType)
|
||||
{
|
||||
return _self.With(new SetBaseTypeCodeGenerator(baseType));
|
||||
}
|
||||
|
||||
public SpanConstructor AsRazorDirectiveAttribute(string key, string value)
|
||||
{
|
||||
return _self.With(new RazorDirectiveAttributeCodeGenerator(key, value));
|
||||
}
|
||||
|
||||
public SpanConstructor As(ISpanCodeGenerator codeGenerator)
|
||||
{
|
||||
return _self.With(codeGenerator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Text;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
public class StringTextBuffer : ITextBuffer, IDisposable
|
||||
{
|
||||
private string _buffer;
|
||||
public bool Disposed { get; set; }
|
||||
|
||||
public StringTextBuffer(string buffer)
|
||||
{
|
||||
_buffer = buffer;
|
||||
}
|
||||
|
||||
public int Length
|
||||
{
|
||||
get { return _buffer.Length; }
|
||||
}
|
||||
|
||||
public int Position { get; set; }
|
||||
|
||||
public int Read()
|
||||
{
|
||||
if (Position >= _buffer.Length)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return _buffer[Position++];
|
||||
}
|
||||
|
||||
public int Peek()
|
||||
{
|
||||
if (Position >= _buffer.Length)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return _buffer[Position];
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
@using MyNamespace
|
||||
@inject MyApp MyPropertyName
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
namespace Razor
|
||||
{
|
||||
#line 1 ""
|
||||
using MyNamespace
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class __CompiledTemplate : RazorView<dynamic>
|
||||
{
|
||||
private static object @__o;
|
||||
private void @__RazorDesignTimeHelpers__()
|
||||
{
|
||||
#pragma warning disable 219
|
||||
#pragma warning restore 219
|
||||
}
|
||||
#line hidden
|
||||
public
|
||||
#line 2 ""
|
||||
MyApp MyPropertyName
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
{ get; private set; }
|
||||
|
||||
#line hidden
|
||||
public __CompiledTemplate(MyApp MyPropertyName)
|
||||
{
|
||||
this.MyPropertyName = MyPropertyName;
|
||||
}
|
||||
|
||||
#pragma warning disable 1998
|
||||
public override async Task ExecuteAsync()
|
||||
{
|
||||
}
|
||||
#pragma warning restore 1998
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"version" : "0.1-alpha-*",
|
||||
"compilationOptions": {
|
||||
"warningsAsErrors": false
|
||||
},
|
||||
"resources": "TestFiles\\**",
|
||||
"dependencies": {
|
||||
"Microsoft.AspNet.Mvc.Razor.Host" : "",
|
||||
"xunit.assert": "2.0.0-aspnet-*",
|
||||
"Xunit.KRunner": "0.1-alpha-*"
|
||||
},
|
||||
"commands": {
|
||||
"test": "Xunit.KRunner"
|
||||
},
|
||||
"configurations": {
|
||||
"net45": {
|
||||
"dependencies": {
|
||||
"Moq": "4.2.1312.1622"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue