-
Notifications
You must be signed in to change notification settings - Fork 222
Parse the whitespace surrounding equals in attribute correctly #503
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -463,6 +463,7 @@ private void BeforeAttribute() | |
// http://dev.w3.org/html5/spec/tokenization.html#attribute-name-state | ||
// Read the 'name' (i.e. read until the '=' or whitespace/newline) | ||
var name = Enumerable.Empty<HtmlSymbol>(); | ||
var whitespaceAfterAttributeName = Enumerable.Empty<HtmlSymbol>(); | ||
if (At(HtmlSymbolType.Text)) | ||
{ | ||
name = ReadWhile(sym => | ||
|
@@ -472,6 +473,10 @@ private void BeforeAttribute() | |
sym.Type != HtmlSymbolType.CloseAngle && | ||
sym.Type != HtmlSymbolType.OpenAngle && | ||
(sym.Type != HtmlSymbolType.ForwardSlash || !NextIs(HtmlSymbolType.CloseAngle))); | ||
|
||
// capture whitespace after attribute name (if any) | ||
whitespaceAfterAttributeName = ReadWhile( | ||
sym => sym.Type == HtmlSymbolType.WhiteSpace || sym.Type == HtmlSymbolType.NewLine); | ||
} | ||
else | ||
{ | ||
|
@@ -485,6 +490,10 @@ private void BeforeAttribute() | |
{ | ||
// Minimized attribute | ||
|
||
// We are at the prefix of the next attribute or the end of tag. Put it back so it is parsed later. | ||
PutCurrentBack(); | ||
PutBack(whitespaceAfterAttributeName); | ||
|
||
// Output anything prior to the attribute, in most cases this will be the tag name: | ||
// |<input| checked />. If in-between other attributes this will noop or output malformed attribute | ||
// content (if the previous attribute was malformed). | ||
|
@@ -507,11 +516,14 @@ private void BeforeAttribute() | |
// Start a new markup block for the attribute | ||
using (Context.StartBlock(BlockType.Markup)) | ||
{ | ||
AttributePrefix(whitespace, name); | ||
AttributePrefix(whitespace, name, whitespaceAfterAttributeName); | ||
} | ||
} | ||
|
||
private void AttributePrefix(IEnumerable<HtmlSymbol> whitespace, IEnumerable<HtmlSymbol> nameSymbols) | ||
private void AttributePrefix( | ||
IEnumerable<HtmlSymbol> whitespace, | ||
IEnumerable<HtmlSymbol> nameSymbols, | ||
IEnumerable<HtmlSymbol> whitespaceAfterAttributeName) | ||
{ | ||
// First, determine if this is a 'data-' attribute (since those can't use conditional attributes) | ||
var name = nameSymbols.GetContent(Span.Start); | ||
|
@@ -520,14 +532,27 @@ private void AttributePrefix(IEnumerable<HtmlSymbol> whitespace, IEnumerable<Htm | |
// Accept the whitespace and name | ||
Accept(whitespace); | ||
Accept(nameSymbols); | ||
|
||
// Since this is not a minimized attribute, the whitespace after attribute name belongs to this attribute. | ||
Accept(whitespaceAfterAttributeName); | ||
Assert(HtmlSymbolType.Equals); // We should be at "=" | ||
AcceptAndMoveNext(); | ||
|
||
var whitespaceAfterEquals = ReadWhile(sym => sym.Type == HtmlSymbolType.WhiteSpace || sym.Type == HtmlSymbolType.NewLine); | ||
var quote = HtmlSymbolType.Unknown; | ||
if (At(HtmlSymbolType.SingleQuote) || At(HtmlSymbolType.DoubleQuote)) | ||
{ | ||
// Found a quote, the whitespace belongs to this attribute. | ||
Accept(whitespaceAfterEquals); | ||
quote = CurrentSymbol.Type; | ||
AcceptAndMoveNext(); | ||
} | ||
else if (whitespaceAfterEquals.Any()) | ||
{ | ||
// No quotes found after the whitespace. Put it back so that it can be parsed later. | ||
PutCurrentBack(); | ||
PutBack(whitespaceAfterEquals); | ||
} | ||
|
||
// We now have the prefix: (i.e. ' foo="') | ||
var prefix = Span.GetContent(); | ||
|
@@ -537,10 +562,15 @@ private void AttributePrefix(IEnumerable<HtmlSymbol> whitespace, IEnumerable<Htm | |
Span.ChunkGenerator = SpanChunkGenerator.Null; // The block chunk generator will render the prefix | ||
Output(SpanKind.Markup); | ||
|
||
// Read the values | ||
while (!EndOfFile && !IsEndOfAttributeValue(quote, CurrentSymbol)) | ||
// Read the attribute value only if the value is quoted | ||
// or if there is no whitespace between '=' and the unquoted value. | ||
if (quote != HtmlSymbolType.Unknown || !whitespaceAfterEquals.Any()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens now with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This condition will return There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
{ | ||
AttributeValue(quote); | ||
// Read the attribute value. | ||
while (!EndOfFile && !IsEndOfAttributeValue(quote, CurrentSymbol)) | ||
{ | ||
AttributeValue(quote); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Values" for a single attribute i.e. name / value pair? Do you mean "symbols in the value"? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Single attribute value. This will run for every part of the attribute value. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please update the comment so that it is clear. Wasn't sure what you meant. |
||
} | ||
|
||
// Capture the suffix | ||
|
@@ -567,6 +597,11 @@ private void AttributePrefix(IEnumerable<HtmlSymbol> whitespace, IEnumerable<Htm | |
// Output the attribute name, the equals and optional quote. Ex: foo=" | ||
Output(SpanKind.Markup); | ||
|
||
if (quote == HtmlSymbolType.Unknown && whitespaceAfterEquals.Any()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What text input makes this evaluate to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Got it. @dougbu you may be misreading the diff. This is the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @NTaylorMullen yup, thought I expanded everything in between but noooo... |
||
{ | ||
return; | ||
} | ||
|
||
// Not a "conditional" attribute, so just read the value | ||
SkipToAndParseCode(sym => IsEndOfAttributeValue(quote, sym)); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -848,32 +848,36 @@ public static TheoryData DesignTimeTagHelperTestData | |
DefaultPAndInputTagHelperDescriptors, | ||
new List<LineMapping> | ||
{ | ||
BuildLineMapping(documentAbsoluteIndex: 14, | ||
documentLineIndex: 0, | ||
generatedAbsoluteIndex: 493, | ||
generatedLineIndex: 15, | ||
characterOffsetIndex: 14, | ||
contentLength: 11), | ||
BuildLineMapping(documentAbsoluteIndex: 62, | ||
documentLineIndex: 3, | ||
documentCharacterOffsetIndex: 26, | ||
generatedAbsoluteIndex: 1289, | ||
generatedLineIndex: 39, | ||
generatedCharacterOffsetIndex: 28, | ||
contentLength: 0), | ||
BuildLineMapping(documentAbsoluteIndex: 122, | ||
documentLineIndex: 5, | ||
generatedAbsoluteIndex: 1634, | ||
generatedLineIndex: 48, | ||
characterOffsetIndex: 30, | ||
contentLength: 0), | ||
BuildLineMapping(documentAbsoluteIndex: 88, | ||
documentLineIndex: 4, | ||
documentCharacterOffsetIndex: 12, | ||
generatedAbsoluteIndex: 1789, | ||
generatedLineIndex: 54, | ||
generatedCharacterOffsetIndex: 19, | ||
contentLength: 0) | ||
BuildLineMapping( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did these change at all or did you just format it? Want to make sure I'm not missing something. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just formatting. |
||
documentAbsoluteIndex: 14, | ||
documentLineIndex: 0, | ||
generatedAbsoluteIndex: 493, | ||
generatedLineIndex: 15, | ||
characterOffsetIndex: 14, | ||
contentLength: 11), | ||
BuildLineMapping( | ||
documentAbsoluteIndex: 63, | ||
documentLineIndex: 3, | ||
documentCharacterOffsetIndex: 27, | ||
generatedAbsoluteIndex: 1289, | ||
generatedLineIndex: 39, | ||
generatedCharacterOffsetIndex: 28, | ||
contentLength: 0), | ||
BuildLineMapping( | ||
documentAbsoluteIndex: 122, | ||
documentLineIndex: 5, | ||
generatedAbsoluteIndex: 1634, | ||
generatedLineIndex: 48, | ||
characterOffsetIndex: 30, | ||
contentLength: 0), | ||
BuildLineMapping( | ||
documentAbsoluteIndex: 89, | ||
documentLineIndex: 4, | ||
documentCharacterOffsetIndex: 13, | ||
generatedAbsoluteIndex: 1789, | ||
generatedLineIndex: 54, | ||
generatedCharacterOffsetIndex: 19, | ||
contentLength: 0), | ||
} | ||
}, | ||
{ | ||
|
@@ -1484,6 +1488,7 @@ public static TheoryData RuntimeTimeTagHelperTestData | |
{ | ||
{ "SingleTagHelper", null, DefaultPAndInputTagHelperDescriptors }, | ||
{ "SingleTagHelperWithNewlineBeforeAttributes", null, DefaultPAndInputTagHelperDescriptors }, | ||
{ "TagHelpersWithWeirdlySpacedAttributes", null, DefaultPAndInputTagHelperDescriptors }, | ||
{ "BasicTagHelpers", null, DefaultPAndInputTagHelperDescriptors }, | ||
{ "BasicTagHelpers.RemoveTagHelper", null, DefaultPAndInputTagHelperDescriptors }, | ||
{ "BasicTagHelpers.Prefixed", null, PrefixedPAndInputTagHelperDescriptors }, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Separate this comment from preceding lines with a blank line.