| File: Language\DocumentClassifierPassBase.cs | Web Access |
| Project: src\roslyn\src\Razor\src\Compiler\Microsoft.CodeAnalysis.Razor.Compiler\src\Microsoft.CodeAnalysis.Razor.Compiler.csproj (Microsoft.CodeAnalysis.Razor.Compiler) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using Microsoft.AspNetCore.Razor.Language.CodeGeneration; using Microsoft.AspNetCore.Razor.Language.Intermediate; using Microsoft.CodeAnalysis; namespace Microsoft.AspNetCore.Razor.Language; public abstract class DocumentClassifierPassBase : IntermediateNodePassBase, IRazorDocumentClassifierPass { private ImmutableArray<ICodeTargetExtension> _targetExtensions; protected abstract string DocumentKind { get; } protected ImmutableArray<ICodeTargetExtension> TargetExtensions => _targetExtensions.NullToEmpty(); protected override void OnInitialized() { _targetExtensions = Engine.TryGetFeature(out IRazorTargetExtensionFeature? feature) ? feature.TargetExtensions.ToImmutable() : []; } protected sealed override void ExecuteCore( RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode, CancellationToken cancellationToken) { if (documentNode.DocumentKind != null) { return; } if (!IsMatch(codeDocument, documentNode)) { return; } documentNode.DocumentKind = DocumentKind; documentNode.Target = CreateTarget(codeDocument); if (documentNode.Target == null) { throw new InvalidOperationException($"{nameof(CreateTarget)} must return a non-null {nameof(CodeTarget)}."); } Rewrite(codeDocument, documentNode); } private void Rewrite(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode) { // 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<IntermediateNode>(documentNode.Children); documentNode.Children.Clear(); var @namespace = new NamespaceDeclarationIntermediateNode { IsPrimaryNamespace = true }; var @class = new ClassDeclarationIntermediateNode { IsPrimaryClass = true }; var method = new MethodDeclarationIntermediateNode { IsPrimaryMethod = true }; var documentBuilder = IntermediateNodeBuilder.Create(documentNode); var namespaceBuilder = IntermediateNodeBuilder.Create(documentBuilder.Current); namespaceBuilder.Push(@namespace); var classBuilder = IntermediateNodeBuilder.Create(namespaceBuilder.Current); classBuilder.Push(@class); var methodBuilder = IntermediateNodeBuilder.Create(classBuilder.Current); methodBuilder.Push(method); var visitor = new Visitor(namespaceBuilder, classBuilder, methodBuilder); for (var i = 0; i < children.Count; i++) { visitor.Visit(children[i]); } // 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, DocumentIntermediateNode documentNode); // virtual to allow replacing the code target wholesale. protected virtual CodeTarget CreateTarget(RazorCodeDocument codeDocument) => CodeTarget.CreateDefault(codeDocument, builder => { builder.TargetExtensions.AddRange(TargetExtensions); }); protected virtual void OnDocumentStructureCreated( RazorCodeDocument codeDocument, NamespaceDeclarationIntermediateNode @namespace, ClassDeclarationIntermediateNode @class, MethodDeclarationIntermediateNode @method) { // Intentionally empty. } private class Visitor : IntermediateNodeVisitor { private readonly IntermediateNodeBuilder _namespace; private readonly IntermediateNodeBuilder _class; private readonly IntermediateNodeBuilder _method; public Visitor(IntermediateNodeBuilder @namespace, IntermediateNodeBuilder @class, IntermediateNodeBuilder method) { _namespace = @namespace; _class = @class; _method = method; } public override void VisitUsingDirective(UsingDirectiveIntermediateNode node) { var children = _namespace.Current.Children; var i = children.Count - 1; for (; i >= 0; i--) { var child = children[i]; if (child is UsingDirectiveIntermediateNode) { break; } } _namespace.Insert(i + 1, node); } public override void VisitDefault(IntermediateNode node) { if (node is MemberDeclarationIntermediateNode) { _class.Add(node); return; } _method.Add(node); } } }