diff --git a/src/Microsoft.AspNet.Razor.Runtime/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Razor.Runtime/Properties/Resources.Designer.cs
index 33d8f46ae5..2ff49efa5c 100644
--- a/src/Microsoft.AspNet.Razor.Runtime/Properties/Resources.Designer.cs
+++ b/src/Microsoft.AspNet.Razor.Runtime/Properties/Resources.Designer.cs
@@ -138,6 +138,38 @@ namespace Microsoft.AspNet.Razor.Runtime
return string.Format(CultureInfo.CurrentCulture, GetString("HtmlElementNameAttribute_InvalidElementName"), p0, p1);
}
+ ///
+ /// Invalid tag helper directive '{0}'. Cannot have multiple '{0}' directives on a page.
+ ///
+ internal static string TagHelperDescriptorResolver_InvalidTagHelperDirective
+ {
+ get { return GetString("TagHelperDescriptorResolver_InvalidTagHelperDirective"); }
+ }
+
+ ///
+ /// Invalid tag helper directive '{0}'. Cannot have multiple '{0}' directives on a page.
+ ///
+ internal static string FormatTagHelperDescriptorResolver_InvalidTagHelperDirective(object p0)
+ {
+ return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorResolver_InvalidTagHelperDirective"), p0);
+ }
+
+ ///
+ /// Invalid tag helper directive '{0}' value. '{1} is not allowed in prefix '{2}'.
+ ///
+ internal static string TagHelperDescriptorResolver_InvalidTagHelperPrefixValue
+ {
+ get { return GetString("TagHelperDescriptorResolver_InvalidTagHelperPrefixValue"); }
+ }
+
+ ///
+ /// Invalid tag helper directive '{0}' value. '{1} is not allowed in prefix '{2}'.
+ ///
+ internal static string FormatTagHelperDescriptorResolver_InvalidTagHelperPrefixValue(object p0, object p1, object p2)
+ {
+ return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorResolver_InvalidTagHelperPrefixValue"), p0, p1, p2);
+ }
+
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
diff --git a/src/Microsoft.AspNet.Razor.Runtime/Resources.resx b/src/Microsoft.AspNet.Razor.Runtime/Resources.resx
index 7469e1a225..48f5cb1fdd 100644
--- a/src/Microsoft.AspNet.Razor.Runtime/Resources.resx
+++ b/src/Microsoft.AspNet.Razor.Runtime/Resources.resx
@@ -141,4 +141,10 @@
Tag helpers cannot target element name '{0}' because it contains a '{1}' character.
+
+ Invalid tag helper directive '{0}'. Cannot have multiple '{0}' directives on a page.
+
+
+ Invalid tag helper directive '{0}' value. '{1} is not allowed in prefix '{2}'.
+
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorResolver.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorResolver.cs
index 2602a5a8d5..5c956dc409 100644
--- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorResolver.cs
+++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorResolver.cs
@@ -23,7 +23,10 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{
{ TagHelperDirectiveType.AddTagHelper, SyntaxConstants.CSharp.AddTagHelperKeyword },
{ TagHelperDirectiveType.RemoveTagHelper, SyntaxConstants.CSharp.RemoveTagHelperKeyword },
+ { TagHelperDirectiveType.TagHelperPrefix, SyntaxConstants.CSharp.TagHelperPrefixKeyword },
};
+ private static readonly HashSet InvalidNonWhitespacePrefixCharacters =
+ new HashSet(new[] { '@', '!', '<', '!', '/', '?', '[', '>', ']', '=', '"', '\'' });
private readonly TagHelperTypeResolver _typeResolver;
@@ -51,7 +54,12 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{
var resolvedDescriptors = new HashSet(TagHelperDescriptorComparer.Default);
- foreach (var directiveDescriptor in context.DirectiveDescriptors)
+ // tagHelperPrefix directives do not affect which TagHelperDescriptors are added or removed from the final
+ // list, need to remove them.
+ var actionableDirectiveDescriptors = context.DirectiveDescriptors.Where(
+ directive => directive.DirectiveType != TagHelperDirectiveType.TagHelperPrefix);
+
+ foreach (var directiveDescriptor in actionableDirectiveDescriptors)
{
try
{
@@ -69,9 +77,10 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
}
else if (directiveDescriptor.DirectiveType == TagHelperDirectiveType.AddTagHelper)
{
- var descriptors = ResolveDescriptorsInAssembly(lookupInfo.AssemblyName,
- directiveDescriptor.Location,
- context.ErrorSink);
+ var descriptors = ResolveDescriptorsInAssembly(
+ lookupInfo.AssemblyName,
+ directiveDescriptor.Location,
+ context.ErrorSink);
// Only use descriptors that match our lookup info
descriptors = descriptors.Where(descriptor => MatchesLookupInfo(descriptor, lookupInfo));
@@ -89,12 +98,14 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
directiveDescriptor.Location,
Resources.FormatTagHelperDescriptorResolver_EncounteredUnexpectedError(
"@" + directiveName,
- directiveDescriptor.LookupText,
+ directiveDescriptor.DirectiveText,
ex.Message));
}
}
- return resolvedDescriptors;
+ var prefixedDescriptors = PrefixDescriptors(context, resolvedDescriptors);
+
+ return prefixedDescriptors;
}
///
@@ -110,9 +121,10 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
/// s for s from the given
/// .
// This is meant to be overridden by tooling to enable assembly level caching.
- protected virtual IEnumerable ResolveDescriptorsInAssembly(string assemblyName,
- SourceLocation documentLocation,
- ParserErrorSink errorSink)
+ protected virtual IEnumerable ResolveDescriptorsInAssembly(
+ string assemblyName,
+ SourceLocation documentLocation,
+ ParserErrorSink errorSink)
{
// Resolve valid tag helper types from the assembly.
var tagHelperTypes = _typeResolver.Resolve(assemblyName, documentLocation, errorSink);
@@ -124,6 +136,84 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
return descriptors;
}
+ private static IEnumerable PrefixDescriptors(
+ TagHelperDescriptorResolutionContext context,
+ IEnumerable descriptors)
+ {
+ var tagHelperPrefix = ResolveTagHelperPrefix(context);
+
+ if (!string.IsNullOrEmpty(tagHelperPrefix))
+ {
+ return descriptors.Select(descriptor =>
+ new TagHelperDescriptor(
+ tagHelperPrefix,
+ descriptor.TagName,
+ descriptor.TypeName,
+ descriptor.AssemblyName,
+ descriptor.Attributes));
+ }
+
+ return descriptors;
+ }
+
+ private static string ResolveTagHelperPrefix(TagHelperDescriptorResolutionContext context)
+ {
+ var prefixDirectiveDescriptors = context.DirectiveDescriptors.Where(
+ descriptor => descriptor.DirectiveType == TagHelperDirectiveType.TagHelperPrefix);
+
+ TagHelperDirectiveDescriptor prefixDirective = null;
+
+ foreach (var directive in prefixDirectiveDescriptors)
+ {
+ if (prefixDirective == null)
+ {
+ prefixDirective = directive;
+ }
+ else
+ {
+ // For each invalid @tagHelperPrefix we need to create an error.
+ context.ErrorSink.OnError(
+ directive.Location,
+ Resources.FormatTagHelperDescriptorResolver_InvalidTagHelperDirective(
+ SyntaxConstants.CSharp.TagHelperPrefixKeyword));
+ }
+ }
+
+ var prefix = prefixDirective?.DirectiveText;
+
+ if (prefix != null && !EnsureValidPrefix(prefix, prefixDirective.Location, context.ErrorSink))
+ {
+ prefix = null;
+ }
+
+ return prefix;
+ }
+
+ private static bool EnsureValidPrefix(
+ string prefix,
+ SourceLocation directiveLocation,
+ ParserErrorSink errorSink)
+ {
+ foreach (var character in prefix)
+ {
+ // Prefixes are correlated with tag names, tag names cannot have whitespace.
+ if (char.IsWhiteSpace(character) ||
+ InvalidNonWhitespacePrefixCharacters.Contains(character))
+ {
+ errorSink.OnError(
+ directiveLocation,
+ Resources.FormatTagHelperDescriptorResolver_InvalidTagHelperPrefixValue(
+ SyntaxConstants.CSharp.TagHelperPrefixKeyword,
+ character,
+ prefix));
+
+ return false;
+ }
+ }
+
+ return true;
+ }
+
private static bool MatchesLookupInfo(TagHelperDescriptor descriptor, LookupInfo lookupInfo)
{
if (!string.Equals(descriptor.AssemblyName, lookupInfo.AssemblyName, StringComparison.Ordinal))
@@ -146,7 +236,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
private static LookupInfo GetLookupInfo(TagHelperDirectiveDescriptor directiveDescriptor,
ParserErrorSink errorSink)
{
- var lookupText = directiveDescriptor.LookupText;
+ var lookupText = directiveDescriptor.DirectiveText;
var lookupStrings = lookupText?.Split(new[] { ',' });
// Ensure that we have valid lookupStrings to work with. Valid formats are:
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 7081785246..f9038ff277 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
@@ -58,17 +58,22 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
}
}
+ protected override void Visit(TagHelperPrefixDirectiveChunk chunk)
+ {
+ VisitTagHelperDirectiveChunk(chunk.Prefix, chunk);
+ }
+
protected override void Visit(AddTagHelperChunk chunk)
{
- VisitAddOrRemoveTagHelperChunk(chunk.LookupText, chunk);
+ VisitTagHelperDirectiveChunk(chunk.LookupText, chunk);
}
protected override void Visit(RemoveTagHelperChunk chunk)
{
- VisitAddOrRemoveTagHelperChunk(chunk.LookupText, chunk);
+ VisitTagHelperDirectiveChunk(chunk.LookupText, chunk);
}
- private void VisitAddOrRemoveTagHelperChunk(string lookupText, Chunk chunk)
+ private void VisitTagHelperDirectiveChunk(string text, Chunk chunk)
{
// We should always be in design time mode because of the calling AcceptTree method verification.
Debug.Assert(Context.Host.DesignTimeMode);
@@ -81,12 +86,10 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
Writer.WriteStartAssignment(TagHelperDirectiveSyntaxHelper);
- // The parsing mechanism for the add/remove TagHelper chunk (CSharpCodeParser.TagHelperDirective())
- // removes quotes that surround the lookupText.
+ // The parsing mechanism for a TagHelper directive chunk (CSharpCodeParser.TagHelperDirective())
+ // removes quotes that surround the text.
_csharpCodeVisitor.CreateExpressionCodeMapping(
- string.Format(
- CultureInfo.InvariantCulture,
- "\"{0}\"", lookupText),
+ string.Format(CultureInfo.InvariantCulture, "\"{0}\"", text),
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 9a901d41e0..f8ba39b70b 100644
--- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/ChunkVisitor.cs
+++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/ChunkVisitor.cs
@@ -57,6 +57,10 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler
{
Visit((TagHelperChunk)chunk);
}
+ else if (chunk is TagHelperPrefixDirectiveChunk)
+ {
+ Visit((TagHelperPrefixDirectiveChunk)chunk);
+ }
else if (chunk is AddTagHelperChunk)
{
Visit((AddTagHelperChunk)chunk);
@@ -119,6 +123,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler
protected abstract void Visit(ExpressionChunk chunk);
protected abstract void Visit(StatementChunk chunk);
protected abstract void Visit(TagHelperChunk chunk);
+ protected abstract void Visit(TagHelperPrefixDirectiveChunk chunk);
protected abstract void Visit(AddTagHelperChunk chunk);
protected abstract void Visit(RemoveTagHelperChunk chunk);
protected abstract void Visit(UsingChunk chunk);
diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CodeVisitor.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CodeVisitor.cs
index 955e4069c6..1227d96a4e 100644
--- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CodeVisitor.cs
+++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CodeVisitor.cs
@@ -33,6 +33,9 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler
protected override void Visit(TagHelperChunk chunk)
{
}
+ protected override void Visit(TagHelperPrefixDirectiveChunk chunk)
+ {
+ }
protected override void Visit(AddTagHelperChunk chunk)
{
}
diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/Chunks/TagHelpers/TagHelperPrefixDirectiveChunk.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/Chunks/TagHelpers/TagHelperPrefixDirectiveChunk.cs
new file mode 100644
index 0000000000..bedc080d57
--- /dev/null
+++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/Chunks/TagHelpers/TagHelperPrefixDirectiveChunk.cs
@@ -0,0 +1,17 @@
+// 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 for the @tagHelperPrefix directive.
+ ///
+ public class TagHelperPrefixDirectiveChunk : Chunk
+ {
+ ///
+ /// Text used as a required prefix when matching HTML start and end tags in the Razor source to available
+ /// tag helpers.
+ ///
+ public string Prefix { 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 35a854a00a..148337eea3 100644
--- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/CodeTreeBuilder.cs
+++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/CodeTreeBuilder.cs
@@ -37,6 +37,17 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler
}
}
+ public void AddTagHelperPrefixDirectiveChunk(string prefix, SyntaxTreeNode association)
+ {
+ AddChunk(
+ new TagHelperPrefixDirectiveChunk
+ {
+ Prefix = prefix
+ },
+ association,
+ topLevel: true);
+ }
+
public void AddAddTagHelperChunk(string lookupText, SyntaxTreeNode association)
{
AddChunk(new AddTagHelperChunk
diff --git a/src/Microsoft.AspNet.Razor/Generator/TagHelperCodeGenerator.cs b/src/Microsoft.AspNet.Razor/Generator/TagHelperCodeGenerator.cs
index a25b393964..b0277a129e 100644
--- a/src/Microsoft.AspNet.Razor/Generator/TagHelperCodeGenerator.cs
+++ b/src/Microsoft.AspNet.Razor/Generator/TagHelperCodeGenerator.cs
@@ -75,9 +75,11 @@ namespace Microsoft.AspNet.Razor.Generator
codeGenerator.Context.CodeTreeBuilder = new CodeTreeBuilder();
}
+ var unprefixedTagName = tagHelperBlock.TagName.Substring(_tagHelperDescriptors.First().Prefix.Length);
+
context.CodeTreeBuilder.StartChunkBlock(
new TagHelperChunk(
- tagHelperBlock.TagName,
+ unprefixedTagName,
tagHelperBlock.SelfClosing,
attributes,
_tagHelperDescriptors),
diff --git a/src/Microsoft.AspNet.Razor/Generator/TagHelpers/TagHelperPrefixDirectiveCodeGenerator.cs b/src/Microsoft.AspNet.Razor/Generator/TagHelpers/TagHelperPrefixDirectiveCodeGenerator.cs
new file mode 100644
index 0000000000..7acd267395
--- /dev/null
+++ b/src/Microsoft.AspNet.Razor/Generator/TagHelpers/TagHelperPrefixDirectiveCodeGenerator.cs
@@ -0,0 +1,43 @@
+// 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 TagHelperPrefixDirectiveCodeGenerator : SpanCodeGenerator
+ {
+ ///
+ /// Instantiates a new .
+ ///
+ ///
+ /// Text used as a required prefix when matching HTML.
+ ///
+ public TagHelperPrefixDirectiveCodeGenerator(string prefix)
+ {
+ Prefix = prefix;
+ }
+
+ ///
+ /// Text used as a required prefix when matching HTML.
+ ///
+ public string Prefix { get; }
+
+ ///
+ /// Generates s.
+ ///
+ ///
+ /// 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.AddTagHelperPrefixDirectiveChunk(Prefix, 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 8ba70aee02..21206c0496 100644
--- a/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.Directives.cs
+++ b/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.Directives.cs
@@ -18,6 +18,7 @@ namespace Microsoft.AspNet.Razor.Parser
{
private void SetupDirectives()
{
+ MapDirectives(TagHelperPrefixDirective, SyntaxConstants.CSharp.TagHelperPrefixKeyword);
MapDirectives(AddTagHelperDirective, SyntaxConstants.CSharp.AddTagHelperKeyword);
MapDirectives(RemoveTagHelperDirective, SyntaxConstants.CSharp.RemoveTagHelperKeyword);
MapDirectives(InheritsDirective, SyntaxConstants.CSharp.InheritsKeyword);
@@ -27,14 +28,27 @@ namespace Microsoft.AspNet.Razor.Parser
MapDirectives(LayoutDirective, SyntaxConstants.CSharp.LayoutKeyword);
}
+ protected virtual void TagHelperPrefixDirective()
+ {
+ TagHelperDirective(
+ SyntaxConstants.CSharp.TagHelperPrefixKeyword,
+ prefix => new TagHelperPrefixDirectiveCodeGenerator(prefix));
+ }
+
protected virtual void AddTagHelperDirective()
{
- TagHelperDirective(SyntaxConstants.CSharp.AddTagHelperKeyword, removeTagHelperDescriptors: false);
+ TagHelperDirective(
+ SyntaxConstants.CSharp.AddTagHelperKeyword,
+ lookupText =>
+ new AddOrRemoveTagHelperCodeGenerator(removeTagHelperDescriptors: false, lookupText: lookupText));
}
protected virtual void RemoveTagHelperDirective()
{
- TagHelperDirective(SyntaxConstants.CSharp.RemoveTagHelperKeyword, removeTagHelperDescriptors: true);
+ TagHelperDirective(
+ SyntaxConstants.CSharp.RemoveTagHelperKeyword,
+ lookupText =>
+ new AddOrRemoveTagHelperCodeGenerator(removeTagHelperDescriptors: true, lookupText: lookupText));
}
protected virtual void LayoutDirective()
@@ -450,7 +464,7 @@ namespace Microsoft.AspNet.Razor.Parser
Output(SpanKind.Code);
}
- private void TagHelperDirective(string keyword, bool removeTagHelperDescriptors)
+ private void TagHelperDirective(string keyword, Func buildCodeGenerator)
{
AssertDirective(keyword);
@@ -492,8 +506,7 @@ 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 =
- new AddOrRemoveTagHelperCodeGenerator(removeTagHelperDescriptors, rawValue.Trim('"'));
+ Span.CodeGenerator = buildCodeGenerator(rawValue.Trim('"'));
}
// We expect the directive to be surrounded in quotes.
diff --git a/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.cs b/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.cs
index 6906293549..bba1a82dd0 100644
--- a/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.cs
+++ b/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.cs
@@ -19,6 +19,7 @@ namespace Microsoft.AspNet.Razor.Parser
internal static ISet DefaultKeywords = new HashSet()
{
+ SyntaxConstants.CSharp.TagHelperPrefixKeyword,
SyntaxConstants.CSharp.AddTagHelperKeyword,
SyntaxConstants.CSharp.RemoveTagHelperKeyword,
"if",
diff --git a/src/Microsoft.AspNet.Razor/Parser/RazorParser.cs b/src/Microsoft.AspNet.Razor/Parser/RazorParser.cs
index 2d538e347c..24b4401214 100644
--- a/src/Microsoft.AspNet.Razor/Parser/RazorParser.cs
+++ b/src/Microsoft.AspNet.Razor/Parser/RazorParser.cs
@@ -213,7 +213,7 @@ namespace Microsoft.AspNet.Razor.Parser
[NotNull] ParserErrorSink errorSink)
{
var addOrRemoveTagHelperSpanVisitor =
- new AddOrRemoveTagHelperSpanVisitor(TagHelperDescriptorResolver, errorSink);
+ new TagHelperDirectiveSpanVisitor(TagHelperDescriptorResolver, errorSink);
return addOrRemoveTagHelperSpanVisitor.GetDescriptors(documentRoot);
}
diff --git a/src/Microsoft.AspNet.Razor/Parser/SyntaxConstants.cs b/src/Microsoft.AspNet.Razor/Parser/SyntaxConstants.cs
index 0485ca3008..7f5ea353a6 100644
--- a/src/Microsoft.AspNet.Razor/Parser/SyntaxConstants.cs
+++ b/src/Microsoft.AspNet.Razor/Parser/SyntaxConstants.cs
@@ -18,6 +18,7 @@ namespace Microsoft.AspNet.Razor.Parser
public static class CSharp
{
public static readonly int UsingKeywordLength = 5;
+ public static readonly string TagHelperPrefixKeyword = "tagHelperPrefix";
public static readonly string AddTagHelperKeyword = "addTagHelper";
public static readonly string RemoveTagHelperKeyword = "removeTagHelper";
public static readonly string InheritsKeyword = "inherits";
diff --git a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/AddOrRemoveTagHelperSpanVisitor.cs b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperDirectiveSpanVisitor.cs
similarity index 69%
rename from src/Microsoft.AspNet.Razor/Parser/TagHelpers/AddOrRemoveTagHelperSpanVisitor.cs
rename to src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperDirectiveSpanVisitor.cs
index aabbc3d895..0f1c9b598e 100644
--- a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/AddOrRemoveTagHelperSpanVisitor.cs
+++ b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperDirectiveSpanVisitor.cs
@@ -12,7 +12,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers
/// A that generates s from
/// tag helper code generators in a Razor document.
///
- public class AddOrRemoveTagHelperSpanVisitor : ParserVisitor
+ public class TagHelperDirectiveSpanVisitor : ParserVisitor
{
private readonly ITagHelperDescriptorResolver _descriptorResolver;
private readonly ParserErrorSink _errorSink;
@@ -20,12 +20,12 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers
private List _directiveDescriptors;
// Internal for testing use
- internal AddOrRemoveTagHelperSpanVisitor(ITagHelperDescriptorResolver descriptorResolver)
+ internal TagHelperDirectiveSpanVisitor(ITagHelperDescriptorResolver descriptorResolver)
: this(descriptorResolver, new ParserErrorSink())
{
}
- public AddOrRemoveTagHelperSpanVisitor([NotNull] ITagHelperDescriptorResolver descriptorResolver,
+ public TagHelperDirectiveSpanVisitor([NotNull] ITagHelperDescriptorResolver descriptorResolver,
[NotNull] ParserErrorSink errorSink)
{
_descriptorResolver = descriptorResolver;
@@ -61,13 +61,26 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers
{
var codeGenerator = (AddOrRemoveTagHelperCodeGenerator)span.CodeGenerator;
- var directive = codeGenerator.RemoveTagHelperDescriptors ?
- TagHelperDirectiveType.RemoveTagHelper :
- TagHelperDirectiveType.AddTagHelper;
+ var directive =
+ codeGenerator.RemoveTagHelperDescriptors ?
+ TagHelperDirectiveType.RemoveTagHelper :
+ TagHelperDirectiveType.AddTagHelper;
- var directiveDescriptor = new TagHelperDirectiveDescriptor(codeGenerator.LookupText,
- span.Start,
- directive);
+ var directiveDescriptor = new TagHelperDirectiveDescriptor(
+ codeGenerator.LookupText,
+ span.Start,
+ directive);
+
+ _directiveDescriptors.Add(directiveDescriptor);
+ }
+ else if (span.CodeGenerator is TagHelperPrefixDirectiveCodeGenerator)
+ {
+ var codeGenerator = (TagHelperPrefixDirectiveCodeGenerator)span.CodeGenerator;
+
+ var directiveDescriptor = new TagHelperDirectiveDescriptor(
+ codeGenerator.Prefix,
+ span.Start,
+ TagHelperDirectiveType.TagHelperPrefix);
_directiveDescriptors.Add(directiveDescriptor);
}
diff --git a/src/Microsoft.AspNet.Razor/ParserResults.cs b/src/Microsoft.AspNet.Razor/ParserResults.cs
index 9d4b9709b0..1eddefcd0b 100644
--- a/src/Microsoft.AspNet.Razor/ParserResults.cs
+++ b/src/Microsoft.AspNet.Razor/ParserResults.cs
@@ -54,6 +54,7 @@ namespace Microsoft.AspNet.Razor
TagHelperDescriptors = tagHelperDescriptors;
ErrorSink = errorSink;
ParserErrors = errorSink.Errors;
+ Prefix = tagHelperDescriptors.FirstOrDefault()?.Prefix;
}
///
@@ -81,5 +82,10 @@ namespace Microsoft.AspNet.Razor
/// The s found for the current Razor document.
///
public IEnumerable TagHelperDescriptors { get; }
+
+ ///
+ /// Text used as a required prefix when matching HTML.
+ ///
+ public string Prefix { get; }
}
}
diff --git a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptor.cs b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptor.cs
index 77c080054f..ce9133081c 100644
--- a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptor.cs
+++ b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptor.cs
@@ -17,10 +17,11 @@ namespace Microsoft.AspNet.Razor.TagHelpers
internal TagHelperDescriptor([NotNull] string tagName,
[NotNull] string typeName,
[NotNull] string assemblyName)
- : this(tagName,
- typeName,
- assemblyName,
- Enumerable.Empty())
+ : this(
+ tagName,
+ typeName,
+ assemblyName,
+ attributes: Enumerable.Empty())
{
}
@@ -35,22 +36,67 @@ namespace Microsoft.AspNet.Razor.TagHelpers
///
/// The s to request from the HTML tag.
///
- public TagHelperDescriptor([NotNull] string tagName,
- [NotNull] string typeName,
- [NotNull] string assemblyName,
- [NotNull] IEnumerable attributes)
+ public TagHelperDescriptor(
+ [NotNull] string tagName,
+ [NotNull] string typeName,
+ [NotNull] string assemblyName,
+ [NotNull] IEnumerable attributes)
+ : this(
+ prefix: string.Empty,
+ tagName: tagName,
+ typeName: typeName,
+ assemblyName: assemblyName,
+ attributes: attributes)
{
+ }
+
+ ///
+ /// Instantiates a new instance of the class with the given
+ /// .
+ ///
+ ///
+ /// Text used as a required prefix when matching HTML start and end tags in the Razor source to available
+ /// tag helpers.
+ ///
+ /// The tag name that the tag helper targets. '*' indicates a catch-all
+ /// which applies to every HTML tag.
+ /// The full name of the tag helper class.
+ /// The name of the assembly containing the tag helper class.
+ ///
+ /// The s to request from the HTML tag.
+ ///
+ public TagHelperDescriptor(
+ string prefix,
+ [NotNull] string tagName,
+ [NotNull] string typeName,
+ [NotNull] string assemblyName,
+ [NotNull] IEnumerable attributes)
+ {
+ Prefix = prefix ?? string.Empty;
TagName = tagName;
+ FullTagName = Prefix + TagName;
TypeName = typeName;
AssemblyName = assemblyName;
Attributes = new List(attributes);
}
+ ///
+ /// Text used as a required prefix when matching HTML start and end tags in the Razor source to available
+ /// tag helpers.
+ ///
+ public string Prefix { get; private set; }
+
///
/// The tag name that the tag helper should target.
///
public string TagName { get; private set; }
+ ///
+ /// The full tag name that is required for the tag helper to target an HTML element.
+ ///
+ /// This is equivalent to and concatenated.
+ public string FullTagName { get; private set; }
+
///
/// The full name of the tag helper class.
///
diff --git a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorComparer.cs b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorComparer.cs
index 0f79c05154..92eb3742f0 100644
--- a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorComparer.cs
+++ b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorComparer.cs
@@ -29,12 +29,14 @@ namespace Microsoft.AspNet.Razor.TagHelpers
/// false otherwise.
///
/// Determines equality based on ,
- /// and .
+ /// , and
+ /// .
///
public bool Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY)
{
return string.Equals(descriptorX.TypeName, descriptorY.TypeName, StringComparison.Ordinal) &&
string.Equals(descriptorX.TagName, descriptorY.TagName, StringComparison.OrdinalIgnoreCase) &&
+ string.Equals(descriptorX.Prefix, descriptorY.Prefix, StringComparison.OrdinalIgnoreCase) &&
string.Equals(descriptorX.AssemblyName, descriptorY.AssemblyName, StringComparison.Ordinal);
}
diff --git a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorProvider.cs b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorProvider.cs
index dc4a6123bb..988998212b 100644
--- a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorProvider.cs
+++ b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorProvider.cs
@@ -15,6 +15,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
private const string CatchAllDescriptorTarget = "*";
private IDictionary> _registrations;
+ private string _tagHelperPrefix;
///
/// Instantiates a new instance of the .
@@ -43,10 +44,18 @@ namespace Microsoft.AspNet.Razor.TagHelpers
{
HashSet descriptors;
- // Ensure there's an ISet to use.
+ if (!string.IsNullOrEmpty(_tagHelperPrefix) &&
+ (tagName.Length <= _tagHelperPrefix.Length ||
+ !tagName.StartsWith(_tagHelperPrefix, StringComparison.OrdinalIgnoreCase)))
+ {
+ // The tagName doesn't have the tag helper prefix, we can short circuit.
+ return Enumerable.Empty();
+ }
+
+ // Ensure there's a HashSet to use.
if (!_registrations.TryGetValue(CatchAllDescriptorTarget, out descriptors))
{
- descriptors = new HashSet();
+ descriptors = new HashSet(TagHelperDescriptorComparer.Default);
}
// If the requested tag name is the catch-all target, we should short circuit.
@@ -72,11 +81,21 @@ namespace Microsoft.AspNet.Razor.TagHelpers
{
HashSet descriptorSet;
- // Ensure there's a List to add the descriptor to.
- if (!_registrations.TryGetValue(descriptor.TagName, out descriptorSet))
+ if (_tagHelperPrefix == null)
+ {
+ _tagHelperPrefix = descriptor.Prefix;
+ }
+
+ var registrationKey =
+ string.Equals(descriptor.TagName, CatchAllDescriptorTarget, StringComparison.Ordinal) ?
+ CatchAllDescriptorTarget :
+ descriptor.FullTagName;
+
+ // Ensure there's a HashSet to add the descriptor to.
+ if (!_registrations.TryGetValue(registrationKey, out descriptorSet))
{
descriptorSet = new HashSet(TagHelperDescriptorComparer.Default);
- _registrations[descriptor.TagName] = descriptorSet;
+ _registrations[registrationKey] = descriptorSet;
}
descriptorSet.Add(descriptor);
diff --git a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDirectiveDescriptor.cs b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDirectiveDescriptor.cs
index 6a662f1dd6..59f85bf42a 100644
--- a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDirectiveDescriptor.cs
+++ b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDirectiveDescriptor.cs
@@ -11,23 +11,24 @@ namespace Microsoft.AspNet.Razor.TagHelpers
public class TagHelperDirectiveDescriptor
{
// Internal for testing purposes.
- internal TagHelperDirectiveDescriptor(string lookupText,
+ internal TagHelperDirectiveDescriptor(string directiveText,
TagHelperDirectiveType directiveType)
- : this(lookupText, SourceLocation.Zero, directiveType)
+ : this(directiveText, SourceLocation.Zero, directiveType)
{
}
///
/// Instantiates a new instance of .
///
- /// A used to find tag helper s.
+ /// A used to understand tag helper
+ /// s.
/// The of the directive.
/// The of this directive.
- public TagHelperDirectiveDescriptor([NotNull] string lookupText,
+ public TagHelperDirectiveDescriptor([NotNull] string directiveText,
SourceLocation location,
TagHelperDirectiveType directiveType)
{
- LookupText = lookupText;
+ DirectiveText = directiveText;
Location = location;
DirectiveType = directiveType;
}
@@ -35,7 +36,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
///
/// A used to find tag helper s.
///
- public string LookupText { get; private set; }
+ public string DirectiveText { get; private set; }
///
/// The of this directive.
diff --git a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDirectiveType.cs b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDirectiveType.cs
index 46193df6db..ff45e8cd0a 100644
--- a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDirectiveType.cs
+++ b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDirectiveType.cs
@@ -9,13 +9,18 @@ namespace Microsoft.AspNet.Razor.TagHelpers
public enum TagHelperDirectiveType
{
///
- /// An @addTagHelper directive.
+ /// An @addTagHelper directive.
///
AddTagHelper,
///
- /// A @removeTagHelper directive.
+ /// A @removeTagHelper directive.
///
- RemoveTagHelper
+ RemoveTagHelper,
+
+ ///
+ /// A @tagHelperPrefix directive.
+ ///
+ TagHelperPrefix
}
}
\ No newline at end of file