зеркало из https://github.com/dotnet/razor.git
Move GetHoverAsync implementation to Workspaces
This commit is contained in:
Родитель
8d53309985
Коммит
908ed82584
|
@ -6,7 +6,6 @@ using System.Threading.Tasks;
|
|||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem;
|
||||
using Microsoft.CodeAnalysis.Razor.Hover;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Microsoft.VisualStudio.LanguageServer.Protocol;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.LanguageServer.Hover;
|
||||
|
@ -17,13 +16,12 @@ internal sealed partial class HoverService
|
|||
|
||||
internal sealed class TestAccessor(HoverService instance)
|
||||
{
|
||||
public Task<VSInternalHover?> GetHoverInfoAsync(
|
||||
string documentFilePath,
|
||||
public Task<VSInternalHover?> GetHoverAsync(
|
||||
RazorCodeDocument codeDocument,
|
||||
string documentFilePath,
|
||||
int absoluteIndex,
|
||||
HoverDisplayOptions options,
|
||||
CancellationToken cancellationToken)
|
||||
=> HoverService.GetHoverInfoAsync(
|
||||
documentFilePath, codeDocument, absoluteIndex, options, instance._projectManager.GetQueryOperations(), cancellationToken);
|
||||
=> HoverFactory.GetHoverAsync(codeDocument, documentFilePath, absoluteIndex, options, instance._projectManager.GetQueryOperations(), cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,27 +1,16 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
using Microsoft.AspNetCore.Razor.Language.Syntax;
|
||||
using Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
|
||||
using Microsoft.CodeAnalysis.Razor.Hover;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Microsoft.CodeAnalysis.Razor.Protocol;
|
||||
using Microsoft.CodeAnalysis.Razor.Tooltip;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using Microsoft.VisualStudio.Editor.Razor;
|
||||
using Microsoft.VisualStudio.LanguageServer.Protocol;
|
||||
using Microsoft.VisualStudio.Text.Adornments;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.LanguageServer.Hover;
|
||||
|
||||
|
@ -54,9 +43,9 @@ internal sealed partial class HoverService(
|
|||
|
||||
var options = HoverDisplayOptions.From(_clientCapabilitiesService.ClientCapabilities);
|
||||
|
||||
return await GetHoverInfoAsync(
|
||||
documentContext.FilePath,
|
||||
return await HoverFactory.GetHoverAsync(
|
||||
codeDocument,
|
||||
documentContext.FilePath,
|
||||
positionInfo.HostDocumentIndex,
|
||||
options,
|
||||
_projectManager.GetQueryOperations(),
|
||||
|
@ -100,258 +89,4 @@ internal sealed partial class HoverService(
|
|||
|
||||
return response;
|
||||
}
|
||||
|
||||
private static async Task<VSInternalHover?> GetHoverInfoAsync(
|
||||
string documentFilePath,
|
||||
RazorCodeDocument codeDocument,
|
||||
int absoluteIndex,
|
||||
HoverDisplayOptions options,
|
||||
ISolutionQueryOperations solutionQueryOperations,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var syntaxTree = codeDocument.GetSyntaxTree();
|
||||
|
||||
var owner = syntaxTree.Root.FindInnermostNode(absoluteIndex);
|
||||
if (owner is null)
|
||||
{
|
||||
Debug.Fail("Owner should never be null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// For cases where the point in the middle of an attribute,
|
||||
// such as <any tes$$t=""></any>
|
||||
// the node desired is the *AttributeSyntax
|
||||
if (owner.Kind is SyntaxKind.MarkupTextLiteral)
|
||||
{
|
||||
owner = owner.Parent;
|
||||
}
|
||||
|
||||
var tagHelperDocumentContext = codeDocument.GetTagHelperContext();
|
||||
|
||||
// We want to find the parent tag, but looking up ancestors in the tree can find other things,
|
||||
// for example when hovering over a start tag, the first ancestor is actually the element it
|
||||
// belongs to, or in other words, the exact same tag! To work around this we just make sure we
|
||||
// only check nodes that are at a different location in the file.
|
||||
var ownerStart = owner.SpanStart;
|
||||
|
||||
if (HtmlFacts.TryGetElementInfo(owner, out var containingTagNameToken, out var attributes, closingForwardSlashOrCloseAngleToken: out _) &&
|
||||
containingTagNameToken.Span.IntersectsWith(absoluteIndex))
|
||||
{
|
||||
if (owner is MarkupStartTagSyntax or MarkupEndTagSyntax &&
|
||||
containingTagNameToken.Content.Equals(SyntaxConstants.TextTagName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// It's possible for there to be a <Text> component that is in scope, and would be found by the GetTagHelperBinding
|
||||
// call below, but a text tag, regardless of casing, inside C# code, is always just a text tag, not a component.
|
||||
return null;
|
||||
}
|
||||
|
||||
// Hovering over HTML tag name
|
||||
var ancestors = owner.Ancestors().Where(n => n.SpanStart != ownerStart);
|
||||
var (parentTag, parentIsTagHelper) = TagHelperFacts.GetNearestAncestorTagInfo(ancestors);
|
||||
var stringifiedAttributes = TagHelperFacts.StringifyAttributes(attributes);
|
||||
var binding = TagHelperFacts.GetTagHelperBinding(
|
||||
tagHelperDocumentContext,
|
||||
containingTagNameToken.Content,
|
||||
stringifiedAttributes,
|
||||
parentTag: parentTag,
|
||||
parentIsTagHelper: parentIsTagHelper);
|
||||
|
||||
if (binding is null)
|
||||
{
|
||||
// No matching tagHelpers, it's just HTML
|
||||
return null;
|
||||
}
|
||||
else if (binding.IsAttributeMatch)
|
||||
{
|
||||
// Hovered over a HTML tag name but the binding matches an attribute
|
||||
return null;
|
||||
}
|
||||
|
||||
Debug.Assert(binding.Descriptors.Any());
|
||||
|
||||
var span = containingTagNameToken.GetLinePositionSpan(codeDocument.Source);
|
||||
|
||||
return await ElementInfoToHoverAsync(
|
||||
documentFilePath, binding.Descriptors, span, options, solutionQueryOperations, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (HtmlFacts.TryGetAttributeInfo(owner, out containingTagNameToken, out _, out var selectedAttributeName, out var selectedAttributeNameLocation, out attributes) &&
|
||||
selectedAttributeNameLocation?.IntersectsWith(absoluteIndex) == true)
|
||||
{
|
||||
// When finding parents for attributes, we make sure to find the parent of the containing tag, otherwise these methods
|
||||
// would return the parent of the attribute, which is not helpful, as its just going to be the containing element
|
||||
var containingTag = containingTagNameToken.Parent;
|
||||
var ancestors = containingTag.Ancestors().Where<SyntaxNode>(n => n.SpanStart != containingTag.SpanStart);
|
||||
var (parentTag, parentIsTagHelper) = TagHelperFacts.GetNearestAncestorTagInfo(ancestors);
|
||||
|
||||
// Hovering over HTML attribute name
|
||||
var stringifiedAttributes = TagHelperFacts.StringifyAttributes(attributes);
|
||||
|
||||
var binding = TagHelperFacts.GetTagHelperBinding(
|
||||
tagHelperDocumentContext,
|
||||
containingTagNameToken.Content,
|
||||
stringifiedAttributes,
|
||||
parentTag: parentTag,
|
||||
parentIsTagHelper: parentIsTagHelper);
|
||||
|
||||
if (binding is null)
|
||||
{
|
||||
// No matching TagHelpers, it's just HTML
|
||||
return null;
|
||||
}
|
||||
|
||||
Debug.Assert(binding.Descriptors.Any());
|
||||
var tagHelperAttributes = TagHelperFacts.GetBoundTagHelperAttributes(
|
||||
tagHelperDocumentContext,
|
||||
selectedAttributeName.AssumeNotNull(),
|
||||
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(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 span = attribute.GetLinePositionSpan(codeDocument.Source);
|
||||
|
||||
// Include the @ in the range
|
||||
switch (attribute.Parent.Kind)
|
||||
{
|
||||
case SyntaxKind.MarkupTagHelperDirectiveAttribute:
|
||||
var directiveAttribute = (MarkupTagHelperDirectiveAttributeSyntax)attribute.Parent;
|
||||
span = span.WithStart(start => start.WithCharacter(ch => ch - directiveAttribute.Transition.FullWidth));
|
||||
attributeName = "@" + attributeName;
|
||||
break;
|
||||
|
||||
case SyntaxKind.MarkupMinimizedTagHelperDirectiveAttribute:
|
||||
var minimizedAttribute = (MarkupMinimizedTagHelperDirectiveAttributeSyntax)containingTag;
|
||||
span = span.WithStart(start => start.WithCharacter(ch => ch - minimizedAttribute.Transition.FullWidth));
|
||||
attributeName = "@" + attributeName;
|
||||
break;
|
||||
}
|
||||
|
||||
return AttributeInfoToHover(tagHelperAttributes, attributeName, span, options);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static VSInternalHover? AttributeInfoToHover(
|
||||
ImmutableArray<BoundAttributeDescriptor> boundAttributes,
|
||||
string attributeName,
|
||||
LinePositionSpan span,
|
||||
HoverDisplayOptions options)
|
||||
{
|
||||
var descriptionInfos = boundAttributes.SelectAsArray(boundAttribute =>
|
||||
{
|
||||
var isIndexer = TagHelperMatchingConventions.SatisfiesBoundAttributeIndexer(boundAttribute, attributeName.AsSpan());
|
||||
return BoundAttributeDescriptionInfo.From(boundAttribute, isIndexer);
|
||||
});
|
||||
|
||||
var attrDescriptionInfo = new AggregateBoundAttributeDescription(descriptionInfos);
|
||||
|
||||
if (options.SupportsVisualStudioExtensions &&
|
||||
ClassifiedTagHelperTooltipFactory.TryCreateTooltip(attrDescriptionInfo, out ContainerElement? classifiedTextElement))
|
||||
{
|
||||
var vsHover = new VSInternalHover
|
||||
{
|
||||
Contents = Array.Empty<SumType<string, MarkedString>>(),
|
||||
Range = span.ToRange(),
|
||||
RawContent = classifiedTextElement,
|
||||
};
|
||||
|
||||
return vsHover;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!MarkupTagHelperTooltipFactory.TryCreateTooltip(attrDescriptionInfo, options.MarkupKind, out var vsMarkupContent))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var markupContent = new MarkupContent()
|
||||
{
|
||||
Value = vsMarkupContent.Value,
|
||||
Kind = vsMarkupContent.Kind,
|
||||
};
|
||||
|
||||
var hover = new VSInternalHover
|
||||
{
|
||||
Contents = markupContent,
|
||||
Range = span.ToRange(),
|
||||
};
|
||||
|
||||
return hover;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<VSInternalHover?> ElementInfoToHoverAsync(
|
||||
string documentFilePath,
|
||||
ImmutableArray<TagHelperDescriptor> descriptors,
|
||||
LinePositionSpan span,
|
||||
HoverDisplayOptions options,
|
||||
ISolutionQueryOperations solutionQueryOperations,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var descriptionInfos = descriptors.SelectAsArray(BoundElementDescriptionInfo.From);
|
||||
var elementDescriptionInfo = new AggregateBoundElementDescription(descriptionInfos);
|
||||
|
||||
if (options.SupportsVisualStudioExtensions)
|
||||
{
|
||||
var classifiedTextElement = await ClassifiedTagHelperTooltipFactory
|
||||
.TryCreateTooltipContainerAsync(documentFilePath, elementDescriptionInfo, solutionQueryOperations, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (classifiedTextElement is not null)
|
||||
{
|
||||
var vsHover = new VSInternalHover
|
||||
{
|
||||
Contents = Array.Empty<SumType<string, MarkedString>>(),
|
||||
Range = span.ToRange(),
|
||||
RawContent = classifiedTextElement,
|
||||
};
|
||||
|
||||
return vsHover;
|
||||
}
|
||||
}
|
||||
|
||||
var vsMarkupContent = await MarkupTagHelperTooltipFactory
|
||||
.TryCreateTooltipAsync(documentFilePath, elementDescriptionInfo, solutionQueryOperations, options.MarkupKind, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (vsMarkupContent is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var markupContent = new MarkupContent()
|
||||
{
|
||||
Value = vsMarkupContent.Value,
|
||||
Kind = vsMarkupContent.Kind,
|
||||
};
|
||||
|
||||
var hover = new VSInternalHover
|
||||
{
|
||||
Contents = markupContent,
|
||||
Range = span.ToRange()
|
||||
};
|
||||
|
||||
return hover;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,266 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
using Microsoft.AspNetCore.Razor.Language.Syntax;
|
||||
using Microsoft.AspNetCore.Razor.Threading;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Microsoft.CodeAnalysis.Razor.Tooltip;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using Microsoft.VisualStudio.Editor.Razor;
|
||||
using Microsoft.VisualStudio.LanguageServer.Protocol;
|
||||
using Microsoft.VisualStudio.Text.Adornments;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.Hover;
|
||||
|
||||
internal static class HoverFactory
|
||||
{
|
||||
public static Task<VSInternalHover?> GetHoverAsync(
|
||||
RazorCodeDocument codeDocument,
|
||||
string documentFilePath,
|
||||
int absoluteIndex,
|
||||
HoverDisplayOptions options,
|
||||
ISolutionQueryOperations solutionQueryOperations,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var syntaxTree = codeDocument.GetSyntaxTree();
|
||||
|
||||
var owner = syntaxTree.Root.FindInnermostNode(absoluteIndex);
|
||||
if (owner is null)
|
||||
{
|
||||
Debug.Fail("Owner should never be null.");
|
||||
return SpecializedTasks.Null<VSInternalHover>();
|
||||
}
|
||||
|
||||
// For cases where the point in the middle of an attribute,
|
||||
// such as <any tes$$t=""></any>
|
||||
// the node desired is the *AttributeSyntax
|
||||
if (owner.Kind is SyntaxKind.MarkupTextLiteral)
|
||||
{
|
||||
owner = owner.Parent;
|
||||
}
|
||||
|
||||
var tagHelperDocumentContext = codeDocument.GetTagHelperContext();
|
||||
|
||||
if (HtmlFacts.TryGetElementInfo(owner, out var containingTagNameToken, out var attributes, closingForwardSlashOrCloseAngleToken: out _) &&
|
||||
containingTagNameToken.Span.IntersectsWith(absoluteIndex))
|
||||
{
|
||||
if (owner is MarkupStartTagSyntax or MarkupEndTagSyntax &&
|
||||
containingTagNameToken.Content.Equals(SyntaxConstants.TextTagName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// It's possible for there to be a <Text> component that is in scope, and would be found by the GetTagHelperBinding
|
||||
// call below, but a text tag, regardless of casing, inside C# code, is always just a text tag, not a component.
|
||||
return SpecializedTasks.Null<VSInternalHover>();
|
||||
}
|
||||
|
||||
// We want to find the parent tag, but looking up ancestors in the tree can find other things,
|
||||
// for example when hovering over a start tag, the first ancestor is actually the element it
|
||||
// belongs to, or in other words, the exact same tag! To work around this we just make sure we
|
||||
// only check nodes that are at a different location in the file.
|
||||
var ownerStart = owner.SpanStart;
|
||||
|
||||
// Hovering over HTML tag name
|
||||
var ancestors = owner.Ancestors().Where(n => n.SpanStart != ownerStart);
|
||||
var (parentTag, parentIsTagHelper) = TagHelperFacts.GetNearestAncestorTagInfo(ancestors);
|
||||
var stringifiedAttributes = TagHelperFacts.StringifyAttributes(attributes);
|
||||
var binding = TagHelperFacts.GetTagHelperBinding(
|
||||
tagHelperDocumentContext,
|
||||
containingTagNameToken.Content,
|
||||
stringifiedAttributes,
|
||||
parentTag: parentTag,
|
||||
parentIsTagHelper: parentIsTagHelper);
|
||||
|
||||
if (binding is null)
|
||||
{
|
||||
// No matching tagHelpers, it's just HTML
|
||||
return SpecializedTasks.Null<VSInternalHover>();
|
||||
}
|
||||
else if (binding.IsAttributeMatch)
|
||||
{
|
||||
// Hovered over a HTML tag name but the binding matches an attribute
|
||||
return SpecializedTasks.Null<VSInternalHover>();
|
||||
}
|
||||
|
||||
Debug.Assert(binding.Descriptors.Any());
|
||||
|
||||
var span = containingTagNameToken.GetLinePositionSpan(codeDocument.Source);
|
||||
|
||||
return ElementInfoToHoverAsync(
|
||||
documentFilePath, binding.Descriptors, span, options, solutionQueryOperations, cancellationToken);
|
||||
}
|
||||
|
||||
if (HtmlFacts.TryGetAttributeInfo(owner, out containingTagNameToken, out _, out var selectedAttributeName, out var selectedAttributeNameLocation, out attributes) &&
|
||||
selectedAttributeNameLocation?.IntersectsWith(absoluteIndex) == true)
|
||||
{
|
||||
// When finding parents for attributes, we make sure to find the parent of the containing tag, otherwise these methods
|
||||
// would return the parent of the attribute, which is not helpful, as its just going to be the containing element
|
||||
var containingTag = containingTagNameToken.Parent;
|
||||
var ancestors = containingTag.Ancestors().Where(n => n.SpanStart != containingTag.SpanStart);
|
||||
var (parentTag, parentIsTagHelper) = TagHelperFacts.GetNearestAncestorTagInfo(ancestors);
|
||||
|
||||
// Hovering over HTML attribute name
|
||||
var stringifiedAttributes = TagHelperFacts.StringifyAttributes(attributes);
|
||||
|
||||
var binding = TagHelperFacts.GetTagHelperBinding(
|
||||
tagHelperDocumentContext,
|
||||
containingTagNameToken.Content,
|
||||
stringifiedAttributes,
|
||||
parentTag: parentTag,
|
||||
parentIsTagHelper: parentIsTagHelper);
|
||||
|
||||
if (binding is null)
|
||||
{
|
||||
// No matching TagHelpers, it's just HTML
|
||||
return SpecializedTasks.Null<VSInternalHover>();
|
||||
}
|
||||
|
||||
Debug.Assert(binding.Descriptors.Any());
|
||||
var tagHelperAttributes = TagHelperFacts.GetBoundTagHelperAttributes(
|
||||
tagHelperDocumentContext,
|
||||
selectedAttributeName.AssumeNotNull(),
|
||||
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(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 span = attribute.GetLinePositionSpan(codeDocument.Source);
|
||||
|
||||
// Include the @ in the range
|
||||
switch (attribute.Parent.Kind)
|
||||
{
|
||||
case SyntaxKind.MarkupTagHelperDirectiveAttribute:
|
||||
var directiveAttribute = (MarkupTagHelperDirectiveAttributeSyntax)attribute.Parent;
|
||||
span = span.WithStart(start => start.WithCharacter(ch => ch - directiveAttribute.Transition.FullWidth));
|
||||
attributeName = "@" + attributeName;
|
||||
break;
|
||||
|
||||
case SyntaxKind.MarkupMinimizedTagHelperDirectiveAttribute:
|
||||
var minimizedAttribute = (MarkupMinimizedTagHelperDirectiveAttributeSyntax)containingTag;
|
||||
span = span.WithStart(start => start.WithCharacter(ch => ch - minimizedAttribute.Transition.FullWidth));
|
||||
attributeName = "@" + attributeName;
|
||||
break;
|
||||
}
|
||||
|
||||
return Task.FromResult(AttributeInfoToHover(tagHelperAttributes, attributeName, span, options));
|
||||
}
|
||||
|
||||
return SpecializedTasks.Null<VSInternalHover>();
|
||||
}
|
||||
|
||||
private static VSInternalHover? AttributeInfoToHover(
|
||||
ImmutableArray<BoundAttributeDescriptor> boundAttributes,
|
||||
string attributeName,
|
||||
LinePositionSpan span,
|
||||
HoverDisplayOptions options)
|
||||
{
|
||||
var descriptionInfos = boundAttributes.SelectAsArray(boundAttribute =>
|
||||
{
|
||||
var isIndexer = TagHelperMatchingConventions.SatisfiesBoundAttributeIndexer(boundAttribute, attributeName.AsSpan());
|
||||
return BoundAttributeDescriptionInfo.From(boundAttribute, isIndexer);
|
||||
});
|
||||
|
||||
var attrDescriptionInfo = new AggregateBoundAttributeDescription(descriptionInfos);
|
||||
|
||||
if (options.SupportsVisualStudioExtensions &&
|
||||
ClassifiedTagHelperTooltipFactory.TryCreateTooltip(attrDescriptionInfo, out ContainerElement? classifiedTextElement))
|
||||
{
|
||||
return new VSInternalHover
|
||||
{
|
||||
Contents = Array.Empty<SumType<string, MarkedString>>(),
|
||||
Range = span.ToRange(),
|
||||
RawContent = classifiedTextElement,
|
||||
};
|
||||
}
|
||||
|
||||
if (!MarkupTagHelperTooltipFactory.TryCreateTooltip(attrDescriptionInfo, options.MarkupKind, out var tooltipContent))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new VSInternalHover
|
||||
{
|
||||
Contents = new MarkupContent()
|
||||
{
|
||||
Value = tooltipContent.Value,
|
||||
Kind = tooltipContent.Kind,
|
||||
},
|
||||
Range = span.ToRange(),
|
||||
};
|
||||
}
|
||||
|
||||
private static async Task<VSInternalHover?> ElementInfoToHoverAsync(
|
||||
string documentFilePath,
|
||||
ImmutableArray<TagHelperDescriptor> descriptors,
|
||||
LinePositionSpan span,
|
||||
HoverDisplayOptions options,
|
||||
ISolutionQueryOperations solutionQueryOperations,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var descriptionInfos = descriptors.SelectAsArray(BoundElementDescriptionInfo.From);
|
||||
var elementDescriptionInfo = new AggregateBoundElementDescription(descriptionInfos);
|
||||
|
||||
if (options.SupportsVisualStudioExtensions)
|
||||
{
|
||||
var classifiedTextElement = await ClassifiedTagHelperTooltipFactory
|
||||
.TryCreateTooltipContainerAsync(documentFilePath, elementDescriptionInfo, solutionQueryOperations, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (classifiedTextElement is not null)
|
||||
{
|
||||
return new VSInternalHover
|
||||
{
|
||||
Contents = Array.Empty<SumType<string, MarkedString>>(),
|
||||
Range = span.ToRange(),
|
||||
RawContent = classifiedTextElement,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
var tooltipContent = await MarkupTagHelperTooltipFactory
|
||||
.TryCreateTooltipAsync(documentFilePath, elementDescriptionInfo, solutionQueryOperations, options.MarkupKind, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (tooltipContent is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new VSInternalHover
|
||||
{
|
||||
Contents = new MarkupContent()
|
||||
{
|
||||
Value = tooltipContent.Value,
|
||||
Kind = tooltipContent.Kind,
|
||||
},
|
||||
Range = span.ToRange()
|
||||
};
|
||||
}
|
||||
}
|
|
@ -50,7 +50,7 @@ public class HoverServiceTest(ITestOutputHelper testOutput) : TagHelperServiceTe
|
|||
var serviceAccessor = service.GetTestAccessor();
|
||||
|
||||
// Act
|
||||
var hover = await serviceAccessor.GetHoverInfoAsync("file.cshtml", codeDocument, code.Position, UseMarkdown, DisposalToken);
|
||||
var hover = await serviceAccessor.GetHoverAsync(codeDocument, "file.cshtml", code.Position, UseMarkdown, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(hover);
|
||||
|
@ -76,7 +76,7 @@ public class HoverServiceTest(ITestOutputHelper testOutput) : TagHelperServiceTe
|
|||
var serviceAccessor = service.GetTestAccessor();
|
||||
|
||||
// Act
|
||||
var hover = await serviceAccessor.GetHoverInfoAsync("file.cshtml", codeDocument, code.Position, UseMarkdown, DisposalToken);
|
||||
var hover = await serviceAccessor.GetHoverAsync(codeDocument, "file.cshtml", code.Position, UseMarkdown, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(hover);
|
||||
|
@ -102,7 +102,7 @@ public class HoverServiceTest(ITestOutputHelper testOutput) : TagHelperServiceTe
|
|||
var serviceAccessor = service.GetTestAccessor();
|
||||
|
||||
// Act
|
||||
var hover = await serviceAccessor.GetHoverInfoAsync("file.cshtml", codeDocument, code.Position, UseMarkdown, DisposalToken);
|
||||
var hover = await serviceAccessor.GetHoverAsync(codeDocument, "file.cshtml", code.Position, UseMarkdown, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(hover);
|
||||
|
@ -126,7 +126,7 @@ public class HoverServiceTest(ITestOutputHelper testOutput) : TagHelperServiceTe
|
|||
var serviceAccessor = service.GetTestAccessor();
|
||||
|
||||
// Act
|
||||
var hover = await serviceAccessor.GetHoverInfoAsync("file.cshtml", codeDocument, code.Position, UseMarkdown, DisposalToken);
|
||||
var hover = await serviceAccessor.GetHoverAsync(codeDocument, "file.cshtml", code.Position, UseMarkdown, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(hover);
|
||||
|
@ -150,7 +150,7 @@ public class HoverServiceTest(ITestOutputHelper testOutput) : TagHelperServiceTe
|
|||
var serviceAccessor = service.GetTestAccessor();
|
||||
|
||||
// Act
|
||||
var hover = await serviceAccessor.GetHoverInfoAsync("file.cshtml", codeDocument, code.Position, UseMarkdown, DisposalToken);
|
||||
var hover = await serviceAccessor.GetHoverAsync(codeDocument, "file.cshtml", code.Position, UseMarkdown, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(hover);
|
||||
|
@ -175,7 +175,7 @@ public class HoverServiceTest(ITestOutputHelper testOutput) : TagHelperServiceTe
|
|||
var serviceAccessor = service.GetTestAccessor();
|
||||
|
||||
// Act
|
||||
var hover = await serviceAccessor.GetHoverInfoAsync("file.cshtml", codeDocument, code.Position, UseMarkdown, DisposalToken);
|
||||
var hover = await serviceAccessor.GetHoverAsync(codeDocument, "file.cshtml", code.Position, UseMarkdown, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(hover);
|
||||
|
@ -200,7 +200,7 @@ public class HoverServiceTest(ITestOutputHelper testOutput) : TagHelperServiceTe
|
|||
var serviceAccessor = service.GetTestAccessor();
|
||||
|
||||
// Act
|
||||
var hover = await serviceAccessor.GetHoverInfoAsync("file.cshtml", codeDocument, code.Position, UseMarkdown, DisposalToken);
|
||||
var hover = await serviceAccessor.GetHoverAsync(codeDocument, "file.cshtml", code.Position, UseMarkdown, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.Null(hover);
|
||||
|
@ -220,7 +220,7 @@ public class HoverServiceTest(ITestOutputHelper testOutput) : TagHelperServiceTe
|
|||
var serviceAccessor = service.GetTestAccessor();
|
||||
|
||||
// Act
|
||||
var hover = await serviceAccessor.GetHoverInfoAsync("file.cshtml", codeDocument, code.Position, UseMarkdown, DisposalToken);
|
||||
var hover = await serviceAccessor.GetHoverAsync(codeDocument, "file.cshtml", code.Position, UseMarkdown, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.Null(hover);
|
||||
|
@ -240,7 +240,7 @@ public class HoverServiceTest(ITestOutputHelper testOutput) : TagHelperServiceTe
|
|||
var serviceAccessor = service.GetTestAccessor();
|
||||
|
||||
// Act
|
||||
var hover = await serviceAccessor.GetHoverInfoAsync("file.cshtml", codeDocument, code.Position, UseMarkdown, DisposalToken);
|
||||
var hover = await serviceAccessor.GetHoverAsync(codeDocument, "file.cshtml", code.Position, UseMarkdown, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.Null(hover);
|
||||
|
@ -260,7 +260,7 @@ public class HoverServiceTest(ITestOutputHelper testOutput) : TagHelperServiceTe
|
|||
var serviceAccessor = service.GetTestAccessor();
|
||||
|
||||
// Act
|
||||
var hover = await serviceAccessor.GetHoverInfoAsync("file.cshtml", codeDocument, code.Position, UseMarkdown, DisposalToken);
|
||||
var hover = await serviceAccessor.GetHoverAsync(codeDocument, "file.cshtml", code.Position, UseMarkdown, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(hover);
|
||||
|
@ -289,7 +289,7 @@ public class HoverServiceTest(ITestOutputHelper testOutput) : TagHelperServiceTe
|
|||
var serviceAccessor = service.GetTestAccessor();
|
||||
|
||||
// Act
|
||||
var hover = await serviceAccessor.GetHoverInfoAsync("file.cshtml", codeDocument, code.Position, UseMarkdown, DisposalToken);
|
||||
var hover = await serviceAccessor.GetHoverAsync(codeDocument, "file.cshtml", code.Position, UseMarkdown, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(hover);
|
||||
|
@ -313,7 +313,7 @@ public class HoverServiceTest(ITestOutputHelper testOutput) : TagHelperServiceTe
|
|||
var serviceAccessor = service.GetTestAccessor();
|
||||
|
||||
// Act
|
||||
var hover = await serviceAccessor.GetHoverInfoAsync("file.cshtml", codeDocument, code.Position, UseMarkdown, DisposalToken);
|
||||
var hover = await serviceAccessor.GetHoverAsync(codeDocument, "file.cshtml", code.Position, UseMarkdown, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(hover);
|
||||
|
@ -337,7 +337,7 @@ public class HoverServiceTest(ITestOutputHelper testOutput) : TagHelperServiceTe
|
|||
var serviceAccessor = service.GetTestAccessor();
|
||||
|
||||
// Act
|
||||
var hover = await serviceAccessor.GetHoverInfoAsync("file.cshtml", codeDocument, code.Position, UseMarkdown, DisposalToken);
|
||||
var hover = await serviceAccessor.GetHoverAsync(codeDocument, "file.cshtml", code.Position, UseMarkdown, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(hover);
|
||||
|
@ -362,7 +362,7 @@ public class HoverServiceTest(ITestOutputHelper testOutput) : TagHelperServiceTe
|
|||
var serviceAccessor = service.GetTestAccessor();
|
||||
|
||||
// Act
|
||||
var hover = await serviceAccessor.GetHoverInfoAsync("file.cshtml", codeDocument, code.Position, UseMarkdown, DisposalToken);
|
||||
var hover = await serviceAccessor.GetHoverAsync(codeDocument, "file.cshtml", code.Position, UseMarkdown, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.Null(hover);
|
||||
|
@ -383,7 +383,7 @@ public class HoverServiceTest(ITestOutputHelper testOutput) : TagHelperServiceTe
|
|||
var serviceAccessor = service.GetTestAccessor();
|
||||
|
||||
// Act
|
||||
var hover = await serviceAccessor.GetHoverInfoAsync("file.cshtml", codeDocument, code.Position, UsePlainText, DisposalToken);
|
||||
var hover = await serviceAccessor.GetHoverAsync(codeDocument, "file.cshtml", code.Position, UsePlainText, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(hover);
|
||||
|
@ -409,7 +409,7 @@ public class HoverServiceTest(ITestOutputHelper testOutput) : TagHelperServiceTe
|
|||
var serviceAccessor = service.GetTestAccessor();
|
||||
|
||||
// Act
|
||||
var hover = await serviceAccessor.GetHoverInfoAsync("file.cshtml", codeDocument, code.Position, UsePlainText, DisposalToken);
|
||||
var hover = await serviceAccessor.GetHoverAsync(codeDocument, "file.cshtml", code.Position, UsePlainText, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(hover);
|
||||
|
@ -434,7 +434,7 @@ public class HoverServiceTest(ITestOutputHelper testOutput) : TagHelperServiceTe
|
|||
var serviceAccessor = service.GetTestAccessor();
|
||||
|
||||
// Act
|
||||
var hover = await serviceAccessor.GetHoverInfoAsync("file.razor", codeDocument, code.Position, UsePlainText, DisposalToken);
|
||||
var hover = await serviceAccessor.GetHoverAsync(codeDocument, "file.razor", code.Position, UsePlainText, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(hover);
|
||||
|
@ -461,7 +461,7 @@ public class HoverServiceTest(ITestOutputHelper testOutput) : TagHelperServiceTe
|
|||
var serviceAccessor = service.GetTestAccessor();
|
||||
|
||||
// Act
|
||||
var hover = await serviceAccessor.GetHoverInfoAsync("file.razor", codeDocument, code.Position, UsePlainText, DisposalToken);
|
||||
var hover = await serviceAccessor.GetHoverAsync(codeDocument, "file.razor", code.Position, UsePlainText, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(hover);
|
||||
|
@ -489,7 +489,7 @@ public class HoverServiceTest(ITestOutputHelper testOutput) : TagHelperServiceTe
|
|||
var serviceAccessor = service.GetTestAccessor();
|
||||
|
||||
// Act
|
||||
var hover = await serviceAccessor.GetHoverInfoAsync("file.razor", codeDocument, code.Position, UsePlainText, DisposalToken);
|
||||
var hover = await serviceAccessor.GetHoverAsync(codeDocument, "file.razor", code.Position, UsePlainText, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.Null(hover);
|
||||
|
@ -514,7 +514,7 @@ public class HoverServiceTest(ITestOutputHelper testOutput) : TagHelperServiceTe
|
|||
var serviceAccessor = service.GetTestAccessor();
|
||||
|
||||
// Act
|
||||
var hover = await serviceAccessor.GetHoverInfoAsync("file.razor", codeDocument, code.Position, UsePlainText, DisposalToken);
|
||||
var hover = await serviceAccessor.GetHoverAsync(codeDocument, "file.razor", code.Position, UsePlainText, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(hover);
|
||||
|
@ -540,7 +540,7 @@ public class HoverServiceTest(ITestOutputHelper testOutput) : TagHelperServiceTe
|
|||
var serviceAccessor = service.GetTestAccessor();
|
||||
|
||||
// Act
|
||||
var hover = await serviceAccessor.GetHoverInfoAsync("file.cshtml", codeDocument, code.Position, UsePlainText, DisposalToken);
|
||||
var hover = await serviceAccessor.GetHoverAsync(codeDocument, "file.cshtml", code.Position, UsePlainText, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(hover);
|
||||
|
@ -567,7 +567,7 @@ public class HoverServiceTest(ITestOutputHelper testOutput) : TagHelperServiceTe
|
|||
var serviceAccessor = service.GetTestAccessor();
|
||||
|
||||
// Act
|
||||
var hover = await serviceAccessor.GetHoverInfoAsync("file.cshtml", codeDocument, code.Position, UsePlainText, DisposalToken);
|
||||
var hover = await serviceAccessor.GetHoverAsync(codeDocument, "file.cshtml", code.Position, UsePlainText, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.Null(hover);
|
||||
|
@ -588,7 +588,7 @@ public class HoverServiceTest(ITestOutputHelper testOutput) : TagHelperServiceTe
|
|||
var serviceAccessor = service.GetTestAccessor();
|
||||
|
||||
// Act
|
||||
var hover = await serviceAccessor.GetHoverInfoAsync("file.cshtml", codeDocument, code.Position, UsePlainText, DisposalToken);
|
||||
var hover = await serviceAccessor.GetHoverAsync(codeDocument, "file.cshtml", code.Position, UsePlainText, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.Null(hover);
|
||||
|
@ -608,7 +608,7 @@ public class HoverServiceTest(ITestOutputHelper testOutput) : TagHelperServiceTe
|
|||
var serviceAccessor = service.GetTestAccessor();
|
||||
|
||||
// Act
|
||||
var vsHover = await serviceAccessor.GetHoverInfoAsync("file.cshtml", codeDocument, code.Position, UseVisualStudio, DisposalToken);
|
||||
var vsHover = await serviceAccessor.GetHoverAsync(codeDocument, "file.cshtml", code.Position, UseVisualStudio, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(vsHover);
|
||||
|
@ -648,7 +648,7 @@ public class HoverServiceTest(ITestOutputHelper testOutput) : TagHelperServiceTe
|
|||
var serviceAccessor = service.GetTestAccessor();
|
||||
|
||||
// Act
|
||||
var vsHover = await serviceAccessor.GetHoverInfoAsync("file.cshtml", codeDocument, code.Position, UseVisualStudio, DisposalToken);
|
||||
var vsHover = await serviceAccessor.GetHoverAsync(codeDocument, "file.cshtml", code.Position, UseVisualStudio, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(vsHover);
|
||||
|
|
Загрузка…
Ссылка в новой задаче