Skip to content
This repository has been archived by the owner on Sep 26, 2024. It is now read-only.

Commit

Permalink
[Blazor] Support @Bind:get, @Bind:set, @Bind:after (#70)
Browse files Browse the repository at this point in the history
Adds support for `@bind:get, @Bind:set, @Bind:after` to the Razor Compiler.

See dotnet/aspnetcore#39837 for details
  • Loading branch information
javiercn authored Jun 21, 2022
1 parent 0033829 commit c34a2e3
Show file tree
Hide file tree
Showing 288 changed files with 8,147 additions and 459 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,16 @@ public static void SetPropertyName(this BoundAttributeParameterDescriptorBuilder
builder.Metadata[TagHelperMetadata.Common.PropertyName] = propertyName;
}

public static void SetBindAttributeGetSet(this BoundAttributeParameterDescriptorBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}

builder.Metadata[ComponentMetadata.Bind.BindAttributeGetSet] = bool.TrueString;
}

public static string GetPropertyName(this BoundAttributeParameterDescriptorBuilder builder)
{
if (builder == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@
<data name="BindTagHelper_Component_Documentation" xml:space="preserve">
<value>Binds the provided expression to the '{0}' property and a change event delegate to the '{1}' property of the component.</value>
</data>
<data name="BindTagHelper_Element_After_Documentation" xml:space="preserve">
<value>Specifies an action to run after the new value has been set.</value>
</data>
<data name="BindTagHelper_Element_Culture_Documentation" xml:space="preserve">
<value>Specifies the culture to use for conversions.</value>
</data>
Expand All @@ -141,6 +144,12 @@
<data name="BindTagHelper_Element_Format_Documentation" xml:space="preserve">
<value>Specifies a format to convert the value specified by the '{0}' attribute. The format string can currently only be used with expressions of type &lt;code&gt;DateTime&lt;/code&gt;.</value>
</data>
<data name="BindTagHelper_Element_Get_Documentation" xml:space="preserve">
<value>Specifies the expression to use for binding the value to the attribute.</value>
</data>
<data name="BindTagHelper_Element_Set_Documentation" xml:space="preserve">
<value>Specifies the expression to use for updating the bound value when a new value is available.</value>
</data>
<data name="BindTagHelper_Fallback_Documentation" xml:space="preserve">
<value>Binds the provided expression to an attribute and a change event, based on the naming of the bind attribute. For example: &lt;code&gt;@bind-value="..."&lt;/code&gt; and &lt;code&gt;@bind-value:event="onchange"&lt;/code&gt; will assign the current value of the expression to the 'value' attribute, and assign a delegate that attempts to set the value to the 'onchange' attribute.</value>
</data>
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable disable
Expand Down Expand Up @@ -456,4 +456,97 @@ public static RazorDiagnostic CreateEventHandlerParameter_Duplicates(SourceSpan?
Environment.NewLine + string.Join(Environment.NewLine, attributes.Select(p => p.TagHelper.DisplayName)));
return diagnostic;
}

public static readonly RazorDiagnosticDescriptor BindAttributeParameter_UseBindGet =
new RazorDiagnosticDescriptor(
$"{DiagnosticPrefix}10015",
() => "Attribute '{0}:get' must be used with attribute '{0}:set'.",
RazorDiagnosticSeverity.Error);


public static RazorDiagnostic CreateBindAttributeParameter_UseBindGet(SourceSpan? source, string attribute)
{
var diagnostic = RazorDiagnostic.Create(
BindAttributeParameter_UseBindGet,
source ?? SourceSpan.Undefined,
attribute);
return diagnostic;
}

public static readonly RazorDiagnosticDescriptor BindAttributeParameter_MissingBindGet =
new RazorDiagnosticDescriptor(
$"{DiagnosticPrefix}10016",
() => "Attribute '{0}:set' was used but no attribute '{0}:get' was found.",
RazorDiagnosticSeverity.Error);


public static RazorDiagnostic CreateBindAttributeParameter_MissingBindGet(SourceSpan? source, string attribute)
{
var diagnostic = RazorDiagnostic.Create(
BindAttributeParameter_MissingBindGet,
source ?? SourceSpan.Undefined,
attribute);
return diagnostic;
}

public static readonly RazorDiagnosticDescriptor BindAttribute_MissingBindSet =
new RazorDiagnosticDescriptor(
$"{DiagnosticPrefix}10017",
() => "The attribute '{0}' must have a companion '{1}' attribute.",
RazorDiagnosticSeverity.Error);

public static RazorDiagnostic CreateBindAttribute_MissingBindSet(SourceSpan? source, string attributeGet, string attributeSet)
{
var diagnostic = RazorDiagnostic.Create(
BindAttribute_MissingBindSet,
source ?? SourceSpan.Undefined,
attributeGet,
attributeSet);
return diagnostic;
}

public static readonly RazorDiagnosticDescriptor BindAttributeParameter_InvalidSyntaxBindAndBindGet =
new RazorDiagnosticDescriptor(
$"{DiagnosticPrefix}10018",
() => "Attribute '{0}' can't be used in conjunction with '{0}:get'.",
RazorDiagnosticSeverity.Error);

public static RazorDiagnostic CreateBindAttributeParameter_InvalidSyntaxBindAndBindGet(SourceSpan? source, string attribute)
{
var diagnostic = RazorDiagnostic.Create(
BindAttributeParameter_InvalidSyntaxBindAndBindGet,
source ?? SourceSpan.Undefined,
attribute);
return diagnostic;
}

public static readonly RazorDiagnosticDescriptor BindAttributeParameter_InvalidSyntaxBindSetAfter =
new RazorDiagnosticDescriptor(
$"{DiagnosticPrefix}10019",
() => "Attribute '{0}:after' can not be used with '{0}:set'. Invoke the code in '{0}:after' inside '{0}:set' instead.",
RazorDiagnosticSeverity.Error);

public static RazorDiagnostic CreateBindAttributeParameter_InvalidSyntaxBindSetAfter(SourceSpan? source, string attribute)
{
var diagnostic = RazorDiagnostic.Create(
BindAttributeParameter_InvalidSyntaxBindSetAfter,
source ?? SourceSpan.Undefined,
attribute);
return diagnostic;
}

public static readonly RazorDiagnosticDescriptor BindAttributeParameter_UnsupportedSyntaxBindGetSet =
new RazorDiagnosticDescriptor(
$"{DiagnosticPrefix}10020",
() => "Attribute '{0}' can only be used with RazorLanguageVersion 7.0 or higher.",
RazorDiagnosticSeverity.Error);

public static RazorDiagnostic CreateBindAttributeParameter_UnsupportedSyntaxBindGetSet(SourceSpan? source, string attribute)
{
var diagnostic = RazorDiagnostic.Create(
BindAttributeParameter_UnsupportedSyntaxBindGetSet,
source ?? SourceSpan.Undefined,
attribute);
return diagnostic;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ public static class Bind

public const string TagHelperKind = "Components.Bind";

public const string BindAttributeGetSet = "Components.Bind.AlternativeNotation";

public const string FallbackKey = "Components.Bind.Fallback";

public const string TypeAttribute = "Components.Bind.TypeAttribute";
Expand Down Expand Up @@ -91,6 +93,8 @@ public static class Component

public const string DelegateSignatureKey = "Components.DelegateSignature";

public const string DelegateWithAwaitableResultKey = "Components.IsDelegateAwaitableResult";

public const string EventCallbackKey = "Components.EventCallback";

public const string WeaklyTypedKey = "Components.IsWeaklyTyped";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ public static class RuntimeHelpers
{
public const string TypeCheck = "global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck";
public const string CreateInferredEventCallback = "global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.CreateInferredEventCallback";
public const string InvokeSynchronousDelegate = "global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.InvokeSynchronousDelegate";
public const string InvokeAsynchronousDelegate = "global::Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.InvokeAsynchronousDelegate";
}

public static class RouteAttribute
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ public static bool IsDelegateProperty(this BoundAttributeDescriptor attribute)
string.Equals(value, bool.TrueString);
}

public static bool IsDelegateWithAwaitableResult(this BoundAttributeDescriptor attribute)
{
var key = ComponentMetadata.Component.DelegateWithAwaitableResultKey;
return
attribute.Metadata.TryGetValue(key, out var value) &&
string.Equals(value, bool.TrueString);
}

/// <summary>
/// Gets a value indicating whether the attribute is of type <c>EventCallback</c> or
/// <c>EventCallback{T}</c>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public static bool IsInputElementBindTagHelper(this TagHelperDescriptor tagHelpe
{
return
tagHelper.IsBindTagHelper() &&
tagHelper.TagMatchingRules.Count == 1 &&
tagHelper.TagMatchingRules.Count == 2 &&
string.Equals("input", tagHelper.TagMatchingRules[0].TagName);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable disable
Expand Down Expand Up @@ -58,7 +58,7 @@ private string Tree
}
}

