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); }
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); } }
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), }); } }
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 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); } }
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 }); }
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); }
private void ValidateParentAllowsTagHelper(string tagName, MarkupStartTagSyntax tagBlock) { if (HasAllowedChildren() && !CurrentTagHelperTracker.PrefixedAllowedChildren.Contains(tagName, StringComparer.OrdinalIgnoreCase)) { OnAllowedChildrenStartTagError(CurrentTagHelperTracker, tagName, tagBlock, _errorSink, _source); } }
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); }
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); } } }
public override void VisitMarkupStartTag(MarkupStartTagSyntax node) { WriteBlock(node, FormattingBlockKind.Tag, n => { var children = GetRewrittenMarkupStartTagChildren(node); foreach (var child in children) { Visit(child); } }); }
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)); }
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); }
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); }
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); }
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)); }
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), })); } }
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) }); }
private static SourceLocation GetStartTagDeclarationErrorStart(MarkupStartTagSyntax tagBlock, RazorSourceDocument source) { return(SourceLocationTracker.Advance(tagBlock.GetSourceLocation(source), "<")); }
private static bool IsPartialStartTag(MarkupStartTagSyntax startTag) { return(startTag.CloseAngle.IsMissing); }
private bool IsPotentialTagHelperStart(string tagName, MarkupStartTagSyntax startTag) { return(!string.Equals(tagName, SyntaxConstants.TextTagName, StringComparison.OrdinalIgnoreCase) || !startTag.IsMarkupTransition); }
// 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); }
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); }
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); }