Esempio n. 1
0
        public static TagMode GetTagMode(
            MarkupStartTagSyntax 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.Mappings[descriptor];
                var nonDefaultRule = boundRules.FirstOrDefault(rule => rule.TagStructure != TagStructure.Unspecified);

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

            return(TagMode.StartTagAndEndTag);
        }
Esempio n. 2
0
        private void AddComponentAccessFromTag(RazorCodeActionContext context, MarkupStartTagSyntax startTag, List <RazorCodeAction> container)
        {
            var matching = FindMatchingTagHelpers(context, startTag);

            // For all the matches, add options for add @using and fully qualify
            foreach (var tagHelperPair in matching.Values)
            {
                if (tagHelperPair._fullyQualified is null)
                {
                    continue;
                }

                var fullyQualifiedName = tagHelperPair._short.Name;

                // Insert @using
                if (AddUsingsCodeActionProviderHelper.TryCreateAddUsingResolutionParams(fullyQualifiedName, context.Request.TextDocument.Uri, out var @namespace, out var resolutionParams))
                {
                    var addUsingCodeAction = RazorCodeActionFactory.CreateAddComponentUsing(@namespace, resolutionParams);
                    container.Add(addUsingCodeAction);
                }

                // Fully qualify
                var renameTagWorkspaceEdit   = CreateRenameTagEdit(context, startTag, fullyQualifiedName);
                var fullyQualifiedCodeAction = RazorCodeActionFactory.CreateFullyQualifyComponent(fullyQualifiedName, renameTagWorkspaceEdit);
                container.Add(fullyQualifiedCodeAction);
            }
        }
        public override void VisitMarkupStartTag(MarkupStartTagSyntax node)
        {
            if (node.IsMarkupTransition)
            {
                AddSemanticRange(node, RazorSemanticTokensLegend.RazorDirective);
            }
            else
            {
                AddSemanticRange(node.OpenAngle, RazorSemanticTokensLegend.MarkupTagDelimiter);
                if (node.Bang != null)
                {
                    AddSemanticRange(node.Bang, RazorSemanticTokensLegend.RazorTransition);
                }

                AddSemanticRange(node.Name, RazorSemanticTokensLegend.MarkupElement);

                Visit(node.Attributes);
                if (node.ForwardSlash != null)
                {
                    AddSemanticRange(node.ForwardSlash, RazorSemanticTokensLegend.MarkupTagDelimiter);
                }

                AddSemanticRange(node.CloseAngle, RazorSemanticTokensLegend.MarkupTagDelimiter);
            }
        }
Esempio n. 4
0
        private void AddCreateComponentFromTag(RazorCodeActionContext context, MarkupStartTagSyntax startTag, List <CommandOrCodeAction> container)
        {
            var path = context.Request.TextDocument.Uri.GetAbsoluteOrUNCPath();

            path = _filePathNormalizer.Normalize(path);
            var newComponentPath = Path.Combine(Path.GetDirectoryName(path), $"{startTag.Name.Content}.razor");

            if (File.Exists(newComponentPath))
            {
                return;
            }

            var actionParams = new CreateComponentCodeActionParams
            {
                Uri  = context.Request.TextDocument.Uri,
                Path = newComponentPath,
            };
            var data = JObject.FromObject(actionParams);

            var resolutionParams = new RazorCodeActionResolutionParams
            {
                Action = LanguageServerConstants.CodeActions.CreateComponentFromTag,
                Data   = data,
            };
            var serializedParams = JToken.FromObject(resolutionParams);
            var arguments        = new JArray(serializedParams);

            container.Add(new CommandOrCodeAction(new Command
            {
                Title     = "Create component from tag",
                Name      = LanguageServerConstants.RazorCodeActionRunnerCommand,
                Arguments = arguments,
            }));
        }
        private void AddComponentAccessFromTag(RazorCodeActionContext context, MarkupStartTagSyntax startTag, List <RazorCodeAction> container)
        {
            var matching = FindMatchingTagHelpers(context, startTag);

            // For all the matches, add options for add @using and fully qualify
            foreach (var tagHelperPair in matching.Values)
            {
                if (tagHelperPair.FullyQualified is null)
                {
                    continue;
                }

                var fullyQualifiedName = tagHelperPair.Short.Name;

                // Insert @using
                var addUsingCodeAction = AddUsingsCodeActionProviderFactory.CreateAddUsingCodeAction(
                    fullyQualifiedName,
                    context.Request.TextDocument.Uri);
                if (addUsingCodeAction != null)
                {
                    container.Add(addUsingCodeAction);
                }

                // Fully qualify
                container.Add(new RazorCodeAction()
                {
                    Title = $"{fullyQualifiedName}",
                    Edit  = CreateRenameTagEdit(context, startTag, fullyQualifiedName),
                });
            }
        }
