private Dictionary <string, TagHelperPair> FindMatchingTagHelpers(RazorCodeActionContext context, MarkupStartTagSyntax startTag)
        {
            // Get all data necessary for matching
            var    tagName       = startTag.Name.Content;
            string parentTagName = null;

            if (startTag.Parent?.Parent is MarkupElementSyntax parentElement)
            {
                parentTagName = parentElement.StartTag.Name.Content;
            }
            else if (startTag.Parent?.Parent is MarkupTagHelperElementSyntax parentTagHelperElement)
            {
                parentTagName = parentTagHelperElement.StartTag.Name.Content;
            }

            var attributes = _tagHelperFactsService.StringifyAttributes(startTag.Attributes);

            // Find all matching tag helpers
            var matching = new Dictionary <string, TagHelperPair>();

            foreach (var tagHelper in context.DocumentSnapshot.Project.TagHelpers)
            {
                if (tagHelper.TagMatchingRules.All(rule => TagHelperMatchingConventions.SatisfiesRule(tagName, parentTagName, attributes, rule)))
                {
                    matching.Add(tagHelper.Name, new TagHelperPair {
                        Short = tagHelper
                    });
                }
            }

            // Iterate and find the fully qualified version
            foreach (var tagHelper in context.DocumentSnapshot.Project.TagHelpers)
            {
                if (matching.TryGetValue(tagHelper.Name, out var tagHelperPair))
                {
                    if (tagHelperPair != null && tagHelper != tagHelperPair.Short)
                    {
                        tagHelperPair.FullyQualified = tagHelper;
                    }
                }
            }

            return(matching);
        }
Ejemplo n.º 2
0
    /// <summary>
    /// Gets all tag helpers that match the given HTML tag criteria.
    /// </summary>
    /// <param name="tagName">The name of the HTML tag to match. Providing a '*' tag name
    /// retrieves catch-all <see cref="TagHelperDescriptor"/>s (descriptors that target every tag).</param>
    /// <param name="attributes">Attributes on the HTML tag.</param>
    /// <param name="parentTagName">The parent tag name of the given <paramref name="tagName"/> tag.</param>
    /// <param name="parentIsTagHelper">Is the parent tag of the given <paramref name="tagName"/> tag a tag helper.</param>
    /// <returns><see cref="TagHelperDescriptor"/>s that apply to the given HTML tag criteria.
    /// Will return <c>null</c> if no <see cref="TagHelperDescriptor"/>s are a match.</returns>
    public TagHelperBinding GetBinding(
        string tagName,
        IReadOnlyList <KeyValuePair <string, string> > attributes,
        string parentTagName,
        bool parentIsTagHelper)
    {
        if (!string.IsNullOrEmpty(_tagHelperPrefix) &&
            (tagName.Length <= _tagHelperPrefix.Length ||
             !tagName.StartsWith(_tagHelperPrefix, StringComparison.OrdinalIgnoreCase)))
        {
            // The tagName doesn't have the tag helper prefix, we can short circuit.
            return(null);
        }

        IEnumerable <TagHelperDescriptor> descriptors;

        // Ensure there's a HashSet to use.
        if (!_registrations.TryGetValue(TagHelperMatchingConventions.ElementCatchAllName, out HashSet <TagHelperDescriptor> catchAllDescriptors))
        {
            descriptors = new HashSet <TagHelperDescriptor>(TagHelperDescriptorComparer.Default);
        }
        else
        {
            descriptors = catchAllDescriptors;
        }

        // If we have a tag name associated with the requested name, we need to combine matchingDescriptors
        // with all the catch-all descriptors.
        if (_registrations.TryGetValue(tagName, out HashSet <TagHelperDescriptor> matchingDescriptors))
        {
            descriptors = matchingDescriptors.Concat(descriptors);
        }

        var           tagNameWithoutPrefix       = _tagHelperPrefix != null ? new StringSegment(tagName, _tagHelperPrefix.Length) : tagName;
        StringSegment parentTagNameWithoutPrefix = parentTagName;

        if (_tagHelperPrefix != null && parentIsTagHelper)
        {
            parentTagNameWithoutPrefix = new StringSegment(parentTagName, _tagHelperPrefix.Length);
        }

        Dictionary <TagHelperDescriptor, IReadOnlyList <TagMatchingRuleDescriptor> > applicableDescriptorMappings = null;

        foreach (var descriptor in descriptors)
        {
            // We're avoiding desccriptor.TagMatchingRules.Where and applicableRules.Any() to avoid
            // Enumerator allocations on this hotpath
            List <TagMatchingRuleDescriptor> applicableRules = null;
            for (var i = 0; i < descriptor.TagMatchingRules.Count; i++)
            {
                var rule = descriptor.TagMatchingRules[i];
                if (TagHelperMatchingConventions.SatisfiesRule(tagNameWithoutPrefix, parentTagNameWithoutPrefix, attributes, rule))
                {
                    if (applicableRules is null)
                    {
                        applicableRules = new List <TagMatchingRuleDescriptor>();
                    }

                    applicableRules.Add(rule);
                }
            }

            if (applicableRules != null && applicableRules.Count > 0)
            {
                if (applicableDescriptorMappings == null)
                {
                    applicableDescriptorMappings = new Dictionary <TagHelperDescriptor, IReadOnlyList <TagMatchingRuleDescriptor> >();
                }

                applicableDescriptorMappings[descriptor] = applicableRules;
            }
        }

        if (applicableDescriptorMappings == null)
        {
            return(null);
        }

        var tagHelperBinding = new TagHelperBinding(
            tagName,
            attributes,
            parentTagName,
            applicableDescriptorMappings,
            _tagHelperPrefix);

        return(tagHelperBinding);
    }