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); }
public override IEnumerable <BoundAttributeDescriptor> GetBoundTagHelperAttributes( TagHelperDocumentContext documentContext, string attributeName, TagHelperBinding binding) { return(_tagHelperFactsService.GetBoundTagHelperAttributes(documentContext, attributeName, binding)); }
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; } } } }
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); }
public TagHelperInfo( string tagName, TagMode tagMode, TagHelperBinding bindingResult) { TagName = tagName; TagMode = tagMode; BindingResult = bindingResult; }
public TagHelperSpanInternal(SourceSpan span, TagHelperBinding binding) { if (binding == null) { throw new ArgumentNullException(nameof(binding)); } Span = span; Binding = binding; }
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)); }
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(); }
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); }
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); }
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; } } } }
public abstract IEnumerable <BoundAttributeDescriptor> GetBoundTagHelperAttributes(TagHelperDocumentContext documentContext, string attributeName, TagHelperBinding binding);
/// <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); }
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()); }
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())); }
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));
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); }