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); }
/// <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); }