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