示例#1
0
        public static TagMode GetTagMode(
            MarkupTagBlockSyntax tagBlock,
            TagHelperBinding bindingResult,
            ErrorSink errorSink)
        {
            var childSpan = tagBlock.GetLastToken()?.Parent;

            // Self-closing tags are always valid despite descriptors[X].TagStructure.
            if (childSpan?.GetContent().EndsWith("/>", StringComparison.Ordinal) ?? false)
            {
                return(TagMode.SelfClosing);
            }

            foreach (var descriptor in bindingResult.Descriptors)
            {
                var boundRules     = bindingResult.GetBoundRules(descriptor);
                var nonDefaultRule = boundRules.FirstOrDefault(rule => rule.TagStructure != TagStructure.Unspecified);

                if (nonDefaultRule?.TagStructure == TagStructure.WithoutEndTag)
                {
                    return(TagMode.StartTagOnly);
                }
            }

            return(TagMode.StartTagAndEndTag);
        }
示例#2
0
 public override IEnumerable <BoundAttributeDescriptor> GetBoundTagHelperAttributes(
     TagHelperDocumentContext documentContext,
     string attributeName,
     TagHelperBinding binding)
 {
     return(_tagHelperFactsService.GetBoundTagHelperAttributes(documentContext, attributeName, binding));
 }
示例#3
0
            private void ValidateBinding(
                TagHelperBinding bindingResult,
                string tagName,
                MarkupStartTagSyntax tagBlock)
            {
                // Ensure that all descriptors associated with this tag have appropriate TagStructures. Cannot have
                // multiple descriptors that expect different TagStructures (other than TagStructure.Unspecified).
                TagHelperDescriptor baseDescriptor = null;
                TagStructure?       baseStructure  = null;

                foreach (var descriptor in bindingResult.Descriptors)
                {
                    var boundRules = bindingResult.Mappings[descriptor];
                    foreach (var rule in boundRules)
                    {
                        if (rule.TagStructure != TagStructure.Unspecified)
                        {
                            // Can't have a set of TagHelpers that expect different structures.
                            if (baseStructure.HasValue && baseStructure != rule.TagStructure)
                            {
                                _errorSink.OnError(
                                    RazorDiagnosticFactory.CreateTagHelper_InconsistentTagStructure(
                                        new SourceSpan(tagBlock.GetSourceLocation(_source), tagBlock.FullWidth),
                                        baseDescriptor.DisplayName,
                                        descriptor.DisplayName,
                                        tagName));
                            }

                            baseDescriptor = descriptor;
                            baseStructure  = rule.TagStructure;
                        }
                    }
                }
            }
示例#4
0
        private static TagMode GetTagMode(
            string tagName,
            Block beginTagBlock,
            TagHelperBinding bindingResult,
            ErrorSink errorSink)
        {
            var childSpan = beginTagBlock.FindLastDescendentSpan();

            // Self-closing tags are always valid despite descriptors[X].TagStructure.
            if (childSpan?.Content.EndsWith("/>", StringComparison.Ordinal) ?? false)
            {
                return(TagMode.SelfClosing);
            }

            foreach (var descriptor in bindingResult.Descriptors)
            {
                var boundRules     = bindingResult.GetBoundRules(descriptor);
                var nonDefaultRule = boundRules.FirstOrDefault(rule => rule.TagStructure != TagStructure.Unspecified);

                if (nonDefaultRule?.TagStructure == TagStructure.WithoutEndTag)
                {
                    return(TagMode.StartTagOnly);
                }
            }

            return(TagMode.StartTagAndEndTag);
        }
示例#5
0
 public TagHelperInfo(
     string tagName,
     TagMode tagMode,
     TagHelperBinding bindingResult)
 {
     TagName       = tagName;
     TagMode       = tagMode;
     BindingResult = bindingResult;
 }
示例#6
0
        public TagHelperSpanInternal(SourceSpan span, TagHelperBinding binding)
        {
            if (binding == null)
            {
                throw new ArgumentNullException(nameof(binding));
            }

            Span    = span;
            Binding = binding;
        }