Esempio n. 6
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;
                        }
                    }
                }
            }
Esempio n. 7
0
            private void ValidateParentAllowsPlainStartTag(MarkupStartTagSyntax tagBlock)
            {
                var tagName = tagBlock.GetTagNameWithOptionalBang();

                // Treat partial tags such as '</' which have no tag names as content.
                if (string.IsNullOrEmpty(tagName))
                {
                    var firstChild = tagBlock.Children.First();
                    Debug.Assert(firstChild is MarkupTextLiteralSyntax);

                    ValidateParentAllowsContent(firstChild);
                    return;
                }

                if (!HasAllowedChildren())
                {
                    return;
                }

                var tagHelperBinding = _tagHelperBinder.GetBinding(
                    tagName,
                    attributes: Array.Empty <KeyValuePair <string, string> >(),
                    parentTagName: CurrentParentTagName,
                    parentIsTagHelper: CurrentParentIsTagHelper);

                // If we found a binding for the current tag, then it is a tag helper. Use the prefixed allowed children to compare.
                var allowedChildren = tagHelperBinding != null ? CurrentTagHelperTracker.PrefixedAllowedChildren : CurrentTagHelperTracker.AllowedChildren;

                if (!allowedChildren.Contains(tagName, StringComparer.OrdinalIgnoreCase))
                {
                    OnAllowedChildrenStartTagError(CurrentTagHelperTracker, tagName, tagBlock, _errorSink, _source);
                }
            }
Esempio n. 8
0
        private void AddCreateComponentFromTag(RazorCodeActionContext context, MarkupStartTagSyntax startTag, List <RazorCodeAction> container)
        {
            var path = context.Request.TextDocument.Uri.GetAbsoluteOrUNCPath();

            path = _filePathNormalizer.Normalize(path);
            var newComponentPath = Path.Combine(Path.GetDirectoryName(path), $"{startTag.Name.Content}.razor");

            if (File.Exists(newComponentPath))
            {
                return;
            }

            var actionParams = new CreateComponentCodeActionParams
            {
                Uri  = context.Request.TextDocument.Uri,
                Path = newComponentPath,
            };

            var resolutionParams = new RazorCodeActionResolutionParams
            {
                Action = LanguageServerConstants.CodeActions.CreateComponentFromTag,
                Data   = actionParams,
            };

            container.Add(new RazorCodeAction()
            {
                Title = CreateComponentFromTagTitle,
                Data  = resolutionParams
            });
        }
Esempio n. 9
0
            private bool IsPotentialTagHelperStart(string tagName, MarkupStartTagSyntax childBlock)
            {
                Debug.Assert(childBlock.Children.Count > 0);
                var child = childBlock.Children[0];

                return(!string.Equals(tagName, SyntaxConstants.TextTagName, StringComparison.OrdinalIgnoreCase) ||
                       child.Kind != SyntaxKind.MarkupTransition);
            }
Esempio n. 10
0
 private void ValidateParentAllowsTagHelper(string tagName, MarkupStartTagSyntax tagBlock)
 {
     if (HasAllowedChildren() &&
         !CurrentTagHelperTracker.PrefixedAllowedChildren.Contains(tagName, StringComparer.OrdinalIgnoreCase))
     {
         OnAllowedChildrenStartTagError(CurrentTagHelperTracker, tagName, tagBlock, _errorSink, _source);
     }
 }
