public async Task Handle_Resolve_TagHelperAttribute_ReturnsCompletionItemWithDocumentation() { // Arrange var lspDescriptionFactory = new Mock <LSPTagHelperTooltipFactory>(MockBehavior.Strict); var markdown = new MarkupContent { Kind = MarkupKind.Markdown, Value = "Some Markdown" }; lspDescriptionFactory.Setup(factory => factory.TryCreateTooltip(It.IsAny <AggregateBoundAttributeDescription>(), out markdown)) .Returns(true); var languageServer = new Mock <ClientNotifierServiceBase>(MockBehavior.Strict); languageServer.Setup(ls => ls.ClientSettings).Returns(new InitializeParams()); var completionEndpoint = new RazorCompletionEndpoint( Dispatcher, EmptyDocumentResolver, CompletionFactsService, lspDescriptionFactory.Object, VSLSPTagHelperTooltipFactory, languageServer.Object, LoggerFactory); var razorCompletionItem = new RazorCompletionItem("TestItem", "TestItem", RazorCompletionItemKind.TagHelperAttribute); razorCompletionItem.SetAttributeCompletionDescription(new AggregateBoundAttributeDescription(Array.Empty <BoundAttributeDescriptionInfo>())); var completionList = completionEndpoint.CreateLSPCompletionList(new[] { razorCompletionItem }); var completionItem = completionList.Items.Single(); // Act var newCompletionItem = await completionEndpoint.Handle(completionItem, default); // Assert Assert.NotNull(newCompletionItem.Documentation); }
public void TryConvert_DirectiveAttribute_Snippet_ReturnsTrue() { // Arrange var completionItem = new RazorCompletionItem("@testDisplay...", "testInsert", RazorCompletionItemKind.DirectiveAttribute); completionItem.SetAttributeCompletionDescription(new AttributeCompletionDescription(new CodeAnalysis.Razor.Completion.AttributeDescriptionInfo[] { new CodeAnalysis.Razor.Completion.AttributeDescriptionInfo("System.String", "System.String", "@testDisplay", "This is docs") })); var descriptionFactory = new Mock <TagHelperDescriptionFactory>(); var completionEndpoint = new RazorCompletionEndpoint(Dispatcher, EmptyDocumentResolver, CompletionFactsService, TagHelperCompletionService, descriptionFactory.Object, LoggerFactory); var capability = new CompletionCapability { CompletionItem = new CompletionItemCapability { SnippetSupport = true } }; completionEndpoint.SetCapability(capability); var expectedInsert = "testInsert$1=\"$2\"$0"; // Act var result = completionEndpoint.TryConvert(completionItem, out var converted); // Assert Assert.True(result); Assert.Equal(completionItem.DisplayText, converted.Label); Assert.Equal(expectedInsert, converted.InsertText); Assert.Equal(completionItem.InsertText, converted.FilterText); Assert.Equal(completionItem.InsertText, converted.SortText); Assert.Null(converted.Detail); Assert.Null(converted.Documentation); Assert.Null(converted.Command); Assert.True(converted.TryGetRazorCompletionKind(out var convertedKind)); Assert.Equal(RazorCompletionItemKind.DirectiveAttribute, convertedKind); }
public void TryConvert_DirectiveAttribute_ReturnsTrue() { // Arrange var completionItem = new RazorCompletionItem("@testDisplay", "testInsert", RazorCompletionItemKind.DirectiveAttribute, new [] { "=", ":" }); completionItem.SetAttributeCompletionDescription(new AttributeCompletionDescription(Array.Empty <CodeAnalysis.Razor.Completion.AttributeDescriptionInfo>())); var descriptionFactory = new Mock <TagHelperDescriptionFactory>(); var completionEndpoint = new RazorCompletionEndpoint(Dispatcher, EmptyDocumentResolver, CompletionFactsService, TagHelperCompletionService, descriptionFactory.Object, LoggerFactory); // Act var result = completionEndpoint.TryConvert(completionItem, out var converted); // Assert Assert.True(result); Assert.Equal(completionItem.DisplayText, converted.Label); Assert.Equal(completionItem.InsertText, converted.InsertText); Assert.Equal(completionItem.InsertText, converted.FilterText); Assert.Equal(completionItem.InsertText, converted.SortText); Assert.Equal(completionItem.CommitCharacters, converted.CommitCharacters); Assert.Null(converted.Detail); Assert.Null(converted.Documentation); Assert.Null(converted.Command); Assert.True(converted.TryGetRazorCompletionKind(out var convertedKind)); Assert.Equal(RazorCompletionItemKind.DirectiveAttribute, convertedKind); }
public async Task Handle_Resolve_TagHelperAttribute_ReturnsCompletionItemWithDocumentation() { // Arrange var lspDescriptionFactory = new Mock <LSPTagHelperTooltipFactory>(MockBehavior.Strict); var markdown = new MarkupContent { Kind = MarkupKind.Markdown, Value = "Some Markdown" }; lspDescriptionFactory.Setup(factory => factory.TryCreateTooltip(It.IsAny <AggregateBoundAttributeDescription>(), MarkupKind.Markdown, out markdown)) .Returns(true); var endpoint = new RazorCompletionResolveEndpoint(lspDescriptionFactory.Object, VSLSPTagHelperTooltipFactory, CompletionListCache, LoggerFactory); endpoint.GetRegistration(DefaultClientCapability); var razorCompletionItem = new RazorCompletionItem("TestItem", "TestItem", RazorCompletionItemKind.TagHelperAttribute); razorCompletionItem.SetAttributeCompletionDescription(new AggregateBoundAttributeDescription(Array.Empty <BoundAttributeDescriptionInfo>())); var completionList = CreateLSPCompletionList(new[] { razorCompletionItem }); var completionItem = completionList.Items.Single(); var parameters = ConvertToBridgedItem(completionItem); // Act var newCompletionItem = await endpoint.Handle(parameters, default); // Assert Assert.NotNull(newCompletionItem.Documentation); }
public async Task Handle_Resolve_DirectiveAttributeParameterCompletion_ReturnsCompletionItemWithDocumentation() { // Arrange var descriptionFactory = new Mock <TagHelperTooltipFactory>(MockBehavior.Strict); var markdown = new MarkupContent { Kind = MarkupKind.Markdown, Value = "Some Markdown" }; descriptionFactory.Setup(factory => factory.TryCreateTooltip(It.IsAny <AggregateBoundAttributeDescription>(), out markdown)) .Returns(true); var completionEndpoint = new RazorCompletionEndpoint(Dispatcher, EmptyDocumentResolver, CompletionFactsService, descriptionFactory.Object, LoggerFactory); var razorCompletionItem = new RazorCompletionItem("TestItem", "TestItem", RazorCompletionItemKind.DirectiveAttributeParameter); razorCompletionItem.SetAttributeCompletionDescription(new AggregateBoundAttributeDescription(Array.Empty <BoundAttributeDescriptionInfo>())); var completionList = completionEndpoint.CreateLSPCompletionList(new[] { razorCompletionItem }); var completionItem = completionList.Items.Single(); // Act var newCompletionItem = await completionEndpoint.Handle(completionItem, default); // Assert Assert.NotNull(newCompletionItem.Documentation); }
public void TryConvert_DirectiveAttributeParameter_ReturnsTrue() { // Arrange var completionItem = new RazorCompletionItem("format", "format", RazorCompletionItemKind.DirectiveAttributeParameter); completionItem.SetAttributeCompletionDescription(new AttributeCompletionDescription(Array.Empty <CodeAnalysis.Razor.Completion.AttributeDescriptionInfo>())); // Act var result = RazorCompletionEndpoint.TryConvert(completionItem, out var converted); // Assert Assert.True(result); Assert.Equal(completionItem.DisplayText, converted.Label); Assert.Equal(completionItem.InsertText, converted.InsertText); Assert.Equal(completionItem.InsertText, converted.FilterText); Assert.Equal(completionItem.InsertText, converted.SortText); Assert.Null(converted.Detail); Assert.Null(converted.Documentation); Assert.Null(converted.Command); Assert.True(converted.TryGetRazorCompletionKind(out var convertedKind)); Assert.Equal(RazorCompletionItemKind.DirectiveAttributeParameter, convertedKind); }
public void TryConvert_TagHelperAttribute_ForHtml_ReturnsTrue() { // Arrange var completionItem = new RazorCompletionItem("format", "format=\"$0\"", RazorCompletionItemKind.TagHelperAttribute, isSnippet: true); var attributeCompletionDescription = new AggregateBoundAttributeDescription(new BoundAttributeDescriptionInfo[] { }); completionItem.SetAttributeCompletionDescription(attributeCompletionDescription); // Act var result = RazorCompletionEndpoint.TryConvert(completionItem, ClientCapabilities, out var converted); // Assert Assert.True(result); Assert.Equal(completionItem.DisplayText, converted.Label); Assert.Equal("format=\"$0\"", converted.InsertText); Assert.Equal(InsertTextFormat.Snippet, converted.InsertTextFormat); Assert.Equal(completionItem.DisplayText, converted.FilterText); Assert.Equal(completionItem.DisplayText, converted.SortText); Assert.Null(converted.Detail); Assert.Null(converted.Documentation); Assert.Null(converted.Command); }
public void TryConvert_TagHelperAttribute_ForBool_ReturnsTrue() { // Arrange var completionItem = new RazorCompletionItem("format", "format", RazorCompletionItemKind.TagHelperAttribute); var attributeCompletionDescription = new AggregateBoundAttributeDescription(new[] { new BoundAttributeDescriptionInfo("System.Boolean", "Stuff", "format", "SomeDocs") }); completionItem.SetAttributeCompletionDescription(attributeCompletionDescription); // Act var result = RazorCompletionEndpoint.TryConvert(completionItem, ClientCapabilities, out var converted); // Assert Assert.True(result); Assert.Equal(completionItem.DisplayText, converted.Label); Assert.Equal("format", converted.InsertText); Assert.Equal(InsertTextFormat.Plaintext, converted.InsertTextFormat); Assert.Equal(completionItem.InsertText, converted.FilterText); Assert.Equal(completionItem.InsertText, converted.SortText); Assert.Null(converted.Detail); Assert.Null(converted.Documentation); Assert.Null(converted.Command); }
private IReadOnlyList <RazorCompletionItem> GetAttributeCompletions( SyntaxNode containingAttribute, string containingTagName, string?selectedAttributeName, IEnumerable <KeyValuePair <string, string> > attributes, TagHelperDocumentContext tagHelperDocumentContext) { var ancestors = containingAttribute.Parent.Ancestors(); 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 <RazorCompletionItem>(); var completionResult = _tagHelperCompletionService.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 RazorCompletionItem( displayText: completion.Key, insertText: filterText, RazorCompletionItemKind.TagHelperAttribute, attributeCommitCharacters); var attributeDescriptions = completion.Value.Select(boundAttribute => { var descriptionInfo = BoundAttributeDescriptionInfo.From(boundAttribute, indexerCompletion); return(descriptionInfo); }); var attributeDescriptionInfo = new AggregateBoundAttributeDescription(attributeDescriptions.ToList()); razorCompletionItem.SetAttributeCompletionDescription(attributeDescriptionInfo); completionItems.Add(razorCompletionItem); } return(completionItems); }
private IReadOnlyList <RazorCompletionItem> GetAttributeCompletions( SyntaxNode containingAttribute, string containingTagName, string?selectedAttributeName, IEnumerable <KeyValuePair <string, string> > attributes, TagHelperDocumentContext tagHelperDocumentContext, RazorCompletionOptions options) { var ancestors = containingAttribute.Parent.Ancestors(); 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 <RazorCompletionItem>(); var completionResult = _tagHelperCompletionService.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 attributeContext = ResolveAttributeContext(completion.Value, indexerCompletion, options.SnippetsSupported); var attributeCommitCharacters = ResolveAttributeCommitCharacters(attributeContext); var isSnippet = false; var insertText = filterText; if (TryResolveInsertText(insertText, attributeContext, out var snippetText)) { isSnippet = true; insertText = snippetText; } // We change the sort text depending on the tag name due to TagHelper/non-TagHelper concerns. For instance lets say you have a TagHelper that binds to `input`. // Chances are you're expecting to get every other `input` completion item in addition to the TagHelper completion items and the sort order should be the default // because HTML completion items are 100% as applicable as other items. // // Next assume that we have a TagHelper that binds `custom` (or even `Custom`); this is a special scenario where the user has effectively created a new HTML tag // meaning they're probably expecting to provide all of the attributes necessary for that tag to operate. Meaning, HTML attribute completions are less important. // To make sure we prioritize our attribute completions above all other types of completions we set the priority to high so they're showed in the completion list // above all other completion items. var sortText = HtmlFactsService.IsHtmlTagName(containingTagName) ? CompletionSortTextHelper.DefaultSortPriority : CompletionSortTextHelper.HighSortPriority; var razorCompletionItem = new RazorCompletionItem( displayText: completion.Key, insertText: insertText, sortText: sortText, kind: RazorCompletionItemKind.TagHelperAttribute, commitCharacters: attributeCommitCharacters, isSnippet: isSnippet); var attributeDescriptions = completion.Value.Select(boundAttribute => { var descriptionInfo = BoundAttributeDescriptionInfo.From(boundAttribute, indexerCompletion); return(descriptionInfo); }); var attributeDescriptionInfo = new AggregateBoundAttributeDescription(attributeDescriptions.ToList()); razorCompletionItem.SetAttributeCompletionDescription(attributeDescriptionInfo); completionItems.Add(razorCompletionItem); } return(completionItems); }