示例#7
0
        public static TagHelperBlockBuilder Rewrite(
            string tagName,
            bool validStructure,
            Block tag,
            TagHelperBinding bindingResult,
            ErrorSink errorSink)
        {
            // There will always be at least one child for the '<'.
            var start      = tag.Children.First().Start;
            var attributes = GetTagAttributes(tagName, validStructure, tag, bindingResult, errorSink);
            var tagMode    = GetTagMode(tagName, tag, bindingResult, errorSink);

            return(new TagHelperBlockBuilder(tagName, tagMode, start, attributes, bindingResult));
        }
示例#8
0
        public static MarkupTagHelperStartTagSyntax Rewrite(
            string tagName,
            bool validStructure,
            RazorParserFeatureFlags featureFlags,
            MarkupTagBlockSyntax tag,
            TagHelperBinding bindingResult,
            ErrorSink errorSink,
            RazorSourceDocument source)
        {
            // There will always be at least one child for the '<'.
            var rewrittenChildren = GetRewrittenChildren(tagName, validStructure, tag, bindingResult, featureFlags, errorSink, source);

            return(SyntaxFactory.MarkupTagHelperStartTag(rewrittenChildren));
        }
 private static bool BindingsMatch(TagHelperBinding left, TagHelperBinding right)
 {
     foreach (var leftDescriptor in left.Descriptors)
     {
         foreach (var rightDescriptor in right.Descriptors)
         {
             if (leftDescriptor.Equals(rightDescriptor))
             {
                 return(true);
             }
         }
     }
     return(false);
 }
 /// <summary>
 /// Instantiates a new instance of the <see cref="TagHelperBlockBuilder"/> class
 /// with the provided values.
 /// </summary>
 /// <param name="tagName">An HTML tag name.</param>
 /// <param name="tagMode">HTML syntax of the element in the Razor source.</param>
 /// <param name="start">Starting location of the <see cref="TagHelperBlock"/>.</param>
 /// <param name="attributes">Attributes of the <see cref="TagHelperBlock"/>.</param>
 /// <param name="bindingResult"></param>
 public TagHelperBlockBuilder(
     string tagName,
     TagMode tagMode,
     SourceLocation start,
     IList <TagHelperAttributeNode> attributes,
     TagHelperBinding bindingResult)
 {
     TagName        = tagName;
     TagMode        = tagMode;
     Start          = start;
     BindingResult  = bindingResult;
     Attributes     = new List <TagHelperAttributeNode>(attributes);
     Type           = BlockKindInternal.Tag;
     ChunkGenerator = new TagHelperChunkGenerator();
 }
示例#11
0
        public static TagMode GetTagMode(
            MarkupStartTagSyntax startTag,
            MarkupEndTagSyntax endTag,
            TagHelperBinding bindingResult)
        {
            var childSpan = startTag.GetLastToken()?.Parent;

            // Self-closing tags are always valid despite descriptors[X].TagStructure.
            if (childSpan?.GetContent().EndsWith("/>", StringComparison.Ordinal) ?? false)
            {
                return(TagMode.SelfClosing);
            }

            var hasDirectiveAttribute = false;

            foreach (var descriptor in bindingResult.Descriptors)
            {
                var boundRules     = bindingResult.Mappings[descriptor];
                var nonDefaultRule = boundRules.FirstOrDefault(rule => rule.TagStructure != TagStructure.Unspecified);

                if (nonDefaultRule?.TagStructure == TagStructure.WithoutEndTag)
                {
                    return(TagMode.StartTagOnly);
                }

                // Directive attribute will tolerate forms that don't work for tag helpers. For instance:
                //
                // <input @onclick="..."> vs <input onclick="..." />
                //
                // We don't want this to become an error just because you added a directive attribute.
                if (descriptor.IsAnyComponentDocumentTagHelper() && !descriptor.IsComponentOrChildContentTagHelper())
                {
                    hasDirectiveAttribute = true;
                }
            }

            if (hasDirectiveAttribute && startTag.IsVoidElement() && endTag == null)
            {
                return(TagMode.StartTagOnly);
            }

            return(TagMode.StartTagAndEndTag);
        }
