Use DocumentClassifierPassBase

Workaround issue with the model directive disappearing
This commit is contained in:
Pranav K 2017-02-09 20:30:31 -08:00
parent 8b03e9ef73
commit bc3a741eee
7 changed files with 113 additions and 194 deletions

View File

@ -1,177 +0,0 @@
// 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.Collections.Generic;
using System.Globalization;
using System.IO;
using Microsoft.AspNetCore.Razor.Evolution;
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNetCore.Mvc.Razor.Host
{
public abstract class BaseDocumentClassifierPass : IRazorIRPass
{
public RazorEngine Engine { get; set; }
// We want to run before the default, but after others since this is the MVC default.
public virtual int Order => RazorIRPass.DefaultDocumentClassifierOrder - 1;
protected abstract string BaseType { get; }
public DocumentIRNode Execute(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
{
if (irDocument.DocumentKind != null)
{
return irDocument;
}
var documentKind = ClassifyDocument(codeDocument, irDocument);
if (documentKind == null)
{
return irDocument;
}
irDocument.DocumentKind = documentKind;
return ExecuteCore(codeDocument, irDocument);
}
protected virtual DocumentIRNode ExecuteCore(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
{
// Rewrite a use default namespace and class declaration.
var children = new List<RazorIRNode>(irDocument.Children);
irDocument.Children.Clear();
var @namespace = new NamespaceDeclarationIRNode
{
Content = "AspNetCore",
};
var @class = new ClassDeclarationIRNode
{
AccessModifier = "public",
Name = GetClassName(codeDocument.Source.Filename) ?? "GeneratedClass",
BaseType = BaseType,
};
var method = new RazorMethodDeclarationIRNode()
{
AccessModifier = "public",
Modifiers = new List<string>() { "async", "override" },
Name = "ExecuteAsync",
ReturnType = "Task",
};
var documentBuilder = RazorIRBuilder.Create(irDocument);
var namespaceBuilder = RazorIRBuilder.Create(documentBuilder.Current);
namespaceBuilder.Push(@namespace);
var classBuilder = RazorIRBuilder.Create(namespaceBuilder.Current);
classBuilder.Push(@class);
var methodBuilder = RazorIRBuilder.Create(classBuilder.Current);
methodBuilder.Push(method);
var visitor = new Visitor(documentBuilder, namespaceBuilder, classBuilder, methodBuilder);
for (var i = 0; i < children.Count; i++)
{
visitor.Visit(children[i]);
}
return irDocument;
}
protected abstract string ClassifyDocument(RazorCodeDocument codeDocument, DocumentIRNode irDocument);
private static string GetClassName(string filename)
{
if (filename == null)
{
return null;
}
return SanitizeClassName("Generated_" + Path.GetFileNameWithoutExtension(filename));
}
// CSharp Spec §2.4.2
private static bool IsIdentifierStart(char character)
{
return char.IsLetter(character) ||
character == '_' ||
CharUnicodeInfo.GetUnicodeCategory(character) == UnicodeCategory.LetterNumber;
}
public static bool IsIdentifierPart(char character)
{
return char.IsDigit(character) ||
IsIdentifierStart(character) ||
IsIdentifierPartByUnicodeCategory(character);
}
private static bool IsIdentifierPartByUnicodeCategory(char character)
{
var category = CharUnicodeInfo.GetUnicodeCategory(character);
return category == UnicodeCategory.NonSpacingMark || // Mn
category == UnicodeCategory.SpacingCombiningMark || // Mc
category == UnicodeCategory.ConnectorPunctuation || // Pc
category == UnicodeCategory.Format; // Cf
}
public static string SanitizeClassName(string inputName)
{
if (!IsIdentifierStart(inputName[0]) && IsIdentifierPart(inputName[0]))
{
inputName = "_" + inputName;
}
var builder = new InplaceStringBuilder(inputName.Length);
for (var i = 0; i < inputName.Length; i++)
{
var ch = inputName[i];
builder.Append(IsIdentifierPart(ch) ? ch : '_');
}
return builder.ToString();
}
private class Visitor : RazorIRNodeVisitor
{
private readonly RazorIRBuilder _document;
private readonly RazorIRBuilder _namespace;
private readonly RazorIRBuilder _class;
private readonly RazorIRBuilder _method;
public Visitor(RazorIRBuilder document, RazorIRBuilder @namespace, RazorIRBuilder @class, RazorIRBuilder method)
{
_document = document;
_namespace = @namespace;
_class = @class;
_method = method;
}
public override void VisitChecksum(ChecksumIRNode node)
{
_document.Insert(0, node);
}
public override void VisitUsingStatement(UsingStatementIRNode node)
{
_namespace.AddAfter<UsingStatementIRNode>(node);
}
public override void VisitDeclareTagHelperFields(DeclareTagHelperFieldsIRNode node)
{
_class.Insert(0, node);
}
public override void VisitDefault(RazorIRNode node)
{
_method.Add(node);
}
}
}
}

View File

@ -0,0 +1,60 @@
using System.Globalization;
using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNetCore.Mvc.Razor.Internal
{
public static class ClassName
{
public static string GetClassNameFromPath(string path)
{
if (string.IsNullOrEmpty(path))
{
return path;
}
return SanitizeClassName(path);
}
// CSharp Spec §2.4.2
private static bool IsIdentifierStart(char character)
{
return char.IsLetter(character) ||
character == '_' ||
CharUnicodeInfo.GetUnicodeCategory(character) == UnicodeCategory.LetterNumber;
}
public static bool IsIdentifierPart(char character)
{
return char.IsDigit(character) ||
IsIdentifierStart(character) ||
IsIdentifierPartByUnicodeCategory(character);
}
private static bool IsIdentifierPartByUnicodeCategory(char character)
{
var category = CharUnicodeInfo.GetUnicodeCategory(character);
return category == UnicodeCategory.NonSpacingMark || // Mn
category == UnicodeCategory.SpacingCombiningMark || // Mc
category == UnicodeCategory.ConnectorPunctuation || // Pc
category == UnicodeCategory.Format; // Cf
}
private static string SanitizeClassName(string inputName)
{
if (!IsIdentifierStart(inputName[0]) && IsIdentifierPart(inputName[0]))
{
inputName = "_" + inputName;
}
var builder = new InplaceStringBuilder(inputName.Length);
for (var i = 0; i < inputName.Length; i++)
{
var ch = inputName[i];
builder.Append(IsIdentifierPart(ch) ? ch : '_');
}
return builder.ToString();
}
}
}

View File

@ -42,11 +42,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Host
var tokens = directive.Tokens.ToArray();
if (tokens.Length >= 1)
{
document.Parent = directive;
return tokens[0].Content;
}
}
if (document.DocumentKind == RazorPageDocumentClassifier.DocumentKind)
if (document.DocumentKind == RazorPageDocumentClassifier.RazorPageDocumentKind)
{
return visitor.Class.Name;
}
@ -94,6 +95,15 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Host
public IList<DirectiveIRNode> ModelDirectives { get; } = new List<DirectiveIRNode>();
public override void VisitDocument(DocumentIRNode node)
{
if (node.Parent != null)
{
ModelDirectives.Add((DirectiveIRNode)node.Parent);
}
base.VisitDocument(node);
}
public override void VisitClass(ClassDeclarationIRNode node)
{
if (Class == null)

View File

@ -1,18 +1,35 @@
// 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 Microsoft.AspNetCore.Mvc.Razor.Internal;
using Microsoft.AspNetCore.Razor.Evolution;
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
namespace Microsoft.AspNetCore.Mvc.Razor.Host
{
public class MvcViewDocumentClassifierPass : BaseDocumentClassifierPass
public class MvcViewDocumentClassifierPass : DocumentClassifierPassBase
{
public static readonly string DocumentKind = "mvc.1.0.view";
public readonly string MvcViewDocumentKind = "mvc.1.0.view";
protected override string BaseType => "Microsoft.AspNetCore.Mvc.Razor.RazorPage<TModel>";
protected override string DocumentKind => MvcViewDocumentKind;
protected override string ClassifyDocument(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
=> DocumentKind;
protected override bool IsMatch(RazorCodeDocument codeDocument, DocumentIRNode irDocument) => true;
protected override void OnDocumentStructureCreated(
RazorCodeDocument codeDocument,
NamespaceDeclarationIRNode @namespace,
ClassDeclarationIRNode @class,
RazorMethodDeclarationIRNode method)
{
base.OnDocumentStructureCreated(codeDocument, @namespace, @class, method);
@class.Name = ClassName.GetClassNameFromPath(codeDocument.Source.Filename);
@class.BaseType = "Microsoft.AspNetCore.Mvc.Razor.RazorPage<TModel>";
@class.AccessModifier = "public";
@namespace.Content = "AspNetCore";
method.Name = "ExecuteAsync";
method.Modifiers = new[] { "async", "override" };
method.AccessModifier = "public";
method.ReturnType = $"global::{typeof(System.Threading.Tasks.Task).FullName}";
}
}
}

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Host
public DocumentIRNode Execute(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
{
if (irDocument.DocumentKind != RazorPageDocumentClassifier.DocumentKind)
if (irDocument.DocumentKind != RazorPageDocumentClassifier.RazorPageDocumentKind)
{
return irDocument;
}

View File

@ -1,26 +1,35 @@
// 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 Microsoft.AspNetCore.Mvc.Razor.Internal;
using Microsoft.AspNetCore.Razor.Evolution;
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
namespace Microsoft.AspNetCore.Mvc.Razor.Host
{
public class RazorPageDocumentClassifier : BaseDocumentClassifierPass
public class RazorPageDocumentClassifier : DocumentClassifierPassBase
{
public static readonly string DocumentKind = "mvc.1.0.razor-page";
public static readonly string RazorPageDocumentKind = "mvc.1.0.razor-page";
protected override string BaseType => "Microsoft.AspNetCore.Mvc.RazorPages.Page";
protected override string DocumentKind => RazorPageDocumentKind;
protected override string ClassifyDocument(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
protected override bool IsMatch(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
{
string routePrefix;
if (PageDirective.TryGetRouteTemplate(irDocument, out routePrefix))
{
return DocumentKind;
}
return PageDirective.TryGetRouteTemplate(irDocument, out routePrefix);
}
return null;
protected override void OnDocumentStructureCreated(RazorCodeDocument codeDocument, NamespaceDeclarationIRNode @namespace, ClassDeclarationIRNode @class, RazorMethodDeclarationIRNode method)
{
base.OnDocumentStructureCreated(codeDocument, @namespace, @class, method);
@class.BaseType = "Microsoft.AspNetCore.Mvc.RazorPages.Page";
@class.Name = ClassName.GetClassNameFromPath(codeDocument.Source.Filename);
@class.AccessModifier = "public";
@namespace.Content = "AspNetCore";
method.Name = "ExecuteAsync";
method.Modifiers = new[] { "async", "override" };
method.AccessModifier = "public";
method.ReturnType = $"global::{typeof(System.Threading.Tasks.Task).FullName}";
}
}
}

View File

@ -1518,7 +1518,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
{
get
{
var bufferedWriter = Assert.IsType<ViewBufferTextWriter>(Writer);
var bufferedWriter = Assert.IsType<ViewBufferTextWriter>(Output);
using (var stringWriter = new StringWriter())
{
bufferedWriter.Buffer.WriteTo(stringWriter, HtmlEncoder);