Esempio n. 11
0
        private static bool IsApplicableTag(MarkupStartTagSyntax startTag)
        {
            if (startTag.Name.FullWidth == 0)
            {
                // Empty tag name, we shouldn't show a light bulb just to create an empty file.
                return(false);
            }

            return(true);
        }
Esempio n. 12
0
        public override SyntaxNode VisitMarkupStartTag(MarkupStartTagSyntax node)
        {
            SpanContext latestSpanContext = null;
            var         newChildren       = SyntaxListBuilder <RazorSyntaxNode> .Create();

            var literals = new List <MarkupTextLiteralSyntax>();

            foreach (var child in node.Children)
            {
                if (child is MarkupTextLiteralSyntax literal)
                {
                    literals.Add(literal);
                    latestSpanContext = literal.GetSpanContext() ?? latestSpanContext;
                }
                else if (child is MarkupMiscAttributeContentSyntax miscContent)
                {
                    foreach (var contentChild in miscContent.Children)
                    {
                        if (contentChild is MarkupTextLiteralSyntax contentLiteral)
                        {
                            literals.Add(contentLiteral);
                            latestSpanContext = contentLiteral.GetSpanContext() ?? latestSpanContext;
                        }
                        else
                        {
                            // Pop stack
                            AddLiteralIfExists();
                            newChildren.Add(contentChild);
                        }
                    }
                }
                else
                {
                    AddLiteralIfExists();
                    newChildren.Add(child);
                }
            }

            AddLiteralIfExists();

            return(SyntaxFactory.MarkupStartTag(newChildren.ToList()).Green.CreateRed(node.Parent, node.Position));

            void AddLiteralIfExists()
            {
                if (literals.Count > 0)
                {
                    var mergedLiteral = SyntaxUtilities.MergeTextLiterals(literals.ToArray());
                    mergedLiteral = mergedLiteral.WithSpanContext(latestSpanContext);
                    literals.Clear();
                    latestSpanContext = null;
                    newChildren.Add(mergedLiteral);
                }
            }
        }
Esempio n. 13
0
 public override void VisitMarkupStartTag(MarkupStartTagSyntax node)
 {
     WriteBlock(node, FormattingBlockKind.Tag, n =>
     {
         var children = GetRewrittenMarkupStartTagChildren(node);
         foreach (var child in children)
         {
             Visit(child);
         }
     });
 }
Esempio n. 14
0
        public static MarkupTagHelperStartTagSyntax Rewrite(
            string tagName,
            bool validStructure,
            RazorParserFeatureFlags featureFlags,
            MarkupStartTagSyntax 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));
        }
Esempio n. 15
0
 public override void VisitMarkupStartTag(MarkupStartTagSyntax node)
 {
     AddSemanticRange(node.OpenAngle);
     if (node.Bang != null)
     {
         AddSemanticRange(node.Bang);
     }
     AddSemanticRange(node.Name, SyntaxKind.MarkupElement);
     base.VisitMarkupStartTag(node);
     if (node.ForwardSlash != null)
     {
         AddSemanticRange(node.ForwardSlash);
     }
     AddSemanticRange(node.CloseAngle);
 }
Esempio n. 16
0
            private bool ValidateStartTagSyntax(string tagName, MarkupStartTagSyntax tag)
            {
                // We assume an invalid syntax until we verify that the tag meets all of our "valid syntax" criteria.
                if (IsPartialStartTag(tag))
                {
                    var errorStart = GetStartTagDeclarationErrorStart(tag, _source);

                    _errorSink.OnError(
                        RazorDiagnosticFactory.CreateParsing_TagHelperMissingCloseAngle(
                            new SourceSpan(errorStart, tagName.Length), tagName));

                    return(false);
                }

                return(true);
            }
