Change TagHelpers to work in partial parsing.

- Prior to this change any query for ownership within a TagHelper would never succeed.
- TagHelpers structure can be influenced greatly by changes to the tags start/end body; therefore, only allowed modifications to TagHelper attribute values to avoid complexity and enable the more widespread scenario of a user typing C# code in an attribute value.
- Updated existing tests to reflect the new edit handlers that were added to TagHelper attributes.
- Added partial parsing tests to verify partial parses succeed/fail when expected.

#792
This commit is contained in:
N. Taylor Mullen 2016-08-12 15:34:52 -07:00
parent 82c9c40709
commit 8d7c51bd60
8 changed files with 416 additions and 97 deletions

View File

@ -154,11 +154,13 @@ namespace Microsoft.AspNetCore.Razor.Parser.SyntaxTree
}
}
public Span LocateOwner(TextChange change)
public virtual Span LocateOwner(TextChange change) => LocateOwner(change, Children);
protected static Span LocateOwner(TextChange change, IEnumerable<SyntaxTreeNode> elements)
{
// Ask each child recursively
Span owner = null;
foreach (SyntaxTreeNode element in Children)
foreach (var element in elements)
{
var span = element as Span;
if (span == null)

View File

@ -7,6 +7,7 @@ using System.Diagnostics;
using System.Linq;
using Microsoft.AspNetCore.Razor.Chunks.Generators;
using Microsoft.AspNetCore.Razor.Compilation.TagHelpers;
using Microsoft.AspNetCore.Razor.Editor;
using Microsoft.AspNetCore.Razor.Parser.Internal;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
using Microsoft.AspNetCore.Razor.TagHelpers;
@ -468,7 +469,8 @@ namespace Microsoft.AspNetCore.Razor.Parser.TagHelpers.Internal
spanBuilder.ChunkGenerator = new MarkupChunkGenerator();
}
spanBuilder.Kind = SpanKind.Code;
ConfigureNonStringAttribute(spanBuilder);
span = spanBuilder.Build();
}
}
@ -628,7 +630,7 @@ namespace Microsoft.AspNetCore.Razor.Parser.TagHelpers.Internal
// SyntaxTreeNode reflects that.
if (isBoundNonStringAttribute)
{
builder.Kind = SpanKind.Code;
ConfigureNonStringAttribute(builder);
}
return builder.Build();
@ -701,6 +703,18 @@ namespace Microsoft.AspNetCore.Razor.Parser.TagHelpers.Internal
htmlSymbol.Type == HtmlSymbolType.SingleQuote;
}
private static void ConfigureNonStringAttribute(SpanBuilder builder)
{
builder.Kind = SpanKind.Code;
builder.EditHandler = new ImplicitExpressionEditHandler(
builder.EditHandler.Tokenizer,
CSharpCodeParser.DefaultKeywords,
acceptTrailingDot: true)
{
AcceptedCharacters = AcceptedCharacters.AnyExceptNewline
};
}
private class TryParseResult
{
public string AttributeName { get; set; }

View File

@ -5,9 +5,10 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Microsoft.AspNetCore.Razor.Compilation.TagHelpers;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.AspNetCore.Razor.Compilation.TagHelpers;
using Microsoft.AspNetCore.Razor.Text;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.Parser.TagHelpers
@ -123,6 +124,45 @@ namespace Microsoft.AspNetCore.Razor.Parser.TagHelpers
}
}
public override Span LocateOwner(TextChange change)
{
var oldPosition = change.OldPosition;
if (oldPosition < Start.AbsoluteIndex)
{
// Change occurs prior to the TagHelper.
return null;
}
var bodyEndLocation = SourceStartTag?.Start.AbsoluteIndex + SourceStartTag?.Length + base.Length;
if (oldPosition > bodyEndLocation)
{
// Change occurs after the TagHelpers body. End tags for TagHelpers cannot claim ownership of changes
// because any change to them impacts whether or not a tag is a TagHelper.
return null;
}
var startTagEndLocation = Start.AbsoluteIndex + SourceStartTag?.Length;
if (oldPosition < startTagEndLocation)
{
// Change occurs in the start tag.
var attributeElements = Attributes
.Select(attribute => attribute.Value)
.Where(value => value != null);
return LocateOwner(change, attributeElements);
}
if (oldPosition < bodyEndLocation)
{
// Change occurs in the body
return base.LocateOwner(change);
}
// TagHelper does not contain a Span that can claim ownership.
return null;
}
/// <inheritdoc />
public override string ToString()
{

View File

@ -139,7 +139,27 @@ namespace Microsoft.AspNetCore.Razor.Test.Framework
public static SpanConstructor CodeMarkup(this SpanFactory self, params string[] content)
{
return self.Span(SpanKind.Code, content, markup: true).With(new MarkupChunkGenerator());
return self
.Span(SpanKind.Code, content, markup: true)
.AsCodeMarkup();
}
public static SpanConstructor CSharpCodeMarkup(this SpanFactory self, string content)
{
return self.Code(content)
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true)
.AsCodeMarkup();
}
public static SpanConstructor AsCodeMarkup(this SpanConstructor self)
{
return self
.With(new ImplicitExpressionEditHandler(
SpanConstructor.TestTokenizer,
CSharpCodeParser.DefaultKeywords,
acceptTrailingDot: true))
.With(new MarkupChunkGenerator())
.Accepts(AcceptedCharacters.AnyExceptNewline);
}
public static SourceLocation GetLocationAndAdvance(this SourceLocationTracker self, string content)