示例#12
0
        public override IEnumerable <BoundAttributeDescriptor> GetBoundTagHelperAttributes(
            TagHelperDocumentContext documentContext,
            string attributeName,
            TagHelperBinding binding)
        {
            if (documentContext == null)
            {
                throw new ArgumentNullException(nameof(documentContext));
            }

            if (attributeName == null)
            {
                throw new ArgumentNullException(nameof(attributeName));
            }

            if (binding == null)
            {
                throw new ArgumentNullException(nameof(binding));
            }

            var matchingBoundAttributes = new List <BoundAttributeDescriptor>();

            foreach (var descriptor in binding.Descriptors)
            {
                foreach (var boundAttributeDescriptor in descriptor.BoundAttributes)
                {
                    if (TagHelperMatchingConventions.CanSatisfyBoundAttribute(attributeName, boundAttributeDescriptor))
                    {
                        matchingBoundAttributes.Add(boundAttributeDescriptor);

                        // Only one bound attribute can match an attribute
                        break;
                    }
                }
            }

            return(matchingBoundAttributes);
        }
示例#13
0
        private static void ValidateBinding(
            TagHelperBinding bindingResult,
            string tagName,
            Block tagBlock,
            ErrorSink errorSink)
        {
            // Ensure that all descriptors associated with this tag have appropriate TagStructures. Cannot have
            // multiple descriptors that expect different TagStructures (other than TagStructure.Unspecified).
            TagHelperDescriptor baseDescriptor = null;
            TagStructure?       baseStructure  = null;

            foreach (var descriptor in bindingResult.Descriptors)
            {
                var boundRules = bindingResult.GetBoundRules(descriptor);
                foreach (var rule in boundRules)
                {
                    if (rule.TagStructure != TagStructure.Unspecified)
                    {
                        // Can't have a set of TagHelpers that expect different structures.
                        if (baseStructure.HasValue && baseStructure != rule.TagStructure)
                        {
                            errorSink.OnError(
                                tagBlock.Start,
                                LegacyResources.FormatTagHelperParseTreeRewriter_InconsistentTagStructure(
                                    baseDescriptor.DisplayName,
                                    descriptor.DisplayName,
                                    tagName,
                                    nameof(TagMatchingRuleDescriptor.TagStructure)),
                                tagBlock.Length);
                        }

                        baseDescriptor = descriptor;
                        baseStructure  = rule.TagStructure;
                    }
                }
            }
        }
示例#14
0
 public abstract IEnumerable <BoundAttributeDescriptor> GetBoundTagHelperAttributes(TagHelperDocumentContext documentContext, string attributeName, TagHelperBinding binding);
示例#15
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);
    }
