private static void AssertRazorCompletionItem(RazorCompletionItem item) { Assert.Equal(item.DisplayText, SyntaxConstants.TextTagName); Assert.Equal(item.InsertText, SyntaxConstants.TextTagName); var completionDescription = item.GetMarkupTransitionCompletionDescription(); Assert.Equal(CodeAnalysisResources.MarkupTransition_Description, completionDescription.Description); }
public static void SetMarkupTransitionCompletionDescription(this RazorCompletionItem completionItem, MarkupTransitionCompletionDescription markupTransitionCompletionDescription) { if (completionItem is null) { throw new ArgumentNullException(nameof(completionItem)); } completionItem.Items[s_markupTransitionDescriptionKey] = markupTransitionCompletionDescription; }
public static void SetDirectiveCompletionDescription(this RazorCompletionItem completionItem, DirectiveCompletionDescription attributeCompletionDescription) { if (completionItem is null) { throw new ArgumentNullException(nameof(completionItem)); } completionItem.Items[s_directiveCompletionDescriptionKey] = attributeCompletionDescription; }
public static void SetAttributeCompletionDescription(this RazorCompletionItem completionItem, AggregateBoundAttributeDescription attributeCompletionDescription) { if (completionItem is null) { throw new ArgumentNullException(nameof(completionItem)); } completionItem.Items[s_attributeCompletionDescriptionKey] = attributeCompletionDescription; }
public static IEnumerable <string> GetAttributeCompletionTypes(this RazorCompletionItem completionItem) { var attributeCompletionDescription = completionItem.GetAttributeCompletionDescription(); if (attributeCompletionDescription is null) { yield break; } foreach (var descriptionInfo in attributeCompletionDescription.DescriptionInfos) { yield return(descriptionInfo.ReturnTypeName); } }
public void GetDirectiveCompletionItems_AllProvidersCompletionItems() { // Arrange var syntaxTree = RazorSyntaxTree.Parse(TestRazorSourceDocument.Create()); var tagHelperDocumentContext = TagHelperDocumentContext.Create(prefix: null, Enumerable.Empty <TagHelperDescriptor>()); var completionItem1 = new RazorCompletionItem("displayText1", "insertText1", RazorCompletionItemKind.Directive); var provider1 = Mock.Of <RazorCompletionItemProvider>(p => p.GetCompletionItems(syntaxTree, tagHelperDocumentContext, default) == new[] { completionItem1 }, MockBehavior.Strict); var completionItem2 = new RazorCompletionItem("displayText2", "insertText2", RazorCompletionItemKind.Directive); var provider2 = Mock.Of <RazorCompletionItemProvider>(p => p.GetCompletionItems(syntaxTree, tagHelperDocumentContext, default) == new[] { completionItem2 }, MockBehavior.Strict); var completionFactsService = new DefaultRazorCompletionFactsService(new[] { provider1, provider2 }); // Act var completionItems = completionFactsService.GetCompletionItems(syntaxTree, tagHelperDocumentContext, default); // Assert Assert.Equal(new[] { completionItem1, completionItem2 }, completionItems); }
public void GetAttributeCompletions_BaseDirectiveAttributeAndParameterVariationsExist_ExcludesCompletion() { // Arrange var expectedCompletion = new RazorCompletionItem("@bind", "@bind", RazorCompletionItemKind.DirectiveAttribute); var attributeNames = new[] { "@bind", "@bind:format", "@bind:event", "@", }; // Act var completions = Provider.GetAttributeCompletions("@", "input", attributeNames, DefaultTagHelperDocumentContext); // Assert AssertDoesNotContain(completions, "bind", "@bind"); }
// Internal for testing internal static List <RazorCompletionItem> GetDirectiveCompletionItems(RazorSyntaxTree syntaxTree) { var defaultDirectives = FileKinds.IsComponent(syntaxTree.Options.FileKind) ? Array.Empty <DirectiveDescriptor>() : DefaultDirectives; var directives = syntaxTree.Options.Directives.Concat(defaultDirectives); var completionItems = new List <RazorCompletionItem>(); foreach (var directive in directives) { var completionDisplayText = directive.DisplayName ?? directive.Directive; var completionItem = new RazorCompletionItem( completionDisplayText, directive.Directive, RazorCompletionItemKind.Directive); var completionDescription = new DirectiveCompletionDescription(directive.Description); completionItem.SetDirectiveCompletionDescription(completionDescription); completionItems.Add(completionItem); } return(completionItems); }
// Internal for testing internal IReadOnlyList <RazorCompletionItem> GetAttributeCompletions( string selectedAttributeName, string containingTagName, IEnumerable <string> attributes, TagHelperDocumentContext tagHelperDocumentContext) { var descriptorsForTag = _tagHelperFactsService.GetTagHelpersGivenTag(tagHelperDocumentContext, containingTagName, parentTag: null); if (descriptorsForTag.Count == 0) { // If the current tag has no possible descriptors then we can't have any directive attributes. return(Array.Empty <RazorCompletionItem>()); } // Attributes are case sensitive when matching var attributeCompletions = new Dictionary <string, (HashSet <AttributeDescriptionInfo>, HashSet <string>)>(StringComparer.Ordinal); for (var i = 0; i < descriptorsForTag.Count; i++) { var descriptor = descriptorsForTag[i]; foreach (var attributeDescriptor in descriptor.BoundAttributes) { if (!attributeDescriptor.IsDirectiveAttribute()) { // We don't care about non-directive attributes continue; } if (!TryAddCompletion(attributeDescriptor.Name, attributeDescriptor, descriptor) && attributeDescriptor.BoundAttributeParameters.Count > 0) { // This attribute has parameters and the base attribute name (@bind) is already satisfied. We need to check if there are any valid // parameters left to be provided, if so, we need to still represent the base attribute name in the completion list. for (var j = 0; j < attributeDescriptor.BoundAttributeParameters.Count; j++) { var parameterDescriptor = attributeDescriptor.BoundAttributeParameters[j]; if (!attributes.Any(name => TagHelperMatchingConventions.SatisfiesBoundAttributeWithParameter(name, attributeDescriptor, parameterDescriptor))) { // This bound attribute parameter has not had a completion entry added for it, re-represent the base attribute name in the completion list AddCompletion(attributeDescriptor.Name, attributeDescriptor, descriptor); break; } } } if (!string.IsNullOrEmpty(attributeDescriptor.IndexerNamePrefix)) { TryAddCompletion(attributeDescriptor.IndexerNamePrefix + "...", attributeDescriptor, descriptor); } } } var completionItems = new List <RazorCompletionItem>(); foreach (var completion in attributeCompletions) { var insertText = completion.Key; if (insertText.EndsWith("...")) { // Indexer attribute, we don't want to insert with the triple dot. insertText = insertText.Substring(0, insertText.Length - 3); } if (insertText.StartsWith("@")) { // Strip off the @ from the insertion text. This change is here to align the insertion text with the // completion hooks into VS and VSCode. Basically, completion triggers when `@` is typed so we don't // want to insert `@bind` because `@` already exists. insertText = insertText.Substring(1); } (var attributeDescriptionInfos, var commitCharacters) = completion.Value; var razorCompletionItem = new RazorCompletionItem( completion.Key, insertText, RazorCompletionItemKind.DirectiveAttribute, commitCharacters); var completionDescription = new AttributeCompletionDescription(attributeDescriptionInfos.ToArray()); razorCompletionItem.SetAttributeCompletionDescription(completionDescription); completionItems.Add(razorCompletionItem); } return(completionItems); bool TryAddCompletion(string attributeName, BoundAttributeDescriptor boundAttributeDescriptor, TagHelperDescriptor tagHelperDescriptor) { if (attributes.Any(name => string.Equals(name, attributeName, StringComparison.Ordinal)) && !string.Equals(selectedAttributeName, attributeName, StringComparison.Ordinal)) { // Attribute is already present on this element and it is not the selected attribute. // It shouldn't exist in the completion list. return(false); } AddCompletion(attributeName, boundAttributeDescriptor, tagHelperDescriptor); return(true); } void AddCompletion(string attributeName, BoundAttributeDescriptor boundAttributeDescriptor, TagHelperDescriptor tagHelperDescriptor) { if (!attributeCompletions.TryGetValue(attributeName, out var attributeDetails)) { attributeDetails = (new HashSet <AttributeDescriptionInfo>(), new HashSet <string>()); attributeCompletions[attributeName] = attributeDetails; } (var attributeDescriptionInfos, var commitCharacters) = attributeDetails; var descriptionInfo = new AttributeDescriptionInfo( boundAttributeDescriptor.TypeName, tagHelperDescriptor.GetTypeName(), boundAttributeDescriptor.GetPropertyName(), boundAttributeDescriptor.Documentation); attributeDescriptionInfos.Add(descriptionInfo); if (attributeName.EndsWith("...")) { // Indexer attribute, we don't want to commit with standard chars return; } commitCharacters.Add("="); if (tagHelperDescriptor.BoundAttributes.Any(b => b.IsBooleanProperty)) { commitCharacters.Add(" "); } if (tagHelperDescriptor.BoundAttributes.Any(b => b.BoundAttributeParameters.Count > 0)) { commitCharacters.Add(":"); } } }
private static void AssertRazorCompletionItem(DirectiveDescriptor directive, RazorCompletionItem item) => AssertRazorCompletionItem(directive.Directive, directive, item);
private static void AssertRazorCompletionItem(string completionDisplayText, DirectiveDescriptor directive, RazorCompletionItem item, IReadOnlyCollection <string> commitCharacters = null) { Assert.Equal(item.DisplayText, completionDisplayText); Assert.Equal(item.InsertText, directive.Directive); var completionDescription = item.GetDirectiveCompletionDescription(); Assert.Equal(directive.Description, completionDescription.Description); Assert.Equal(item.CommitCharacters, commitCharacters ?? DirectiveCompletionItemProvider.SingleLineDirectiveCommitCharacters); }
public static MarkupTransitionCompletionDescription?GetMarkupTransitionCompletionDescription(this RazorCompletionItem completionItem) { if (completionItem is null) { throw new ArgumentNullException(nameof(completionItem)); } var markupTransitionCompletionDescription = completionItem.Items[s_markupTransitionDescriptionKey] as MarkupTransitionCompletionDescription; return(markupTransitionCompletionDescription); }
public static DirectiveCompletionDescription?GetDirectiveCompletionDescription(this RazorCompletionItem completionItem) { if (completionItem is null) { throw new ArgumentNullException(nameof(completionItem)); } var attributeCompletionDescription = completionItem.Items[s_directiveCompletionDescriptionKey] as DirectiveCompletionDescription; return(attributeCompletionDescription); }
public static AggregateBoundAttributeDescription?GetAttributeCompletionDescription(this RazorCompletionItem completionItem) { if (completionItem is null) { throw new ArgumentNullException(nameof(completionItem)); } var attributeCompletionDescription = completionItem.Items[s_attributeCompletionDescriptionKey] as AggregateBoundAttributeDescription; return(attributeCompletionDescription); }
// Internal for testing internal IReadOnlyList <RazorCompletionItem> GetAttributeParameterCompletions( string attributeName, string parameterName, string containingTagName, IEnumerable <string> attributes, TagHelperDocumentContext tagHelperDocumentContext) { var descriptorsForTag = _tagHelperFactsService.GetTagHelpersGivenTag(tagHelperDocumentContext, containingTagName, parentTag: null); if (descriptorsForTag.Count == 0) { // If the current tag has no possible descriptors then we can't have any additional attributes. return(Array.Empty <RazorCompletionItem>()); } // Attribute parameters are case sensitive when matching var attributeCompletions = new Dictionary <string, HashSet <AttributeDescriptionInfo> >(StringComparer.Ordinal); foreach (var descriptor in descriptorsForTag) { for (var i = 0; i < descriptor.BoundAttributes.Count; i++) { var attributeDescriptor = descriptor.BoundAttributes[i]; var boundAttributeParameters = attributeDescriptor.BoundAttributeParameters; if (boundAttributeParameters.Count == 0) { continue; } if (TagHelperMatchingConventions.CanSatisfyBoundAttribute(attributeName, attributeDescriptor)) { for (var j = 0; j < boundAttributeParameters.Count; j++) { var parameterDescriptor = boundAttributeParameters[j]; if (attributes.Any(name => TagHelperMatchingConventions.SatisfiesBoundAttributeWithParameter(name, attributeDescriptor, parameterDescriptor))) { // There's already an existing attribute that satisfies this parameter, don't show it in the completion list. continue; } if (!attributeCompletions.TryGetValue(parameterDescriptor.Name, out var attributeDescriptionInfos)) { attributeDescriptionInfos = new HashSet <AttributeDescriptionInfo>(); attributeCompletions[parameterDescriptor.Name] = attributeDescriptionInfos; } var descriptionInfo = new AttributeDescriptionInfo( parameterDescriptor.TypeName, descriptor.GetTypeName(), parameterDescriptor.GetPropertyName(), parameterDescriptor.Documentation); attributeDescriptionInfos.Add(descriptionInfo); } } } } var completionItems = new List <RazorCompletionItem>(); foreach (var completion in attributeCompletions) { if (string.Equals(completion.Key, parameterName, StringComparison.Ordinal)) { // This completion is identical to the selected parameter, don't provide for completions for what's already // present in the document. continue; } var razorCompletionItem = new RazorCompletionItem( completion.Key, completion.Key, RazorCompletionItemKind.DirectiveAttributeParameter); var completionDescription = new AttributeCompletionDescription(completion.Value.ToArray()); razorCompletionItem.SetAttributeCompletionDescription(completionDescription); completionItems.Add(razorCompletionItem); } return(completionItems); }
private static void AssertRazorCompletionItem(string completionDisplayText, DirectiveDescriptor directive, RazorCompletionItem item) { Assert.Equal(item.DisplayText, completionDisplayText); Assert.Equal(item.InsertText, directive.Directive); var completionDescription = item.GetDirectiveCompletionDescription(); Assert.Equal(directive.Description, completionDescription.Description); }