Esempio n. 17
0
 private bool IsTagUnknown(MarkupStartTagSyntax startTag, RazorCodeActionContext context)
 {
     foreach (var diagnostic in context.CodeDocument.GetCSharpDocument().Diagnostics)
     {
         // Check that the diagnostic is to do with our start tag
         if (!(diagnostic.Span.AbsoluteIndex > startTag.Span.End || startTag.Span.Start > diagnostic.Span.AbsoluteIndex + diagnostic.Span.Length))
         {
             // Component is not recognized in environment
             if (diagnostic.Id == ComponentDiagnosticFactory.UnexpectedMarkupElement.Id)
             {
                 return(true);
             }
         }
     }
     return(false);
 }
Esempio n. 18
0
            private static void OnAllowedChildrenStartTagError(
                TagHelperTracker tracker,
                string tagName,
                MarkupStartTagSyntax tagBlock,
                ErrorSink errorSink,
                RazorSourceDocument source)
            {
                var allowedChildrenString = string.Join(", ", tracker.AllowedChildren);
                var errorStart            = GetStartTagDeclarationErrorStart(tagBlock, source);

                errorSink.OnError(
                    RazorDiagnosticFactory.CreateTagHelper_InvalidNestedTag(
                        new SourceSpan(errorStart, tagName.Length),
                        tagName,
                        tracker.TagName,
                        allowedChildrenString));
            }
Esempio n. 19
0
        private void AddComponentAccessFromTag(RazorCodeActionContext context, MarkupStartTagSyntax startTag, List <CommandOrCodeAction> container)
        {
            var matching = FindMatchingTagHelpers(context, startTag);

            // For all the matches, add options for add @using and fully qualify
            foreach (var tagHelperPair in matching.Values)
            {
                if (tagHelperPair.FullyQualified is null)
                {
                    continue;
                }

                var fullyQualifiedComponentName = tagHelperPair.Short.Name;  // We assume .Name is the fully qualified component name
                DefaultRazorTagHelperBinderPhase.ComponentDirectiveVisitor.TrySplitNamespaceAndType(fullyQualifiedComponentName, out var namespaceSpan, out var _);
                var namespaceName = tagHelperPair.Short.Name.Substring(namespaceSpan.Start, namespaceSpan.Length);
                var actionParams  = new AddUsingsCodeActionParams
                {
                    Uri       = context.Request.TextDocument.Uri,
                    Namespace = namespaceName,
                };
                var data = JObject.FromObject(actionParams);

                var resolutionParams = new RazorCodeActionResolutionParams
                {
                    Action = LanguageServerConstants.CodeActions.AddUsing,
                    Data   = data,
                };
                var serializedParams = JToken.FromObject(resolutionParams);
                var arguments        = new JArray(serializedParams);

                // Insert @using
                container.Add(new CommandOrCodeAction(new Command
                {
                    Title     = $"@using {namespaceName}",
                    Name      = LanguageServerConstants.RazorCodeActionRunnerCommand,
                    Arguments = arguments,
                }));

                // Fully qualify
                container.Add(new CommandOrCodeAction(new CodeAction
                {
                    Title = $"{tagHelperPair.Short.Name}",
                    Edit  = CreateRenameTagEdit(context, startTag, tagHelperPair.Short.Name),
                }));
            }
        }
Esempio n. 20
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);
        }
            private static bool IsPartialStartTag(MarkupStartTagSyntax tagBlock)
            {
                // No need to validate the tag end because in order to be a tag block it must start with '<'.
                var tagEnd = tagBlock.Children[tagBlock.Children.Count - 1];

                // If our tag end is not a markup span it means it's some sort of code SyntaxTreeNode (not a valid format)
                if (tagEnd != null && tagEnd is MarkupTextLiteralSyntax tagEndLiteral)
                {
                    var endToken = tagEndLiteral.LiteralTokens.Count > 0 ?
                                   tagEndLiteral.LiteralTokens[tagEndLiteral.LiteralTokens.Count - 1] :
                                   null;

                    if (endToken != null && endToken.Kind == SyntaxKind.CloseAngle)
                    {
                        return(false);
                    }
                }

                return(true);
            }
        private void AddCreateComponentFromTag(RazorCodeActionContext context, MarkupStartTagSyntax startTag, List <RazorCodeAction> container)
        {
            if (context is null)
            {
                return;
            }

            if (!context.SupportsFileCreation)
            {
                return;
            }

            var path = context.Request.TextDocument.Uri.GetAbsoluteOrUNCPath();

            path = _filePathNormalizer.Normalize(path);
            var newComponentPath = Path.Combine(Path.GetDirectoryName(path), $"{startTag.Name.Content}.razor");

            if (File.Exists(newComponentPath))
            {
                return;
            }

            var actionParams = new CreateComponentCodeActionParams
            {
                Uri  = context.Request.TextDocument.Uri,
                Path = newComponentPath,
            };

            var resolutionParams = new RazorCodeActionResolutionParams
            {
                Action   = LanguageServerConstants.CodeActions.CreateComponentFromTag,
                Language = LanguageServerConstants.CodeActions.Languages.Razor,
                Data     = actionParams,
            };

            container.Add(new RazorCodeAction()
            {
                Title = RazorLS.Resources.Create_Component_FromTag_Title,
                Data  = JToken.FromObject(resolutionParams)
            });
        }
