diff --git a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlock.cs b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlock.cs index 01ce3eda60..e483e7b454 100644 --- a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlock.cs +++ b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlock.cs @@ -32,6 +32,8 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers Attributes = new Dictionary(source.Attributes); _start = source.Start; SelfClosing = source.SelfClosing; + SourceStartTag = source.SourceStartTag; + SourceEndTag = source.SourceEndTag; source.Reset(); @@ -41,6 +43,18 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers } } + /// + /// Gets the unrewritten source start tag. + /// + /// This is used by design time to properly format s. + public Block SourceStartTag { get; } + + /// + /// Gets the unrewritten source end tag. + /// + /// This is used by design time to properly format s. + public Block SourceEndTag { get; } + /// /// Indicates whether or not the tag is self closing. /// @@ -70,6 +84,18 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers /// public string TagName { get; private set; } + public override int Length + { + get + { + var startTagLength = SourceStartTag?.Length ?? 0; + var childrenLength = base.Length; + var endTagLength = SourceEndTag?.Length ?? 0; + + return startTagLength + childrenLength + endTagLength; + } + } + /// public override string ToString() { @@ -99,11 +125,12 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers /// public override int GetHashCode() { - return HashCodeCombiner.Start() - .Add(TagName) - .Add(Attributes) - .Add(base.GetHashCode()) - .CombinedHash; + return HashCodeCombiner + .Start() + .Add(TagName) + .Add(Attributes) + .Add(base.GetHashCode()) + .CombinedHash; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlockBuilder.cs b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlockBuilder.cs index 5854125e45..ca8d064951 100644 --- a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlockBuilder.cs +++ b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlockBuilder.cs @@ -74,6 +74,18 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers } } + /// + /// Gets or sets the unrewritten source start tag. + /// + /// This is used by design time to properly format s. + public Block SourceStartTag { get; set; } + + /// + /// Gets or sets the unrewritten source end tag. + /// + /// This is used by design time to properly format s. + public Block SourceEndTag { get; set; } + /// /// Gets a value indicating whether or not the tag in the Razor source was self-closing. /// diff --git a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlockRewriter.cs b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlockRewriter.cs index 5f04ad04f1..c0696be646 100644 --- a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlockRewriter.cs +++ b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlockRewriter.cs @@ -17,11 +17,12 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal { private static readonly string StringTypeName = typeof(string).FullName; - public static TagHelperBlockBuilder Rewrite(string tagName, - bool validStructure, - Block tag, - IEnumerable descriptors, - ParserErrorSink errorSink) + public static TagHelperBlockBuilder Rewrite( + string tagName, + bool validStructure, + Block tag, + IEnumerable descriptors, + ParserErrorSink errorSink) { // There will always be at least one child for the '<'. var start = tag.Children.First().Start; @@ -74,8 +75,8 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal // Check if it's a bound attribute that is not of type string and happens to be null or whitespace. string attributeValueType; if (attributeValueTypes.TryGetValue(attribute.Key, out attributeValueType) && - !IsStringAttribute(attributeValueType) && - IsNullOrWhitespaceAttributeValue(attribute.Value)) + !IsStringAttribute(attributeValueType) && + IsNullOrWhitespaceAttributeValue(attribute.Value)) { var errorLocation = GetAttributeNameStartLocation(child); diff --git a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperParseTreeRewriter.cs b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperParseTreeRewriter.cs index 2be211dbe7..26ded3556f 100644 --- a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperParseTreeRewriter.cs +++ b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperParseTreeRewriter.cs @@ -121,11 +121,16 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal var validTagStructure = ValidTagStructure(tagName, tagBlock, context); - var builder = TagHelperBlockRewriter.Rewrite(tagName, - validTagStructure, - tagBlock, - descriptors, - context.ErrorSink); + var builder = TagHelperBlockRewriter.Rewrite( + tagName, + validTagStructure, + tagBlock, + descriptors, + context.ErrorSink); + + // Track the original start tag so the editor knows where each piece of the TagHelperBlock lies + // for formatting. + builder.SourceStartTag = tagBlock; // Found a new tag helper block TrackTagHelperBlock(builder); @@ -134,7 +139,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal // within the tag... complete it. if (builder.SelfClosing) { - BuildCurrentlyTrackedTagHelperBlock(); + BuildCurrentlyTrackedTagHelperBlock(endTag: null); } } else @@ -149,14 +154,14 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal { ValidTagStructure(tagName, tagBlock, context); - BuildCurrentlyTrackedTagHelperBlock(); + BuildCurrentlyTrackedTagHelperBlock(tagBlock); } else { // Current tag helper scope does not match the end tag. Attempt to recover the tag // helper by looking up the previous tag helper scopes for a matching tag. If we // can't recover it means there was no corresponding tag helper begin tag. - if (TryRecoverTagHelper(tagName, context)) + if (TryRecoverTagHelper(tagName, tagBlock, context)) { ValidTagStructure(tagName, tagBlock, context); @@ -230,9 +235,11 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal } } - private void BuildCurrentlyTrackedTagHelperBlock() + private void BuildCurrentlyTrackedTagHelperBlock(Block endTag) { - _tagStack.Pop(); + // Track the original end tag so the editor knows where each piece of the TagHelperBlock lies + // for formatting. + _tagStack.Pop().SourceEndTag = endTag; BuildCurrentlyTrackedBlock(); } @@ -268,7 +275,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal TrackBlock(builder); } - private bool TryRecoverTagHelper(string tagName, RewritingContext context) + private bool TryRecoverTagHelper(string tagName, Block endTag, RewritingContext context) { var malformedTagHelperCount = 0; @@ -289,7 +296,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal BuildMalformedTagHelpers(malformedTagHelperCount, context); // One final build, this is the build that completes our target tag helper block which is not malformed. - BuildCurrentlyTrackedTagHelperBlock(); + BuildCurrentlyTrackedTagHelperBlock(endTag); // We were able to recover return true; @@ -310,7 +317,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal RazorResources.FormatTagHelpersParseTreeRewriter_FoundMalformedTagHelper( malformedTagHelper.TagName)); - BuildCurrentlyTrackedTagHelperBlock(); + BuildCurrentlyTrackedTagHelperBlock(endTag: null); } }