示例#16
0
        private static SyntaxList <RazorSyntaxNode> GetRewrittenChildren(
            string tagName,
            bool validStructure,
            MarkupTagBlockSyntax tagBlock,
            TagHelperBinding bindingResult,
            RazorParserFeatureFlags featureFlags,
            ErrorSink errorSink,
            RazorSourceDocument source)
        {
            var tagHelperBuilder = SyntaxListBuilder <RazorSyntaxNode> .Create();

            var processedBoundAttributeNames = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

            if (tagBlock.Children.Count == 1)
            {
                // Tag with no attributes. We have nothing to rewrite here.
                return(tagBlock.Children);
            }

            // Add the tag start
            tagHelperBuilder.Add(tagBlock.Children.First());

            // We skip the first child "<tagname" and take everything up to the ending portion of the tag ">" or "/>".
            // If the tag does not have a valid structure then there's no close angle to ignore.
            var tokenOffset = validStructure ? 1 : 0;

            for (var i = 1; i < tagBlock.Children.Count - tokenOffset; i++)
            {
                var            isMinimized           = false;
                var            attributeNameLocation = SourceLocation.Undefined;
                var            child = tagBlock.Children[i];
                TryParseResult result;
                if (child is MarkupAttributeBlockSyntax attributeBlock)
                {
                    attributeNameLocation = attributeBlock.Name.GetSourceLocation(source);
                    result = TryParseAttribute(
                        tagName,
                        attributeBlock,
                        bindingResult.Descriptors,
                        errorSink,
                        processedBoundAttributeNames);
                    tagHelperBuilder.Add(result.RewrittenAttribute);
                }
                else if (child is MarkupMinimizedAttributeBlockSyntax minimizedAttributeBlock)
                {
                    isMinimized           = true;
                    attributeNameLocation = minimizedAttributeBlock.Name.GetSourceLocation(source);
                    result = TryParseMinimizedAttribute(
                        tagName,
                        minimizedAttributeBlock,
                        bindingResult.Descriptors,
                        errorSink,
                        processedBoundAttributeNames);
                    tagHelperBuilder.Add(result.RewrittenAttribute);
                }
                else if (child is CSharpCodeBlockSyntax)
                {
                    // TODO: Accept more than just Markup attributes: https://github.com/aspnet/Razor/issues/96.
                    // Something like:
                    // <input @checked />
                    var location   = new SourceSpan(child.GetSourceLocation(source), child.FullWidth);
                    var diagnostic = RazorDiagnosticFactory.CreateParsing_TagHelpersCannotHaveCSharpInTagDeclaration(location, tagName);
                    errorSink.OnError(diagnostic);

                    result = null;
                }
                else if (child is MarkupTextLiteralSyntax)
                {
                    // If the original span content was whitespace it ultimately means the tag
                    // that owns this "attribute" is malformed and is expecting a user to type a new attribute.
                    // ex: <myTH class="btn"| |
                    var literalContent = child.GetContent();
                    if (!string.IsNullOrWhiteSpace(literalContent))
                    {
                        var location   = child.GetSourceSpan(source);
                        var diagnostic = RazorDiagnosticFactory.CreateParsing_TagHelperAttributeListMustBeWellFormed(location);
                        errorSink.OnError(diagnostic);
                    }
                    result = null;
                }
                else
                {
                    result = null;
                }

                // Only want to track the attribute if we succeeded in parsing its corresponding Block/Span.
                if (result == null)
                {
                    // Error occurred while parsing the attribute. Don't try parsing the rest to avoid misleading errors.
                    for (var j = i; j < tagBlock.Children.Count; j++)
                    {
                        tagHelperBuilder.Add(tagBlock.Children[j]);
                    }

                    return(tagHelperBuilder.ToList());
                }

                // Check if it's a non-boolean bound attribute that is minimized or if it's a bound
                // non-string attribute that has null or whitespace content.
                var isValidMinimizedAttribute = featureFlags.AllowMinimizedBooleanTagHelperAttributes && result.IsBoundBooleanAttribute;
                if ((isMinimized &&
                     result.IsBoundAttribute &&
                     !isValidMinimizedAttribute) ||
                    (!isMinimized &&
                     result.IsBoundNonStringAttribute &&
                     string.IsNullOrWhiteSpace(GetAttributeValueContent(result.RewrittenAttribute))))
                {
                    var errorLocation    = new SourceSpan(attributeNameLocation, result.AttributeName.Length);
                    var propertyTypeName = GetPropertyType(result.AttributeName, bindingResult.Descriptors);
                    var diagnostic       = RazorDiagnosticFactory.CreateTagHelper_EmptyBoundAttribute(errorLocation, result.AttributeName, tagName, propertyTypeName);
                    errorSink.OnError(diagnostic);
                }

                // Check if the attribute was a prefix match for a tag helper dictionary property but the
                // dictionary key would be the empty string.
                if (result.IsMissingDictionaryKey)
                {
                    var errorLocation = new SourceSpan(attributeNameLocation, result.AttributeName.Length);
                    var diagnostic    = RazorDiagnosticFactory.CreateParsing_TagHelperIndexerAttributeNameMustIncludeKey(errorLocation, result.AttributeName, tagName);
                    errorSink.OnError(diagnostic);
                }
            }

            if (validStructure)
            {
                // Add the tag end.
                tagHelperBuilder.Add(tagBlock.Children[tagBlock.Children.Count - 1]);
            }

            return(tagHelperBuilder.ToList());
        }