View File

@ -1013,9 +1013,8 @@ namespace Microsoft.AspNetCore.Razor.Parser.TagHelpers.Internal
new ExpressionBlock(
factory.CodeTransition(),
factory
.Code("DateTime.Now.Year")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharacters.NonWhiteSpace)))))
.CSharpCodeMarkup("DateTime.Now.Year")
.With(new ExpressionChunkGenerator())))))
}))
},
{
@ -1031,14 +1030,10 @@ namespace Microsoft.AspNetCore.Razor.Parser.TagHelpers.Internal
new MarkupBlock(
factory.CodeMarkup(" "),
new ExpressionBlock(
factory.CSharpCodeMarkup("@"),
factory
.CodeTransition()
.As(SpanKind.Code)
.With(new MarkupChunkGenerator()),
factory
.Code("DateTime.Now.Year")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharacters.NonWhiteSpace)))))
.CSharpCodeMarkup("DateTime.Now.Year")
.With(new ExpressionChunkGenerator())))))
}))
},
{
@ -1078,14 +1073,9 @@ namespace Microsoft.AspNetCore.Razor.Parser.TagHelpers.Internal
new MarkupBlock(
factory.CodeMarkup(" "),
new ExpressionBlock(
factory
.CodeTransition()
.As(SpanKind.Code)
.With(new MarkupChunkGenerator()),
factory
.Code("value")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharacters.NonWhiteSpace))),
factory.CSharpCodeMarkup("@"),
factory.CSharpCodeMarkup("value")
.With(new ExpressionChunkGenerator()))),
factory.CodeMarkup(" +"),
factory.CodeMarkup(" 2"))),
new TagHelperAttributeNode(
@ -1094,33 +1084,26 @@ namespace Microsoft.AspNetCore.Razor.Parser.TagHelpers.Internal
factory.CodeMarkup("(bool)"),
new MarkupBlock(
new ExpressionBlock(
factory.CSharpCodeMarkup("@"),
factory
.CodeTransition()
.As(SpanKind.Code)
.With(new MarkupChunkGenerator()),
factory
.Code("Bag[\"val\"]")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharacters.NonWhiteSpace))),
.CSharpCodeMarkup("Bag[\"val\"]")
.With(new ExpressionChunkGenerator()))),
factory.CodeMarkup(" ?"),
new MarkupBlock(
factory.CodeMarkup(" @").Accepts(AcceptedCharacters.None),
factory.CodeMarkup(" @")
.As(SpanKind.Code),
factory.CodeMarkup("@")
.With(SpanChunkGenerator.Null)
.Accepts(AcceptedCharacters.None)),
.As(SpanKind.Code)
.With(SpanChunkGenerator.Null)),
factory.CodeMarkup("DateTime"),
factory.CodeMarkup(" :"),
new MarkupBlock(
factory.CodeMarkup(" "),
new ExpressionBlock(
factory.CSharpCodeMarkup("@"),
factory
.CodeTransition()
.As(SpanKind.Code)
.With(new MarkupChunkGenerator()),
factory
.Code("DateTime.Now")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharacters.NonWhiteSpace)))),
.CSharpCodeMarkup("DateTime.Now")
.With(new ExpressionChunkGenerator())))),
HtmlAttributeValueStyle.SingleQuotes)
}))
},
@ -1196,27 +1179,19 @@ namespace Microsoft.AspNetCore.Razor.Parser.TagHelpers.Internal
"age",
new MarkupBlock(
new MarkupBlock(
factory.CodeMarkup("@"),
factory.CodeMarkup("@")
.Accepts(AcceptedCharacters.None)
.With(new MarkupChunkGenerator()),
factory.CodeMarkup("@")
.With(SpanChunkGenerator.Null)
.Accepts(AcceptedCharacters.None)),
.With(SpanChunkGenerator.Null)),
new MarkupBlock(
factory.EmptyHtml().As(SpanKind.Code),
factory.EmptyHtml()
.AsCodeMarkup()
.As(SpanKind.Code),
new ExpressionBlock(
factory.CodeTransition()
.As(SpanKind.Code)
.With(new MarkupChunkGenerator()),
factory.MetaCode("(")
.Accepts(AcceptedCharacters.None)
.As(SpanKind.Code)
.With(new MarkupChunkGenerator()),
factory.Code("11+1").AsExpression(),
factory.MetaCode(")")
.Accepts(AcceptedCharacters.None)
.As(SpanKind.Code)
.With(new MarkupChunkGenerator()))))),
factory.CSharpCodeMarkup("@"),
factory.CSharpCodeMarkup("("),
factory.CSharpCodeMarkup("11+1")
.With(new ExpressionChunkGenerator()),
factory.CSharpCodeMarkup(")"))))),
new TagHelperAttributeNode(
"birthday",
factory.CodeMarkup("DateTime.Now")),
@ -2237,12 +2212,9 @@ namespace Microsoft.AspNetCore.Razor.Parser.TagHelpers.Internal
new MarkupBlock(
factory.CodeMarkup(" "),
new ExpressionBlock(
factory.CodeTransition()
.As(SpanKind.Code)
.With(new MarkupChunkGenerator()),
factory.Code("true")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharacters.NonWhiteSpace))),
factory.CSharpCodeMarkup("@"),
factory.CSharpCodeMarkup("true")
.With(new ExpressionChunkGenerator()))),
factory.CodeMarkup(" ")),
HtmlAttributeValueStyle.SingleQuotes)
}
@ -2264,18 +2236,11 @@ namespace Microsoft.AspNetCore.Razor.Parser.TagHelpers.Internal
new MarkupBlock(
factory.CodeMarkup(" "),
new ExpressionBlock(
factory.CodeTransition()
.As(SpanKind.Code)
.With(new MarkupChunkGenerator()),
factory.MetaCode("(")
.Accepts(AcceptedCharacters.None)
.As(SpanKind.Code)
.With(new MarkupChunkGenerator()),
factory.Code("true").AsExpression(),
factory.MetaCode(")")
.Accepts(AcceptedCharacters.None)
.As(SpanKind.Code)
.With(new MarkupChunkGenerator()))),
factory.CSharpCodeMarkup("@"),
factory.CSharpCodeMarkup("("),
factory.CSharpCodeMarkup("true")
.With(new ExpressionChunkGenerator()),
factory.CSharpCodeMarkup(")"))),
factory.CodeMarkup(" ")),
HtmlAttributeValueStyle.SingleQuotes)
}

