diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/Directives/ChunkInheritanceUtility.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/Directives/ChunkInheritanceUtility.cs index 9e773e1c42..78b4b09dbc 100644 --- a/src/Microsoft.AspNet.Mvc.Razor.Host/Directives/ChunkInheritanceUtility.cs +++ b/src/Microsoft.AspNet.Mvc.Razor.Host/Directives/ChunkInheritanceUtility.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using Microsoft.AspNet.FileSystems; using Microsoft.AspNet.Razor; using Microsoft.AspNet.Razor.Generator.Compiler; @@ -13,63 +12,57 @@ using Microsoft.AspNet.Razor.Parser; namespace Microsoft.AspNet.Mvc.Razor.Directives { /// - /// A utility type for supporting inheritance of chunks into a page from _ViewStart pages that apply to it. + /// A utility type for supporting inheritance of tag helpers and chunks into a page from applicable _ViewStart + /// pages. /// public class ChunkInheritanceUtility { - private readonly IReadOnlyList _defaultInheritedChunks; + private readonly Dictionary _parsedCodeTrees; + private readonly MvcRazorHost _razorHost; + private readonly IFileSystem _fileSystem; + private readonly IEnumerable _defaultInheritedChunks; /// - /// Instantiates a new instance of . - /// - /// The instance to add s to. - /// The list of s inherited by default. - /// The model type used in the event no model is specified via the - /// @model keyword. - public ChunkInheritanceUtility([NotNull] CodeTree codeTree, - [NotNull] IReadOnlyList defaultInheritedChunks, - [NotNull] string defaultModel) - { - CodeTree = codeTree; - _defaultInheritedChunks = defaultInheritedChunks; - ChunkMergers = GetMergerMappings(codeTree, defaultModel); - } - - /// - /// Gets the CodeTree to add inherited instances to. - /// - public CodeTree CodeTree { get; private set; } - - /// - /// Gets a dictionary mapping type to the used to merge - /// chunks of that type. - /// - public IDictionary ChunkMergers { get; private set; } - - /// - /// Gets the list of chunks that are to be inherited by a specified page. - /// Chunks are inherited from _ViewStarts that are applicable to the page. + /// Initializes a new instance of . /// /// The used to parse _ViewStart pages. /// The filesystem that represents the application. + /// Sequence of s inherited by default. + public ChunkInheritanceUtility([NotNull] MvcRazorHost razorHost, + [NotNull] IFileSystem fileSystem, + [NotNull] IEnumerable defaultInheritedChunks) + { + _razorHost = razorHost; + _fileSystem = fileSystem; + _defaultInheritedChunks = defaultInheritedChunks; + _parsedCodeTrees = new Dictionary(StringComparer.Ordinal); + } + + /// + /// Gets a of containing parsed results of _ViewStart files + /// that are used for inheriting tag helpers and chunks to the page located at . + /// /// The path of the page to locate inherited chunks for. - /// A list of chunks that are applicable to the given page. - public List GetInheritedChunks([NotNull] MvcRazorHost razorHost, - [NotNull] IFileSystem fileSystem, - [NotNull] string pagePath) + /// A of from _ViewStart pages. + public IReadOnlyList GetInheritedChunks([NotNull] string pagePath) { var inheritedChunks = new List(); - var templateEngine = new RazorTemplateEngine(razorHost); - foreach (var viewStart in ViewStartUtility.GetViewStartLocations(fileSystem, pagePath)) + var templateEngine = new RazorTemplateEngine(_razorHost); + foreach (var viewStart in ViewStartUtility.GetViewStartLocations(_fileSystem, pagePath)) { + CodeTree codeTree; IFileInfo fileInfo; - if (fileSystem.TryGetFileInfo(viewStart, out fileInfo)) + + if (_parsedCodeTrees.TryGetValue(viewStart, out codeTree)) { - var parsedTree = ParseViewFile(templateEngine, fileInfo); - var chunksToAdd = parsedTree.Chunks - .Where(chunk => ChunkMergers.ContainsKey(chunk.GetType())); - inheritedChunks.AddRange(chunksToAdd); + inheritedChunks.AddRange(codeTree.Chunks); + } + else if (_fileSystem.TryGetFileInfo(viewStart, out fileInfo)) + { + codeTree = ParseViewFile(templateEngine, fileInfo); + _parsedCodeTrees.Add(viewStart, codeTree); + inheritedChunks.AddRange(codeTree.Chunks); } } @@ -79,19 +72,22 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives } /// - /// Merges a list of chunks into the instance. + /// Merges a list of chunks into the specified The to mere /// The list of chunks to merge. - public void MergeInheritedChunks(List inherited) + public void MergeInheritedChunks([NotNull] CodeTree codeTree, + [NotNull] IReadOnlyList inherited, + string defaultModel) { - var current = CodeTree.Chunks; + var mergerMappings = GetMergerMappings(codeTree, defaultModel); IChunkMerger merger; // We merge chunks into the codeTree in two passes. In the first pass, we traverse the CodeTree visiting // a mapped IChunkMerger for types that are registered. - foreach (var chunk in current) + foreach (var chunk in codeTree.Chunks) { - if (ChunkMergers.TryGetValue(chunk.GetType(), out merger)) + if (mergerMappings.TryGetValue(chunk.GetType(), out merger)) { merger.VisitChunk(chunk); } @@ -102,11 +98,11 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives // rules. foreach (var chunk in inherited) { - if (ChunkMergers.TryGetValue(chunk.GetType(), out merger)) + if (mergerMappings.TryGetValue(chunk.GetType(), out merger)) { // TODO: When mapping chunks, we should remove mapping information since it would be incorrect // to generate it in the page that inherits it. Tracked by #945 - merger.Merge(CodeTree, chunk); + merger.Merge(codeTree, chunk); } } } @@ -123,14 +119,14 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives } // TODO: This needs to be cached (#1016) - private CodeTree ParseViewFile(RazorTemplateEngine engine, - IFileInfo fileInfo) + private static CodeTree ParseViewFile(RazorTemplateEngine engine, + IFileInfo fileInfo) { using (var stream = fileInfo.CreateReadStream()) { using (var streamReader = new StreamReader(stream)) { - var parseResults = engine.ParseTemplate(streamReader); + var parseResults = engine.ParseTemplate(streamReader, fileInfo.PhysicalPath); var className = ParserHelpers.SanitizeClassName(fileInfo.Name); var language = engine.Host.CodeLanguage; var codeGenerator = language.CreateCodeGenerator(className, @@ -138,6 +134,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives fileInfo.PhysicalPath, engine.Host); codeGenerator.Visit(parseResults); + return codeGenerator.Context.CodeTreeBuilder.CodeTree; } } diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorHost.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorHost.cs index 11a628d597..d4112923fa 100644 --- a/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorHost.cs +++ b/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorHost.cs @@ -36,6 +36,7 @@ namespace Microsoft.AspNet.Mvc.Razor // CodeGenerationContext.DefaultBaseClass is set to MyBaseType. // This field holds the type name without the generic decoration (MyBaseType) private readonly string _baseType; + private ChunkInheritanceUtility _chunkInheritanceUtility; #if NET45 /// @@ -154,6 +155,20 @@ namespace Microsoft.AspNet.Mvc.Razor get { return "CreateModelExpression"; } } + private ChunkInheritanceUtility ChunkInheritanceUtility + { + get + { + if (_chunkInheritanceUtility == null) + { + // This needs to be lazily evaluated to support DefaultInheritedChunks being virtual. + _chunkInheritanceUtility = new ChunkInheritanceUtility(this, _fileSystem, DefaultInheritedChunks); + } + + return _chunkInheritanceUtility; + } + } + /// public GeneratorResults GenerateCode(string rootRelativePath, Stream inputStream) { @@ -163,6 +178,13 @@ namespace Microsoft.AspNet.Mvc.Razor return engine.GenerateCode(inputStream, className, DefaultNamespace, rootRelativePath); } + /// + public override RazorParser DecorateRazorParser([NotNull] RazorParser razorParser, string sourceFileName) + { + var inheritedChunks = ChunkInheritanceUtility.GetInheritedChunks(sourceFileName); + return new MvcRazorParser(razorParser, inheritedChunks); + } + /// public override ParserBase DecorateCodeParser([NotNull] ParserBase incomingCodeParser) { @@ -173,10 +195,14 @@ namespace Microsoft.AspNet.Mvc.Razor public override CodeBuilder DecorateCodeBuilder([NotNull] CodeBuilder incomingBuilder, [NotNull] CodeBuilderContext context) { - UpdateCodeBuilder(context); + var inheritedChunks = ChunkInheritanceUtility.GetInheritedChunks(context.SourceFile); + + ChunkInheritanceUtility.MergeInheritedChunks(context.CodeTreeBuilder.CodeTree, + inheritedChunks, + DefaultModel); return new MvcCSharpCodeBuilder(context, - DefaultModel, + DefaultModel, ActivateAttribute, new GeneratedTagHelperAttributeContext { @@ -184,14 +210,5 @@ namespace Microsoft.AspNet.Mvc.Razor CreateModelExpressionMethodName = CreateModelExpressionMethod }); } - - private void UpdateCodeBuilder(CodeGeneratorContext context) - { - var chunkUtility = new ChunkInheritanceUtility(context.CodeTreeBuilder.CodeTree, - DefaultInheritedChunks, - DefaultModel); - var inheritedChunks = chunkUtility.GetInheritedChunks(this, _fileSystem, context.SourceFile); - chunkUtility.MergeInheritedChunks(inheritedChunks); - } } } diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorParser.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorParser.cs new file mode 100644 index 0000000000..efcc0f8c4e --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorParser.cs @@ -0,0 +1,48 @@ +// 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.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.TagHelpers; + +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 IReadOnlyList _viewStartChunks; + + /// + /// Initializes a new instance of . + /// + /// The to copy properties from. + /// The of s that are inherited + /// by parsed pages from _ViewStart files. + public MvcRazorParser(RazorParser parser, IReadOnlyList viewStartChunks) + : base(parser) + { + _viewStartChunks = viewStartChunks; + } + + /// + protected override IEnumerable GetTagHelperDescriptors([NotNull] Block documentRoot) + { + var descriptors = base.GetTagHelperDescriptors(documentRoot); + + // TODO: https://github.com/aspnet/Razor/issues/112 Needs to support RemvoeHelperChunks too. + + // Grab all the @addTagHelper chunks from view starts + var viewStartDescriptors = _viewStartChunks.OfType() + .Select(c => c.LookupText) + .SelectMany(TagHelperDescriptorResolver.Resolve); + + return descriptors.Concat(viewStartDescriptors); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/TagHelpersTest.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/TagHelpersTest.cs index 962755d3f0..7b3fde1c22 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/TagHelpersTest.cs +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/TagHelpersTest.cs @@ -51,5 +51,26 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests Assert.Equal(expectedMediaType, response.Content.Headers.ContentType); Assert.Equal(expectedContent, responseContent); } + + [Theory] + [InlineData("NestedViewStartTagHelper")] + [InlineData("ViewWithLayoutAndNestedTagHelper")] + public async Task TagHelpersAreInheritedFromViewStartPages(string action) + { + // Arrange + var expected = string.Join(Environment.NewLine, + "root-content", + "", + "", + "nested-content"); + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var result = await client.GetStringAsync("http://localhost/Home/" + action); + + // Assert + Assert.Equal(expected, result.Trim()); + } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.About.html b/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.About.html index 9b9a96357b..0e319e19bc 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.About.html +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.About.html @@ -34,6 +34,7 @@ +

Hello, you've reached the about page.

diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.Help.html b/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.Help.html index 6b4eb8c191..f4ef7b21d0 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.Help.html +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.Help.html @@ -33,6 +33,7 @@ +

Hello, you've reached the help page. If you're having troubles try visiting our My Approved Home Page

diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.Index.html b/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.Index.html index ce0fcbfdf6..180d61bac5 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.Index.html +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/compiler/resources/TagHelpersWebSite.Home.Index.html @@ -41,6 +41,7 @@ +

This website has not been approved yet. Visit www.contoso.com for more information.

diff --git a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/Directives/ChunkInheritanceUtilityTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/Directives/ChunkInheritanceUtilityTest.cs index 1cf55f8b28..8854adcb1e 100644 --- a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/Directives/ChunkInheritanceUtilityTest.cs +++ b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/Directives/ChunkInheritanceUtilityTest.cs @@ -1,10 +1,7 @@ // 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 Microsoft.AspNet.Razor.Generator.Compiler; -using Moq; using Xunit; namespace Microsoft.AspNet.Mvc.Razor.Directives @@ -29,24 +26,30 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives "); var host = new MvcRazorHost(fileSystem); - var utility = new ChunkInheritanceUtility(new CodeTree(), new Chunk[0], "dynamic"); + var utility = new ChunkInheritanceUtility(host, fileSystem, new Chunk[0]); // Act - var chunks = utility.GetInheritedChunks(host, - fileSystem, - @"x:\myapproot\views\home\Index.cshtml"); + var chunks = utility.GetInheritedChunks(@"x:\myapproot\views\home\Index.cshtml"); // Assert - Assert.Equal(3, chunks.Count); - var usingChunk = Assert.IsType(chunks[0]); + Assert.Equal(8, chunks.Count); + Assert.IsType(chunks[0]); + + var usingChunk = Assert.IsType(chunks[1]); Assert.Equal("MyNamespace", usingChunk.Namespace); - var injectChunk = Assert.IsType(chunks[1]); + Assert.IsType(chunks[2]); + Assert.IsType(chunks[3]); + + var injectChunk = Assert.IsType(chunks[4]); Assert.Equal("MyHelper", injectChunk.TypeName); Assert.Equal("Helper", injectChunk.MemberName); - var setBaseTypeChunk = Assert.IsType(chunks[2]); + var setBaseTypeChunk = Assert.IsType(chunks[5]); Assert.Equal("MyBaseType", setBaseTypeChunk.TypeName); + + Assert.IsType(chunks[6]); + Assert.IsType(chunks[7]); } [Fact] @@ -58,12 +61,10 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives fileSystem.AddFile(@"x:\myapproot\views\_Layout.cshtml", string.Empty); fileSystem.AddFile(@"x:\myapproot\views\home\_not-viewstart.cshtml", string.Empty); var host = new MvcRazorHost(fileSystem); - var utility = new ChunkInheritanceUtility(new CodeTree(), new Chunk[0], "dynamic"); + var utility = new ChunkInheritanceUtility(host, fileSystem, new Chunk[0]); // Act - var chunks = utility.GetInheritedChunks(host, - fileSystem, - @"x:\myapproot\views\home\Index.cshtml"); + var chunks = utility.GetInheritedChunks(@"x:\myapproot\views\home\Index.cshtml"); // Assert Assert.Empty(chunks); @@ -75,73 +76,26 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives // Arrange var fileSystem = new TestFileSystem(); fileSystem.AddFile(@"x:\myapproot\views\_viewstart.cshtml", -@"@inject DifferentHelper Html -@using AppNamespace.Models -@{ - Layout = ""test.cshtml""; -} - -"); + "@inject DifferentHelper Html"); var host = new MvcRazorHost(fileSystem); var defaultChunks = new Chunk[] { new InjectChunk("MyTestHtmlHelper", "Html"), new UsingChunk { Namespace = "AppNamespace.Model" }, }; - var utility = new ChunkInheritanceUtility(new CodeTree(), defaultChunks, "dynamic"); + var utility = new ChunkInheritanceUtility(host, fileSystem, defaultChunks); // Act - var chunks = utility.GetInheritedChunks(host, - fileSystem, - @"x:\myapproot\views\home\Index.cshtml"); + var chunks = utility.GetInheritedChunks(@"x:\myapproot\views\home\Index.cshtml"); // Assert Assert.Equal(4, chunks.Count); - var injectChunk = Assert.IsType(chunks[0]); + var injectChunk = Assert.IsType(chunks[1]); Assert.Equal("DifferentHelper", injectChunk.TypeName); Assert.Equal("Html", injectChunk.MemberName); - var usingChunk = Assert.IsType(chunks[1]); - Assert.Equal("AppNamespace.Models", usingChunk.Namespace); - - injectChunk = Assert.IsType(chunks[2]); - Assert.Equal("MyTestHtmlHelper", injectChunk.TypeName); - Assert.Equal("Html", injectChunk.MemberName); - - usingChunk = Assert.IsType(chunks[3]); - Assert.Equal("AppNamespace.Model", usingChunk.Namespace); - } - - [Fact] - public void MergeChunks_VisitsChunksPriorToMerging() - { - // Arrange - var codeTree = new CodeTree(); - codeTree.Chunks.Add(new LiteralChunk()); - codeTree.Chunks.Add(new ExpressionBlockChunk()); - codeTree.Chunks.Add(new ExpressionBlockChunk()); - - var merger = new Mock(); - var mockSequence = new MockSequence(); - merger.InSequence(mockSequence) - .Setup(m => m.VisitChunk(It.IsAny())) - .Verifiable(); - merger.InSequence(mockSequence) - .Setup(m => m.Merge(codeTree, It.IsAny())) - .Verifiable(); - var inheritedChunks = new List - { - new CodeAttributeChunk(), - new LiteralChunk() - }; - var utility = new ChunkInheritanceUtility(codeTree, inheritedChunks, "dynamic"); - - // Act - utility.ChunkMergers[typeof(LiteralChunk)] = merger.Object; - utility.MergeInheritedChunks(inheritedChunks); - - // Assert - merger.Verify(); + Assert.Same(defaultChunks[0], chunks[2]); + Assert.Same(defaultChunks[1], chunks[3]); } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/TestFileSystem.cs b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/TestFileSystem.cs index 99fea29910..7762c1ca92 100644 --- a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/TestFileSystem.cs +++ b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/TestFileSystem.cs @@ -24,7 +24,7 @@ namespace Microsoft.AspNet.Mvc.Razor { var fileInfo = new Mock(); fileInfo.Setup(f => f.CreateReadStream()) - .Returns(new MemoryStream(Encoding.UTF8.GetBytes(contents))); + .Returns(() => new MemoryStream(Encoding.UTF8.GetBytes(contents))); fileInfo.SetupGet(f => f.PhysicalPath) .Returns(path); fileInfo.SetupGet(f => f.Name) diff --git a/test/WebSites/TagHelpersWebSite/Controllers/HomeController.cs b/test/WebSites/TagHelpersWebSite/Controllers/HomeController.cs index 11a64028cf..5005a39e68 100644 --- a/test/WebSites/TagHelpersWebSite/Controllers/HomeController.cs +++ b/test/WebSites/TagHelpersWebSite/Controllers/HomeController.cs @@ -29,5 +29,15 @@ namespace TagHelpersWebSite.Controllers { return View(); } + + public ViewResult NestedViewStartTagHelper() + { + return View(); + } + + public ViewResult ViewWithLayoutAndNestedTagHelper() + { + return View(); + } } } \ No newline at end of file diff --git a/test/WebSites/TagHelpersWebSite/TagHelpers/NestedViewStartTagHelper.cs b/test/WebSites/TagHelpersWebSite/TagHelpers/NestedViewStartTagHelper.cs new file mode 100644 index 0000000000..4dc77707c3 --- /dev/null +++ b/test/WebSites/TagHelpersWebSite/TagHelpers/NestedViewStartTagHelper.cs @@ -0,0 +1,18 @@ +// 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 Microsoft.AspNet.Razor.Runtime.TagHelpers; +using Microsoft.AspNet.Razor.TagHelpers; + +namespace TagHelpersWebSite.TagHelpers +{ + [TagName("nested")] + [ContentBehavior(ContentBehavior.Modify)] + public class NestedViewStartTagHelper : TagHelper + { + public override void Process(TagHelperContext context, TagHelperOutput output) + { + output.Content = "nested-content"; + } + } +} \ No newline at end of file diff --git a/test/WebSites/TagHelpersWebSite/TagHelpers/RootViewStartTagHelper.cs b/test/WebSites/TagHelpersWebSite/TagHelpers/RootViewStartTagHelper.cs new file mode 100644 index 0000000000..18fdd2fed8 --- /dev/null +++ b/test/WebSites/TagHelpersWebSite/TagHelpers/RootViewStartTagHelper.cs @@ -0,0 +1,18 @@ +// 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 Microsoft.AspNet.Razor.Runtime.TagHelpers; +using Microsoft.AspNet.Razor.TagHelpers; + +namespace TagHelpersWebSite.TagHelpers +{ + [TagName("root")] + [ContentBehavior(ContentBehavior.Replace)] + public class RootViewStartTagHelper : TagHelper + { + public override void Process(TagHelperContext context, TagHelperOutput output) + { + output.Content = "root-content"; + } + } +} \ No newline at end of file diff --git a/test/WebSites/TagHelpersWebSite/Views/Home/NestedViewStartTagHelper.cshtml b/test/WebSites/TagHelpersWebSite/Views/Home/NestedViewStartTagHelper.cshtml new file mode 100644 index 0000000000..dd0b4f2c1b --- /dev/null +++ b/test/WebSites/TagHelpersWebSite/Views/Home/NestedViewStartTagHelper.cshtml @@ -0,0 +1,7 @@ +@{ + Layout = null; +} + + + +some-content diff --git a/test/WebSites/TagHelpersWebSite/Views/Home/_ViewStart.cshtml b/test/WebSites/TagHelpersWebSite/Views/Home/_ViewStart.cshtml new file mode 100644 index 0000000000..05d7a078a5 --- /dev/null +++ b/test/WebSites/TagHelpersWebSite/Views/Home/_ViewStart.cshtml @@ -0,0 +1 @@ +@addtaghelper "TagHelpersWebSite.TagHelpers.NestedViewStartTagHelper, TagHelpersWebSite" \ No newline at end of file diff --git a/test/WebSites/TagHelpersWebSite/Views/Shared/ViewWithLayoutAndNestedTagHelper.cshtml b/test/WebSites/TagHelpersWebSite/Views/Shared/ViewWithLayoutAndNestedTagHelper.cshtml new file mode 100644 index 0000000000..65bca2e70e --- /dev/null +++ b/test/WebSites/TagHelpersWebSite/Views/Shared/ViewWithLayoutAndNestedTagHelper.cshtml @@ -0,0 +1,5 @@ +@{ + Layout = "~/Views/Shared/_LayoutWithRootTagHelper.cshtml"; +} +@addtaghelper "TagHelpersWebSite.TagHelpers.NestedViewStartTagHelper, TagHelpersWebSite" +some-content diff --git a/test/WebSites/TagHelpersWebSite/Views/Shared/_LayoutWithRootTagHelper.cshtml b/test/WebSites/TagHelpersWebSite/Views/Shared/_LayoutWithRootTagHelper.cshtml new file mode 100644 index 0000000000..d08aa242dd --- /dev/null +++ b/test/WebSites/TagHelpersWebSite/Views/Shared/_LayoutWithRootTagHelper.cshtml @@ -0,0 +1,2 @@ + +@RenderBody() \ No newline at end of file diff --git a/test/WebSites/TagHelpersWebSite/Views/_ViewStart.cshtml b/test/WebSites/TagHelpersWebSite/Views/_ViewStart.cshtml index ab23e9a239..543a92c7ac 100644 --- a/test/WebSites/TagHelpersWebSite/Views/_ViewStart.cshtml +++ b/test/WebSites/TagHelpersWebSite/Views/_ViewStart.cshtml @@ -1,3 +1,4 @@ @{ Layout = "/Views/Shared/_Layout.cshtml"; -} \ No newline at end of file +} +@addtaghelper "TagHelpersWebSite.TagHelpers.RootViewStartTagHelper, TagHelpersWebSite" \ No newline at end of file