示例#17
0
        public static MarkupTagHelperStartTagSyntax Rewrite(
            string tagName,
            RazorParserFeatureFlags featureFlags,
            MarkupStartTagSyntax startTag,
            TagHelperBinding bindingResult,
            ErrorSink errorSink,
            RazorSourceDocument source)
        {
            var processedBoundAttributeNames = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

            var attributes       = startTag.Attributes;
            var attributeBuilder = SyntaxListBuilder <RazorSyntaxNode> .Create();

            for (var i = 0; i < startTag.Attributes.Count; i++)
            {
                var            isMinimized           = false;
                var            attributeNameLocation = SourceLocation.Undefined;
                var            child = startTag.Attributes[i];
                TryParseResult result;
                if (child is MarkupAttributeBlockSyntax attributeBlock)
                {
                    attributeNameLocation = attributeBlock.Name.GetSourceLocation(source);
                    result = TryParseAttribute(
                        tagName,
                        attributeBlock,
                        bindingResult.Descriptors,
                        errorSink,
                        processedBoundAttributeNames);
                    attributeBuilder.Add(result.RewrittenAttribute);
                }
                else if (child is MarkupMinimizedAttributeBlockSyntax minimizedAttributeBlock)
                {
                    isMinimized           = true;
                    attributeNameLocation = minimizedAttributeBlock.Name.GetSourceLocation(source);
                    result = TryParseMinimizedAttribute(
                        tagName,
                        minimizedAttributeBlock,
                        bindingResult.Descriptors,
                        errorSink,
                        processedBoundAttributeNames);
                    attributeBuilder.Add(result.RewrittenAttribute);
                }
                else if (child is MarkupMiscAttributeContentSyntax miscContent)
                {
                    foreach (var contentChild in miscContent.Children)
                    {
                        if (contentChild is CSharpCodeBlockSyntax codeBlock)
                        {
                            // TODO: Accept more than just Markup attributes: https://github.com/aspnet/Razor/issues/96.
                            // Something like:
                            // <input @checked />
                            var location   = new SourceSpan(codeBlock.GetSourceLocation(source), codeBlock.FullWidth);
                            var diagnostic = RazorDiagnosticFactory.CreateParsing_TagHelpersCannotHaveCSharpInTagDeclaration(location, tagName);
                            errorSink.OnError(diagnostic);
                            break;
                        }
                        else
                        {
                            // If the original span content was whitespace it ultimately means the tag
                            // that owns this "attribute" is malformed and is expecting a user to type a new attribute.
                            // ex: <myTH class="btn"| |
                            var literalContent = contentChild.GetContent();
                            if (!string.IsNullOrWhiteSpace(literalContent))
                            {
                                var location   = contentChild.GetSourceSpan(source);
                                var diagnostic = RazorDiagnosticFactory.CreateParsing_TagHelperAttributeListMustBeWellFormed(location);
                                errorSink.OnError(diagnostic);
                                break;
                            }
                        }
                    }

                    result = null;
                }
                else
                {
                    result = null;
                }

                // Only want to track the attribute if we succeeded in parsing its corresponding Block/Span.
                if (result == null)
                {
                    // Error occurred while parsing the attribute. Don't try parsing the rest to avoid misleading errors.
                    for (var j = i; j < startTag.Attributes.Count; j++)
                    {
                        attributeBuilder.Add(startTag.Attributes[j]);
                    }

                    break;
                }

                // Check if it's a non-boolean bound attribute that is minimized or if it's a bound
                // non-string attribute that has null or whitespace content.
                var isValidMinimizedAttribute = featureFlags.AllowMinimizedBooleanTagHelperAttributes && result.IsBoundBooleanAttribute;
                if ((isMinimized &&
                     result.IsBoundAttribute &&
                     !isValidMinimizedAttribute) ||
                    (!isMinimized &&
                     result.IsBoundNonStringAttribute &&
                     string.IsNullOrWhiteSpace(GetAttributeValueContent(result.RewrittenAttribute))))
                {
                    var errorLocation    = new SourceSpan(attributeNameLocation, result.AttributeName.Length);
                    var propertyTypeName = GetPropertyType(result.AttributeName, bindingResult.Descriptors);
                    var diagnostic       = RazorDiagnosticFactory.CreateTagHelper_EmptyBoundAttribute(errorLocation, result.AttributeName, tagName, propertyTypeName);
                    errorSink.OnError(diagnostic);
                }

                // Check if the attribute was a prefix match for a tag helper dictionary property but the
                // dictionary key would be the empty string.
                if (result.IsMissingDictionaryKey)
                {
                    var errorLocation = new SourceSpan(attributeNameLocation, result.AttributeName.Length);
                    var diagnostic    = RazorDiagnosticFactory.CreateParsing_TagHelperIndexerAttributeNameMustIncludeKey(errorLocation, result.AttributeName, tagName);
                    errorSink.OnError(diagnostic);
                }
            }

            if (attributeBuilder.Count > 0)
            {
                // This means we rewrote something. Use the new set of attributes.
                attributes = attributeBuilder.ToList();
            }

            var tagHelperStartTag = SyntaxFactory.MarkupTagHelperStartTag(
                startTag.OpenAngle, startTag.Bang, startTag.Name, attributes, startTag.ForwardSlash, startTag.CloseAngle);

            return(tagHelperStartTag.WithSpanContext(startTag.GetSpanContext()));
        }