private string DebuggerToString()
internal string DebuggerToString()
{
var formatter = new DebuggerDisplayFormatter();
formatter.FormatNode(this);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable disable

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace Microsoft.AspNetCore.Razor.Language.Intermediate;

[DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")]
public struct IntermediateNodeReference
{
public IntermediateNodeReference(IntermediateNode parent, IntermediateNode node)
Expand Down Expand Up @@ -207,4 +209,9 @@ public IntermediateNodeReference Replace(IntermediateNode node)
Parent.Children[index] = node;
return new IntermediateNodeReference(Parent, node);
}

private string GetDebuggerDisplay()
{
return $"ref: {Parent.DebuggerToString()} - {Node.DebuggerToString()}";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ public sealed class RazorLanguageVersion : IEquatable<RazorLanguageVersion>, ICo

public static readonly RazorLanguageVersion Version_6_0 = new RazorLanguageVersion(6, 0);

public static readonly RazorLanguageVersion Latest = Version_6_0;
public static readonly RazorLanguageVersion Version_7_0 = new RazorLanguageVersion(7, 0);

public static readonly RazorLanguageVersion Latest = Version_7_0;

public static readonly RazorLanguageVersion Experimental = new RazorLanguageVersion(1337, 1337);

Expand All @@ -47,6 +49,11 @@ public static bool TryParse(string languageVersion, out RazorLanguageVersion ver
version = Experimental;
return true;
}
else if (languageVersion == "7.0")
{
version = Version_7_0;
return true;
}
else if (languageVersion == "6.0")
{
version = Version_6_0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ private static void AddComponentFeatures(RazorProjectEngineBuilder builder, Razo
builder.Features.Add(new ComponentKeyLoweringPass());
builder.Features.Add(new ComponentReferenceCaptureLoweringPass());
builder.Features.Add(new ComponentSplatLoweringPass());
builder.Features.Add(new ComponentBindLoweringPass());
builder.Features.Add(new ComponentBindLoweringPass(razorLanguageVersion.CompareTo(RazorLanguageVersion.Version_7_0) >= 0));
builder.Features.Add(new ComponentCssScopePass());
builder.Features.Add(new ComponentTemplateDiagnosticPass());
builder.Features.Add(new ComponentGenericTypePass());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Microsoft.AspNetCore.Razor.Language;

[DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")]
public abstract class TagHelperDescriptor : IEquatable<TagHelperDescriptor>
{
private IEnumerable<RazorDiagnostic> _allDiagnostics;
Expand Down Expand Up @@ -137,4 +139,9 @@ public ParsedTypeInformation(bool success, StringSegment @namespace, StringSegme
public StringSegment Namespace { get; }
public StringSegment TypeName { get; }
}

private string GetDebuggerDisplay()
{
return $"{DisplayName} - {string.Join(" | ", TagMatchingRules.Select(r => r.GetDebuggerDisplay()))}";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Microsoft.AspNetCore.Razor.Language;

[DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")]
public abstract class TagMatchingRuleDescriptor : IEquatable<TagMatchingRuleDescriptor>
{
private int? _hashCode;
Expand Down Expand Up @@ -65,4 +67,29 @@ public override int GetHashCode()
_hashCode ??= TagMatchingRuleDescriptorComparer.Default.GetHashCode(this);
return _hashCode.Value;
}

internal string GetDebuggerDisplay()
{
var tagName = TagName ?? "*";
tagName += TagStructure == TagStructure.WithoutEndTag ? "/" : "";
return $"{TagName ?? "*"}[{string.Join(", ", Attributes.Select(a => DescribeAttribute(a)))}]";
static string DescribeAttribute(RequiredAttributeDescriptor attribute)
{
var name = attribute.Name switch
{
null => "*",
var prefix when attribute.NameComparison == RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch => $"^{prefix}",
var full => full,
};

var value = attribute.Value switch
{
null => "",
var prefix when attribute.ValueComparison == RequiredAttributeDescriptor.ValueComparisonMode.PrefixMatch => $"^={prefix}",
var suffix when attribute.ValueComparison == RequiredAttributeDescriptor.ValueComparisonMode.SuffixMatch => $"$={suffix}",
var full => $"={full}",
};
return name + value;
}
}
}
Loading

0 comments on commit c34a2e3

Please sign in to comment.