Add line mappings to project TagHelper attribute values.
- We now create LineMappings for instances where a TagHelper's attribute value is not of a string type. - This will enable the Razor editor to create projections from .cshtml => .cs for TagHelper attributes. - Modified the TagHelperCodeGenerator and TagHelperBlockBuiler to accurately track the attribute values start locations so it could flow into code generation. - Modified existing tests to account for the new line mappings. #207
This commit is contained in:
parent
ceaf257cd5
commit
a86b0dca3e
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNet.Razor.TagHelpers;
|
using Microsoft.AspNet.Razor.TagHelpers;
|
||||||
|
using Microsoft.AspNet.Razor.Text;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
|
namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
|
||||||
{
|
{
|
||||||
|
|
@ -265,7 +266,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
|
||||||
|
|
||||||
// We aren't a bufferable attribute which means we have no Razor code in our value.
|
// We aren't a bufferable attribute which means we have no Razor code in our value.
|
||||||
// Therefore we can just use the "textValue" as the attribute value.
|
// Therefore we can just use the "textValue" as the attribute value.
|
||||||
RenderRawAttributeValue(textValue, attributeDescriptor);
|
RenderRawAttributeValue(textValue, attributeValueChunk.Start, attributeDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
// End the assignment to the attribute.
|
// End the assignment to the attribute.
|
||||||
|
|
@ -421,13 +422,25 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RenderRawAttributeValue(string value, TagHelperAttributeDescriptor attributeDescriptor)
|
private void RenderRawAttributeValue(string value,
|
||||||
|
SourceLocation documentLocation,
|
||||||
|
TagHelperAttributeDescriptor attributeDescriptor)
|
||||||
{
|
{
|
||||||
RenderAttributeValue(
|
RenderAttributeValue(
|
||||||
attributeDescriptor,
|
attributeDescriptor,
|
||||||
valueRenderer: (writer) =>
|
valueRenderer: (writer) =>
|
||||||
{
|
{
|
||||||
writer.Write(value);
|
if (_context.Host.DesignTimeMode)
|
||||||
|
{
|
||||||
|
using (new CSharpLineMappingWriter(writer, documentLocation, value.Length))
|
||||||
|
{
|
||||||
|
writer.Write(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.Write(value);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,12 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Microsoft.AspNet.Razor.Generator.Compiler;
|
using Microsoft.AspNet.Razor.Generator.Compiler;
|
||||||
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
||||||
using Microsoft.AspNet.Razor.Parser.TagHelpers;
|
using Microsoft.AspNet.Razor.Parser.TagHelpers;
|
||||||
using Microsoft.AspNet.Razor.TagHelpers;
|
using Microsoft.AspNet.Razor.TagHelpers;
|
||||||
|
using Microsoft.AspNet.Razor.Text;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Razor.Generator
|
namespace Microsoft.AspNet.Razor.Generator
|
||||||
{
|
{
|
||||||
|
|
@ -60,10 +62,12 @@ namespace Microsoft.AspNet.Razor.Generator
|
||||||
attribute.Value.Accept(codeGenerator);
|
attribute.Value.Accept(codeGenerator);
|
||||||
|
|
||||||
var chunks = codeGenerator.Context.CodeTreeBuilder.CodeTree.Chunks;
|
var chunks = codeGenerator.Context.CodeTreeBuilder.CodeTree.Chunks;
|
||||||
|
var first = chunks.FirstOrDefault();
|
||||||
|
|
||||||
attributes[attribute.Key] = new ChunkBlock
|
attributes[attribute.Key] = new ChunkBlock
|
||||||
{
|
{
|
||||||
Children = chunks
|
Children = chunks,
|
||||||
|
Start = first == null ? SourceLocation.Zero : first.Start
|
||||||
};
|
};
|
||||||
|
|
||||||
// Reset the code tree builder so we can build a new one for the next attribute
|
// Reset the code tree builder so we can build a new one for the next attribute
|
||||||
|
|
|
||||||
|
|
@ -166,6 +166,8 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers
|
||||||
Kind = span.Kind
|
Kind = span.Kind
|
||||||
};
|
};
|
||||||
var htmlSymbols = span.Symbols.OfType<HtmlSymbol>().ToArray();
|
var htmlSymbols = span.Symbols.OfType<HtmlSymbol>().ToArray();
|
||||||
|
var capturedAttributeValueStart = false;
|
||||||
|
var attributeValueStartLocation = span.Start;
|
||||||
var symbolOffset = 1;
|
var symbolOffset = 1;
|
||||||
string name = null;
|
string name = null;
|
||||||
|
|
||||||
|
|
@ -175,13 +177,24 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers
|
||||||
{
|
{
|
||||||
var symbol = htmlSymbols[i];
|
var symbol = htmlSymbols[i];
|
||||||
|
|
||||||
if (name == null && symbol.Type == HtmlSymbolType.Text)
|
if (afterEquals)
|
||||||
|
{
|
||||||
|
// When symbols are accepted into SpanBuilders, their locations get altered to be offset by the
|
||||||
|
// parent which is why we need to mark our start location prior to adding the symbol.
|
||||||
|
// This is needed to know the location of the attribute value start within the document.
|
||||||
|
if (!capturedAttributeValueStart)
|
||||||
|
{
|
||||||
|
capturedAttributeValueStart = true;
|
||||||
|
|
||||||
|
attributeValueStartLocation = span.Start + symbol.Start;
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Accept(symbol);
|
||||||
|
}
|
||||||
|
else if (name == null && symbol.Type == HtmlSymbolType.Text)
|
||||||
{
|
{
|
||||||
name = symbol.Content;
|
name = symbol.Content;
|
||||||
}
|
attributeValueStartLocation = SourceLocation.Advance(span.Start, name);
|
||||||
else if (afterEquals)
|
|
||||||
{
|
|
||||||
builder.Accept(symbol);
|
|
||||||
}
|
}
|
||||||
else if (symbol.Type == HtmlSymbolType.Equals)
|
else if (symbol.Type == HtmlSymbolType.Equals)
|
||||||
{
|
{
|
||||||
|
|
@ -193,22 +206,36 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers
|
||||||
// TODO: Handle malformed tags, if there's an '=' then there MUST be a value.
|
// TODO: Handle malformed tags, if there's an '=' then there MUST be a value.
|
||||||
// https://github.com/aspnet/Razor/issues/104
|
// https://github.com/aspnet/Razor/issues/104
|
||||||
|
|
||||||
|
SourceLocation symbolStartLocation;
|
||||||
|
|
||||||
// Check for attribute start values, aka single or double quote
|
// Check for attribute start values, aka single or double quote
|
||||||
if (IsQuote(htmlSymbols[i + 1]))
|
if (IsQuote(htmlSymbols[i + 1]))
|
||||||
{
|
{
|
||||||
// Move past the attribute start so we can accept the true value.
|
// Move past the attribute start so we can accept the true value.
|
||||||
i++;
|
i++;
|
||||||
|
symbolStartLocation = htmlSymbols[i + 1].Start;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
symbolStartLocation = symbol.Start;
|
||||||
|
|
||||||
// Set the symbol offset to 0 so we don't attempt to skip an end quote that doesn't exist.
|
// Set the symbol offset to 0 so we don't attempt to skip an end quote that doesn't exist.
|
||||||
symbolOffset = 0;
|
symbolOffset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
attributeValueStartLocation = symbolStartLocation +
|
||||||
|
span.Start +
|
||||||
|
new SourceLocation(absoluteIndex: 1,
|
||||||
|
lineIndex: 0,
|
||||||
|
characterIndex: 1);
|
||||||
afterEquals = true;
|
afterEquals = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// After all symbols have been added we need to set the builders start position so we do not indirectly
|
||||||
|
// modify each symbol's Start location.
|
||||||
|
builder.Start = attributeValueStartLocation;
|
||||||
|
|
||||||
return CreateMarkupAttribute(name, builder, attributeValueTypes);
|
return CreateMarkupAttribute(name, builder, attributeValueTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -174,7 +174,14 @@ namespace Microsoft.AspNet.Razor.Test.Generator
|
||||||
generatedAbsoluteIndex: 475,
|
generatedAbsoluteIndex: 475,
|
||||||
generatedLineIndex: 15,
|
generatedLineIndex: 15,
|
||||||
characterOffsetIndex: 14,
|
characterOffsetIndex: 14,
|
||||||
contentLength: 11)
|
contentLength: 11),
|
||||||
|
BuildLineMapping(documentAbsoluteIndex: 57,
|
||||||
|
documentLineIndex: 2,
|
||||||
|
documentCharacterOffsetIndex: 28,
|
||||||
|
generatedAbsoluteIndex: 927,
|
||||||
|
generatedLineIndex: 33,
|
||||||
|
generatedCharacterOffsetIndex: 31,
|
||||||
|
contentLength: 4)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -188,7 +195,14 @@ namespace Microsoft.AspNet.Razor.Test.Generator
|
||||||
generatedAbsoluteIndex: 475,
|
generatedAbsoluteIndex: 475,
|
||||||
generatedLineIndex: 15,
|
generatedLineIndex: 15,
|
||||||
characterOffsetIndex: 14,
|
characterOffsetIndex: 14,
|
||||||
contentLength: 11)
|
contentLength: 11),
|
||||||
|
BuildLineMapping(documentAbsoluteIndex: 189,
|
||||||
|
documentLineIndex: 6,
|
||||||
|
documentCharacterOffsetIndex: 40,
|
||||||
|
generatedAbsoluteIndex: 1599,
|
||||||
|
generatedLineIndex: 44,
|
||||||
|
generatedCharacterOffsetIndex: 40,
|
||||||
|
contentLength: 4)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -218,6 +232,7 @@ namespace Microsoft.AspNet.Razor.Test.Generator
|
||||||
BuildLineMapping(218, 9, 13, 1356, 56, 12, 27),
|
BuildLineMapping(218, 9, 13, 1356, 56, 12, 27),
|
||||||
BuildLineMapping(346, 12, 1754, 68, 0, 48),
|
BuildLineMapping(346, 12, 1754, 68, 0, 48),
|
||||||
BuildLineMapping(440, 15, 46, 2004, 78, 6, 8),
|
BuildLineMapping(440, 15, 46, 2004, 78, 6, 8),
|
||||||
|
BuildLineMapping(457, 15, 63, 2267, 85, 40, 4),
|
||||||
BuildLineMapping(501, 16, 31, 2384, 88, 6, 30),
|
BuildLineMapping(501, 16, 31, 2384, 88, 6, 30),
|
||||||
BuildLineMapping(568, 17, 30, 2733, 97, 0, 10),
|
BuildLineMapping(568, 17, 30, 2733, 97, 0, 10),
|
||||||
BuildLineMapping(601, 17, 63, 2815, 103, 0, 8),
|
BuildLineMapping(601, 17, 63, 2815, 103, 0, 8),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue