// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Linq; using Microsoft.AspNet.Razor.Generator.Compiler; using Microsoft.AspNet.Razor.Parser; using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.Parser.TagHelpers; using Microsoft.AspNet.Razor.TagHelpers; using Microsoft.AspNet.Razor.Text; namespace Microsoft.AspNet.Mvc.Razor { /// /// A subtype of that uses to support inheritance of tag /// helpers from _ViewStart files. /// public class MvcRazorParser : RazorParser { private readonly IEnumerable _viewStartDirectiveDescriptors; /// /// Initializes a new instance of . /// /// The to copy properties from. /// The s that are inherited /// from parsed pages from _ViewStart files. /// The inherited by /// default by all Razor pages in the application. public MvcRazorParser([NotNull] RazorParser parser, [NotNull] IReadOnlyList inheritedCodeTrees, [NotNull] IReadOnlyList defaultInheritedChunks) : base(parser) { // Construct tag helper descriptors from @addTagHelper and @removeTagHelper chunks _viewStartDirectiveDescriptors = GetTagHelperDescriptors(inheritedCodeTrees, defaultInheritedChunks); } /// protected override IEnumerable GetTagHelperDescriptors( [NotNull] Block documentRoot, [NotNull] ParserErrorSink errorSink) { var visitor = new ViewStartAddRemoveTagHelperVisitor(TagHelperDescriptorResolver, _viewStartDirectiveDescriptors, errorSink); return visitor.GetDescriptors(documentRoot); } private static IEnumerable GetTagHelperDescriptors( IReadOnlyList inheritedCodeTrees, IReadOnlyList defaultInheritedChunks) { var descriptors = new List(); // For tag helpers, the @removeTagHelper only applies tag helpers that were added prior to it. // Consequently we must visit tag helpers outside-in - furthest _ViewStart first and nearest one last. This // is different from the behavior of chunk merging where we visit the nearest one first and ignore chunks // that were previously visited. var chunksFromViewStarts = inheritedCodeTrees.Reverse() .SelectMany(tree => tree.Chunks); var chunksInOrder = defaultInheritedChunks.Concat(chunksFromViewStarts); foreach (var chunk in chunksInOrder) { var addHelperChunk = chunk as AddTagHelperChunk; if (addHelperChunk != null) { var descriptor = new TagHelperDirectiveDescriptor(addHelperChunk.LookupText, SourceLocation.Undefined, TagHelperDirectiveType.AddTagHelper); descriptors.Add(descriptor); } else { var removeHelperChunk = chunk as RemoveTagHelperChunk; if (removeHelperChunk != null) { var descriptor = new TagHelperDirectiveDescriptor(removeHelperChunk.LookupText, SourceLocation.Undefined, TagHelperDirectiveType.RemoveTagHelper); descriptors.Add(descriptor); } } } return descriptors; } private class ViewStartAddRemoveTagHelperVisitor : AddOrRemoveTagHelperSpanVisitor { private readonly IEnumerable _viewStartDirectiveDescriptors; public ViewStartAddRemoveTagHelperVisitor( ITagHelperDescriptorResolver descriptorResolver, IEnumerable viewStartDirectiveDescriptors, ParserErrorSink errorSink) : base(descriptorResolver, errorSink) { _viewStartDirectiveDescriptors = viewStartDirectiveDescriptors; } protected override TagHelperDescriptorResolutionContext GetTagHelperDescriptorResolutionContext( IEnumerable descriptors, ParserErrorSink errorSink) { return base.GetTagHelperDescriptorResolutionContext( _viewStartDirectiveDescriptors.Concat(descriptors), errorSink); } } } }