Add attribute only bindings and fix dotnet/aspnetcore-tooling#6373
Adds a new API for WTE to call given a TagHelperBinding to determine if
the binding should colorize/classify only the attributes of the HTML
element in source code. This is driven by a new metadata item that the
Components 'directive attributes' all set. There's no way for a user to
access this feature via tag helpers currently, but it could be added
easily in the future.
Also fixing dotnet/aspnetcore-tooling#6376 while I'm in there. 👍
\n\nCommit migrated from df449beea9
This commit is contained in:
parent
148bc99cce
commit
40633dde21
|
|
@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
foreach (var descriptor in bindingResult.Descriptors)
|
foreach (var descriptor in bindingResult.Descriptors)
|
||||||
{
|
{
|
||||||
var boundRules = bindingResult.GetBoundRules(descriptor);
|
var boundRules = bindingResult.Mappings[descriptor];
|
||||||
var nonDefaultRule = boundRules.FirstOrDefault(rule => rule.TagStructure != TagStructure.Unspecified);
|
var nonDefaultRule = boundRules.FirstOrDefault(rule => rule.TagStructure != TagStructure.Unspecified);
|
||||||
|
|
||||||
if (nonDefaultRule?.TagStructure == TagStructure.WithoutEndTag)
|
if (nonDefaultRule?.TagStructure == TagStructure.WithoutEndTag)
|
||||||
|
|
|
||||||
|
|
@ -336,7 +336,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
foreach (var descriptor in tagHelperBinding.Descriptors)
|
foreach (var descriptor in tagHelperBinding.Descriptors)
|
||||||
{
|
{
|
||||||
var boundRules = tagHelperBinding.GetBoundRules(descriptor);
|
var boundRules = tagHelperBinding.Mappings[descriptor];
|
||||||
var invalidRule = boundRules.FirstOrDefault(rule => rule.TagStructure == TagStructure.WithoutEndTag);
|
var invalidRule = boundRules.FirstOrDefault(rule => rule.TagStructure == TagStructure.WithoutEndTag);
|
||||||
|
|
||||||
if (invalidRule != null)
|
if (invalidRule != null)
|
||||||
|
|
@ -456,7 +456,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
TagStructure? baseStructure = null;
|
TagStructure? baseStructure = null;
|
||||||
foreach (var descriptor in bindingResult.Descriptors)
|
foreach (var descriptor in bindingResult.Descriptors)
|
||||||
{
|
{
|
||||||
var boundRules = bindingResult.GetBoundRules(descriptor);
|
var boundRules = bindingResult.Mappings[descriptor];
|
||||||
foreach (var rule in boundRules)
|
foreach (var rule in boundRules)
|
||||||
{
|
{
|
||||||
if (rule.TagStructure != TagStructure.Unspecified)
|
if (rule.TagStructure != TagStructure.Unspecified)
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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.Collections.Generic;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language
|
namespace Microsoft.AspNetCore.Razor.Language
|
||||||
{
|
{
|
||||||
public sealed class TagHelperBinding
|
public sealed class TagHelperBinding
|
||||||
{
|
{
|
||||||
private IReadOnlyDictionary<TagHelperDescriptor, IReadOnlyList<TagMatchingRuleDescriptor>> _mappings;
|
|
||||||
|
|
||||||
internal TagHelperBinding(
|
internal TagHelperBinding(
|
||||||
string tagName,
|
string tagName,
|
||||||
IReadOnlyList<KeyValuePair<string, string>> attributes,
|
IReadOnlyList<KeyValuePair<string, string>> attributes,
|
||||||
|
|
@ -19,12 +18,42 @@ namespace Microsoft.AspNetCore.Razor.Language
|
||||||
TagName = tagName;
|
TagName = tagName;
|
||||||
Attributes = attributes;
|
Attributes = attributes;
|
||||||
ParentTagName = parentTagName;
|
ParentTagName = parentTagName;
|
||||||
|
Mappings = mappings;
|
||||||
TagHelperPrefix = tagHelperPrefix;
|
TagHelperPrefix = tagHelperPrefix;
|
||||||
|
|
||||||
_mappings = mappings;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<TagHelperDescriptor> Descriptors => _mappings.Keys;
|
public IEnumerable<TagHelperDescriptor> Descriptors => Mappings.Keys;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value that indicates whether the the binding matched on attributes only.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><c>false</c> if the entire element should be classified as a tag helper.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// If this returns <c>true</c>, use <c>TagHelperFactsService.GetBoundTagHelperAttributes</c> to find the
|
||||||
|
/// set of attributes that should be considered part of the match.
|
||||||
|
/// </remarks>
|
||||||
|
public bool IsAttributeMatch
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
foreach (var descriptor in Mappings.Keys)
|
||||||
|
{
|
||||||
|
if (!descriptor.Metadata.TryGetValue(TagHelperMetadata.Common.ClassifyAttributesOnly, out var value) ||
|
||||||
|
!string.Equals(value, bool.TrueString, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All the matching tag helpers want to be classified with **attributes only**.
|
||||||
|
//
|
||||||
|
// Ex: (components)
|
||||||
|
//
|
||||||
|
// <button onclick="..." />
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public string TagName { get; }
|
public string TagName { get; }
|
||||||
|
|
||||||
|
|
@ -32,11 +61,18 @@ namespace Microsoft.AspNetCore.Razor.Language
|
||||||
|
|
||||||
public IReadOnlyList<KeyValuePair<string, string>> Attributes { get; }
|
public IReadOnlyList<KeyValuePair<string, string>> Attributes { get; }
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<TagHelperDescriptor, IReadOnlyList<TagMatchingRuleDescriptor>> Mappings { get; }
|
||||||
|
|
||||||
public string TagHelperPrefix { get; }
|
public string TagHelperPrefix { get; }
|
||||||
|
|
||||||
public IReadOnlyList<TagMatchingRuleDescriptor> GetBoundRules(TagHelperDescriptor descriptor)
|
public IReadOnlyList<TagMatchingRuleDescriptor> GetBoundRules(TagHelperDescriptor descriptor)
|
||||||
{
|
{
|
||||||
return _mappings[descriptor];
|
if (descriptor == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(descriptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Mappings[descriptor];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -10,6 +10,8 @@ namespace Microsoft.AspNetCore.Razor.Language
|
||||||
public static readonly string PropertyName = "Common.PropertyName";
|
public static readonly string PropertyName = "Common.PropertyName";
|
||||||
|
|
||||||
public static readonly string TypeName = "Common.TypeName";
|
public static readonly string TypeName = "Common.TypeName";
|
||||||
|
|
||||||
|
public static readonly string ClassifyAttributesOnly = "Common.ClassifyAttributesOnly";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Runtime
|
public static class Runtime
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
builder.Documentation = ComponentResources.BindTagHelper_Fallback_Documentation;
|
builder.Documentation = ComponentResources.BindTagHelper_Fallback_Documentation;
|
||||||
|
|
||||||
builder.Metadata.Add(BlazorMetadata.SpecialKindKey, BlazorMetadata.Bind.TagHelperKind);
|
builder.Metadata.Add(BlazorMetadata.SpecialKindKey, BlazorMetadata.Bind.TagHelperKind);
|
||||||
|
builder.Metadata.Add(TagHelperMetadata.Common.ClassifyAttributesOnly, bool.TrueString);
|
||||||
builder.Metadata[TagHelperMetadata.Runtime.Name] = BlazorMetadata.Bind.RuntimeName;
|
builder.Metadata[TagHelperMetadata.Runtime.Name] = BlazorMetadata.Bind.RuntimeName;
|
||||||
builder.Metadata[BlazorMetadata.Bind.FallbackKey] = bool.TrueString;
|
builder.Metadata[BlazorMetadata.Bind.FallbackKey] = bool.TrueString;
|
||||||
|
|
||||||
|
|
@ -257,6 +258,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
entry.ChangeAttribute);
|
entry.ChangeAttribute);
|
||||||
|
|
||||||
builder.Metadata.Add(BlazorMetadata.SpecialKindKey, BlazorMetadata.Bind.TagHelperKind);
|
builder.Metadata.Add(BlazorMetadata.SpecialKindKey, BlazorMetadata.Bind.TagHelperKind);
|
||||||
|
builder.Metadata.Add(TagHelperMetadata.Common.ClassifyAttributesOnly, bool.TrueString);
|
||||||
builder.Metadata[TagHelperMetadata.Runtime.Name] = BlazorMetadata.Bind.RuntimeName;
|
builder.Metadata[TagHelperMetadata.Runtime.Name] = BlazorMetadata.Bind.RuntimeName;
|
||||||
builder.Metadata[BlazorMetadata.Bind.ValueAttribute] = entry.ValueAttribute;
|
builder.Metadata[BlazorMetadata.Bind.ValueAttribute] = entry.ValueAttribute;
|
||||||
builder.Metadata[BlazorMetadata.Bind.ChangeAttribute] = entry.ChangeAttribute;
|
builder.Metadata[BlazorMetadata.Bind.ChangeAttribute] = entry.ChangeAttribute;
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var eventHandlerData = GetEventHandlerData(compilation);
|
var eventHandlerData = GetEventHandlerData(compilation);
|
||||||
|
|
||||||
foreach (var tagHelper in CreateEventHandlerTagHelpers(eventHandlerData))
|
foreach (var tagHelper in CreateEventHandlerTagHelpers(eventHandlerData))
|
||||||
|
|
@ -112,6 +111,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
|
|
||||||
builder.Metadata.Add(BlazorMetadata.SpecialKindKey, BlazorMetadata.EventHandler.TagHelperKind);
|
builder.Metadata.Add(BlazorMetadata.SpecialKindKey, BlazorMetadata.EventHandler.TagHelperKind);
|
||||||
builder.Metadata.Add(BlazorMetadata.EventHandler.EventArgsType, entry.EventArgsType.ToDisplayString());
|
builder.Metadata.Add(BlazorMetadata.EventHandler.EventArgsType, entry.EventArgsType.ToDisplayString());
|
||||||
|
builder.Metadata.Add(TagHelperMetadata.Common.ClassifyAttributesOnly, bool.TrueString);
|
||||||
builder.Metadata[TagHelperMetadata.Runtime.Name] = BlazorMetadata.EventHandler.RuntimeName;
|
builder.Metadata[TagHelperMetadata.Runtime.Name] = BlazorMetadata.EventHandler.RuntimeName;
|
||||||
|
|
||||||
// WTE has a bug in 15.7p1 where a Tag Helper without a display-name that looks like
|
// WTE has a bug in 15.7p1 where a Tag Helper without a display-name that looks like
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,20 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var compilation = context.GetCompilation();
|
||||||
|
if (compilation == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var elementRef = compilation.GetTypeByMetadataName(ComponentsApi.ElementRef.FullTypeName);
|
||||||
|
if (elementRef == null)
|
||||||
|
{
|
||||||
|
// If we can't find ElementRef, then just bail. We won't be able to compile the
|
||||||
|
// generated code anyway.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
context.Results.Add(CreateRefTagHelper());
|
context.Results.Add(CreateRefTagHelper());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -30,6 +44,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
builder.Documentation = ComponentResources.RefTagHelper_Documentation;
|
builder.Documentation = ComponentResources.RefTagHelper_Documentation;
|
||||||
|
|
||||||
builder.Metadata.Add(BlazorMetadata.SpecialKindKey, BlazorMetadata.Ref.TagHelperKind);
|
builder.Metadata.Add(BlazorMetadata.SpecialKindKey, BlazorMetadata.Ref.TagHelperKind);
|
||||||
|
builder.Metadata.Add(TagHelperMetadata.Common.ClassifyAttributesOnly, bool.TrueString);
|
||||||
builder.Metadata[TagHelperMetadata.Runtime.Name] = BlazorMetadata.Ref.RuntimeName;
|
builder.Metadata[TagHelperMetadata.Runtime.Name] = BlazorMetadata.Ref.RuntimeName;
|
||||||
|
|
||||||
// WTE has a bug in 15.7p1 where a Tag Helper without a display-name that looks like
|
// WTE has a bug in 15.7p1 where a Tag Helper without a display-name that looks like
|
||||||
|
|
|
||||||
|
|
@ -207,6 +207,7 @@ namespace Test
|
||||||
Assert.Empty(bind.Diagnostics);
|
Assert.Empty(bind.Diagnostics);
|
||||||
Assert.False(bind.HasErrors);
|
Assert.False(bind.HasErrors);
|
||||||
Assert.Equal(BlazorMetadata.Bind.TagHelperKind, bind.Kind);
|
Assert.Equal(BlazorMetadata.Bind.TagHelperKind, bind.Kind);
|
||||||
|
Assert.Equal(bool.TrueString, bind.Metadata[TagHelperMetadata.Common.ClassifyAttributesOnly]);
|
||||||
Assert.Equal(BlazorMetadata.Bind.RuntimeName, bind.Metadata[TagHelperMetadata.Runtime.Name]);
|
Assert.Equal(BlazorMetadata.Bind.RuntimeName, bind.Metadata[TagHelperMetadata.Runtime.Name]);
|
||||||
Assert.False(bind.IsDefaultKind());
|
Assert.False(bind.IsDefaultKind());
|
||||||
Assert.False(bind.KindUsesDefaultTagHelperRuntime());
|
Assert.False(bind.KindUsesDefaultTagHelperRuntime());
|
||||||
|
|
@ -571,6 +572,7 @@ namespace Test
|
||||||
Assert.Empty(bind.Diagnostics);
|
Assert.Empty(bind.Diagnostics);
|
||||||
Assert.False(bind.HasErrors);
|
Assert.False(bind.HasErrors);
|
||||||
Assert.Equal(BlazorMetadata.Bind.TagHelperKind, bind.Kind);
|
Assert.Equal(BlazorMetadata.Bind.TagHelperKind, bind.Kind);
|
||||||
|
Assert.Equal(bool.TrueString, bind.Metadata[TagHelperMetadata.Common.ClassifyAttributesOnly]);
|
||||||
Assert.Equal(BlazorMetadata.Bind.RuntimeName, bind.Metadata[TagHelperMetadata.Runtime.Name]);
|
Assert.Equal(BlazorMetadata.Bind.RuntimeName, bind.Metadata[TagHelperMetadata.Runtime.Name]);
|
||||||
Assert.False(bind.IsDefaultKind());
|
Assert.False(bind.IsDefaultKind());
|
||||||
Assert.False(bind.KindUsesDefaultTagHelperRuntime());
|
Assert.False(bind.KindUsesDefaultTagHelperRuntime());
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
using Microsoft.CodeAnalysis.Razor;
|
using Microsoft.AspNetCore.Razor.Language.Components;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
namespace Microsoft.CodeAnalysis.Razor
|
||||||
{
|
{
|
||||||
public class EventHandlerTagHelperDescriptorProviderTest : BaseTagHelperDescriptorProviderTest
|
public class EventHandlerTagHelperDescriptorProviderTest : BaseTagHelperDescriptorProviderTest
|
||||||
{
|
{
|
||||||
|
|
@ -52,6 +52,7 @@ namespace Test
|
||||||
Assert.Empty(item.Diagnostics);
|
Assert.Empty(item.Diagnostics);
|
||||||
Assert.False(item.HasErrors);
|
Assert.False(item.HasErrors);
|
||||||
Assert.Equal(BlazorMetadata.EventHandler.TagHelperKind, item.Kind);
|
Assert.Equal(BlazorMetadata.EventHandler.TagHelperKind, item.Kind);
|
||||||
|
Assert.Equal(bool.TrueString, item.Metadata[TagHelperMetadata.Common.ClassifyAttributesOnly]);
|
||||||
Assert.Equal(BlazorMetadata.EventHandler.RuntimeName, item.Metadata[TagHelperMetadata.Runtime.Name]);
|
Assert.Equal(BlazorMetadata.EventHandler.RuntimeName, item.Metadata[TagHelperMetadata.Runtime.Name]);
|
||||||
Assert.False(item.IsDefaultKind());
|
Assert.False(item.IsDefaultKind());
|
||||||
Assert.False(item.KindUsesDefaultTagHelperRuntime());
|
Assert.False(item.KindUsesDefaultTagHelperRuntime());
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var context = TagHelperDescriptorProviderContext.Create();
|
var context = TagHelperDescriptorProviderContext.Create();
|
||||||
|
context.SetCompilation(BaseCompilation);
|
||||||
|
|
||||||
var provider = new RefTagHelperDescriptorProvider();
|
var provider = new RefTagHelperDescriptorProvider();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -29,6 +31,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
Assert.Empty(item.Diagnostics);
|
Assert.Empty(item.Diagnostics);
|
||||||
Assert.False(item.HasErrors);
|
Assert.False(item.HasErrors);
|
||||||
Assert.Equal(BlazorMetadata.Ref.TagHelperKind, item.Kind);
|
Assert.Equal(BlazorMetadata.Ref.TagHelperKind, item.Kind);
|
||||||
|
Assert.Equal(bool.TrueString, item.Metadata[TagHelperMetadata.Common.ClassifyAttributesOnly]);
|
||||||
Assert.Equal(BlazorMetadata.Ref.RuntimeName, item.Metadata[TagHelperMetadata.Runtime.Name]);
|
Assert.Equal(BlazorMetadata.Ref.RuntimeName, item.Metadata[TagHelperMetadata.Runtime.Name]);
|
||||||
Assert.False(item.IsDefaultKind());
|
Assert.False(item.IsDefaultKind());
|
||||||
Assert.False(item.KindUsesDefaultTagHelperRuntime());
|
Assert.False(item.KindUsesDefaultTagHelperRuntime());
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue