private bool TryRewriteTagHelperStart(
            MarkupStartTagSyntax startTag,
            MarkupEndTagSyntax endTag,
            out MarkupTagHelperStartTagSyntax rewritten,
            out TagHelperInfo tagHelperInfo)
        {
            rewritten     = null;
            tagHelperInfo = null;

            // Get tag name of the current block
            var tagName = startTag.GetTagNameWithOptionalBang();

            // Could not determine tag name, it can't be a TagHelper, continue on and track the element.
            if (string.IsNullOrEmpty(tagName) || tagName.StartsWith("!", StringComparison.Ordinal))
            {
                return(false);
            }

            TagHelperBinding tagHelperBinding;

            if (!IsPotentialTagHelperStart(tagName, startTag))
            {
                return(false);
            }

            var tracker      = CurrentTagHelperTracker;
            var tagNameScope = tracker?.TagName ?? string.Empty;

            // We're now in a start tag block, we first need to see if the tag block is a tag helper.
            var elementAttributes = GetAttributeNameValuePairs(startTag);

            tagHelperBinding = _tagHelperBinder.GetBinding(
                tagName,
                elementAttributes,
                CurrentParentTagName,
                CurrentParentIsTagHelper);

            // If there aren't any TagHelperDescriptors registered then we aren't a TagHelper
            if (tagHelperBinding == null)
            {
                // If the current tag matches the current TagHelper scope it means the parent TagHelper matched
                // all the required attributes but the current one did not; therefore, we need to increment the
                // OpenMatchingTags counter for current the TagHelperBlock so we don't end it too early.
                // ex: <myth req="..."><myth></myth></myth> We don't want the first myth to close on the inside
                // tag.
                if (string.Equals(tagNameScope, tagName, StringComparison.OrdinalIgnoreCase))
                {
                    tracker.OpenMatchingTags++;
                }

                return(false);
            }

            ValidateParentAllowsTagHelper(tagName, startTag);
            ValidateBinding(tagHelperBinding, tagName, startTag);

            // We're in a start TagHelper block.
            ValidateStartTagSyntax(tagName, startTag);

            var rewrittenStartTag = TagHelperBlockRewriter.Rewrite(
                tagName,
                _featureFlags,
                startTag,
                tagHelperBinding,
                _errorSink,
                _source);

            var tagMode = TagHelperBlockRewriter.GetTagMode(startTag, endTag, tagHelperBinding);

            tagHelperInfo = new TagHelperInfo(tagName, tagMode, tagHelperBinding);
            rewritten     = rewrittenStartTag;

            return(true);
        }
Example #2
0
            public override SyntaxNode VisitMarkupElement(MarkupElementSyntax node)
            {
                if (IsPartOfStartTag(node))
                {
                    // If this element is inside a start tag, it is some sort of malformed case like
                    // <p @do { someattribute=\"btn\"></p>, where the end "p" tag is inside the start "p" tag.
                    // We don't want to do tag helper parsing for this tag.
                    return(base.VisitMarkupElement(node));
                }

                MarkupTagHelperStartTagSyntax tagHelperStart = null;
                MarkupTagHelperEndTagSyntax   tagHelperEnd   = null;
                TagHelperInfo tagHelperInfo = null;

                // Visit the start tag.
                var startTag = (MarkupTagBlockSyntax)Visit(node.StartTag);

                if (startTag != null)
                {
                    var tagName = startTag.GetTagName();
                    if (TryRewriteTagHelperStart(startTag, out tagHelperStart, out tagHelperInfo))
                    {
                        // This is a tag helper.
                        if (tagHelperInfo.TagMode == TagMode.SelfClosing || tagHelperInfo.TagMode == TagMode.StartTagOnly)
                        {
                            var tagHelperElement   = SyntaxFactory.MarkupTagHelperElement(tagHelperStart, body: new SyntaxList <RazorSyntaxNode>(), endTag: null);
                            var rewrittenTagHelper = tagHelperElement.WithTagHelperInfo(tagHelperInfo);
                            if (node.Body.Count == 0)
                            {
                                return(rewrittenTagHelper);
                            }

                            // This tag contains a body which needs to be moved to the parent.
                            var rewrittenNodes = SyntaxListBuilder <RazorSyntaxNode> .Create();

                            rewrittenNodes.Add(rewrittenTagHelper);
                            var rewrittenBody = VisitList(node.Body);
                            rewrittenNodes.AddRange(rewrittenBody);

                            return(SyntaxFactory.MarkupElement(startTag: null, body: rewrittenNodes.ToList(), endTag: null));
                        }
                        else if (node.EndTag == null)
                        {
                            // Start tag helper with no corresponding end tag.
                            _errorSink.OnError(
                                RazorDiagnosticFactory.CreateParsing_TagHelperFoundMalformedTagHelper(
                                    new SourceSpan(SourceLocationTracker.Advance(startTag.GetSourceLocation(_source), "<"), tagName.Length),
                                    tagName));
                        }
                        else
                        {
                            // Tag helper start tag. Keep track.
                            var tracker = new TagHelperTracker(_tagHelperPrefix, tagHelperInfo);
                            _trackerStack.Push(tracker);
                        }
                    }
                    else
                    {
                        // Non-TagHelper tag.
                        ValidateParentAllowsPlainTag(startTag);

                        if (!startTag.IsSelfClosing() && !startTag.IsVoidElement())
                        {
                            var tracker = new TagTracker(tagName, isTagHelper: false);
                            _trackerStack.Push(tracker);
                        }
                    }
                }

                // Visit body between start and end tags.
                var body = VisitList(node.Body);

                // Visit end tag.
                var endTag = (MarkupTagBlockSyntax)Visit(node.EndTag);

                if (endTag != null)
                {
                    var tagName = endTag.GetTagName();
                    if (TryRewriteTagHelperEnd(endTag, out tagHelperEnd))
                    {
                        // This is a tag helper
                        if (startTag == null)
                        {
                            // The end tag helper has no corresponding start tag, create an error.
                            _errorSink.OnError(
                                RazorDiagnosticFactory.CreateParsing_TagHelperFoundMalformedTagHelper(
                                    new SourceSpan(SourceLocationTracker.Advance(endTag.GetSourceLocation(_source), "</"), tagName.Length), tagName));
                        }
                    }
                    else
                    {
                        // Non tag helper end tag.
                        if (startTag == null)
                        {
                            // Standalone end tag. We may need to error if it is not supposed to be here.
                            // If there was a corresponding start tag, we would have already added this error.
                            ValidateParentAllowsPlainTag(endTag);
                        }
                        else if (!endTag.IsVoidElement())
                        {
                            // Since a start tag exists, we must already be tracking it.
                            // Pop the stack as we're done with the end tag.
                            _trackerStack.Pop();
                        }
                    }
                }

                if (tagHelperInfo != null)
                {
                    // If we get here it means this element was rewritten as a tag helper.
                    var tagHelperElement = SyntaxFactory.MarkupTagHelperElement(tagHelperStart, body, tagHelperEnd);
                    return(tagHelperElement.WithTagHelperInfo(tagHelperInfo));
                }

                // There was no matching tag helper for this element. Return.
                return(node.Update(startTag, body, endTag));
            }