public override IReadOnlyList <RazorCompletionItem> GetCompletionItems(RazorSyntaxTree syntaxTree, TagHelperDocumentContext tagHelperDocumentContext, SourceSpan location)
        {
            if (syntaxTree is null)
            {
                throw new ArgumentNullException(nameof(syntaxTree));
            }

            if (tagHelperDocumentContext is null)
            {
                throw new ArgumentNullException(nameof(tagHelperDocumentContext));
            }

            var change = new SourceChange(location, string.Empty);
            var owner  = syntaxTree.Root.LocateOwner(change);

            if (owner == null)
            {
                Debug.Fail("Owner should never be null.");
                return(Array.Empty <RazorCompletionItem>());
            }

            var parent = owner.Parent;

            if (_htmlFactsService.TryGetElementInfo(parent, out var containingTagNameToken, out var attributes) &&
                containingTagNameToken.Span.IntersectsWith(location.AbsoluteIndex))
            {
                var stringifiedAttributes = _tagHelperFactsService.StringifyAttributes(attributes);
                var elementCompletions    = GetElementCompletions(parent, containingTagNameToken.Content, stringifiedAttributes, tagHelperDocumentContext);
                return(elementCompletions);
            }

            if (_htmlFactsService.TryGetAttributeInfo(
                    parent,
                    out containingTagNameToken,
                    out var prefixLocation,
                    out var selectedAttributeName,
                    out var selectedAttributeNameLocation,
                    out attributes) &&
                (selectedAttributeName == null ||
                 selectedAttributeNameLocation?.IntersectsWith(location.AbsoluteIndex) == true ||
                 (prefixLocation?.IntersectsWith(location.AbsoluteIndex) ?? false)))
            {
                var stringifiedAttributes = _tagHelperFactsService.StringifyAttributes(attributes);
                var attributeCompletions  = GetAttributeCompletions(parent, containingTagNameToken.Content, selectedAttributeName, stringifiedAttributes, tagHelperDocumentContext);
                return(attributeCompletions);
            }

            // Invalid location for TagHelper completions.
            return(Array.Empty <RazorCompletionItem>());
        }
Пример #2
0
        public override IReadOnlyList <CompletionItem> GetCompletionsAt(SourceSpan location, RazorCodeDocument codeDocument)
        {
            if (codeDocument == 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(Array.Empty <CompletionItem>());
            }

            var parent = owner.Parent;

            if (_htmlFactsService.TryGetElementInfo(parent, out var containingTagNameToken, out var attributes) &&
                containingTagNameToken.Span.IntersectsWith(location.AbsoluteIndex))
            {
                var stringifiedAttributes = _tagHelperFactsService.StringifyAttributes(attributes);
                var elementCompletions    = GetElementCompletions(parent, containingTagNameToken.Content, stringifiedAttributes, codeDocument);
                return(elementCompletions);
            }

            if (_htmlFactsService.TryGetAttributeInfo(parent, out containingTagNameToken, out var selectedAttributeName, out attributes) &&
                attributes.Span.IntersectsWith(location.AbsoluteIndex))
            {
                var stringifiedAttributes = _tagHelperFactsService.StringifyAttributes(attributes);
                var attributeCompletions  = GetAttributeCompletions(parent, containingTagNameToken.Content, selectedAttributeName, stringifiedAttributes, codeDocument);
                return(attributeCompletions);
            }

            // Invalid location for TagHelper completions.
            return(Array.Empty <CompletionItem>());
        }
Пример #3
0
        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);
        }
Пример #4
0
        public override IReadOnlyList <RazorCompletionItem> GetCompletionItems(RazorCompletionContext context, SourceSpan location)
        {
            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            var change = new SourceChange(location, string.Empty);
            var owner  = context.SyntaxTree.Root.LocateOwner(change);

            if (owner is null)
            {
                Debug.Fail("Owner should never be null.");
                return(Array.Empty <RazorCompletionItem>());
            }

            var parent = owner.Parent;

            if (_htmlFactsService.TryGetElementInfo(parent, out var containingTagNameToken, out var attributes) &&
                containingTagNameToken.Span.IntersectsWith(location.AbsoluteIndex))
            {
                if ((containingTagNameToken.FullWidth > 1 || containingTagNameToken.Content == "-") &&
                    containingTagNameToken.Span.Start != location.AbsoluteIndex)
                {
                    // To align with HTML completion behavior we only want to provide completion items if we're trying to resolve completion at the
                    // beginning of an HTML element name.
                    return(Array.Empty <RazorCompletionItem>());
                }

                var stringifiedAttributes = _tagHelperFactsService.StringifyAttributes(attributes);
                var containingElement     = parent.Parent;
                var elementCompletions    = GetElementCompletions(containingElement, containingTagNameToken.Content, stringifiedAttributes, context.TagHelperDocumentContext);
                return(elementCompletions);
            }

            if (_htmlFactsService.TryGetAttributeInfo(
                    parent,
                    out containingTagNameToken,
                    out var prefixLocation,
                    out var selectedAttributeName,
                    out var selectedAttributeNameLocation,
                    out attributes) &&
                (selectedAttributeName is null ||
                 selectedAttributeNameLocation?.IntersectsWith(location.AbsoluteIndex) == true ||
                 (prefixLocation?.IntersectsWith(location.AbsoluteIndex) ?? false)))
            {
                if (prefixLocation.HasValue &&
                    prefixLocation.Value.Length == 1 &&
                    selectedAttributeNameLocation.HasValue &&
                    selectedAttributeNameLocation.Value.Length > 1 &&
                    selectedAttributeNameLocation.Value.Start != location.AbsoluteIndex)
                {
                    // To align with HTML completion behavior we only want to provide completion items if we're trying to resolve completion at the
                    // beginning of an HTML attribute name. We do extra checks on prefix locations here in order to rule out malformed cases when the Razor
                    // compiler incorrectly parses multi-line attributes while in the middle of typing out an element. For instance:
                    //
                    // <SurveyPrompt |
                    // @code { ... }
                    //
                    // Will be interpreted as having an `@code` attribute name due to multi-line attributes being a thing. Ultimately this is mostly a
                    // heuristic that we have to apply in order to workaround limitations of the Razor compiler.
                    return(Array.Empty <RazorCompletionItem>());
                }

                var stringifiedAttributes = _tagHelperFactsService.StringifyAttributes(attributes);
                var attributeCompletions  = GetAttributeCompletions(parent, containingTagNameToken.Content, selectedAttributeName, stringifiedAttributes, context.TagHelperDocumentContext, context.Options);
                return(attributeCompletions);
            }

            // Invalid location for TagHelper completions.
            return(Array.Empty <RazorCompletionItem>());
        }
Пример #5
0
        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);
        }