View File

@ -7,6 +7,7 @@ using System.Globalization;
using System.Linq;
using Microsoft.AspNetCore.Razor.Chunks.Generators;
using Microsoft.AspNetCore.Razor.Compilation.TagHelpers;
using Microsoft.AspNetCore.Razor.Editor;
using Microsoft.AspNetCore.Razor.Parser.Internal;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
using Microsoft.AspNetCore.Razor.TagHelpers;
@ -2452,8 +2453,8 @@ namespace Microsoft.AspNetCore.Razor.Parser.TagHelpers.Internal
new ExpressionBlock(
factory.CodeTransition(),
factory.Code("DateTime.Now")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharacters.NonWhiteSpace)))))
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true)
.Accepts(AcceptedCharacters.AnyExceptNewline)))))
}
})),
availableDescriptorsColon
@ -2474,8 +2475,8 @@ namespace Microsoft.AspNetCore.Razor.Parser.TagHelpers.Internal
new ExpressionBlock(
factory.CodeTransition(),
factory.Code("DateTime.Now")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharacters.NonWhiteSpace)))))
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true)
.Accepts(AcceptedCharacters.AnyExceptNewline)))))
}
})),
availableDescriptorsText
@ -2493,24 +2494,19 @@ namespace Microsoft.AspNetCore.Razor.Parser.TagHelpers.Internal
"bound",
new MarkupBlock(
new MarkupBlock(
factory.CodeMarkup("@"),
factory
.CodeMarkup("@")
.With(new MarkupChunkGenerator())
.Accepts(AcceptedCharacters.None),
factory
.CodeMarkup("@")
.With(SpanChunkGenerator.Null)
.Accepts(AcceptedCharacters.None)),
.With(SpanChunkGenerator.Null)),
new MarkupBlock(
factory.EmptyHtml().As(SpanKind.Code),
factory
.EmptyHtml()
.As(SpanKind.Code)
.AsCodeMarkup(),
new ExpressionBlock(
factory
.CodeTransition()
.As(SpanKind.Code)
.With(new MarkupChunkGenerator()),
factory.Code("DateTime.Now")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharacters.NonWhiteSpace)))))
factory.CSharpCodeMarkup("@"),
factory.CSharpCodeMarkup("DateTime.Now")
.With(new ExpressionChunkGenerator())))))
}
})),
availableDescriptorsText

