public override IReadOnlyList <RazorCompletionItem> GetCompletionItems(RazorSyntaxTree syntaxTree, TagHelperDocumentContext tagHelperDocumentContext, SourceSpan location) { if (syntaxTree is null) { throw new ArgumentNullException(nameof(syntaxTree)); } if (tagHelperDocumentContext is null) { throw new ArgumentNullException(nameof(tagHelperDocumentContext)); } var change = new SourceChange(location, string.Empty); var owner = syntaxTree.Root.LocateOwner(change); if (owner == null) { Debug.Fail("Owner should never be null."); return(Array.Empty <RazorCompletionItem>()); } var parent = owner.Parent; if (_htmlFactsService.TryGetElementInfo(parent, out var containingTagNameToken, out var attributes) && containingTagNameToken.Span.IntersectsWith(location.AbsoluteIndex)) { var stringifiedAttributes = _tagHelperFactsService.StringifyAttributes(attributes); var elementCompletions = GetElementCompletions(parent, containingTagNameToken.Content, stringifiedAttributes, tagHelperDocumentContext); return(elementCompletions); } if (_htmlFactsService.TryGetAttributeInfo( parent, out containingTagNameToken, out var prefixLocation, out var selectedAttributeName, out var selectedAttributeNameLocation, out attributes) && (selectedAttributeName == null || selectedAttributeNameLocation?.IntersectsWith(location.AbsoluteIndex) == true || (prefixLocation?.IntersectsWith(location.AbsoluteIndex) ?? false))) { var stringifiedAttributes = _tagHelperFactsService.StringifyAttributes(attributes); var attributeCompletions = GetAttributeCompletions(parent, containingTagNameToken.Content, selectedAttributeName, stringifiedAttributes, tagHelperDocumentContext); return(attributeCompletions); } // Invalid location for TagHelper completions. return(Array.Empty <RazorCompletionItem>()); }
public override IReadOnlyList <RazorCompletionItem> GetCompletionItems(RazorSyntaxTree syntaxTree, TagHelperDocumentContext tagHelperDocumentContext, SourceSpan location) { if (syntaxTree is null) { throw new ArgumentNullException(nameof(syntaxTree)); } var change = new SourceChange(location, string.Empty); var owner = syntaxTree.Root.LocateOwner(change); if (owner == null) { Debug.Fail("Owner should never be null."); return(Array.Empty <RazorCompletionItem>()); } if (!AtMarkupTransitionCompletionPoint(owner)) { return(Array.Empty <RazorCompletionItem>()); } var parent = owner.Parent; // Also helps filter out edge cases like `< te` and `< te=""` // (see comment in AtMarkupTransitionCompletionPoint) if (!_htmlFactsService.TryGetElementInfo(parent, out var containingTagNameToken, out var attributes) || !containingTagNameToken.Span.IntersectsWith(location.AbsoluteIndex)) { return(Array.Empty <RazorCompletionItem>()); } var completions = new List <RazorCompletionItem>() { MarkupTransitionCompletionItem }; return(completions); }
public override IReadOnlyList <CompletionItem> GetCompletionsAt(SourceSpan location, RazorCodeDocument codeDocument) { if (codeDocument == null) { throw new ArgumentNullException(nameof(codeDocument)); } var syntaxTree = codeDocument.GetSyntaxTree(); var change = new SourceChange(location, ""); var owner = syntaxTree.Root.LocateOwner(change); if (owner == null) { Debug.Fail("Owner should never be null."); return(Array.Empty <CompletionItem>()); } var parent = owner.Parent; if (_htmlFactsService.TryGetElementInfo(parent, out var containingTagNameToken, out var attributes) && containingTagNameToken.Span.IntersectsWith(location.AbsoluteIndex)) { var stringifiedAttributes = _tagHelperFactsService.StringifyAttributes(attributes); var elementCompletions = GetElementCompletions(parent, containingTagNameToken.Content, stringifiedAttributes, codeDocument); return(elementCompletions); } if (_htmlFactsService.TryGetAttributeInfo(parent, out containingTagNameToken, out var selectedAttributeName, out attributes) && attributes.Span.IntersectsWith(location.AbsoluteIndex)) { var stringifiedAttributes = _tagHelperFactsService.StringifyAttributes(attributes); var attributeCompletions = GetAttributeCompletions(parent, containingTagNameToken.Content, selectedAttributeName, stringifiedAttributes, codeDocument); return(attributeCompletions); } // Invalid location for TagHelper completions. return(Array.Empty <CompletionItem>()); }
public override HoverModel GetHoverInfo(RazorCodeDocument codeDocument, SourceLocation location, ClientCapabilities clientCapabilities) { if (codeDocument is null) { throw new ArgumentNullException(nameof(codeDocument)); } var syntaxTree = codeDocument.GetSyntaxTree(); var change = new SourceChange(location.AbsoluteIndex, length: 0, newText: ""); var owner = syntaxTree.Root.LocateOwner(change); if (owner is null) { Debug.Fail("Owner should never be null."); return(null); } var parent = owner.Parent; var position = new Position(location.LineIndex, location.CharacterIndex); var tagHelperDocumentContext = codeDocument.GetTagHelperContext(); var ancestors = owner.Ancestors(); var(parentTag, parentIsTagHelper) = _tagHelperFactsService.GetNearestAncestorTagInfo(ancestors); if (_htmlFactsService.TryGetElementInfo(parent, out var containingTagNameToken, out var attributes) && containingTagNameToken.Span.IntersectsWith(location.AbsoluteIndex)) { // Hovering over HTML tag name var stringifiedAttributes = _tagHelperFactsService.StringifyAttributes(attributes); var binding = _tagHelperFactsService.GetTagHelperBinding( tagHelperDocumentContext, containingTagNameToken.Content, stringifiedAttributes, parentTag: parentTag, parentIsTagHelper: parentIsTagHelper); if (binding is null) { // No matching tagHelpers, it's just HTML return(null); } else { Debug.Assert(binding.Descriptors.Any()); var range = containingTagNameToken.GetRange(codeDocument.Source); var result = ElementInfoToHover(binding.Descriptors, range, clientCapabilities); return(result); } } if (_htmlFactsService.TryGetAttributeInfo(parent, out containingTagNameToken, out _, out var selectedAttributeName, out var selectedAttributeNameLocation, out attributes) && selectedAttributeNameLocation?.IntersectsWith(location.AbsoluteIndex) == true) { // Hovering over HTML attribute name var stringifiedAttributes = _tagHelperFactsService.StringifyAttributes(attributes); var binding = _tagHelperFactsService.GetTagHelperBinding( tagHelperDocumentContext, containingTagNameToken.Content, stringifiedAttributes, parentTag: parentTag, parentIsTagHelper: parentIsTagHelper); if (binding is null) { // No matching TagHelpers, it's just HTML return(null); } else { Debug.Assert(binding.Descriptors.Any()); var tagHelperAttributes = _tagHelperFactsService.GetBoundTagHelperAttributes(tagHelperDocumentContext, selectedAttributeName, binding); // Grab the first attribute that we find that intersects with this location. That way if there are multiple attributes side-by-side aka hovering over: // <input checked| minimized /> // Then we take the left most attribute (attributes are returned in source order). var attribute = attributes.First(a => a.Span.IntersectsWith(location.AbsoluteIndex)); if (attribute is MarkupTagHelperAttributeSyntax thAttributeSyntax) { attribute = thAttributeSyntax.Name; } else if (attribute is MarkupMinimizedTagHelperAttributeSyntax thMinimizedAttribute) { attribute = thMinimizedAttribute.Name; } else if (attribute is MarkupTagHelperDirectiveAttributeSyntax directiveAttribute) { attribute = directiveAttribute.Name; } else if (attribute is MarkupMinimizedTagHelperDirectiveAttributeSyntax miniDirectiveAttribute) { attribute = miniDirectiveAttribute; } var attributeName = attribute.GetContent(); var range = attribute.GetRange(codeDocument.Source); // Include the @ in the range switch (attribute.Parent.Kind) { case SyntaxKind.MarkupTagHelperDirectiveAttribute: var directiveAttribute = attribute.Parent as MarkupTagHelperDirectiveAttributeSyntax; range.Start.Character -= directiveAttribute.Transition.FullWidth; attributeName = "@" + attributeName; break; case SyntaxKind.MarkupMinimizedTagHelperDirectiveAttribute: var minimizedAttribute = containingTagNameToken.Parent as MarkupMinimizedTagHelperDirectiveAttributeSyntax; range.Start.Character -= minimizedAttribute.Transition.FullWidth; attributeName = "@" + attributeName; break; } var attributeHoverModel = AttributeInfoToHover(tagHelperAttributes, range, attributeName, clientCapabilities); return(attributeHoverModel); } } return(null); }
public override IReadOnlyList <RazorCompletionItem> GetCompletionItems(RazorCompletionContext context, SourceSpan location) { if (context is null) { throw new ArgumentNullException(nameof(context)); } var change = new SourceChange(location, string.Empty); var owner = context.SyntaxTree.Root.LocateOwner(change); if (owner is null) { Debug.Fail("Owner should never be null."); return(Array.Empty <RazorCompletionItem>()); } var parent = owner.Parent; if (_htmlFactsService.TryGetElementInfo(parent, out var containingTagNameToken, out var attributes) && containingTagNameToken.Span.IntersectsWith(location.AbsoluteIndex)) { if ((containingTagNameToken.FullWidth > 1 || containingTagNameToken.Content == "-") && containingTagNameToken.Span.Start != location.AbsoluteIndex) { // To align with HTML completion behavior we only want to provide completion items if we're trying to resolve completion at the // beginning of an HTML element name. return(Array.Empty <RazorCompletionItem>()); } var stringifiedAttributes = _tagHelperFactsService.StringifyAttributes(attributes); var containingElement = parent.Parent; var elementCompletions = GetElementCompletions(containingElement, containingTagNameToken.Content, stringifiedAttributes, context.TagHelperDocumentContext); return(elementCompletions); } if (_htmlFactsService.TryGetAttributeInfo( parent, out containingTagNameToken, out var prefixLocation, out var selectedAttributeName, out var selectedAttributeNameLocation, out attributes) && (selectedAttributeName is null || selectedAttributeNameLocation?.IntersectsWith(location.AbsoluteIndex) == true || (prefixLocation?.IntersectsWith(location.AbsoluteIndex) ?? false))) { if (prefixLocation.HasValue && prefixLocation.Value.Length == 1 && selectedAttributeNameLocation.HasValue && selectedAttributeNameLocation.Value.Length > 1 && selectedAttributeNameLocation.Value.Start != location.AbsoluteIndex) { // To align with HTML completion behavior we only want to provide completion items if we're trying to resolve completion at the // beginning of an HTML attribute name. We do extra checks on prefix locations here in order to rule out malformed cases when the Razor // compiler incorrectly parses multi-line attributes while in the middle of typing out an element. For instance: // // <SurveyPrompt | // @code { ... } // // Will be interpreted as having an `@code` attribute name due to multi-line attributes being a thing. Ultimately this is mostly a // heuristic that we have to apply in order to workaround limitations of the Razor compiler. return(Array.Empty <RazorCompletionItem>()); } var stringifiedAttributes = _tagHelperFactsService.StringifyAttributes(attributes); var attributeCompletions = GetAttributeCompletions(parent, containingTagNameToken.Content, selectedAttributeName, stringifiedAttributes, context.TagHelperDocumentContext, context.Options); return(attributeCompletions); } // Invalid location for TagHelper completions. return(Array.Empty <RazorCompletionItem>()); }
public override HoverModel GetHoverInfo(RazorCodeDocument codeDocument, SourceSpan location) { if (codeDocument is null) { throw new ArgumentNullException(nameof(codeDocument)); } var syntaxTree = codeDocument.GetSyntaxTree(); var change = new SourceChange(location, ""); var owner = syntaxTree.Root.LocateOwner(change); if (owner == null) { Debug.Fail("Owner should never be null."); return(null); } var parent = owner.Parent; var position = new Position(location.LineIndex, location.CharacterIndex); var tagHelperDocumentContext = codeDocument.GetTagHelperContext(); var ancestors = owner.Ancestors(); var(parentTag, parentIsTagHelper) = _tagHelperFactsService.GetNearestAncestorTagInfo(ancestors); if (_htmlFactsService.TryGetElementInfo(parent, out var containingTagNameToken, out var attributes) && containingTagNameToken.Span.IntersectsWith(location.AbsoluteIndex)) { // Hovering over HTML tag name var stringifiedAttributes = _tagHelperFactsService.StringifyAttributes(attributes); var binding = _tagHelperFactsService.GetTagHelperBinding( tagHelperDocumentContext, containingTagNameToken.Content, stringifiedAttributes, parentTag: parentTag, parentIsTagHelper: parentIsTagHelper); if (binding is null) { // No matching tagHelpers, it's just HTML return(null); } else { Debug.Assert(binding.Descriptors.Count() > 0); var range = GetRangeFromSyntaxNode(containingTagNameToken, codeDocument); var result = ElementInfoToHover(binding.Descriptors, range); return(result); } } if (_htmlFactsService.TryGetAttributeInfo(parent, out containingTagNameToken, out var selectedAttributeName, out attributes) && attributes.Span.IntersectsWith(location.AbsoluteIndex)) { // Hovering over HTML attribute name var stringifiedAttributes = _tagHelperFactsService.StringifyAttributes(attributes); var binding = _tagHelperFactsService.GetTagHelperBinding( tagHelperDocumentContext, containingTagNameToken.Content, stringifiedAttributes, parentTag: parentTag, parentIsTagHelper: parentIsTagHelper); if (binding is null) { // No matching TagHelpers, it's just HTML return(null); } else { Debug.Assert(binding.Descriptors.Count() > 0); var tagHelperAttributes = _tagHelperFactsService.GetBoundTagHelperAttributes(tagHelperDocumentContext, selectedAttributeName, binding); var attribute = attributes.Single(a => a.Span.IntersectsWith(location.AbsoluteIndex)); var range = GetRangeFromSyntaxNode(attribute, codeDocument); var attributeHoverModel = AttributeInfoToHover(tagHelperAttributes, range); return(attributeHoverModel); } } return(null); }