Esempio n. 23
0
 private static SourceLocation GetStartTagDeclarationErrorStart(MarkupStartTagSyntax tagBlock, RazorSourceDocument source)
 {
     return(SourceLocationTracker.Advance(tagBlock.GetSourceLocation(source), "<"));
 }
Esempio n. 24
0
 private static bool IsPartialStartTag(MarkupStartTagSyntax startTag)
 {
     return(startTag.CloseAngle.IsMissing);
 }
Esempio n. 25
0
 private bool IsPotentialTagHelperStart(string tagName, MarkupStartTagSyntax startTag)
 {
     return(!string.Equals(tagName, SyntaxConstants.TextTagName, StringComparison.OrdinalIgnoreCase) ||
            !startTag.IsMarkupTransition);
 }
Esempio n. 26
0
            // Internal for testing
            internal IReadOnlyList <KeyValuePair <string, string> > GetAttributeNameValuePairs(MarkupStartTagSyntax tagBlock)
            {
                if (tagBlock.Attributes.Count == 0)
                {
                    return(Array.Empty <KeyValuePair <string, string> >());
                }

                _htmlAttributeTracker.Clear();

                var attributes = _htmlAttributeTracker;

                for (var i = 0; i < tagBlock.Attributes.Count; i++)
                {
                    if (tagBlock.Attributes[i] is CSharpCodeBlockSyntax)
                    {
                        // Code blocks in the attribute area of tags mangles following attributes.
                        // It's also not supported by TagHelpers, bail early to avoid creating bad attribute value pairs.
                        break;
                    }

                    if (tagBlock.Attributes[i] is MarkupMinimizedAttributeBlockSyntax minimizedAttributeBlock)
                    {
                        if (minimizedAttributeBlock.Name == null)
                        {
                            _attributeValueBuilder.Append(InvalidAttributeValueMarker);
                            continue;
                        }

                        var minimizedAttribute = new KeyValuePair <string, string>(minimizedAttributeBlock.Name.GetContent(), string.Empty);
                        attributes.Add(minimizedAttribute);
                        continue;
                    }

                    if (!(tagBlock.Attributes[i] is MarkupAttributeBlockSyntax attributeBlock))
                    {
                        // If the parser thought these aren't attributes, we don't care about them. Move on.
                        continue;
                    }

                    if (attributeBlock.Name == null)
                    {
                        _attributeValueBuilder.Append(InvalidAttributeValueMarker);
                        continue;
                    }

                    if (attributeBlock.Value != null)
                    {
                        for (var j = 0; j < attributeBlock.Value.Children.Count; j++)
                        {
                            var child = attributeBlock.Value.Children[j];
                            if (child is MarkupLiteralAttributeValueSyntax literalValue)
                            {
                                _attributeValueBuilder.Append(literalValue.GetContent());
                            }
                            else
                            {
                                _attributeValueBuilder.Append(InvalidAttributeValueMarker);
                            }
                        }
                    }

                    var attributeName  = attributeBlock.Name.GetContent();
                    var attributeValue = _attributeValueBuilder.ToString();
                    var attribute      = new KeyValuePair <string, string>(attributeName, attributeValue);
                    attributes.Add(attribute);

                    _attributeValueBuilder.Clear();
                }

                return(attributes);
            }
