Skip to content
This repository has been archived by the owner on Dec 19, 2018. It is now read-only.

Commit

Permalink
Allow optional quotes around tag helper directives
Browse files Browse the repository at this point in the history
Fixes #636
  • Loading branch information
pranavkm committed Dec 29, 2015
1 parent 77b74b0 commit 3c00a3a
Show file tree
Hide file tree
Showing 21 changed files with 399 additions and 108 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ nuget.exe
*.*sdf
*.ipch
project.lock.json
.vs
.vs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,19 @@ namespace Microsoft.AspNet.Razor.Chunks.Generators
/// </summary>
public class AddTagHelperChunkGenerator : SpanChunkGenerator
{
private readonly string _lookupText;

/// <summary>
/// Initializes a new instance of <see cref="AddTagHelperChunkGenerator"/>.
/// </summary>
/// <param name="lookupText">
/// Text used to look up <see cref="Compilation.TagHelpers.TagHelperDescriptor"/>s that should be added.
/// </param>
public AddTagHelperChunkGenerator(string lookupText)
{
_lookupText = lookupText;
}

/// <summary>
/// Generates <see cref="AddTagHelperChunk"/>s.
/// </summary>
Expand All @@ -20,9 +33,7 @@ public class AddTagHelperChunkGenerator : SpanChunkGenerator
/// the current chunk generation process.</param>
public override void GenerateChunk(Span target, ChunkGeneratorContext context)
{
var lookupText = target.Content.Trim();

context.ChunkTreeBuilder.AddAddTagHelperChunk(lookupText, target);
context.ChunkTreeBuilder.AddAddTagHelperChunk(_lookupText, target);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,19 @@ namespace Microsoft.AspNet.Razor.Chunks.Generators
/// </summary>
public class RemoveTagHelperChunkGenerator : SpanChunkGenerator
{
private readonly string _lookupText;

/// <summary>
/// Initializes a new instance of <see cref="RemoveTagHelperChunkGenerator"/>.
/// </summary>
/// <param name="lookupText">
/// Text used to look up <see cref="Compilation.TagHelpers.TagHelperDescriptor"/>s that should be removed.
/// </param>
public RemoveTagHelperChunkGenerator(string lookupText)
{
_lookupText = lookupText;
}

/// <summary>
/// Generates <see cref="RemoveTagHelperChunk"/>s.
/// </summary>
Expand All @@ -20,9 +33,7 @@ public class RemoveTagHelperChunkGenerator : SpanChunkGenerator
/// the current chunk generation process.</param>
public override void GenerateChunk(Span target, ChunkGeneratorContext context)
{
var lookupText = target.Content.Trim();

context.ChunkTreeBuilder.AddRemoveTagHelperChunk(lookupText, target);
context.ChunkTreeBuilder.AddRemoveTagHelperChunk(_lookupText, target);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@ namespace Microsoft.AspNet.Razor.Chunks.Generators
/// </summary>
public class TagHelperPrefixDirectiveChunkGenerator : SpanChunkGenerator
{
private readonly string _prefix;

/// <summary>
/// Initializes a new instance of <see cref="TagHelperPrefixDirectiveChunkGenerator"/>.
/// </summary>
/// <param name="prefix">
/// Text used as a required prefix when matching HTML.
/// </param>
public TagHelperPrefixDirectiveChunkGenerator(string prefix)
{
_prefix = prefix;
}

/// <summary>
/// Generates <see cref="TagHelperPrefixDirectiveChunk"/>s.
/// </summary>
Expand All @@ -21,9 +34,7 @@ public class TagHelperPrefixDirectiveChunkGenerator : SpanChunkGenerator
/// the current chunk generation process.</param>
public override void GenerateChunk(Span target, ChunkGeneratorContext context)
{
var prefix = target.Content.Trim();

context.ChunkTreeBuilder.AddTagHelperPrefixDirectiveChunk(prefix, target);
context.ChunkTreeBuilder.AddTagHelperPrefixDirectiveChunk(_prefix, target);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Diagnostics;
using System.Globalization;
using Microsoft.AspNet.Razor.Chunks;
using Microsoft.AspNet.Razor.Parser.SyntaxTree;

namespace Microsoft.AspNet.Razor.CodeGenerators.Visitors
{
Expand Down Expand Up @@ -74,20 +75,20 @@ protected override void Visit(SetBaseTypeChunk chunk)

protected override void Visit(TagHelperPrefixDirectiveChunk chunk)
{
VisitTagHelperDirectiveChunk(chunk.Prefix, chunk);
VisitTagHelperDirectiveChunk(chunk);
}

protected override void Visit(AddTagHelperChunk chunk)
{
VisitTagHelperDirectiveChunk(chunk.LookupText, chunk);
VisitTagHelperDirectiveChunk(chunk);
}

protected override void Visit(RemoveTagHelperChunk chunk)
{
VisitTagHelperDirectiveChunk(chunk.LookupText, chunk);
VisitTagHelperDirectiveChunk(chunk);
}

private void VisitTagHelperDirectiveChunk(string text, Chunk chunk)
private void VisitTagHelperDirectiveChunk(Chunk chunk)
{
// We should always be in design time mode because of the calling AcceptTree method verification.
Debug.Assert(Context.Host.DesignTimeMode);
Expand All @@ -98,16 +99,26 @@ private void VisitTagHelperDirectiveChunk(string text, Chunk chunk)
Writer.WriteVariableDeclaration("string", TagHelperDirectiveSyntaxHelper, "null");
}

Writer
.WriteStartAssignment(TagHelperDirectiveSyntaxHelper)
.Write("\"");
var text = ((Span)chunk.Association).Content.Trim();

Writer.WriteStartAssignment(TagHelperDirectiveSyntaxHelper);

if (!text.StartsWith("\"", StringComparison.Ordinal))
{
Writer.Write("\"");
}

using (new CSharpLineMappingWriter(Writer, chunk.Start, text.Length))
{
Writer.Write(text);
}

Writer.WriteLine("\";");
if (!text.EndsWith("\"", StringComparison.Ordinal))
{
Writer.Write("\"");
}

Writer.WriteLine(";");
}
}
}
43 changes: 39 additions & 4 deletions src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.Directives.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,21 @@ protected virtual void TagHelperPrefixDirective()
{
TagHelperDirective(
SyntaxConstants.CSharp.TagHelperPrefixKeyword,
new TagHelperPrefixDirectiveChunkGenerator());
prefix => new TagHelperPrefixDirectiveChunkGenerator(prefix));
}

protected virtual void AddTagHelperDirective()
{
TagHelperDirective(SyntaxConstants.CSharp.AddTagHelperKeyword, new AddTagHelperChunkGenerator());
TagHelperDirective(
SyntaxConstants.CSharp.AddTagHelperKeyword,
lookupText => new AddTagHelperChunkGenerator(lookupText));
}

protected virtual void RemoveTagHelperDirective()
{
TagHelperDirective(SyntaxConstants.CSharp.RemoveTagHelperKeyword, new RemoveTagHelperChunkGenerator());
TagHelperDirective(
SyntaxConstants.CSharp.RemoveTagHelperKeyword,
lookupText => new RemoveTagHelperChunkGenerator(lookupText));
}

protected virtual void SectionDirective()
Expand Down Expand Up @@ -285,7 +289,7 @@ protected void BaseTypeDirective(string noTypeNameError, Func<string, SpanChunkG
Output(SpanKind.Code, AcceptedCharacters.AnyExceptNewline);
}

private void TagHelperDirective(string keyword, ISpanChunkGenerator chunkGenerator)
private void TagHelperDirective(string keyword, Func<string, ISpanChunkGenerator> chunkGeneratorFactory)
{
AssertDirective(keyword);
var keywordStartLocation = CurrentLocation;
Expand All @@ -305,12 +309,15 @@ private void TagHelperDirective(string keyword, ISpanChunkGenerator chunkGenerat
// to the document. We can't accept it.
Output(SpanKind.MetaCode, foundWhitespace ? AcceptedCharacters.None : AcceptedCharacters.AnyExceptNewline);

ISpanChunkGenerator chunkGenerator;
if (EndOfFile || At(CSharpSymbolType.NewLine))
{
Context.OnError(
keywordStartLocation,
RazorResources.FormatParseError_DirectiveMustHaveValue(keyword),
keywordLength);

chunkGenerator = chunkGeneratorFactory(string.Empty);
}
else
{
Expand All @@ -320,6 +327,34 @@ private void TagHelperDirective(string keyword, ISpanChunkGenerator chunkGenerat
// Parse to the end of the line. Essentially accepts anything until end of line, comments, invalid code
// etc.
AcceptUntil(CSharpSymbolType.NewLine);

// Pull out the value and remove whitespaces and optional quotes
var rawValue = Span.GetContent().Value.Trim();

var startsWithQuote = rawValue.StartsWith("\"", StringComparison.Ordinal);
var endsWithQuote = rawValue.EndsWith("\"", StringComparison.Ordinal);
if (startsWithQuote != endsWithQuote)
{
Context.OnError(
startLocation,
RazorResources.FormatParseError_IncompleteQuotesAroundDirective(keyword),
rawValue.Length);
}
else if (startsWithQuote)
{
if (rawValue.Length > 2)
{
// Remove extra quotes
rawValue = rawValue.Substring(1, rawValue.Length - 2);
}
else
{
// raw value is only quotes
rawValue = string.Empty;
}
}

chunkGenerator = chunkGeneratorFactory(rawValue);
}

Span.ChunkGenerator = chunkGenerator;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNet.Razor.Chunks.Generators;
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
using Microsoft.AspNet.Razor.Compilation.TagHelpers;
using System.Diagnostics;
using Microsoft.AspNet.Razor.Parser.SyntaxTree;

namespace Microsoft.AspNet.Razor.Parser.TagHelpers
{
Expand Down
16 changes: 16 additions & 0 deletions src/Microsoft.AspNet.Razor/Properties/RazorResources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/Microsoft.AspNet.Razor/RazorResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -425,4 +425,7 @@ Instead, wrap the contents of the block in "{{}}":
<data name="ParseError_HelperDirectiveNotAvailable" xml:space="preserve">
<value>The {0} directive is not supported.</value>
</data>
<data name="ParseError_IncompleteQuotesAroundDirective" xml:space="preserve">
<value>Optional quote around the directive '{0}' is missing the corresponding opening or closing quote.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -404,11 +404,12 @@ public static TheoryData TagHelperDescriptorFlowTestData
{
get
{
return new TheoryData<string, // Test name
string, // Baseline name
IEnumerable<TagHelperDescriptor>, // TagHelperDescriptors provided
IEnumerable<TagHelperDescriptor>, // Expected TagHelperDescriptors
bool> // Design time mode.
return new TheoryData<
string, // Test name
string, // Baseline name
IEnumerable<TagHelperDescriptor>, // TagHelperDescriptors provided
IEnumerable<TagHelperDescriptor>, // Expected TagHelperDescriptors
bool> // Design time mode.
{
{
"SingleTagHelper",
Expand Down Expand Up @@ -527,9 +528,10 @@ public void TagHelpers_RenderingOutputFlowsFoundTagHelperDescriptors(
tagHelperDescriptors: tagHelperDescriptors,
onResults: (results) =>
{
Assert.Equal(expectedTagHelperDescriptors,
results.TagHelperDescriptors,
TagHelperDescriptorComparer.Default);
Assert.Equal(
expectedTagHelperDescriptors,
results.TagHelperDescriptors,
TagHelperDescriptorComparer.Default);
},
designTimeMode: designTimeMode);
}
Expand Down Expand Up @@ -569,25 +571,25 @@ public static TheoryData DesignTimeTagHelperTestData
"BasicTagHelpers",
"BasicTagHelpers.DesignTime",
DefaultPAndInputTagHelperDescriptors,
new List<LineMapping>
new[]
{
BuildLineMapping(
documentAbsoluteIndex: 14,
documentLineIndex: 0,
documentCharacterOffsetIndex: 14,
generatedAbsoluteIndex: 372,
generatedAbsoluteIndex: 371,
generatedLineIndex: 12,
generatedCharacterOffsetIndex: 48,
contentLength: 15),
generatedCharacterOffsetIndex: 47,
contentLength: 17),
BuildLineMapping(
documentAbsoluteIndex: 200,
documentAbsoluteIndex: 202,
documentLineIndex: 5,
generatedAbsoluteIndex: 1293,
generatedLineIndex: 31,
characterOffsetIndex: 38,
contentLength: 23),
BuildLineMapping(
documentAbsoluteIndex: 283,
documentAbsoluteIndex: 285,
documentLineIndex: 6,
documentCharacterOffsetIndex: 40,
generatedAbsoluteIndex: 1934,
Expand All @@ -600,26 +602,26 @@ public static TheoryData DesignTimeTagHelperTestData
"BasicTagHelpers.Prefixed",
"BasicTagHelpers.Prefixed.DesignTime",
PrefixedPAndInputTagHelperDescriptors,
new List<LineMapping>
new[]
{
BuildLineMapping(
documentAbsoluteIndex: 17,
documentLineIndex: 0,
documentCharacterOffsetIndex: 17,
generatedAbsoluteIndex: 381,
generatedAbsoluteIndex: 380,
generatedLineIndex: 12,
generatedCharacterOffsetIndex: 48,
contentLength: 3),
generatedCharacterOffsetIndex: 47,
contentLength: 5),
BuildLineMapping(
documentAbsoluteIndex: 36,
documentAbsoluteIndex: 38,
documentLineIndex: 1,
documentCharacterOffsetIndex: 14,
generatedAbsoluteIndex: 436,
generatedLineIndex: 13,
generatedCharacterOffsetIndex: 48,
contentLength: 15),
BuildLineMapping(
documentAbsoluteIndex: 222,
documentAbsoluteIndex: 224,
documentLineIndex: 7,
generatedAbsoluteIndex: 1437,
generatedLineIndex: 33,
Expand Down
Loading

0 comments on commit 3c00a3a

Please sign in to comment.