private IReadOnlyList <CompletionItem> GetElementCompletions( SyntaxNode containingTag, string containingTagName, IEnumerable <KeyValuePair <string, string> > attributes, RazorCodeDocument codeDocument) { var ancestors = containingTag.Ancestors(); var tagHelperDocumentContext = codeDocument.GetTagHelperContext(); var(ancestorTagName, ancestorIsTagHelper) = _tagHelperFactsService.GetNearestAncestorTagInfo(ancestors); var elementCompletionContext = new ElementCompletionContext( tagHelperDocumentContext, existingCompletions: Enumerable.Empty <string>(), containingTagName, attributes, ancestorTagName, ancestorIsTagHelper, HtmlFactsService.IsHtmlTagName); var completionItems = new List <CompletionItem>(); var completionResult = _razorTagHelperCompletionService.GetElementCompletions(elementCompletionContext); foreach (var completion in completionResult.Completions) { var razorCompletionItem = new CompletionItem() { Label = completion.Key, InsertText = completion.Key, FilterText = completion.Key, SortText = completion.Key, Kind = CompletionItemKind.TypeParameter, CommitCharacters = ElementCommitCharacters, }; var tagHelperDescriptions = completion.Value.Select(tagHelper => new TagHelperDescriptionInfo(tagHelper.GetTypeName(), tagHelper.Documentation)); var elementDescription = new ElementDescriptionInfo(tagHelperDescriptions.ToList()); razorCompletionItem.SetDescriptionInfo(elementDescription); completionItems.Add(razorCompletionItem); } return(completionItems); }
private IReadOnlyList <CompletionItem> GetAttributeCompletions( SyntaxNode containingAttribute, string containingTagName, string selectedAttributeName, IEnumerable <KeyValuePair <string, string> > attributes, RazorCodeDocument codeDocument) { var ancestors = containingAttribute.Parent.Ancestors(); var tagHelperDocumentContext = codeDocument.GetTagHelperContext(); var nonDirectiveAttributeTagHelpers = tagHelperDocumentContext.TagHelpers.Where(tagHelper => !tagHelper.BoundAttributes.Any(attribute => attribute.IsDirectiveAttribute())); var filteredContext = TagHelperDocumentContext.Create(tagHelperDocumentContext.Prefix, nonDirectiveAttributeTagHelpers); var(ancestorTagName, ancestorIsTagHelper) = _tagHelperFactsService.GetNearestAncestorTagInfo(ancestors); var attributeCompletionContext = new AttributeCompletionContext( filteredContext, existingCompletions: Enumerable.Empty <string>(), containingTagName, selectedAttributeName, attributes, ancestorTagName, ancestorIsTagHelper, HtmlFactsService.IsHtmlTagName); var completionItems = new List <CompletionItem>(); var completionResult = _razorTagHelperCompletionService.GetAttributeCompletions(attributeCompletionContext); foreach (var completion in completionResult.Completions) { var filterText = completion.Key; // This is a little bit of a hack because the information returned by _razorTagHelperCompletionService.GetAttributeCompletions // does not have enough information for us to determine if a completion is an indexer completion or not. Therefore we have to // jump through a few hoops below to: // 1. Determine if this specific completion is an indexer based completion // 2. Resolve an appropriate snippet if it is. This is more troublesome because we need to remove the ... suffix to accurately // build a snippet that makes sense for the user to type. var indexerCompletion = filterText.EndsWith("..."); if (indexerCompletion) { filterText = filterText.Substring(0, filterText.Length - 3); } var insertTextFormat = InsertTextFormat.Snippet; if (!TryResolveAttributeInsertionSnippet(filterText, completion.Value, indexerCompletion, out var insertText)) { insertTextFormat = InsertTextFormat.PlainText; insertText = filterText; } var razorCompletionItem = new CompletionItem() { Label = completion.Key, InsertText = insertText, InsertTextFormat = insertTextFormat, FilterText = filterText, SortText = filterText, Kind = CompletionItemKind.TypeParameter, CommitCharacters = AttributeCommitCharacters, }; var attributeDescriptions = completion.Value.Select(boundAttribute => new TagHelperAttributeDescriptionInfo( boundAttribute.DisplayName, boundAttribute.GetPropertyName(), indexerCompletion ? boundAttribute.IndexerTypeName : boundAttribute.TypeName, boundAttribute.Documentation)); var attributeDescriptionInfo = new AttributeDescriptionInfo(attributeDescriptions.ToList()); razorCompletionItem.SetDescriptionInfo(attributeDescriptionInfo); completionItems.Add(razorCompletionItem); } return(completionItems); }
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 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); }