Esempio n. 27
0
            private bool TryRewriteTagHelperEnd(MarkupStartTagSyntax startTag, MarkupEndTagSyntax endTag, out MarkupTagHelperEndTagSyntax rewritten)
            {
                rewritten = null;
                var tagName = endTag.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);
                }

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

                if (!IsPotentialTagHelperEnd(tagName, endTag))
                {
                    return(false);
                }

                // Validate that our end tag matches the currently scoped tag, if not we may need to error.
                if (startTag != null && tagNameScope.Equals(tagName, StringComparison.OrdinalIgnoreCase))
                {
                    // If there are additional end tags required before we can build our block it means we're in a
                    // situation like this: <myth req="..."><myth></myth></myth> where we're at the inside </myth>.
                    if (tracker.OpenMatchingTags > 0)
                    {
                        tracker.OpenMatchingTags--;

                        return(false);
                    }

                    ValidateEndTagSyntax(tagName, endTag);

                    _trackerStack.Pop();
                }
                else
                {
                    var tagHelperBinding = _tagHelperBinder.GetBinding(
                        tagName,
                        attributes: Array.Empty <KeyValuePair <string, string> >(),
                        parentTagName: CurrentParentTagName,
                        parentIsTagHelper: CurrentParentIsTagHelper);

                    // If there are not TagHelperDescriptors associated with the end tag block that also have no
                    // required attributes then it means we can't be a TagHelper, bail out.
                    if (tagHelperBinding == null)
                    {
                        return(false);
                    }

                    foreach (var descriptor in tagHelperBinding.Descriptors)
                    {
                        var boundRules  = tagHelperBinding.Mappings[descriptor];
                        var invalidRule = boundRules.FirstOrDefault(rule => rule.TagStructure == TagStructure.WithoutEndTag);

                        if (invalidRule != null)
                        {
                            // End tag TagHelper that states it shouldn't have an end tag.
                            _errorSink.OnError(
                                RazorDiagnosticFactory.CreateParsing_TagHelperMustNotHaveAnEndTag(
                                    new SourceSpan(SourceLocationTracker.Advance(endTag.GetSourceLocation(_source), "</"), tagName.Length),
                                    tagName,
                                    descriptor.DisplayName,
                                    invalidRule.TagStructure));

                            return(false);
                        }
                    }
                }

                rewritten = SyntaxFactory.MarkupTagHelperEndTag(
                    endTag.OpenAngle, endTag.ForwardSlash, endTag.Bang, endTag.Name, endTag.MiscAttributeContent, endTag.CloseAngle);

                return(true);
            }
Esempio n. 28
0
            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);
            }
        private static WorkspaceEdit CreateRenameTagEdit(RazorCodeActionContext context, MarkupStartTagSyntax startTag, string newTagName)
        {
            var textEdits = new List <TextEdit>();
            var codeDocumentIdentifier = new VersionedTextDocumentIdentifier()
            {
                Uri = context.Request.TextDocument.Uri
            };

            var startTagTextEdit = new TextEdit
            {
                Range   = startTag.Name.GetRange(context.CodeDocument.Source),
                NewText = newTagName,
            };

            textEdits.Add(startTagTextEdit);

            var endTag = (startTag.Parent as MarkupElementSyntax).EndTag;

            if (endTag != null)
            {
                var endTagTextEdit = new TextEdit
                {
                    Range   = endTag.Name.GetRange(context.CodeDocument.Source),
                    NewText = newTagName,
                };

                textEdits.Add(endTagTextEdit);
            }

            return(new WorkspaceEdit
            {
                DocumentChanges = new List <WorkspaceEditDocumentChange>()
                {
                    new WorkspaceEditDocumentChange(
                        new TextDocumentEdit()
                    {
                        TextDocument = codeDocumentIdentifier,
                        Edits = textEdits,
                    }
                        )
                }
            });
        }
        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);
        }