Use DocumentClassifierPassBase
Workaround issue with the model directive disappearing
This commit is contained in:
parent
8b03e9ef73
commit
bc3a741eee
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -42,11 +42,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Host
|
||||||
var tokens = directive.Tokens.ToArray();
|
var tokens = directive.Tokens.ToArray();
|
||||||
if (tokens.Length >= 1)
|
if (tokens.Length >= 1)
|
||||||
{
|
{
|
||||||
|
document.Parent = directive;
|
||||||
return tokens[0].Content;
|
return tokens[0].Content;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (document.DocumentKind == RazorPageDocumentClassifier.DocumentKind)
|
if (document.DocumentKind == RazorPageDocumentClassifier.RazorPageDocumentKind)
|
||||||
{
|
{
|
||||||
return visitor.Class.Name;
|
return visitor.Class.Name;
|
||||||
}
|
}
|
||||||
|
|
@ -94,6 +95,15 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Host
|
||||||
|
|
||||||
public IList<DirectiveIRNode> ModelDirectives { get; } = new List<DirectiveIRNode>();
|
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)
|
public override void VisitClass(ClassDeclarationIRNode node)
|
||||||
{
|
{
|
||||||
if (Class == null)
|
if (Class == null)
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,35 @@
|
||||||
// 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 Microsoft.AspNetCore.Mvc.Razor.Internal;
|
||||||
using Microsoft.AspNetCore.Razor.Evolution;
|
using Microsoft.AspNetCore.Razor.Evolution;
|
||||||
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.Razor.Host
|
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)
|
protected override bool IsMatch(RazorCodeDocument codeDocument, DocumentIRNode irDocument) => true;
|
||||||
=> DocumentKind;
|
|
||||||
|
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}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Host
|
||||||
|
|
||||||
public DocumentIRNode Execute(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
public DocumentIRNode Execute(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
||||||
{
|
{
|
||||||
if (irDocument.DocumentKind != RazorPageDocumentClassifier.DocumentKind)
|
if (irDocument.DocumentKind != RazorPageDocumentClassifier.RazorPageDocumentKind)
|
||||||
{
|
{
|
||||||
return irDocument;
|
return irDocument;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,35 @@
|
||||||
// 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 Microsoft.AspNetCore.Mvc.Razor.Internal;
|
||||||
using Microsoft.AspNetCore.Razor.Evolution;
|
using Microsoft.AspNetCore.Razor.Evolution;
|
||||||
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.Razor.Host
|
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;
|
string routePrefix;
|
||||||
if (PageDirective.TryGetRouteTemplate(irDocument, out routePrefix))
|
return PageDirective.TryGetRouteTemplate(irDocument, out routePrefix);
|
||||||
{
|
}
|
||||||
return DocumentKind;
|
|
||||||
}
|
|
||||||
|
|
||||||
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}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1518,7 +1518,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var bufferedWriter = Assert.IsType<ViewBufferTextWriter>(Writer);
|
var bufferedWriter = Assert.IsType<ViewBufferTextWriter>(Output);
|
||||||
using (var stringWriter = new StringWriter())
|
using (var stringWriter = new StringWriter())
|
||||||
{
|
{
|
||||||
bufferedWriter.Buffer.WriteTo(stringWriter, HtmlEncoder);
|
bufferedWriter.Buffer.WriteTo(stringWriter, HtmlEncoder);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue