Update `TagHelperDirectiveSpanVisitor` to properly capture TagHelper directive text.

- Added a test with optional quotes for each of the `TagHelperDirectiveDescriptor`s, add, remove and prefix.

#744
This commit is contained in:
N. Taylor Mullen 2016-05-17 16:45:17 -07:00
parent cacd16fa85
commit 80ebb4a068
5 changed files with 144 additions and 22 deletions

View File

@ -12,8 +12,6 @@ namespace Microsoft.AspNetCore.Razor.Chunks.Generators
/// </summary> /// </summary>
public class AddTagHelperChunkGenerator : SpanChunkGenerator public class AddTagHelperChunkGenerator : SpanChunkGenerator
{ {
private readonly string _lookupText;
/// <summary> /// <summary>
/// Initializes a new instance of <see cref="AddTagHelperChunkGenerator"/>. /// Initializes a new instance of <see cref="AddTagHelperChunkGenerator"/>.
/// </summary> /// </summary>
@ -22,9 +20,14 @@ namespace Microsoft.AspNetCore.Razor.Chunks.Generators
/// </param> /// </param>
public AddTagHelperChunkGenerator(string lookupText) public AddTagHelperChunkGenerator(string lookupText)
{ {
_lookupText = lookupText; LookupText = lookupText;
} }
/// <summary>
/// Gets the text used to look up <see cref="Compilation.TagHelpers.TagHelperDescriptor"/>s that should be added.
/// </summary>
public string LookupText { get; }
/// <summary> /// <summary>
/// Generates <see cref="AddTagHelperChunk"/>s. /// Generates <see cref="AddTagHelperChunk"/>s.
/// </summary> /// </summary>
@ -35,7 +38,7 @@ namespace Microsoft.AspNetCore.Razor.Chunks.Generators
/// the current chunk generation process.</param> /// the current chunk generation process.</param>
public override void GenerateChunk(Span target, ChunkGeneratorContext context) public override void GenerateChunk(Span target, ChunkGeneratorContext context)
{ {
context.ChunkTreeBuilder.AddAddTagHelperChunk(_lookupText, target); context.ChunkTreeBuilder.AddAddTagHelperChunk(LookupText, target);
} }
/// <inheritdoc /> /// <inheritdoc />
@ -43,7 +46,7 @@ namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{ {
var other = obj as AddTagHelperChunkGenerator; var other = obj as AddTagHelperChunkGenerator;
return base.Equals(other) && return base.Equals(other) &&
string.Equals(_lookupText, other._lookupText, StringComparison.Ordinal); string.Equals(LookupText, other.LookupText, StringComparison.Ordinal);
} }
/// <inheritdoc /> /// <inheritdoc />
@ -51,7 +54,7 @@ namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{ {
var combiner = HashCodeCombiner.Start(); var combiner = HashCodeCombiner.Start();
combiner.Add(base.GetHashCode()); combiner.Add(base.GetHashCode());
combiner.Add(_lookupText, StringComparer.Ordinal); combiner.Add(LookupText, StringComparer.Ordinal);
return combiner.CombinedHash; return combiner.CombinedHash;
} }

View File

@ -12,8 +12,6 @@ namespace Microsoft.AspNetCore.Razor.Chunks.Generators
/// </summary> /// </summary>
public class RemoveTagHelperChunkGenerator : SpanChunkGenerator public class RemoveTagHelperChunkGenerator : SpanChunkGenerator
{ {
private readonly string _lookupText;
/// <summary> /// <summary>
/// Initializes a new instance of <see cref="RemoveTagHelperChunkGenerator"/>. /// Initializes a new instance of <see cref="RemoveTagHelperChunkGenerator"/>.
/// </summary> /// </summary>
@ -22,9 +20,14 @@ namespace Microsoft.AspNetCore.Razor.Chunks.Generators
/// </param> /// </param>
public RemoveTagHelperChunkGenerator(string lookupText) public RemoveTagHelperChunkGenerator(string lookupText)
{ {
_lookupText = lookupText; LookupText = lookupText;
} }
/// <summary>
/// Text used to look up <see cref="Compilation.TagHelpers.TagHelperDescriptor"/>s that should be removed.
/// </summary>
public string LookupText { get; }
/// <summary> /// <summary>
/// Generates <see cref="RemoveTagHelperChunk"/>s. /// Generates <see cref="RemoveTagHelperChunk"/>s.
/// </summary> /// </summary>
@ -35,7 +38,7 @@ namespace Microsoft.AspNetCore.Razor.Chunks.Generators
/// the current chunk generation process.</param> /// the current chunk generation process.</param>
public override void GenerateChunk(Span target, ChunkGeneratorContext context) public override void GenerateChunk(Span target, ChunkGeneratorContext context)
{ {
context.ChunkTreeBuilder.AddRemoveTagHelperChunk(_lookupText, target); context.ChunkTreeBuilder.AddRemoveTagHelperChunk(LookupText, target);
} }
/// <inheritdoc /> /// <inheritdoc />
@ -43,7 +46,7 @@ namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{ {
var other = obj as RemoveTagHelperChunkGenerator; var other = obj as RemoveTagHelperChunkGenerator;
return base.Equals(other) && return base.Equals(other) &&
string.Equals(_lookupText, other._lookupText, StringComparison.Ordinal); string.Equals(LookupText, other.LookupText, StringComparison.Ordinal);
} }
/// <inheritdoc /> /// <inheritdoc />
@ -51,7 +54,7 @@ namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{ {
var combiner = HashCodeCombiner.Start(); var combiner = HashCodeCombiner.Start();
combiner.Add(base.GetHashCode()); combiner.Add(base.GetHashCode());
combiner.Add(_lookupText, StringComparer.Ordinal); combiner.Add(LookupText, StringComparer.Ordinal);
return combiner.CombinedHash; return combiner.CombinedHash;
} }

View File

@ -13,8 +13,6 @@ namespace Microsoft.AspNetCore.Razor.Chunks.Generators
/// </summary> /// </summary>
public class TagHelperPrefixDirectiveChunkGenerator : SpanChunkGenerator public class TagHelperPrefixDirectiveChunkGenerator : SpanChunkGenerator
{ {
private readonly string _prefix;
/// <summary> /// <summary>
/// Initializes a new instance of <see cref="TagHelperPrefixDirectiveChunkGenerator"/>. /// Initializes a new instance of <see cref="TagHelperPrefixDirectiveChunkGenerator"/>.
/// </summary> /// </summary>
@ -23,9 +21,14 @@ namespace Microsoft.AspNetCore.Razor.Chunks.Generators
/// </param> /// </param>
public TagHelperPrefixDirectiveChunkGenerator(string prefix) public TagHelperPrefixDirectiveChunkGenerator(string prefix)
{ {
_prefix = prefix; Prefix = prefix;
} }
/// <summary>
/// Text used as a required prefix when matching HTML.
/// </summary>
public string Prefix { get; }
/// <summary> /// <summary>
/// Generates <see cref="TagHelperPrefixDirectiveChunk"/>s. /// Generates <see cref="TagHelperPrefixDirectiveChunk"/>s.
/// </summary> /// </summary>
@ -36,7 +39,7 @@ namespace Microsoft.AspNetCore.Razor.Chunks.Generators
/// the current chunk generation process.</param> /// the current chunk generation process.</param>
public override void GenerateChunk(Span target, ChunkGeneratorContext context) public override void GenerateChunk(Span target, ChunkGeneratorContext context)
{ {
context.ChunkTreeBuilder.AddTagHelperPrefixDirectiveChunk(_prefix, target); context.ChunkTreeBuilder.AddTagHelperPrefixDirectiveChunk(Prefix, target);
} }
/// <inheritdoc /> /// <inheritdoc />
@ -44,7 +47,7 @@ namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{ {
var other = obj as TagHelperPrefixDirectiveChunkGenerator; var other = obj as TagHelperPrefixDirectiveChunkGenerator;
return base.Equals(other) && return base.Equals(other) &&
string.Equals(_prefix, other._prefix, StringComparison.Ordinal); string.Equals(Prefix, other.Prefix, StringComparison.Ordinal);
} }
/// <inheritdoc /> /// <inheritdoc />
@ -52,7 +55,7 @@ namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{ {
var combiner = HashCodeCombiner.Start(); var combiner = HashCodeCombiner.Start();
combiner.Add(base.GetHashCode()); combiner.Add(base.GetHashCode());
combiner.Add(_prefix, StringComparer.Ordinal); combiner.Add(Prefix, StringComparer.Ordinal);
return combiner.CombinedHash; return combiner.CombinedHash;
} }

View File

@ -87,18 +87,27 @@ namespace Microsoft.AspNetCore.Razor.Parser.TagHelpers
throw new ArgumentNullException(nameof(span)); throw new ArgumentNullException(nameof(span));
} }
string directiveText;
TagHelperDirectiveType directiveType; TagHelperDirectiveType directiveType;
if (span.ChunkGenerator is AddTagHelperChunkGenerator)
var addTagHelperChunkGenerator = span.ChunkGenerator as AddTagHelperChunkGenerator;
var removeTagHelperChunkGenerator = span.ChunkGenerator as RemoveTagHelperChunkGenerator;
var tagHelperPrefixChunkGenerator = span.ChunkGenerator as TagHelperPrefixDirectiveChunkGenerator;
if (addTagHelperChunkGenerator != null)
{ {
directiveType = TagHelperDirectiveType.AddTagHelper; directiveType = TagHelperDirectiveType.AddTagHelper;
directiveText = addTagHelperChunkGenerator.LookupText;
} }
else if (span.ChunkGenerator is RemoveTagHelperChunkGenerator) else if (removeTagHelperChunkGenerator != null)
{ {
directiveType = TagHelperDirectiveType.RemoveTagHelper; directiveType = TagHelperDirectiveType.RemoveTagHelper;
directiveText = removeTagHelperChunkGenerator.LookupText;
} }
else if (span.ChunkGenerator is TagHelperPrefixDirectiveChunkGenerator) else if (tagHelperPrefixChunkGenerator != null)
{ {
directiveType = TagHelperDirectiveType.TagHelperPrefix; directiveType = TagHelperDirectiveType.TagHelperPrefix;
directiveText = tagHelperPrefixChunkGenerator.Prefix;
} }
else else
{ {
@ -106,7 +115,7 @@ namespace Microsoft.AspNetCore.Razor.Parser.TagHelpers
return; return;
} }
var directiveText = span.Content.Trim(); directiveText = directiveText.Trim();
var startOffset = span.Content.IndexOf(directiveText, StringComparison.Ordinal); var startOffset = span.Content.IndexOf(directiveText, StringComparison.Ordinal);
var offsetContent = span.Content.Substring(0, startOffset); var offsetContent = span.Content.Substring(0, startOffset);
var offsetTextLocation = SourceLocation.Advance(span.Start, offsetContent); var offsetTextLocation = SourceLocation.Advance(span.Start, offsetContent);

View File

@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Razor.Parser;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree; using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
using Microsoft.AspNetCore.Razor.Parser.TagHelpers; using Microsoft.AspNetCore.Razor.Parser.TagHelpers;
using Microsoft.AspNetCore.Razor.Test.Framework; using Microsoft.AspNetCore.Razor.Test.Framework;
using Microsoft.AspNetCore.Razor.Tokenizer;
using Microsoft.Extensions.Internal; using Microsoft.Extensions.Internal;
using Moq; using Moq;
using Xunit; using Xunit;
@ -18,6 +19,109 @@ namespace Microsoft.AspNetCore.Razor.Compilation.TagHelpers
{ {
private static readonly SpanFactory Factory = SpanFactory.CreateCsHtml(); private static readonly SpanFactory Factory = SpanFactory.CreateCsHtml();
public static TheoryData QuotedTagHelperDirectivesData
{
get
{
var factory = new SpanFactory
{
MarkupTokenizerFactory = doc => new HtmlTokenizer(doc),
CodeTokenizerFactory = doc => new CSharpTokenizer(doc),
};
// document, expectedDescriptors
return new TheoryData<MarkupBlock, IEnumerable<TagHelperDirectiveDescriptor>>
{
{
new MarkupBlock(factory.Code("\"*, someAssembly\"").AsAddTagHelper("*, someAssembly")),
new[]
{
new TagHelperDirectiveDescriptor
{
DirectiveText = "*, someAssembly",
DirectiveType = TagHelperDirectiveType.AddTagHelper
},
}
},
{
new MarkupBlock(factory.Code("\"*, someAssembly\"").AsRemoveTagHelper("*, someAssembly")),
new[]
{
new TagHelperDirectiveDescriptor
{
DirectiveText = "*, someAssembly",
DirectiveType = TagHelperDirectiveType.RemoveTagHelper
},
}
},
{
new MarkupBlock(factory.Code("\"th:\"").AsTagHelperPrefixDirective("th:")),
new[]
{
new TagHelperDirectiveDescriptor
{
DirectiveText = "th:",
DirectiveType = TagHelperDirectiveType.TagHelperPrefix
},
}
},
{
new MarkupBlock(factory.Code(" \"*, someAssembly \" ").AsAddTagHelper("*, someAssembly ")),
new[]
{
new TagHelperDirectiveDescriptor
{
DirectiveText = "*, someAssembly",
DirectiveType = TagHelperDirectiveType.AddTagHelper
},
}
},
{
new MarkupBlock(factory.Code(" \"*, someAssembly \" ").AsRemoveTagHelper("*, someAssembly ")),
new[]
{
new TagHelperDirectiveDescriptor
{
DirectiveText = "*, someAssembly",
DirectiveType = TagHelperDirectiveType.RemoveTagHelper
},
}
},
{
new MarkupBlock(factory.Code(" \" th :\"").AsTagHelperPrefixDirective(" th :")),
new[]
{
new TagHelperDirectiveDescriptor
{
DirectiveText = "th :",
DirectiveType = TagHelperDirectiveType.TagHelperPrefix
},
}
},
};
}
}
[Theory]
[MemberData(nameof(QuotedTagHelperDirectivesData))]
public void GetDescriptors_LocatesQuotedTagHelperDirectives_CreatesDirectiveDescriptors(
MarkupBlock document,
IEnumerable<TagHelperDirectiveDescriptor> expectedDescriptors)
{
// Arrange
var resolver = new TestTagHelperDescriptorResolver();
var tagHelperDirectiveSpanVisitor = new TagHelperDirectiveSpanVisitor(resolver, new ErrorSink());
// Act
tagHelperDirectiveSpanVisitor.GetDescriptors(document);
// Assert
Assert.Equal(
expectedDescriptors,
resolver.DirectiveDescriptors,
TagHelperDirectiveDescriptorComparer.Default);
}
[Fact] [Fact]
public void GetDescriptors_InvokesResolveOnceForAllDirectives() public void GetDescriptors_InvokesResolveOnceForAllDirectives()
{ {