зеркало из https://github.com/dotnet/razor.git
Make TagHelper & Razor comments locatable at design time.
- Added a step to full fidelity verification that does a `LocateOwner` at every source location. If we find a `null` owner we build a snippet with a detailed response to enable us to diagnose the problem. Addresses aspnet/AspNetCore#7718
This commit is contained in:
Родитель
49f7a26e36
Коммит
08587a30fd
|
@ -23,6 +23,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
|||
|
||||
private static readonly SyntaxKind[] CommentSpanKinds = new SyntaxKind[]
|
||||
{
|
||||
SyntaxKind.RazorCommentTransition,
|
||||
SyntaxKind.RazorCommentStar,
|
||||
SyntaxKind.RazorCommentLiteral,
|
||||
};
|
||||
|
||||
|
@ -100,7 +102,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
|||
}
|
||||
|
||||
SyntaxNode owner = null;
|
||||
IEnumerable<SyntaxNode> children = null;
|
||||
IEnumerable<SyntaxNode> children;
|
||||
if (node is MarkupStartTagSyntax startTag)
|
||||
{
|
||||
children = startTag.Children;
|
||||
|
@ -109,6 +111,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
|||
{
|
||||
children = endTag.Children;
|
||||
}
|
||||
else if (node is MarkupTagHelperStartTagSyntax startTagHelper)
|
||||
{
|
||||
children = startTagHelper.Children;
|
||||
}
|
||||
else if (node is MarkupTagHelperEndTagSyntax endTagHelper)
|
||||
{
|
||||
children = endTagHelper.Children;
|
||||
}
|
||||
else
|
||||
{
|
||||
children = node.ChildNodes();
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Syntax
|
||||
{
|
||||
internal partial class MarkupTagHelperEndTagSyntax
|
||||
{
|
||||
// Copied directly from MarkupEndTagSyntax Children & GetLegacyChildren.
|
||||
public SyntaxList<RazorSyntaxNode> Children => GetLegacyChildren();
|
||||
|
||||
private SyntaxList<RazorSyntaxNode> GetLegacyChildren()
|
||||
{
|
||||
// This method returns the children of this end tag in legacy format.
|
||||
// This is needed to generate the same classified spans as the legacy syntax tree.
|
||||
var builder = new SyntaxListBuilder(3);
|
||||
var tokens = SyntaxListBuilder<SyntaxToken>.Create();
|
||||
var context = this.GetSpanContext();
|
||||
if (!OpenAngle.IsMissing)
|
||||
{
|
||||
tokens.Add(OpenAngle);
|
||||
}
|
||||
if (!ForwardSlash.IsMissing)
|
||||
{
|
||||
tokens.Add(ForwardSlash);
|
||||
}
|
||||
if (Bang != null)
|
||||
{
|
||||
// The prefix of an end tag(E.g '|</|!foo>') will have 'Any' accepted characters if a bang exists.
|
||||
var acceptsAnyContext = new SpanContext(context.ChunkGenerator, SpanEditHandler.CreateDefault());
|
||||
acceptsAnyContext.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.Any;
|
||||
builder.Add(SyntaxFactory.MarkupTextLiteral(tokens.Consume()).WithSpanContext(acceptsAnyContext));
|
||||
|
||||
tokens.Add(Bang);
|
||||
var acceptsNoneContext = new SpanContext(context.ChunkGenerator, SpanEditHandler.CreateDefault());
|
||||
acceptsNoneContext.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
|
||||
builder.Add(SyntaxFactory.RazorMetaCode(tokens.Consume()).WithSpanContext(acceptsNoneContext));
|
||||
}
|
||||
if (!Name.IsMissing)
|
||||
{
|
||||
tokens.Add(Name);
|
||||
}
|
||||
if (MiscAttributeContent?.Children != null && MiscAttributeContent.Children.Count > 0)
|
||||
{
|
||||
foreach (var content in MiscAttributeContent.Children)
|
||||
{
|
||||
tokens.AddRange(((MarkupTextLiteralSyntax)content).LiteralTokens);
|
||||
}
|
||||
}
|
||||
if (!CloseAngle.IsMissing)
|
||||
{
|
||||
tokens.Add(CloseAngle);
|
||||
}
|
||||
builder.Add(SyntaxFactory.MarkupTextLiteral(tokens.Consume()).WithSpanContext(context));
|
||||
|
||||
return new SyntaxList<RazorSyntaxNode>(builder.ToListNode().CreateRed(this, Position));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Syntax
|
||||
{
|
||||
internal partial class MarkupTagHelperStartTagSyntax
|
||||
{
|
||||
// Copied directly from MarkupStartTagSyntax Children & GetLegacyChildren.
|
||||
|
||||
public SyntaxList<RazorSyntaxNode> Children => GetLegacyChildren();
|
||||
|
||||
private SyntaxList<RazorSyntaxNode> GetLegacyChildren()
|
||||
{
|
||||
// This method returns the children of this start tag in legacy format.
|
||||
// This is needed to generate the same classified spans as the legacy syntax tree.
|
||||
var builder = new SyntaxListBuilder(5);
|
||||
var tokens = SyntaxListBuilder<SyntaxToken>.Create();
|
||||
var context = this.GetSpanContext();
|
||||
|
||||
// We want to know if this tag contains non-whitespace attribute content to set the appropriate AcceptedCharacters.
|
||||
// The prefix of a start tag(E.g '|<foo| attr>') will have 'Any' accepted characters if non-whitespace attribute content exists.
|
||||
var acceptsAnyContext = new SpanContext(context.ChunkGenerator, SpanEditHandler.CreateDefault());
|
||||
acceptsAnyContext.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.Any;
|
||||
var containsAttributesContent = false;
|
||||
foreach (var attribute in Attributes)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(attribute.GetContent()))
|
||||
{
|
||||
containsAttributesContent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!OpenAngle.IsMissing)
|
||||
{
|
||||
tokens.Add(OpenAngle);
|
||||
}
|
||||
if (Bang != null)
|
||||
{
|
||||
builder.Add(SyntaxFactory.MarkupTextLiteral(tokens.Consume()).WithSpanContext(acceptsAnyContext));
|
||||
|
||||
tokens.Add(Bang);
|
||||
var acceptsNoneContext = new SpanContext(context.ChunkGenerator, SpanEditHandler.CreateDefault());
|
||||
acceptsNoneContext.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.None;
|
||||
builder.Add(SyntaxFactory.RazorMetaCode(tokens.Consume()).WithSpanContext(acceptsNoneContext));
|
||||
}
|
||||
if (!Name.IsMissing)
|
||||
{
|
||||
tokens.Add(Name);
|
||||
}
|
||||
|
||||
builder.Add(SyntaxFactory.MarkupTextLiteral(tokens.Consume()).WithSpanContext(containsAttributesContent ? acceptsAnyContext : context));
|
||||
|
||||
builder.AddRange(Attributes);
|
||||
|
||||
if (ForwardSlash != null)
|
||||
{
|
||||
tokens.Add(ForwardSlash);
|
||||
}
|
||||
if (!CloseAngle.IsMissing)
|
||||
{
|
||||
tokens.Add(CloseAngle);
|
||||
}
|
||||
|
||||
if (tokens.Count > 0)
|
||||
{
|
||||
builder.Add(SyntaxFactory.MarkupTextLiteral(tokens.Consume()).WithSpanContext(context));
|
||||
}
|
||||
|
||||
return new SyntaxList<RazorSyntaxNode>(builder.ToListNode().CreateRed(this, Position));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ using System.Text;
|
|||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
using Microsoft.AspNetCore.Razor.Language.Syntax;
|
||||
using Xunit;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
|
@ -29,6 +30,38 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
|
||||
// Make sure the syntax tree contains all of the text in the document.
|
||||
Assert.Equal(sourceString, syntaxTreeString);
|
||||
|
||||
// Ensure all source is locatable
|
||||
for (var i = 0; i < syntaxTree.Source.Length; i++)
|
||||
{
|
||||
var span = new SourceSpan(i, 0);
|
||||
var location = new SourceChange(span, string.Empty);
|
||||
var owner = syntaxTree.Root.LocateOwner(location);
|
||||
|
||||
if (owner == null)
|
||||
{
|
||||
var snippetStartIndex = Math.Max(0, i - 10);
|
||||
var snippetStartLength = i - snippetStartIndex;
|
||||
var snippetStart = new char[snippetStartLength];
|
||||
syntaxTree.Source.CopyTo(snippetStartIndex, snippetStart, 0, snippetStartLength);
|
||||
|
||||
var snippetEndIndex = Math.Min(syntaxTree.Source.Length - 1, i + 10);
|
||||
var snippetEndLength = snippetEndIndex - i;
|
||||
var snippetEnd = new char[snippetEndLength];
|
||||
syntaxTree.Source.CopyTo(i, snippetEnd, 0, snippetEndLength);
|
||||
|
||||
var snippet = new char[snippetStart.Length + snippetEnd.Length + 1];
|
||||
snippetStart.CopyTo(snippet, 0);
|
||||
snippet[snippetStart.Length] = '|';
|
||||
snippetEnd.CopyTo(snippet, snippetStart.Length + 1);
|
||||
|
||||
var snippetString = new string(snippet);
|
||||
|
||||
throw new XunitException(
|
||||
$@"Could not locate Syntax Node owner at position '{i}':
|
||||
{snippetString}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче