public static BoundAttributeDescriptionInfo From(BoundAttributeDescriptor boundAttribute, bool indexer, string?parentTagHelperTypeName)
        {
            if (boundAttribute is null)
            {
                throw new ArgumentNullException(nameof(boundAttribute));
            }

            var returnTypeName = indexer ? boundAttribute.IndexerTypeName : boundAttribute.TypeName;
            var propertyName   = boundAttribute.GetPropertyName();

            if (parentTagHelperTypeName == null)
            {
                // The BoundAttributeDescriptor does not directly have the TagHelperTypeName information available. Because of this we need to resolve it from other parts of it.
                parentTagHelperTypeName = ResolveTagHelperTypeName(returnTypeName, propertyName, boundAttribute.DisplayName);
            }

            var descriptionInfo = new BoundAttributeDescriptionInfo(
                returnTypeName,
                parentTagHelperTypeName,
                propertyName,
                boundAttribute.Documentation);

            return(descriptionInfo);
        }
        private List <TagHelperDescriptor> CreateComponentBindTagHelpers(ICollection <TagHelperDescriptor> tagHelpers)
        {
            var results = new List <TagHelperDescriptor>();

            foreach (var tagHelper in tagHelpers)
            {
                if (!tagHelper.IsComponentTagHelper())
                {
                    continue;
                }

                // We want to create a 'bind' tag helper everywhere we see a pair of properties like `Foo`, `FooChanged`
                // where `FooChanged` is a delegate and `Foo` is not.
                //
                // The easiest way to figure this out without a lot of backtracking is to look for `FooChanged` and then
                // try to find a matching "Foo".
                //
                // We also look for a corresponding FooExpression attribute, though its presence is optional.
                for (var i = 0; i < tagHelper.BoundAttributes.Count; i++)
                {
                    var changeAttribute = tagHelper.BoundAttributes[i];
                    if (!changeAttribute.Name.EndsWith("Changed", StringComparison.Ordinal) ||

                        // Allow the ValueChanged attribute to be a delegate or EventCallback<>.
                        //
                        // We assume that the Delegate or EventCallback<> has a matching type, and the C# compiler will help
                        // you figure figure it out if you did it wrongly.
                        (!changeAttribute.IsDelegateProperty() && !changeAttribute.IsEventCallbackProperty()))
                    {
                        continue;
                    }

                    BoundAttributeDescriptor valueAttribute      = null;
                    BoundAttributeDescriptor expressionAttribute = null;
                    var valueAttributeName      = changeAttribute.Name.Substring(0, changeAttribute.Name.Length - "Changed".Length);
                    var expressionAttributeName = valueAttributeName + "Expression";
                    for (var j = 0; j < tagHelper.BoundAttributes.Count; j++)
                    {
                        if (tagHelper.BoundAttributes[j].Name == valueAttributeName)
                        {
                            valueAttribute = tagHelper.BoundAttributes[j];
                        }

                        if (tagHelper.BoundAttributes[j].Name == expressionAttributeName)
                        {
                            expressionAttribute = tagHelper.BoundAttributes[j];
                        }

                        if (valueAttribute != null && expressionAttribute != null)
                        {
                            // We found both, so we can stop looking now
                            break;
                        }
                    }

                    if (valueAttribute == null)
                    {
                        // No matching attribute found.
                        continue;
                    }

                    var builder = TagHelperDescriptorBuilder.Create(ComponentMetadata.Bind.TagHelperKind, tagHelper.Name, tagHelper.AssemblyName);
                    builder.DisplayName   = tagHelper.DisplayName;
                    builder.CaseSensitive = true;
                    builder.Documentation = string.Format(
                        CultureInfo.CurrentCulture,
                        ComponentResources.BindTagHelper_Component_Documentation,
                        valueAttribute.Name,
                        changeAttribute.Name);

                    builder.Metadata.Add(ComponentMetadata.SpecialKindKey, ComponentMetadata.Bind.TagHelperKind);
                    builder.Metadata[TagHelperMetadata.Runtime.Name]         = ComponentMetadata.Bind.RuntimeName;
                    builder.Metadata[ComponentMetadata.Bind.ValueAttribute]  = valueAttribute.Name;
                    builder.Metadata[ComponentMetadata.Bind.ChangeAttribute] = changeAttribute.Name;

                    if (expressionAttribute != null)
                    {
                        builder.Metadata[ComponentMetadata.Bind.ExpressionAttribute] = expressionAttribute.Name;
                    }

                    // WTE has a bug 15.7p1 where a Tag Helper without a display-name that looks like
                    // a C# property will crash trying to create the toolips.
                    builder.SetTypeName(tagHelper.GetTypeName());

                    // Match the component and attribute name
                    builder.TagMatchingRule(rule =>
                    {
                        rule.TagName = tagHelper.TagMatchingRules.Single().TagName;
                        rule.Attribute(attribute =>
                        {
                            attribute.Name = "@bind-" + valueAttribute.Name;
                            attribute.NameComparisonMode = RequiredAttributeDescriptor.NameComparisonMode.FullMatch;
                            attribute.Metadata[ComponentMetadata.Common.DirectiveAttribute] = bool.TrueString;
                        });
                    });

                    builder.BindAttribute(attribute =>
                    {
                        attribute.Metadata[ComponentMetadata.Common.DirectiveAttribute] = bool.TrueString;
                        attribute.Documentation = string.Format(
                            CultureInfo.CurrentCulture,
                            ComponentResources.BindTagHelper_Component_Documentation,
                            valueAttribute.Name,
                            changeAttribute.Name);

                        attribute.Name     = "@bind-" + valueAttribute.Name;
                        attribute.TypeName = changeAttribute.TypeName;
                        attribute.IsEnum   = valueAttribute.IsEnum;

                        // WTE has a bug 15.7p1 where a Tag Helper without a display-name that looks like
                        // a C# property will crash trying to create the toolips.
                        attribute.SetPropertyName(valueAttribute.GetPropertyName());
                    });

                    if (tagHelper.IsComponentFullyQualifiedNameMatch())
                    {
                        builder.Metadata[ComponentMetadata.Component.NameMatchKey] = ComponentMetadata.Component.FullyQualifiedNameMatch;
                    }

                    results.Add(builder.Build());
                }
            }

            return(results);
        }