View File

@ -5,6 +5,7 @@ using System;
using System.Threading;
using System.Web.WebPages.TestUtils;
using Microsoft.AspNetCore.Razor.CodeGenerators;
using Microsoft.AspNetCore.Razor.Compilation.TagHelpers;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
using Microsoft.AspNetCore.Razor.Test.Framework;
using Microsoft.AspNetCore.Razor.Test.Utils;
@ -58,7 +59,7 @@ namespace Microsoft.AspNetCore.Razor
return new TestParserManager(parser);
}
protected static RazorEngineHost CreateHost()
protected static RazorEngineHost CreateHost(ITagHelperDescriptorResolver descriptorResolver = null)
{
return new RazorEngineHost(new TLanguage())
{
@ -71,7 +72,8 @@ namespace Microsoft.AspNetCore.Razor
"Template",
"DefineSection",
new GeneratedTagHelperContext()),
DesignTimeMode = true
DesignTimeMode = true,
TagHelperDescriptorResolver = descriptorResolver
};
}

View File

@ -2,17 +2,21 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Web.WebPages.TestUtils;
using Microsoft.AspNetCore.Razor.Compilation.TagHelpers;
using Microsoft.AspNetCore.Razor.Editor;
using Microsoft.AspNetCore.Razor.Parser;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
using Microsoft.AspNetCore.Razor.Parser.TagHelpers;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.AspNetCore.Razor.Test.CodeGenerators;
using Microsoft.AspNetCore.Razor.Test.Framework;
using Microsoft.AspNetCore.Razor.Test.Utils;
using Microsoft.AspNetCore.Razor.Text;
using Microsoft.AspNetCore.Testing;
using Moq;
using Xunit;
namespace Microsoft.AspNetCore.Razor
@ -23,6 +27,273 @@ namespace Microsoft.AspNetCore.Razor
private static readonly TestFile SimpleCSHTMLDocumentGenerated = TestFile.Create("TestFiles/DesignTime/Simple.txt");
private const string TestLinePragmaFileName = "C:\\This\\Path\\Is\\Just\\For\\Line\\Pragmas.cshtml";
public static TheoryData TagHelperPartialParseRejectData
{
get
{
var factory = SpanFactory.CreateCsHtml();
// change, expectedDocument
return new TheoryData<TextChange, MarkupBlock>
{
{
CreateInsertionChange("<p></p>", 2, " "),
new MarkupBlock(
new MarkupTagHelperBlock("p"))
},
{
CreateInsertionChange("<p></p>", 6, " "),
new MarkupBlock(
new MarkupTagHelperBlock("p"))
},
{
CreateInsertionChange("<p some-attr></p>", 12, " "),
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
attributes: new List<TagHelperAttributeNode>
{
new TagHelperAttributeNode(
"some-attr",
value: null,
valueStyle: HtmlAttributeValueStyle.Minimized)
}))
},
{
CreateInsertionChange("<p some-attr></p>", 12, "ibute"),
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
attributes: new List<TagHelperAttributeNode>
{
new TagHelperAttributeNode(
"some-attribute",
value: null,
valueStyle: HtmlAttributeValueStyle.Minimized)
}))
},
{
CreateInsertionChange("<p some-attr></p>", 2, " before"),
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
attributes: new List<TagHelperAttributeNode>
{
new TagHelperAttributeNode(
"before",
value: null,
valueStyle: HtmlAttributeValueStyle.Minimized),
new TagHelperAttributeNode(
"some-attr",
value: null,
valueStyle: HtmlAttributeValueStyle.Minimized)
}))
},
};
}
}
[Theory]
[MemberData(nameof(TagHelperPartialParseRejectData))]
public void TagHelperTagBodiesRejectPartialChanges(TextChange change, MarkupBlock expectedDocument)
{
// Arrange
var descriptors = new[]
{
new TagHelperDescriptor
{
TagName = "p",
TypeName = "PTagHelper"
},
};
var descriptorResolver = new Mock<ITagHelperDescriptorResolver>();
descriptorResolver
.Setup(resolver => resolver.Resolve(It.IsAny<TagHelperDescriptorResolutionContext>()))
.Returns(descriptors);
var host = CreateHost(descriptorResolver.Object);
var parser = new RazorEditorParser(host, @"C:\This\Is\A\Test\Path");
using (var manager = new TestParserManager(parser))
{
manager.InitializeWithDocument(change.OldBuffer);
// Act
var result = manager.CheckForStructureChangesAndWait(change);
// Assert
Assert.Equal(PartialParseResult.Rejected, result);
Assert.Equal(2, manager.ParseCount);
ParserTestBase.EvaluateParseTree(manager.Parser.CurrentParseTree, expectedDocument);
}
}
public static TheoryData TagHelperAttributeAcceptData
{
get
{
var factory = SpanFactory.CreateCsHtml();
// change, expectedDocument, partialParseResult
return new TheoryData<TextChange, MarkupBlock, PartialParseResult>
{
{
CreateInsertionChange("<p str-attr='@DateTime'></p>", 22, "."),
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
attributes: new List<TagHelperAttributeNode>
{
new TagHelperAttributeNode(
"str-attr",
new MarkupBlock(
new MarkupBlock(
new ExpressionBlock(
factory.CodeTransition(),
factory
.Code("DateTime.")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharacters.NonWhiteSpace)))),
HtmlAttributeValueStyle.SingleQuotes)
})),
PartialParseResult.Accepted | PartialParseResult.Provisional
},
{
CreateInsertionChange("<p obj-attr='DateTime'></p>", 21, "."),
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
attributes: new List<TagHelperAttributeNode>
{
new TagHelperAttributeNode(
"obj-attr",
factory.CodeMarkup("DateTime."),
HtmlAttributeValueStyle.SingleQuotes)
})),
PartialParseResult.Accepted
},
{
CreateInsertionChange("<p obj-attr='1 + DateTime'></p>", 25, "."),
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
attributes: new List<TagHelperAttributeNode>
{
new TagHelperAttributeNode(
"obj-attr",
factory.CodeMarkup("1 + DateTime."),
HtmlAttributeValueStyle.SingleQuotes)
})),
PartialParseResult.Accepted
},
{
CreateInsertionChange("<p before-attr str-attr='@DateTime' after-attr></p>", 34, "."),
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
attributes: new List<TagHelperAttributeNode>
{
new TagHelperAttributeNode(
"before-attr",
value: null,
valueStyle: HtmlAttributeValueStyle.Minimized),
new TagHelperAttributeNode(
"str-attr",
new MarkupBlock(
new MarkupBlock(
new ExpressionBlock(
factory.CodeTransition(),
factory
.Code("DateTime.")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharacters.NonWhiteSpace)))),
HtmlAttributeValueStyle.SingleQuotes),
new TagHelperAttributeNode(
"after-attr",
value: null,
valueStyle: HtmlAttributeValueStyle.Minimized),
})),
PartialParseResult.Accepted | PartialParseResult.Provisional
},
{
CreateInsertionChange("<p str-attr='before @DateTime after'></p>", 29, "."),
new MarkupBlock(
new MarkupTagHelperBlock(
"p",
attributes: new List<TagHelperAttributeNode>
{
new TagHelperAttributeNode(
"str-attr",
new MarkupBlock(
factory.Markup("before"),
new MarkupBlock(
factory.Markup(" "),
new ExpressionBlock(
factory.CodeTransition(),
factory
.Code("DateTime.")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharacters.NonWhiteSpace))),
factory.Markup(" after")),
HtmlAttributeValueStyle.SingleQuotes)
})),
PartialParseResult.Accepted | PartialParseResult.Provisional
},
};
}
}
[Theory]
[MemberData(nameof(TagHelperAttributeAcceptData))]
public void TagHelperAttributesAreLocatedAndAcceptChangesCorrectly(
TextChange change,
MarkupBlock expectedDocument,
PartialParseResult partialParseResult)
{
// Arrange
var descriptors = new[]
{
new TagHelperDescriptor
{
TagName = "p",
TypeName = "PTagHelper",
Attributes = new[]
{
new TagHelperAttributeDescriptor
{
Name = "obj-attr",
TypeName = typeof(object).FullName,
PropertyName = "ObjectAttribute",
},
new TagHelperAttributeDescriptor
{
Name = "str-attr",
TypeName = typeof(string).FullName,
PropertyName = "StringAttribute",
},
}
},
};
var descriptorResolver = new Mock<ITagHelperDescriptorResolver>();
descriptorResolver
.Setup(resolver => resolver.Resolve(It.IsAny<TagHelperDescriptorResolutionContext>()))
.Returns(descriptors);
var host = CreateHost(descriptorResolver.Object);
var parser = new RazorEditorParser(host, @"C:\This\Is\A\Test\Path");
using (var manager = new TestParserManager(parser))
{
manager.InitializeWithDocument(change.OldBuffer);
// Act
var result = manager.CheckForStructureChangesAndWait(change);
// Assert
Assert.Equal(partialParseResult, result);
Assert.Equal(1, manager.ParseCount);
ParserTestBase.EvaluateParseTree(manager.Parser.CurrentParseTree, expectedDocument);
}
}
[Fact]
public void ConstructorRequiresNonNullPhysicalPath()
{
@ -898,5 +1169,14 @@ namespace Microsoft.AspNetCore.Razor
{
return new CodeGenTestHost(new CSharpRazorCodeLanguage()) { DesignTimeMode = true };
}
private static TextChange CreateInsertionChange(string initialText, int insertionLocation, string insertionText)
{
var changedText = initialText.Insert(insertionLocation, insertionText);
var original = new StringTextBuffer(initialText);
var changed = new StringTextBuffer(changedText);
return new TextChange(insertionLocation, 0, original, insertionText.Length, changed);
}
}
}