| File: Mvc\ModelDirective.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.Linq; using System.Threading; using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.Language.Intermediate; namespace Microsoft.AspNetCore.Mvc.Razor.Extensions; public static class ModelDirective { public static readonly DirectiveDescriptor Directive = DirectiveDescriptor.CreateDirective( "model", DirectiveKind.SingleLine, builder => { builder.AddTypeToken(RazorExtensionsResources.ModelDirective_TypeToken_Name, RazorExtensionsResources.ModelDirective_TypeToken_Description); builder.Usage = DirectiveUsage.FileScopedSinglyOccurring; builder.Description = RazorExtensionsResources.ModelDirective_Description; }); public static RazorProjectEngineBuilder Register(RazorProjectEngineBuilder builder) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } builder.AddDirective(Directive); builder.Features.Add(new Pass()); return builder; } public static IntermediateToken GetModelType(DocumentIntermediateNode document) { if (document == null) { throw new ArgumentNullException(nameof(document)); } var visitor = new Visitor(); return GetModelType(document, visitor); } private static IntermediateToken GetModelType(DocumentIntermediateNode document, Visitor visitor) { visitor.Visit(document); for (var i = visitor.ModelDirectives.Count - 1; i >= 0; i--) { var directive = visitor.ModelDirectives[i]; var tokens = directive.Tokens.ToArray(); if (tokens.Length >= 1) { return IntermediateNodeFactory.CSharpToken(tokens[0].Content, tokens[0].Source); } } if (document.DocumentKind == RazorPageDocumentClassifierPass.RazorPageDocumentKind) { return IntermediateNodeFactory.CSharpToken(visitor.Class!.Name!); } else { return IntermediateNodeFactory.CSharpToken("dynamic"); } } internal sealed class Pass : IntermediateNodePassBase, IRazorDirectiveClassifierPass { // Runs after the @inherits directive public override int Order => 5; protected override void ExecuteCore( RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode, CancellationToken cancellationToken) { if (documentNode.DocumentKind != RazorPageDocumentClassifierPass.RazorPageDocumentKind && documentNode.DocumentKind != MvcViewDocumentClassifierPass.MvcViewDocumentKind) { // Not a MVC file. Skip. return; } var visitor = new Visitor(); var modelType = GetModelType(documentNode, visitor); if (visitor.Class?.BaseType is BaseTypeWithModel { ModelType: not null } existingBaseType) { existingBaseType.ModelType = modelType; } } } private class Visitor : IntermediateNodeWalker { public NamespaceDeclarationIntermediateNode? Namespace { get; private set; } public ClassDeclarationIntermediateNode? Class { get; private set; } public IList<DirectiveIntermediateNode> ModelDirectives { get; } = new List<DirectiveIntermediateNode>(); public override void VisitNamespaceDeclaration(NamespaceDeclarationIntermediateNode node) { if (Namespace == null) { Namespace = node; } base.VisitNamespaceDeclaration(node); } public override void VisitClassDeclaration(ClassDeclarationIntermediateNode node) { if (Class == null) { Class = node; } base.VisitClassDeclaration(node); } public override void VisitDirective(DirectiveIntermediateNode node) { if (node.Directive == Directive) { ModelDirectives.Add(node); } } } }