Implement a simple base for document classifiers
This should allow us to de-dupe a lot of code in MVC.
This commit is contained in:
parent
eaadfb70eb
commit
ea778b9b6d
|
|
@ -0,0 +1,19 @@
|
||||||
|
// 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.Razor.Evolution.Intermediate;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
internal class DefaultDocumentClassifierPass : DocumentClassifierPassBase
|
||||||
|
{
|
||||||
|
public override int Order => RazorIRPass.DefaultDocumentClassifierOrder;
|
||||||
|
|
||||||
|
protected override string DocumentKind => "default";
|
||||||
|
|
||||||
|
protected override bool IsMatch(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,22 +6,35 @@ using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
internal class DefaultDocumentClassifier : RazorIRPassBase
|
public abstract class DocumentClassifierPassBase : RazorIRPassBase
|
||||||
{
|
{
|
||||||
public override int Order => RazorIRPass.DefaultDocumentClassifierOrder;
|
protected abstract string DocumentKind { get; }
|
||||||
|
|
||||||
public static string DocumentKind = "default";
|
public override int Order => RazorIRPass.DocumentClassifierOrder;
|
||||||
|
|
||||||
public override DocumentIRNode ExecuteCore(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
public sealed override DocumentIRNode ExecuteCore(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
||||||
{
|
{
|
||||||
if (irDocument.DocumentKind != null)
|
if (irDocument.DocumentKind != null)
|
||||||
{
|
{
|
||||||
return irDocument;
|
return irDocument;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!IsMatch(codeDocument, irDocument))
|
||||||
|
{
|
||||||
|
return irDocument;
|
||||||
|
}
|
||||||
|
|
||||||
irDocument.DocumentKind = DocumentKind;
|
irDocument.DocumentKind = DocumentKind;
|
||||||
|
|
||||||
// Rewrite a use default namespace and class declaration.
|
Rewrite(codeDocument, irDocument);
|
||||||
|
|
||||||
|
return irDocument;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Rewrite(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
||||||
|
{
|
||||||
|
// Rewrite the document from a flat structure to use a sensible default structure,
|
||||||
|
// a namespace and class declaration with a single 'razor' method.
|
||||||
var children = new List<RazorIRNode>(irDocument.Children);
|
var children = new List<RazorIRNode>(irDocument.Children);
|
||||||
irDocument.Children.Clear();
|
irDocument.Children.Clear();
|
||||||
|
|
||||||
|
|
@ -39,7 +52,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
var method = new RazorMethodDeclarationIRNode()
|
var method = new RazorMethodDeclarationIRNode()
|
||||||
{
|
{
|
||||||
//AccessModifier = "public",
|
//AccessModifier = "public",
|
||||||
// Modifiers = new List<string>() { "async" },
|
// Modifiers = new List<string>() { "async" },
|
||||||
//Name = "Execute",
|
//Name = "Execute",
|
||||||
//ReturnType = "Task",
|
//ReturnType = "Task",
|
||||||
};
|
};
|
||||||
|
|
@ -62,7 +75,20 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
visitor.Visit(children[i]);
|
visitor.Visit(children[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return irDocument;
|
// Note that this is called at the *end* of rewriting so that user code can see the tree
|
||||||
|
// and look at its content to make a decision.
|
||||||
|
OnDocumentStructureCreated(codeDocument, @namespace, @class, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract bool IsMatch(RazorCodeDocument codeDocument, DocumentIRNode irDocument);
|
||||||
|
|
||||||
|
protected virtual void OnDocumentStructureCreated(
|
||||||
|
RazorCodeDocument codeDocument,
|
||||||
|
NamespaceDeclarationIRNode @namespace,
|
||||||
|
ClassDeclarationIRNode @class,
|
||||||
|
RazorMethodDeclarationIRNode @method)
|
||||||
|
{
|
||||||
|
// Intentionally empty.
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Visitor : RazorIRNodeVisitor
|
private class Visitor : RazorIRNodeVisitor
|
||||||
|
|
@ -59,7 +59,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
builder.Features.Add(new TagHelperBinderSyntaxTreePass());
|
builder.Features.Add(new TagHelperBinderSyntaxTreePass());
|
||||||
|
|
||||||
// IR Passes
|
// IR Passes
|
||||||
builder.Features.Add(new DefaultDocumentClassifier());
|
builder.Features.Add(new DefaultDocumentClassifierPass());
|
||||||
builder.Features.Add(new DefaultDirectiveIRPass());
|
builder.Features.Add(new DefaultDirectiveIRPass());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -226,7 +226,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
Assert.NotNull(irDocument);
|
Assert.NotNull(irDocument);
|
||||||
|
|
||||||
// These tests depend on the document->namespace->class structure.
|
// These tests depend on the document->namespace->class structure.
|
||||||
irDocument = new DefaultDocumentClassifier() { Engine = engine, }.Execute(codeDocument, irDocument);
|
irDocument = new DefaultDocumentClassifierPass() { Engine = engine, }.Execute(codeDocument, irDocument);
|
||||||
return irDocument;
|
return irDocument;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
// 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.Razor.Evolution.Intermediate;
|
||||||
|
using Xunit;
|
||||||
|
using static Microsoft.AspNetCore.Razor.Evolution.Intermediate.RazorIRAssert;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
// We're purposely lean on tests here because the functionality is well covered by
|
||||||
|
// integration tests, and is mostly implemented by the base class.
|
||||||
|
public class DefaultDocumentClassifierPassTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Execute_IgnoresDocumentsWithDocumentKind()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var irDocument = new DocumentIRNode()
|
||||||
|
{
|
||||||
|
DocumentKind = "ignore",
|
||||||
|
};
|
||||||
|
|
||||||
|
var pass = new DefaultDocumentClassifierPass();
|
||||||
|
pass.Engine = RazorEngine.CreateEmpty(b => { });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
pass.Execute(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("ignore", irDocument.DocumentKind);
|
||||||
|
NoChildren(irDocument);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Execute_CreatesClassStructure()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var irDocument = new DocumentIRNode();
|
||||||
|
|
||||||
|
var pass = new DefaultDocumentClassifierPass();
|
||||||
|
pass.Engine = RazorEngine.CreateEmpty(b =>{ });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
pass.Execute(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("default", irDocument.DocumentKind);
|
||||||
|
|
||||||
|
var @namespace = SingleChild<NamespaceDeclarationIRNode>(irDocument);
|
||||||
|
var @class = SingleChild<ClassDeclarationIRNode>(@namespace);
|
||||||
|
var method = SingleChild<RazorMethodDeclarationIRNode>(@class);
|
||||||
|
NoChildren(method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,16 +2,17 @@
|
||||||
// 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.AspNetCore.Razor.Evolution.Intermediate;
|
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using static Microsoft.AspNetCore.Razor.Evolution.Intermediate.RazorIRAssert;
|
using static Microsoft.AspNetCore.Razor.Evolution.Intermediate.RazorIRAssert;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
public class DefaultDocumentClassifierTest
|
public class DocumentClassifierPassBaseTest
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Execute_IgnoresDocumentsWithDocumentKind()
|
public void Execute_HasDocumentKind_IgnoresDocument()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var irDocument = new DocumentIRNode()
|
var irDocument = new DocumentIRNode()
|
||||||
|
|
@ -19,7 +20,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
DocumentKind = "ignore",
|
DocumentKind = "ignore",
|
||||||
};
|
};
|
||||||
|
|
||||||
var pass = new DefaultDocumentClassifier();
|
var pass = new TestDocumentClassifierPass();
|
||||||
pass.Engine = RazorEngine.CreateEmpty(b => { });
|
pass.Engine = RazorEngine.CreateEmpty(b => { });
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -31,19 +32,39 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Execute_CreatesClassStructure()
|
public void Execute_NoMatch_IgnoresDocument()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var irDocument = new DocumentIRNode();
|
var irDocument = new DocumentIRNode();
|
||||||
|
|
||||||
var pass = new DefaultDocumentClassifier();
|
var pass = new TestDocumentClassifierPass()
|
||||||
pass.Engine = RazorEngine.CreateEmpty(b =>{ });
|
{
|
||||||
|
Engine = RazorEngine.CreateEmpty(b => { }),
|
||||||
|
ShouldMatch = false,
|
||||||
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
pass.Execute(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
pass.Execute(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(DefaultDocumentClassifier.DocumentKind, irDocument.DocumentKind);
|
Assert.Null(irDocument.DocumentKind);
|
||||||
|
NoChildren(irDocument);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Execute_Match_SetsDocumentType_AndCreatesStructure()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var irDocument = new DocumentIRNode();
|
||||||
|
|
||||||
|
var pass = new TestDocumentClassifierPass();
|
||||||
|
pass.Engine = RazorEngine.CreateEmpty(b => { });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
pass.Execute(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("test", irDocument.DocumentKind);
|
||||||
|
|
||||||
var @namespace = SingleChild<NamespaceDeclarationIRNode>(irDocument);
|
var @namespace = SingleChild<NamespaceDeclarationIRNode>(irDocument);
|
||||||
var @class = SingleChild<ClassDeclarationIRNode>(@namespace);
|
var @class = SingleChild<ClassDeclarationIRNode>(@namespace);
|
||||||
|
|
@ -60,7 +81,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
var builder = RazorIRBuilder.Create(irDocument);
|
var builder = RazorIRBuilder.Create(irDocument);
|
||||||
builder.Add(new ChecksumIRNode());
|
builder.Add(new ChecksumIRNode());
|
||||||
|
|
||||||
var pass = new DefaultDocumentClassifier();
|
var pass = new TestDocumentClassifierPass();
|
||||||
pass.Engine = RazorEngine.CreateEmpty(b => { });
|
pass.Engine = RazorEngine.CreateEmpty(b => { });
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -82,7 +103,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
var builder = RazorIRBuilder.Create(irDocument);
|
var builder = RazorIRBuilder.Create(irDocument);
|
||||||
builder.Add(new UsingStatementIRNode());
|
builder.Add(new UsingStatementIRNode());
|
||||||
|
|
||||||
var pass = new DefaultDocumentClassifier();
|
var pass = new TestDocumentClassifierPass();
|
||||||
pass.Engine = RazorEngine.CreateEmpty(b => { });
|
pass.Engine = RazorEngine.CreateEmpty(b => { });
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -105,7 +126,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
var builder = RazorIRBuilder.Create(irDocument);
|
var builder = RazorIRBuilder.Create(irDocument);
|
||||||
builder.Add(new DeclareTagHelperFieldsIRNode());
|
builder.Add(new DeclareTagHelperFieldsIRNode());
|
||||||
|
|
||||||
var pass = new DefaultDocumentClassifier();
|
var pass = new TestDocumentClassifierPass();
|
||||||
pass.Engine = RazorEngine.CreateEmpty(b => { });
|
pass.Engine = RazorEngine.CreateEmpty(b => { });
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -130,7 +151,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
builder.Add(new HtmlContentIRNode());
|
builder.Add(new HtmlContentIRNode());
|
||||||
builder.Add(new CSharpStatementIRNode());
|
builder.Add(new CSharpStatementIRNode());
|
||||||
|
|
||||||
var pass = new DefaultDocumentClassifier();
|
var pass = new TestDocumentClassifierPass();
|
||||||
pass.Engine = RazorEngine.CreateEmpty(b => { });
|
pass.Engine = RazorEngine.CreateEmpty(b => { });
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -145,5 +166,66 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
n => Assert.IsType<HtmlContentIRNode>(n),
|
n => Assert.IsType<HtmlContentIRNode>(n),
|
||||||
n => Assert.IsType<CSharpStatementIRNode>(n));
|
n => Assert.IsType<CSharpStatementIRNode>(n));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Execute_CanInitializeDefaults()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var irDocument = new DocumentIRNode();
|
||||||
|
|
||||||
|
var builder = RazorIRBuilder.Create(irDocument);
|
||||||
|
builder.Add(new HtmlContentIRNode());
|
||||||
|
builder.Add(new CSharpStatementIRNode());
|
||||||
|
|
||||||
|
var pass = new TestDocumentClassifierPass()
|
||||||
|
{
|
||||||
|
Engine = RazorEngine.CreateEmpty(b => { }),
|
||||||
|
Namespace = "TestNamespace",
|
||||||
|
Class = "TestClass",
|
||||||
|
Method = "TestMethod",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
pass.Execute(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var @namespace = SingleChild<NamespaceDeclarationIRNode>(irDocument);
|
||||||
|
Assert.Equal("TestNamespace", @namespace.Content);
|
||||||
|
|
||||||
|
var @class = SingleChild<ClassDeclarationIRNode>(@namespace);
|
||||||
|
Assert.Equal("TestClass", @class.Name);
|
||||||
|
|
||||||
|
var method = SingleChild<RazorMethodDeclarationIRNode>(@class);
|
||||||
|
Assert.Equal("TestMethod", method.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestDocumentClassifierPass : DocumentClassifierPassBase
|
||||||
|
{
|
||||||
|
public bool ShouldMatch { get; set; } = true;
|
||||||
|
|
||||||
|
public string Namespace { get; set; }
|
||||||
|
|
||||||
|
public string Class { get; set; }
|
||||||
|
|
||||||
|
public string Method { get; set; }
|
||||||
|
|
||||||
|
protected override string DocumentKind => "test";
|
||||||
|
|
||||||
|
protected override bool IsMatch(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
||||||
|
{
|
||||||
|
return ShouldMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDocumentStructureCreated(
|
||||||
|
RazorCodeDocument codeDocument,
|
||||||
|
NamespaceDeclarationIRNode @namespace,
|
||||||
|
ClassDeclarationIRNode @class,
|
||||||
|
RazorMethodDeclarationIRNode method)
|
||||||
|
{
|
||||||
|
@namespace.Content = Namespace;
|
||||||
|
@class.Name = Class;
|
||||||
|
@method.Name = Method;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -139,7 +139,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
feature => Assert.IsType<DefaultDirectiveSyntaxTreePass>(feature),
|
feature => Assert.IsType<DefaultDirectiveSyntaxTreePass>(feature),
|
||||||
feature => Assert.IsType<HtmlNodeOptimizationPass>(feature),
|
feature => Assert.IsType<HtmlNodeOptimizationPass>(feature),
|
||||||
feature => Assert.IsType<TagHelperBinderSyntaxTreePass>(feature),
|
feature => Assert.IsType<TagHelperBinderSyntaxTreePass>(feature),
|
||||||
feature => Assert.IsType<DefaultDocumentClassifier>(feature),
|
feature => Assert.IsType<DefaultDocumentClassifierPass>(feature),
|
||||||
feature => Assert.IsType<DefaultDirectiveIRPass>(feature),
|
feature => Assert.IsType<DefaultDirectiveIRPass>(feature),
|
||||||
feature => Assert.IsType<RazorPreallocatedTagHelperAttributeOptimizationPass>(feature));
|
feature => Assert.IsType<RazorPreallocatedTagHelperAttributeOptimizationPass>(feature));
|
||||||
}
|
}
|
||||||
|
|
@ -162,7 +162,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
feature => Assert.IsType<DefaultDirectiveSyntaxTreePass>(feature),
|
feature => Assert.IsType<DefaultDirectiveSyntaxTreePass>(feature),
|
||||||
feature => Assert.IsType<HtmlNodeOptimizationPass>(feature),
|
feature => Assert.IsType<HtmlNodeOptimizationPass>(feature),
|
||||||
feature => Assert.IsType<TagHelperBinderSyntaxTreePass>(feature),
|
feature => Assert.IsType<TagHelperBinderSyntaxTreePass>(feature),
|
||||||
feature => Assert.IsType<DefaultDocumentClassifier>(feature),
|
feature => Assert.IsType<DefaultDocumentClassifierPass>(feature),
|
||||||
feature => Assert.IsType<DefaultDirectiveIRPass>(feature),
|
feature => Assert.IsType<DefaultDirectiveIRPass>(feature),
|
||||||
feature => Assert.IsType<RazorEngine.ConfigureDesignTimeOptions>(feature),
|
feature => Assert.IsType<RazorEngine.ConfigureDesignTimeOptions>(feature),
|
||||||
feature => Assert.IsType<RazorDesignTimeIRPass>(feature));
|
feature => Assert.IsType<RazorDesignTimeIRPass>(feature));
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue