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);
        }
Exemple #2
0
        public static void SetMarkupTransitionCompletionDescription(this RazorCompletionItem completionItem, MarkupTransitionCompletionDescription markupTransitionCompletionDescription)
        {
            if (completionItem is null)
            {
                throw new ArgumentNullException(nameof(completionItem));
            }

            completionItem.Items[s_markupTransitionDescriptionKey] = markupTransitionCompletionDescription;
        }
Exemple #3
0
        public static void SetDirectiveCompletionDescription(this RazorCompletionItem completionItem, DirectiveCompletionDescription attributeCompletionDescription)
        {
            if (completionItem is null)
            {
                throw new ArgumentNullException(nameof(completionItem));
            }

            completionItem.Items[s_directiveCompletionDescriptionKey] = attributeCompletionDescription;
        }
Exemple #4
0
        public static void SetAttributeCompletionDescription(this RazorCompletionItem completionItem, AggregateBoundAttributeDescription attributeCompletionDescription)
        {
            if (completionItem is null)
            {
                throw new ArgumentNullException(nameof(completionItem));
            }

            completionItem.Items[s_attributeCompletionDescriptionKey] = attributeCompletionDescription;
        }
Exemple #5
0
        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);
            }
        }
Exemple #6
0
        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);
        }
Exemple #9
0
        // 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);
        }
Exemple #12
0
        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);
        }
Exemple #13
0
        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);
        }
Exemple #14
0
        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);
        }
Exemple #15
0
        // 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);
        }
Exemple #16
0
        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);
        }