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("...", StringComparison.Ordinal); if (indexerCompletion) { filterText = filterText.Substring(0, filterText.Length - 3); } var attributeCommitCharacters = ResolveAttributeCommitCharacters(completion.Value, indexerCompletion); var razorCompletionItem = new CompletionItem() { Label = completion.Key, InsertText = filterText, InsertTextFormat = InsertTextFormat.PlainText, 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); }