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

Support @bind:get, @bind:set, @bind:after #70

Merged
merged 33 commits into from
Jun 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
ed7692a
Initial support for 'bind:after' in elements
javiercn Feb 4, 2022
d4b1225
Support @bind:get and @bind:set
javiercn Feb 7, 2022
3fe7011
Baselines
javiercn Feb 7, 2022
9b5bc10
Align code generation with runtime
javiercn Feb 9, 2022
d2f775b
tmp
javiercn Feb 10, 2022
61d078e
Fix tests and update baselines
javiercn Feb 10, 2022
addd707
Code generation for component bindings
javiercn Feb 10, 2022
f88b09c
Fixes
javiercn Feb 11, 2022
beefae6
Make component bindings work
javiercn Feb 11, 2022
3d42627
Code generation fixes
javiercn Feb 11, 2022
ad8d836
Get set after baselines
javiercn Feb 11, 2022
0713ea4
Update other baselines
javiercn Feb 11, 2022
48530b1
Update runtime helpers and update code gen
javiercn Feb 11, 2022
770c516
update baselines
javiercn Feb 11, 2022
2214496
Add additional tests for checking errors
javiercn Feb 16, 2022
c44cb60
Add baselines for error cases
javiercn Feb 16, 2022
9da42b2
renames and test fixes
javiercn Feb 17, 2022
72f199f
Cleanups
javiercn Mar 11, 2022
2dffd55
Add additional test asserts for the new parameters
javiercn Mar 11, 2022
d2d4b4d
Remove unnecessary change to ComponentAttributeIntermediateNode and u…
javiercn Mar 11, 2022
ac02503
Fix bad refactoring
javiercn Mar 14, 2022
39483c8
Undo accidental test renames
javiercn Mar 14, 2022
fab5b4e
Fix baselines
javiercn Mar 14, 2022
473432c
Fix incorrect 'async' added to a method
javiercn Mar 14, 2022
c44b5aa
Fix regression
javiercn Mar 15, 2022
dc35dcc
Clarifying comment
javiercn Mar 15, 2022
cfe3530
Add version 7.0 to RazorLanguageVersion, check for Version 7.0 for bi…
javiercn Mar 15, 2022
2441c43
Update src/Microsoft.AspNetCore.Razor.Language/src/Components/Compone…
javiercn Mar 23, 2022
feb4d63
Update baseline
javiercn Mar 24, 2022
7151eb5
Unskip test
javiercn Jun 21, 2022
0f2c6cd
Add tests to cover generics and type inference
javiercn Jun 21, 2022
eaab3d6
Update the Razor Language Version on the SDK
javiercn Jun 21, 2022
a8ed31c
Update baselines
javiercn Jun 21, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd be interested to see how these helpers will look (e.g., their range of overloads). Does that info exist anywhere yet?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

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