示例#18
0
        private static IList <TagHelperAttributeNode> GetTagAttributes(
            string tagName,
            bool validStructure,
            Block tagBlock,
            TagHelperBinding bindingResult,
            ErrorSink errorSink,
            RazorParserFeatureFlags featureFlags)
        {
            var attributes = new List <TagHelperAttributeNode>();

            // We skip the first child "<tagname" and take everything up to the ending portion of the tag ">" or "/>".
            // The -2 accounts for both the start and end tags. If the tag does not have a valid structure then there's
            // no end tag to ignore.
            var symbolOffset                 = validStructure ? 2 : 1;
            var attributeChildren            = tagBlock.Children.Skip(1).Take(tagBlock.Children.Count() - symbolOffset);
            var processedBoundAttributeNames = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

            foreach (var child in attributeChildren)
            {
                TryParseResult result;
                if (child.IsBlock)
                {
                    result = TryParseBlock(tagName, (Block)child, bindingResult.Descriptors, errorSink, processedBoundAttributeNames);
                }
                else
                {
                    result = TryParseSpan((Span)child, bindingResult.Descriptors, errorSink, processedBoundAttributeNames);
                }

                // Only want to track the attribute if we succeeded in parsing its corresponding Block/Span.
                if (result != null)
                {
                    // Check if it's a non-boolean bound attribute that is minimized or if it's a bound
                    // non-string attribute that has null or whitespace content.
                    var isMinimized = result.AttributeValueNode == null;
                    var isValidMinimizedAttribute = featureFlags.AllowMinimizedBooleanTagHelperAttributes && result.IsBoundBooleanAttribute;
                    if ((isMinimized &&
                         result.IsBoundAttribute &&
                         !isValidMinimizedAttribute) ||
                        (!isMinimized &&
                         result.IsBoundNonStringAttribute &&
                         IsNullOrWhitespaceAttributeValue(result.AttributeValueNode)))
                    {
                        var errorLocation    = GetAttributeNameLocation(child, result.AttributeName);
                        var propertyTypeName = GetPropertyType(result.AttributeName, bindingResult.Descriptors);
                        var diagnostic       = RazorDiagnosticFactory.CreateTagHelper_EmptyBoundAttribute(errorLocation, result.AttributeName, tagName, propertyTypeName);
                        errorSink.OnError(diagnostic);
                    }

                    // Check if the attribute was a prefix match for a tag helper dictionary property but the
                    // dictionary key would be the empty string.
                    if (result.IsMissingDictionaryKey)
                    {
                        var errorLocation = GetAttributeNameLocation(child, result.AttributeName);
                        var diagnostic    = RazorDiagnosticFactory.CreateParsing_TagHelperIndexerAttributeNameMustIncludeKey(errorLocation, result.AttributeName, tagName);
                        errorSink.OnError(diagnostic);
                    }

                    var attributeNode = new TagHelperAttributeNode(
                        result.AttributeName,
                        result.AttributeValueNode,
                        result.AttributeStructure);

                    attributes.Add(attributeNode);
                }
                else
                {
                    // Error occured while parsing the attribute. Don't try parsing the rest to avoid misleading errors.
                    break;
                }
            }

            return(attributes);
        }
        public void AddEditsForCodeDocument(List <WorkspaceEditDocumentChange> documentChanges, TagHelperBinding originTagHelperBinding, string newName, Uri uri, RazorCodeDocument codeDocument)
        {
            var documentIdentifier = new VersionedTextDocumentIdentifier {
                Uri = uri
            };
            var tagHelperElements = codeDocument.GetSyntaxTree().Root
                                    .DescendantNodes()
                                    .Where(n => n.Kind == SyntaxKind.MarkupTagHelperElement)
                                    .OfType <MarkupTagHelperElementSyntax>();

            foreach (var node in tagHelperElements)
            {
                if (node is MarkupTagHelperElementSyntax tagHelperElement && BindingsMatch(originTagHelperBinding, tagHelperElement.TagHelperInfo.BindingResult))
                {
                    documentChanges.Add(new WorkspaceEditDocumentChange(new TextDocumentEdit
                    {
                        TextDocument = documentIdentifier,
                        Edits        = CreateEditsForMarkupTagHelperElement(tagHelperElement, codeDocument, newName)
                    }));
                }
            }
        }
 private static bool BindingContainsTagHelper(TagHelperDescriptor tagHelper, TagHelperBinding potentialBinding) => potentialBinding.Descriptors.Any(descriptor => descriptor.Equals(tagHelper));
