diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpDesignTimeHelpersVisitor.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpDesignTimeHelpersVisitor.cs index ce56958acd..1b01f815f2 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpDesignTimeHelpersVisitor.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpDesignTimeHelpersVisitor.cs @@ -59,6 +59,16 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp } protected override void Visit(AddTagHelperChunk chunk) + { + VisitAddOrRemoveTagHelperChunk(chunk.LookupText, chunk); + } + + protected override void Visit(RemoveTagHelperChunk chunk) + { + VisitAddOrRemoveTagHelperChunk(chunk.LookupText, chunk); + } + + private void VisitAddOrRemoveTagHelperChunk(string lookupText, Chunk chunk) { // We should always be in design time mode because of the calling AcceptTree method verification. Debug.Assert(Context.Host.DesignTimeMode); @@ -71,12 +81,12 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp Writer.WriteStartAssignment(TagHelperDirectiveSyntaxHelper); - // The parsing mechanism for the AddTagHelperChunk (CSharpCodeParser.TagHelperDirective()) removes quotes - // that surround the chunk.LookupText. + // The parsing mechanism for the add/remove TagHelper chunk (CSharpCodeParser.TagHelperDirective()) + // removes quotes that surround the lookupText. _csharpCodeVisitor.CreateExpressionCodeMapping( string.Format( CultureInfo.InvariantCulture, - "\"{0}\"", chunk.LookupText), + "\"{0}\"", lookupText), chunk); Writer.WriteLine(";"); diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/ChunkVisitor.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/ChunkVisitor.cs index 22db1736ae..0ebd5279c6 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/ChunkVisitor.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/ChunkVisitor.cs @@ -61,6 +61,10 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler { Visit((AddTagHelperChunk)chunk); } + else if (chunk is RemoveTagHelperChunk) + { + Visit((RemoveTagHelperChunk)chunk); + } else if(chunk is SetLayoutChunk) { Visit((SetLayoutChunk)chunk); @@ -120,6 +124,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler protected abstract void Visit(StatementChunk chunk); protected abstract void Visit(TagHelperChunk chunk); protected abstract void Visit(AddTagHelperChunk chunk); + protected abstract void Visit(RemoveTagHelperChunk chunk); protected abstract void Visit(UsingChunk chunk); protected abstract void Visit(ChunkBlock chunk); protected abstract void Visit(DynamicCodeAttributeChunk chunk); diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CodeVisitor.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CodeVisitor.cs index 618524f252..08e7709e67 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CodeVisitor.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CodeVisitor.cs @@ -35,6 +35,9 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler protected override void Visit(AddTagHelperChunk chunk) { } + protected override void Visit(RemoveTagHelperChunk chunk) + { + } protected override void Visit(LiteralCodeAttributeChunk chunk) { } diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/Chunks/TagHelpers/RemoveTagHelperChunk.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/Chunks/TagHelpers/RemoveTagHelperChunk.cs new file mode 100644 index 0000000000..c17107dcd1 --- /dev/null +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/Chunks/TagHelpers/RemoveTagHelperChunk.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. + +namespace Microsoft.AspNet.Razor.Generator.Compiler +{ + /// + /// A used to look up s that should be ignored + /// within the Razor page. + /// + public class RemoveTagHelperChunk : Chunk + { + /// + /// Text used to look up s that should be ignored within the Razor + /// page. + /// + public string LookupText { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/CodeTreeBuilder.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/CodeTreeBuilder.cs index 1003d0b2ce..5ac2422251 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/CodeTreeBuilder.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/CodeTreeBuilder.cs @@ -45,6 +45,14 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler }, association, topLevel: true); } + public void AddRemoveTagHelperChunk(string lookupText, SyntaxTreeNode association) + { + AddChunk(new RemoveTagHelperChunk + { + LookupText = lookupText + }, association, topLevel: true); + } + public void AddLiteralChunk(string literal, SyntaxTreeNode association) { // If the previous chunk was also a LiteralChunk, append the content of the current node to the previous one. diff --git a/src/Microsoft.AspNet.Razor/Generator/TagHelpers/AddOrRemoveTagHelperCodeGenerator.cs b/src/Microsoft.AspNet.Razor/Generator/TagHelpers/AddOrRemoveTagHelperCodeGenerator.cs new file mode 100644 index 0000000000..4e37e1a8a1 --- /dev/null +++ b/src/Microsoft.AspNet.Razor/Generator/TagHelpers/AddOrRemoveTagHelperCodeGenerator.cs @@ -0,0 +1,60 @@ +// 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.Parser.SyntaxTree; + +namespace Microsoft.AspNet.Razor.Generator +{ + /// + /// A responsible for generating s and + /// s. + /// + public class AddOrRemoveTagHelperCodeGenerator : SpanCodeGenerator + { + /// + /// Instantiates a new . + /// + /// + /// Text used to look up s that should be added or removed. + /// + public AddOrRemoveTagHelperCodeGenerator(bool removeTagHelperDescriptors, string lookupText) + { + RemoveTagHelperDescriptors = removeTagHelperDescriptors; + LookupText = lookupText; + } + + /// + /// Gets the text used to look up s that should be added to or + /// removed from the Razor page. + /// + public string LookupText { get; } + + /// + /// Whether we want to remove s from the Razor page. + /// + /// If true generates s, + /// s otherwise. + public bool RemoveTagHelperDescriptors { get; } + + /// + /// Generates s if is + /// true, otherwise s are generated. + /// + /// + /// The responsible for this . + /// + /// A instance that contains information about + /// the current code generation process. + public override void GenerateCode(Span target, CodeGeneratorContext context) + { + if (RemoveTagHelperDescriptors) + { + context.CodeTreeBuilder.AddRemoveTagHelperChunk(LookupText, target); + } + else + { + context.CodeTreeBuilder.AddAddTagHelperChunk(LookupText, target); + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Razor/Generator/TagHelpers/AddTagHelperCodeGenerator.cs b/src/Microsoft.AspNet.Razor/Generator/TagHelpers/AddTagHelperCodeGenerator.cs deleted file mode 100644 index 9f79630974..0000000000 --- a/src/Microsoft.AspNet.Razor/Generator/TagHelpers/AddTagHelperCodeGenerator.cs +++ /dev/null @@ -1,42 +0,0 @@ -// 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.Parser.SyntaxTree; - -namespace Microsoft.AspNet.Razor.Generator -{ - /// - /// A responsible for generating s. - /// - public class AddTagHelperCodeGenerator : SpanCodeGenerator - { - /// - /// Instantiates a new . - /// - /// - /// Text used to look up s. - /// - public AddTagHelperCodeGenerator(string lookupText) - { - LookupText = lookupText; - } - - /// - /// Text used to look up s. - /// - public string LookupText { get; private set; } - - /// - /// Generates a . - /// - /// - /// The responsible for this . - /// - /// A instance that contains information about - /// the current code generation process. - public override void GenerateCode(Span target, CodeGeneratorContext context) - { - context.CodeTreeBuilder.AddAddTagHelperChunk(LookupText, target); - } - } -} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.Directives.cs b/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.Directives.cs index db0d49678c..e5ea9684f1 100644 --- a/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.Directives.cs +++ b/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.Directives.cs @@ -20,6 +20,7 @@ namespace Microsoft.AspNet.Razor.Parser private void SetupDirectives() { MapDirectives(AddTagHelperDirective, SyntaxConstants.CSharp.AddTagHelperKeyword); + MapDirectives(RemoveTagHelperDirective, SyntaxConstants.CSharp.RemoveTagHelperKeyword); MapDirectives(InheritsDirective, SyntaxConstants.CSharp.InheritsKeyword); MapDirectives(FunctionsDirective, SyntaxConstants.CSharp.FunctionsKeyword); MapDirectives(SectionDirective, SyntaxConstants.CSharp.SectionKeyword); @@ -30,10 +31,12 @@ namespace Microsoft.AspNet.Razor.Parser protected virtual void AddTagHelperDirective() { - TagHelperDirective(SyntaxConstants.CSharp.AddTagHelperKeyword, (lookupText) => - { - return new AddTagHelperCodeGenerator(lookupText); - }); + TagHelperDirective(SyntaxConstants.CSharp.AddTagHelperKeyword, removeTagHelperDescriptors: false); + } + + protected virtual void RemoveTagHelperDirective() + { + TagHelperDirective(SyntaxConstants.CSharp.RemoveTagHelperKeyword, removeTagHelperDescriptors: true); } protected virtual void LayoutDirective() @@ -508,7 +511,7 @@ namespace Microsoft.AspNet.Razor.Parser Output(SpanKind.Code); } - private void TagHelperDirective(string keyword, Func codeGeneratorBuilder) + private void TagHelperDirective(string keyword, bool removeTagHelperDescriptors) { AssertDirective(keyword); @@ -550,7 +553,8 @@ namespace Microsoft.AspNet.Razor.Parser // renders the C# to colorize the user provided value. We trim the quotes around the user's value // so when we render the code we can project the users value into double quotes to not invoke C# // IntelliSense. - Span.CodeGenerator = codeGeneratorBuilder(rawValue.Trim('"')); + Span.CodeGenerator = + new AddOrRemoveTagHelperCodeGenerator(removeTagHelperDescriptors, rawValue.Trim('"')); } // We expect the directive to be surrounded in quotes. diff --git a/src/Microsoft.AspNet.Razor/Parser/RazorParser.cs b/src/Microsoft.AspNet.Razor/Parser/RazorParser.cs index 240acfc4b4..619d2da07f 100644 --- a/src/Microsoft.AspNet.Razor/Parser/RazorParser.cs +++ b/src/Microsoft.AspNet.Razor/Parser/RazorParser.cs @@ -210,8 +210,8 @@ namespace Microsoft.AspNet.Razor.Parser /// protected virtual IEnumerable GetTagHelperDescriptors([NotNull] Block documentRoot) { - var tagHelperRegistrationVisitor = new TagHelperRegistrationVisitor(TagHelperDescriptorResolver); - return tagHelperRegistrationVisitor.GetDescriptors(documentRoot); + var addOrRemoveTagHelperSpanVisitor = new AddOrRemoveTagHelperSpanVisitor(TagHelperDescriptorResolver); + return addOrRemoveTagHelperSpanVisitor.GetDescriptors(documentRoot); } private static IEnumerable GetDefaultRewriters(ParserBase markupParser) diff --git a/src/Microsoft.AspNet.Razor/Parser/SyntaxConstants.cs b/src/Microsoft.AspNet.Razor/Parser/SyntaxConstants.cs index e931fd0f5f..2160cd1090 100644 --- a/src/Microsoft.AspNet.Razor/Parser/SyntaxConstants.cs +++ b/src/Microsoft.AspNet.Razor/Parser/SyntaxConstants.cs @@ -19,6 +19,7 @@ namespace Microsoft.AspNet.Razor.Parser { public static readonly int UsingKeywordLength = 5; public static readonly string AddTagHelperKeyword = "addtaghelper"; + public static readonly string RemoveTagHelperKeyword = "removetaghelper"; public static readonly string InheritsKeyword = "inherits"; public static readonly string FunctionsKeyword = "functions"; public static readonly string SectionKeyword = "section"; diff --git a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/AddOrRemoveTagHelperSpanVisitor.cs b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/AddOrRemoveTagHelperSpanVisitor.cs new file mode 100644 index 0000000000..fa5cc139bf --- /dev/null +++ b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/AddOrRemoveTagHelperSpanVisitor.cs @@ -0,0 +1,77 @@ +// 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; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.TagHelpers; + +namespace Microsoft.AspNet.Razor.Parser.TagHelpers +{ + /// + /// A that generates s from + /// tag helper code generators in a Razor document. + /// + public class AddOrRemoveTagHelperSpanVisitor : ParserVisitor + { + private readonly ITagHelperDescriptorResolver _descriptorResolver; + + private List _descriptors; + + public AddOrRemoveTagHelperSpanVisitor(ITagHelperDescriptorResolver descriptorResolver) + { + _descriptorResolver = descriptorResolver; + } + + public IEnumerable GetDescriptors([NotNull] Block root) + { + _descriptors = new List(); + + // This will recurse through the syntax tree. + VisitBlock(root); + + return _descriptors; + } + + public override void VisitSpan(Span span) + { + // We're only interested in spans with an AddOrRemoveTagHelperCodeGenerator. + + if (span.CodeGenerator is AddOrRemoveTagHelperCodeGenerator) + { + var codeGenerator = (AddOrRemoveTagHelperCodeGenerator)span.CodeGenerator; + + if (_descriptorResolver == null) + { + var directive = codeGenerator.RemoveTagHelperDescriptors ? + SyntaxConstants.CSharp.RemoveTagHelperKeyword : + SyntaxConstants.CSharp.AddTagHelperKeyword; + + throw new InvalidOperationException( + RazorResources.FormatTagHelpers_CannotUseDirectiveWithNoTagHelperDescriptorResolver( + directive, typeof(ITagHelperDescriptorResolver).FullName, typeof(RazorParser).FullName)); + } + + // Look up all the descriptors associated with the "LookupText". + var descriptors = _descriptorResolver.Resolve(codeGenerator.LookupText); + + if (codeGenerator.RemoveTagHelperDescriptors) + { + var evaluatedDescriptors = + new HashSet(descriptors, TagHelperDescriptorComparer.Default); + + // We remove all found descriptors from the descriptor list to ignore the associated TagHelpers on the + // Razor page. + _descriptors.RemoveAll(descriptor => evaluatedDescriptors.Contains(descriptor)); + } + else + { + // Add all the found descriptors to our list. + _descriptors.AddRange(descriptors); + } + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperRegistrationVisitor.cs b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperRegistrationVisitor.cs deleted file mode 100644 index 818852f666..0000000000 --- a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperRegistrationVisitor.cs +++ /dev/null @@ -1,61 +0,0 @@ -// 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; -using Microsoft.AspNet.Razor.Parser.SyntaxTree; -using Microsoft.AspNet.Razor.TagHelpers; - -namespace Microsoft.AspNet.Razor.Parser.TagHelpers -{ - /// - /// A that generates s from - /// tag helper code generators in a Razor document. - /// - public class TagHelperRegistrationVisitor : ParserVisitor - { - private readonly ITagHelperDescriptorResolver _descriptorResolver; - - private List _descriptors; - - public TagHelperRegistrationVisitor(ITagHelperDescriptorResolver descriptorResolver) - { - _descriptorResolver = descriptorResolver; - } - - public IEnumerable GetDescriptors([NotNull] Block root) - { - _descriptors = new List(); - - // This will recurse through the syntax tree. - VisitBlock(root); - - return _descriptors; - } - - public override void VisitSpan(Span span) - { - // We're only interested in spans with an AddTagHelperCodeGenerator. - if (span.CodeGenerator is AddTagHelperCodeGenerator) - { - if (_descriptorResolver == null) - { - throw new InvalidOperationException( - RazorResources.FormatTagHelpers_CannotUseDirectiveWithNoTagHelperDescriptorResolver( - SyntaxConstants.CSharp.AddTagHelperKeyword, - nameof(ITagHelperDescriptorResolver), - nameof(RazorParser))); - } - - var addGenerator = (AddTagHelperCodeGenerator)span.CodeGenerator; - - // Look up all the descriptors associated with the "LookupText". - var descriptors = _descriptorResolver.Resolve(addGenerator.LookupText); - - // Add all the found descriptors to our HashSet. - _descriptors.AddRange(descriptors); - } - } - } -} \ No newline at end of file