예제 #3
0
        private List <TagHelperDescriptor> CreateComponentBindTagHelpers(ICollection <TagHelperDescriptor> tagHelpers)
        {
            var results = new List <TagHelperDescriptor>();

            foreach (var tagHelper in tagHelpers)
            {
                if (!tagHelper.IsComponentTagHelper())
                {
                    continue;
                }

                // We want to create a 'bind' tag helper everywhere we see a pair of properties like `Foo`, `FooChanged`
                // where `FooChanged` is a delegate and `Foo` is not.
                //
                // The easiest way to figure this out without a lot of backtracking is to look for `FooChanged` and then
                // try to find a matching "Foo".
                for (var i = 0; i < tagHelper.BoundAttributes.Count; i++)
                {
                    var changeAttribute = tagHelper.BoundAttributes[i];
                    if (!changeAttribute.Name.EndsWith("Changed") || !changeAttribute.IsDelegateProperty())
                    {
                        continue;
                    }

                    BoundAttributeDescriptor valueAttribute = null;
                    var valueAttributeName = changeAttribute.Name.Substring(0, changeAttribute.Name.Length - "Changed".Length);
                    for (var j = 0; j < tagHelper.BoundAttributes.Count; j++)
                    {
                        if (tagHelper.BoundAttributes[j].Name == valueAttributeName && !tagHelper.BoundAttributes[j].IsDelegateProperty())
                        {
                            valueAttribute = tagHelper.BoundAttributes[j];
                            break;
                        }
                    }

                    if (valueAttribute == null)
                    {
                        // No matching attribute found.
                        continue;
                    }

                    var builder = TagHelperDescriptorBuilder.Create(BlazorMetadata.Bind.TagHelperKind, tagHelper.Name, tagHelper.AssemblyName);
                    builder.DisplayName   = tagHelper.DisplayName;
                    builder.Documentation = string.Format(
                        Resources.BindTagHelper_Component_Documentation,
                        valueAttribute.Name,
                        changeAttribute.Name);

                    builder.Metadata.Add(BlazorMetadata.SpecialKindKey, BlazorMetadata.Bind.TagHelperKind);
                    builder.Metadata[TagHelperMetadata.Runtime.Name]      = BlazorMetadata.Bind.RuntimeName;
                    builder.Metadata[BlazorMetadata.Bind.ValueAttribute]  = valueAttribute.Name;
                    builder.Metadata[BlazorMetadata.Bind.ChangeAttribute] = changeAttribute.Name;

                    // WTE has a bug 15.7p1 where a Tag Helper without a display-name that looks like
                    // a C# property will crash trying to create the toolips.
                    builder.SetTypeName(tagHelper.GetTypeName());

                    // Match the component and attribute name
                    builder.TagMatchingRule(rule =>
                    {
                        rule.TagName = tagHelper.TagMatchingRules.Single().TagName;
                        rule.Attribute(attribute =>
                        {
                            attribute.Name = "bind-" + valueAttribute.Name;
                            attribute.NameComparisonMode = RequiredAttributeDescriptor.NameComparisonMode.FullMatch;
                        });
                    });

                    builder.BindAttribute(attribute =>
                    {
                        attribute.Documentation = string.Format(
                            Resources.BindTagHelper_Component_Documentation,
                            valueAttribute.Name,
                            changeAttribute.Name);

                        attribute.Name     = "bind-" + valueAttribute.Name;
                        attribute.TypeName = valueAttribute.TypeName;
                        attribute.IsEnum   = valueAttribute.IsEnum;

                        // WTE has a bug 15.7p1 where a Tag Helper without a display-name that looks like
                        // a C# property will crash trying to create the toolips.
                        attribute.SetPropertyName(valueAttribute.GetPropertyName());
                    });

                    results.Add(builder.Build());
                }
            }

            return(results);
        }