示例#21
0
        private static IList <TagHelperAttributeNode> GetTagAttributes(
            string tagName,
            bool validStructure,
            Block tagBlock,
            TagHelperBinding bindingResult,
            ErrorSink errorSink)
        {
            var attributes = new List <TagHelperAttributeNode>();

            // We skip the first child "<tagname" and take everything up to the ending portion of the tag ">" or "/>".
            // The -2 accounts for both the start and end tags. If the tag does not have a valid structure then there's
            // no end tag to ignore.
            var symbolOffset                 = validStructure ? 2 : 1;
            var attributeChildren            = tagBlock.Children.Skip(1).Take(tagBlock.Children.Count() - symbolOffset);
            var processedBoundAttributeNames = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

            foreach (var child in attributeChildren)
            {
                TryParseResult result;
                if (child.IsBlock)
                {
                    result = TryParseBlock(tagName, (Block)child, bindingResult.Descriptors, errorSink, processedBoundAttributeNames);
                }
                else
                {
                    result = TryParseSpan((Span)child, bindingResult.Descriptors, errorSink, processedBoundAttributeNames);
                }

                // Only want to track the attribute if we succeeded in parsing its corresponding Block/Span.
                if (result != null)
                {
                    SourceLocation?errorLocation = null;

                    // Check if it's a bound attribute that is minimized or if it's a bound non-string attribute that
                    // is null or whitespace.
                    if ((result.IsBoundAttribute && result.AttributeValueNode == null) ||
                        (result.IsBoundNonStringAttribute &&
                         IsNullOrWhitespaceAttributeValue(result.AttributeValueNode)))
                    {
                        errorLocation = GetAttributeNameStartLocation(child);

                        errorSink.OnError(
                            errorLocation.Value,
                            LegacyResources.FormatRewriterError_EmptyTagHelperBoundAttribute(
                                result.AttributeName,
                                tagName,
                                GetPropertyType(result.AttributeName, bindingResult.Descriptors)),
                            result.AttributeName.Length);
                    }

                    // Check if the attribute was a prefix match for a tag helper dictionary property but the
                    // dictionary key would be the empty string.
                    if (result.IsMissingDictionaryKey)
                    {
                        if (!errorLocation.HasValue)
                        {
                            errorLocation = GetAttributeNameStartLocation(child);
                        }

                        errorSink.OnError(
                            errorLocation.Value,
                            LegacyResources.FormatTagHelperBlockRewriter_IndexerAttributeNameMustIncludeKey(
                                result.AttributeName,
                                tagName),
                            result.AttributeName.Length);
                    }

                    var attributeNode = new TagHelperAttributeNode(
                        result.AttributeName,
                        result.AttributeValueNode,
                        result.AttributeStructure);

                    attributes.Add(attributeNode);
                }
                else
                {
                    // Error occured while parsing the attribute. Don't try parsing the rest to avoid misleading errors.
                    break;
                }
            }

            return(attributes);
        }
        public async Task AddEditsForCodeDocument(List <WorkspaceEditDocumentChange> documentChanges, TagHelperBinding originTagHelperBinding, string newName, DocumentSnapshot documentSnapshot, CancellationToken cancellationToken)
        {
            if (documentSnapshot is null)
            {
                return;
            }

            var codeDocument = await documentSnapshot.GetGeneratedOutputAsync().ConfigureAwait(false);

            if (codeDocument.IsUnsupported())
            {
                return;
            }

            if (!FileKinds.IsComponent(codeDocument.GetFileKind()))
            {
                return;
            }

            var uri = new UriBuilder
            {
                Path   = documentSnapshot.FilePath,
                Host   = string.Empty,
                Scheme = Uri.UriSchemeFile,
            }.Uri;

            AddEditsForCodeDocument(documentChanges, originTagHelperBinding, newName, uri, codeDocument);
        }