зеркало из https://github.com/dotnet/razor.git
Merge remote-tracking branch 'upstream/main' into dev/dawengie/CodeActionsPart5
This commit is contained in:
Коммит
04da68965e
|
@ -6,9 +6,9 @@
|
|||
<Sha>839cdfb0ecca5e0be3dbccd926e7651ef50fdf10</Sha>
|
||||
</Dependency>
|
||||
<!-- Intermediate is necessary for source build. -->
|
||||
<Dependency Name="Microsoft.SourceBuild.Intermediate.source-build-reference-packages" Version="10.0.0-alpha.1.24521.1">
|
||||
<Dependency Name="Microsoft.SourceBuild.Intermediate.source-build-reference-packages" Version="10.0.0-alpha.1.24530.1">
|
||||
<Uri>https://github.com/dotnet/source-build-reference-packages</Uri>
|
||||
<Sha>ccd0927e3823fb178c7151594f5d2eaba81bba81</Sha>
|
||||
<Sha>136e43e45e20bd58bf86eeabba0a0fa7e1a4b3ae</Sha>
|
||||
<SourceBuild RepoName="source-build-reference-packages" ManagedOnly="true" />
|
||||
</Dependency>
|
||||
<Dependency Name="Microsoft.Net.Compilers.Toolset" Version="4.13.0-2.24554.8">
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
<PropertyGroup Label="Automated">
|
||||
<MicrosoftNETCoreBrowserDebugHostTransportPackageVersion>6.0.2-servicing.22064.6</MicrosoftNETCoreBrowserDebugHostTransportPackageVersion>
|
||||
<MicrosoftNETCorePlatformsPackageVersion>6.0.1</MicrosoftNETCorePlatformsPackageVersion>
|
||||
<MicrosoftSourceBuildIntermediatesourcebuildreferencepackagesPackageVersion>10.0.0-alpha.1.24521.1</MicrosoftSourceBuildIntermediatesourcebuildreferencepackagesPackageVersion>
|
||||
<MicrosoftSourceBuildIntermediatesourcebuildreferencepackagesPackageVersion>10.0.0-alpha.1.24530.1</MicrosoftSourceBuildIntermediatesourcebuildreferencepackagesPackageVersion>
|
||||
<MicrosoftSourceBuildIntermediatearcadePackageVersion>9.0.0-beta.24516.2</MicrosoftSourceBuildIntermediatearcadePackageVersion>
|
||||
<MicrosoftDotNetXliffTasksPackageVersion>1.0.0-beta.23475.1</MicrosoftDotNetXliffTasksPackageVersion>
|
||||
<MicrosoftSourceBuildIntermediatexlifftasksPackageVersion>1.0.0-beta.23475.1</MicrosoftSourceBuildIntermediatexlifftasksPackageVersion>
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
<ServiceHubService Include="Microsoft.VisualStudio.Razor.GoToImplementation" ClassName="Microsoft.CodeAnalysis.Remote.Razor.RemoteGoToImplementationService+Factory" />
|
||||
<ServiceHubService Include="Microsoft.VisualStudio.Razor.SpellCheck" ClassName="Microsoft.CodeAnalysis.Remote.Razor.RemoteSpellCheckService+Factory" />
|
||||
<ServiceHubService Include="Microsoft.VisualStudio.Razor.Diagnostics" ClassName="Microsoft.CodeAnalysis.Remote.Razor.RemoteDiagnosticsService+Factory" />
|
||||
<ServiceHubService Include="Microsoft.VisualStudio.Razor.Hover" ClassName="Microsoft.CodeAnalysis.Remote.Razor.RemoteHoverService+Factory" />
|
||||
<ServiceHubService Include="Microsoft.VisualStudio.Razor.Completion" ClassName="Microsoft.CodeAnalysis.Remote.Razor.RemoteCompletionService+Factory" />
|
||||
<ServiceHubService Include="Microsoft.VisualStudio.Razor.CodeActions" ClassName="Microsoft.CodeAnalysis.Remote.Razor.RemoteCodeActionsService+Factory" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -130,7 +130,6 @@ internal partial class RazorLanguageServer : SystemTextJsonLanguageServer<RazorR
|
|||
services.AddDocumentManagementServices(featureOptions);
|
||||
services.AddFormattingServices(featureOptions);
|
||||
services.AddOptionsServices(_lspOptions);
|
||||
services.AddHoverServices();
|
||||
services.AddTextDocumentServices(featureOptions);
|
||||
|
||||
if (!featureOptions.UseRazorCohostServer)
|
||||
|
@ -157,6 +156,9 @@ internal partial class RazorLanguageServer : SystemTextJsonLanguageServer<RazorR
|
|||
services.AddSingleton<IRazorFoldingRangeProvider, UsingsFoldingRangeProvider>();
|
||||
|
||||
services.AddSingleton<IFoldingRangeService, FoldingRangeService>();
|
||||
|
||||
// Hover
|
||||
services.AddHoverServices();
|
||||
}
|
||||
|
||||
// Other
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServer.Protocol;
|
||||
|
||||
internal static class ClientCapabilitiesExtensions
|
||||
{
|
||||
public static MarkupKind GetMarkupKind(this ClientCapabilities clientCapabilities)
|
||||
{
|
||||
// If MarkDown is supported, we'll use that.
|
||||
if (clientCapabilities.TextDocument?.Hover?.ContentFormat is MarkupKind[] contentFormat &&
|
||||
Array.IndexOf(contentFormat, MarkupKind.Markdown) >= 0)
|
||||
{
|
||||
return MarkupKind.Markdown;
|
||||
}
|
||||
|
||||
return MarkupKind.PlainText;
|
||||
}
|
||||
|
||||
public static bool SupportsMarkdown(this ClientCapabilities clientCapabilities)
|
||||
{
|
||||
return clientCapabilities.GetMarkupKind() == MarkupKind.Markdown;
|
||||
}
|
||||
|
||||
public static bool SupportsVisualStudioExtensions(this ClientCapabilities clientCapabilities)
|
||||
{
|
||||
return (clientCapabilities as VSInternalClientCapabilities)?.SupportsVisualStudioExtensions ?? false;
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
// 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 Microsoft.VisualStudio.LanguageServer.Protocol;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.Hover;
|
||||
|
@ -10,16 +9,8 @@ internal readonly record struct HoverDisplayOptions(MarkupKind MarkupKind, bool
|
|||
{
|
||||
public static HoverDisplayOptions From(ClientCapabilities clientCapabilities)
|
||||
{
|
||||
var markupKind = MarkupKind.PlainText;
|
||||
|
||||
// If MarkDown is supported, we'll use that.
|
||||
if (clientCapabilities.TextDocument?.Hover?.ContentFormat is MarkupKind[] contentFormat &&
|
||||
Array.IndexOf(contentFormat, MarkupKind.Markdown) >= 0)
|
||||
{
|
||||
markupKind = MarkupKind.Markdown;
|
||||
}
|
||||
|
||||
var supportsVisualStudioExtensions = (clientCapabilities as VSInternalClientCapabilities)?.SupportsVisualStudioExtensions ?? false;
|
||||
var markupKind = clientCapabilities.GetMarkupKind();
|
||||
var supportsVisualStudioExtensions = clientCapabilities.SupportsVisualStudioExtensions();
|
||||
|
||||
return new(markupKind, supportsVisualStudioExtensions);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.ExternalAccess.Razor;
|
||||
using RoslynHover = Roslyn.LanguageServer.Protocol.Hover;
|
||||
using RoslynPosition = Roslyn.LanguageServer.Protocol.Position;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.Remote;
|
||||
|
||||
internal interface IRemoteHoverService : IRemoteJsonService
|
||||
{
|
||||
ValueTask<RemoteResponse<RoslynHover?>> GetHoverAsync(
|
||||
JsonSerializableRazorPinnedSolutionInfoWrapper solutionInfo,
|
||||
JsonSerializableDocumentId documentId,
|
||||
RoslynPosition position,
|
||||
CancellationToken cancellationToken);
|
||||
}
|
|
@ -30,6 +30,7 @@ internal static class RazorServices
|
|||
[
|
||||
(typeof(IRemoteClientInitializationService), null),
|
||||
(typeof(IRemoteGoToDefinitionService), null),
|
||||
(typeof(IRemoteHoverService), null),
|
||||
(typeof(IRemoteSignatureHelpService), null),
|
||||
(typeof(IRemoteInlayHintService), null),
|
||||
(typeof(IRemoteDocumentSymbolService), null),
|
||||
|
|
|
@ -0,0 +1,215 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.ExternalAccess.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
|
||||
using Microsoft.CodeAnalysis.Razor.Hover;
|
||||
using Microsoft.CodeAnalysis.Razor.Protocol;
|
||||
using Microsoft.CodeAnalysis.Razor.Remote;
|
||||
using Microsoft.CodeAnalysis.Remote.Razor.ProjectSystem;
|
||||
using Roslyn.LanguageServer.Protocol;
|
||||
using static Microsoft.CodeAnalysis.Razor.Remote.RemoteResponse<Roslyn.LanguageServer.Protocol.Hover?>;
|
||||
using static Microsoft.VisualStudio.LanguageServer.Protocol.ClientCapabilitiesExtensions;
|
||||
using static Microsoft.VisualStudio.LanguageServer.Protocol.VsLspExtensions;
|
||||
using static Roslyn.LanguageServer.Protocol.RoslynLspExtensions;
|
||||
using ExternalHandlers = Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost.Handlers;
|
||||
using Range = Roslyn.LanguageServer.Protocol.Range;
|
||||
using VsLsp = Microsoft.VisualStudio.LanguageServer.Protocol;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Remote.Razor;
|
||||
|
||||
internal sealed class RemoteHoverService(in ServiceArgs args) : RazorDocumentServiceBase(in args), IRemoteHoverService
|
||||
{
|
||||
internal sealed class Factory : FactoryBase<IRemoteHoverService>
|
||||
{
|
||||
protected override IRemoteHoverService CreateService(in ServiceArgs args)
|
||||
=> new RemoteHoverService(in args);
|
||||
}
|
||||
|
||||
private readonly IClientCapabilitiesService _clientCapabilitiesService = args.ExportProvider.GetExportedValue<IClientCapabilitiesService>();
|
||||
|
||||
protected override IDocumentPositionInfoStrategy DocumentPositionInfoStrategy => PreferAttributeNameDocumentPositionInfoStrategy.Instance;
|
||||
|
||||
public ValueTask<RemoteResponse<Hover?>> GetHoverAsync(
|
||||
JsonSerializableRazorPinnedSolutionInfoWrapper solutionInfo,
|
||||
JsonSerializableDocumentId documentId,
|
||||
Position position,
|
||||
CancellationToken cancellationToken)
|
||||
=> RunServiceAsync(
|
||||
solutionInfo,
|
||||
documentId,
|
||||
context => GetHoverAsync(context, position, cancellationToken),
|
||||
cancellationToken);
|
||||
|
||||
private async ValueTask<RemoteResponse<Hover?>> GetHoverAsync(
|
||||
RemoteDocumentContext context,
|
||||
Position position,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var codeDocument = await context.GetCodeDocumentAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (!codeDocument.Source.Text.TryGetAbsoluteIndex(position, out var hostDocumentIndex))
|
||||
{
|
||||
return NoFurtherHandling;
|
||||
}
|
||||
|
||||
var clientCapabilities = _clientCapabilitiesService.ClientCapabilities;
|
||||
var positionInfo = GetPositionInfo(codeDocument, hostDocumentIndex, preferCSharpOverHtml: true);
|
||||
|
||||
if (positionInfo.LanguageKind == RazorLanguageKind.CSharp)
|
||||
{
|
||||
var generatedDocument = await context.Snapshot
|
||||
.GetGeneratedDocumentAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var csharpHover = await ExternalHandlers.Hover
|
||||
.GetHoverAsync(
|
||||
generatedDocument,
|
||||
positionInfo.Position.ToLinePosition(),
|
||||
clientCapabilities.SupportsVisualStudioExtensions(),
|
||||
clientCapabilities.SupportsMarkdown(),
|
||||
cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
// Roslyn couldn't provide a hover, so we're done.
|
||||
if (csharpHover is null)
|
||||
{
|
||||
return NoFurtherHandling;
|
||||
}
|
||||
|
||||
// Map the hover range back to the host document
|
||||
if (csharpHover.Range is { } range &&
|
||||
DocumentMappingService.TryMapToHostDocumentRange(codeDocument.GetCSharpDocument(), range.ToLinePositionSpan(), out var hostDocumentSpan))
|
||||
{
|
||||
csharpHover.Range = RoslynLspFactory.CreateRange(hostDocumentSpan);
|
||||
}
|
||||
|
||||
return Results(csharpHover);
|
||||
}
|
||||
|
||||
if (positionInfo.LanguageKind is not (RazorLanguageKind.Html or RazorLanguageKind.Razor))
|
||||
{
|
||||
Debug.Fail($"Encountered an unexpected {nameof(RazorLanguageKind)}: {positionInfo.LanguageKind}");
|
||||
return NoFurtherHandling;
|
||||
}
|
||||
|
||||
// If this is Html or Razor, try to retrieve a hover from Razor.
|
||||
var options = HoverDisplayOptions.From(clientCapabilities);
|
||||
|
||||
var razorHover = await HoverFactory
|
||||
.GetHoverAsync(codeDocument, hostDocumentIndex, options, context.GetSolutionQueryOperations(), cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
// Roslyn couldn't provide a hover, so we're done.
|
||||
if (razorHover is null)
|
||||
{
|
||||
return CallHtml;
|
||||
}
|
||||
|
||||
// Ensure that we convert our Hover to a Roslyn Hover.
|
||||
var resultHover = ConvertHover(razorHover);
|
||||
|
||||
return Results(resultHover);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="VsLsp.Hover"/> to a <see cref="Hover"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Once Razor moves wholly over to Roslyn.LanguageServer.Protocol, this method can be removed.
|
||||
/// </remarks>
|
||||
private Hover ConvertHover(VsLsp.Hover hover)
|
||||
{
|
||||
// Note: Razor only ever produces a Hover with MarkupContent or a VSInternalHover with RawContents.
|
||||
// Both variants return a Range.
|
||||
|
||||
return hover switch
|
||||
{
|
||||
VsLsp.VSInternalHover { Range: var range, RawContent: { } rawContent } => new VSInternalHover()
|
||||
{
|
||||
Range = ConvertRange(range),
|
||||
Contents = string.Empty,
|
||||
RawContent = ConvertVsContent(rawContent)
|
||||
},
|
||||
VsLsp.Hover { Range: var range, Contents.Fourth: VsLsp.MarkupContent contents } => new Hover()
|
||||
{
|
||||
Range = ConvertRange(range),
|
||||
Contents = ConvertMarkupContent(contents)
|
||||
},
|
||||
_ => Assumed.Unreachable<Hover>(),
|
||||
};
|
||||
|
||||
static Range? ConvertRange(VsLsp.Range? range)
|
||||
{
|
||||
return range is not null
|
||||
? RoslynLspFactory.CreateRange(range.ToLinePositionSpan())
|
||||
: null;
|
||||
}
|
||||
|
||||
static object ConvertVsContent(object obj)
|
||||
{
|
||||
return obj switch
|
||||
{
|
||||
VisualStudio.Core.Imaging.ImageId imageId => ConvertImageId(imageId),
|
||||
VisualStudio.Text.Adornments.ImageElement element => ConvertImageElement(element),
|
||||
VisualStudio.Text.Adornments.ClassifiedTextRun run => ConvertClassifiedTextRun(run),
|
||||
VisualStudio.Text.Adornments.ClassifiedTextElement element => ConvertClassifiedTextElement(element),
|
||||
VisualStudio.Text.Adornments.ContainerElement element => ConvertContainerElement(element),
|
||||
_ => Assumed.Unreachable<object>()
|
||||
};
|
||||
|
||||
static Roslyn.Core.Imaging.ImageId ConvertImageId(VisualStudio.Core.Imaging.ImageId imageId)
|
||||
{
|
||||
return new(imageId.Guid, imageId.Id);
|
||||
}
|
||||
|
||||
static Roslyn.Text.Adornments.ImageElement ConvertImageElement(VisualStudio.Text.Adornments.ImageElement element)
|
||||
{
|
||||
return new(ConvertImageId(element.ImageId), element.AutomationName);
|
||||
}
|
||||
|
||||
static Roslyn.Text.Adornments.ClassifiedTextRun ConvertClassifiedTextRun(VisualStudio.Text.Adornments.ClassifiedTextRun run)
|
||||
{
|
||||
return new(
|
||||
run.ClassificationTypeName,
|
||||
run.Text,
|
||||
(Roslyn.Text.Adornments.ClassifiedTextRunStyle)run.Style,
|
||||
run.MarkerTagType,
|
||||
run.NavigationAction,
|
||||
run.Tooltip);
|
||||
}
|
||||
|
||||
static Roslyn.Text.Adornments.ClassifiedTextElement ConvertClassifiedTextElement(VisualStudio.Text.Adornments.ClassifiedTextElement element)
|
||||
{
|
||||
return new(element.Runs.Select(ConvertClassifiedTextRun));
|
||||
}
|
||||
|
||||
static Roslyn.Text.Adornments.ContainerElement ConvertContainerElement(VisualStudio.Text.Adornments.ContainerElement element)
|
||||
{
|
||||
return new((Roslyn.Text.Adornments.ContainerElementStyle)element.Style, element.Elements.Select(ConvertVsContent));
|
||||
}
|
||||
}
|
||||
|
||||
static MarkupContent ConvertMarkupContent(VsLsp.MarkupContent value)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Kind = ConvertMarkupKind(value.Kind),
|
||||
Value = value.Value
|
||||
};
|
||||
}
|
||||
|
||||
static MarkupKind ConvertMarkupKind(VsLsp.MarkupKind value)
|
||||
{
|
||||
return value == VsLsp.MarkupKind.Markdown
|
||||
? MarkupKind.Markdown
|
||||
: MarkupKind.PlainText;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,7 +32,7 @@ internal class CohostDocumentColorEndpoint(
|
|||
|
||||
protected override bool RequiresLSPSolution => true;
|
||||
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext)
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext)
|
||||
{
|
||||
if (clientCapabilities.SupportsVisualStudioExtensions)
|
||||
{
|
||||
|
@ -40,9 +40,6 @@ internal class CohostDocumentColorEndpoint(
|
|||
{
|
||||
Method = Methods.TextDocumentDocumentColorName,
|
||||
RegisterOptions = new DocumentColorRegistrationOptions()
|
||||
{
|
||||
DocumentSelector = filter
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ internal sealed class CohostDocumentCompletionEndpoint(
|
|||
|
||||
protected override bool RequiresLSPSolution => true;
|
||||
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext)
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext)
|
||||
{
|
||||
if (clientCapabilities.TextDocument?.Completion?.DynamicRegistration is true)
|
||||
{
|
||||
|
@ -66,7 +66,6 @@ internal sealed class CohostDocumentCompletionEndpoint(
|
|||
{
|
||||
ResolveProvider = false, // TODO - change to true when Resolve is implemented
|
||||
TriggerCharacters = CompletionTriggerAndCommitCharacters.AllTriggerCharacters,
|
||||
DocumentSelector = filter,
|
||||
AllCommitCharacters = CompletionTriggerAndCommitCharacters.AllCommitCharacters
|
||||
}
|
||||
}];
|
||||
|
|
|
@ -45,7 +45,7 @@ internal sealed class CohostDocumentFormattingEndpoint(
|
|||
|
||||
protected override bool RequiresLSPSolution => true;
|
||||
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext)
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext)
|
||||
{
|
||||
if (clientCapabilities.TextDocument?.Formatting?.DynamicRegistration is true)
|
||||
{
|
||||
|
@ -53,9 +53,6 @@ internal sealed class CohostDocumentFormattingEndpoint(
|
|||
{
|
||||
Method = Methods.TextDocumentFormattingName,
|
||||
RegisterOptions = new DocumentFormattingRegistrationOptions()
|
||||
{
|
||||
DocumentSelector = filter
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ internal class CohostDocumentHighlightEndpoint(
|
|||
|
||||
protected override bool RequiresLSPSolution => true;
|
||||
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext)
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext)
|
||||
{
|
||||
if (clientCapabilities.SupportsVisualStudioExtensions)
|
||||
{
|
||||
|
@ -46,9 +46,6 @@ internal class CohostDocumentHighlightEndpoint(
|
|||
{
|
||||
Method = Methods.TextDocumentDocumentHighlightName,
|
||||
RegisterOptions = new DocumentHighlightRegistrationOptions()
|
||||
{
|
||||
DocumentSelector = filter
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ internal class CohostDocumentPullDiagnosticsEndpoint(
|
|||
|
||||
protected override bool RequiresLSPSolution => true;
|
||||
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext)
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext)
|
||||
{
|
||||
// TODO: if (clientCapabilities.TextDocument?.Diagnostic?.DynamicRegistration is true)
|
||||
{
|
||||
|
@ -59,7 +59,6 @@ internal class CohostDocumentPullDiagnosticsEndpoint(
|
|||
Method = VSInternalMethods.DocumentPullDiagnosticName,
|
||||
RegisterOptions = new VSInternalDiagnosticRegistrationOptions()
|
||||
{
|
||||
DocumentSelector = filter,
|
||||
DiagnosticKinds = [VSInternalDiagnosticKind.Syntax]
|
||||
}
|
||||
}];
|
||||
|
|
|
@ -31,7 +31,7 @@ internal sealed class CohostDocumentSpellCheckEndpoint(
|
|||
|
||||
protected override bool RequiresLSPSolution => true;
|
||||
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext)
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext)
|
||||
{
|
||||
if (clientCapabilities.SupportsVisualStudioExtensions)
|
||||
{
|
||||
|
@ -39,9 +39,6 @@ internal sealed class CohostDocumentSpellCheckEndpoint(
|
|||
{
|
||||
Method = VSInternalMethods.TextDocumentSpellCheckableRangesName,
|
||||
RegisterOptions = new TextDocumentRegistrationOptions()
|
||||
{
|
||||
DocumentSelector = filter
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ internal class CohostDocumentSymbolEndpoint(IRemoteServiceInvoker remoteServiceI
|
|||
|
||||
protected override bool RequiresLSPSolution => true;
|
||||
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext)
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext)
|
||||
{
|
||||
if (clientCapabilities.TextDocument?.DocumentSymbol?.DynamicRegistration == true)
|
||||
{
|
||||
|
@ -40,9 +40,6 @@ internal class CohostDocumentSymbolEndpoint(IRemoteServiceInvoker remoteServiceI
|
|||
{
|
||||
Method = Methods.TextDocumentDocumentSymbolName,
|
||||
RegisterOptions = new DocumentSymbolRegistrationOptions()
|
||||
{
|
||||
DocumentSelector = filter
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost;
|
|||
#pragma warning restore RS0030 // Do not use banned APIs
|
||||
internal sealed class CohostEndpointRegistration : IDynamicRegistrationProvider
|
||||
{
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext)
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext)
|
||||
{
|
||||
return [
|
||||
// Project Context, for the nav bar
|
||||
|
@ -26,25 +26,18 @@ internal sealed class CohostEndpointRegistration : IDynamicRegistrationProvider
|
|||
{
|
||||
Method = VSMethods.GetProjectContextsName,
|
||||
RegisterOptions = new TextDocumentRegistrationOptions()
|
||||
{
|
||||
DocumentSelector = filter
|
||||
}
|
||||
},
|
||||
// DidOpen, DidChange, DidClose, for document synchronization
|
||||
new Registration
|
||||
{
|
||||
Method = Methods.TextDocumentDidOpenName,
|
||||
RegisterOptions = new TextDocumentRegistrationOptions()
|
||||
{
|
||||
DocumentSelector = filter
|
||||
}
|
||||
},
|
||||
new Registration
|
||||
{
|
||||
Method = Methods.TextDocumentDidChangeName,
|
||||
RegisterOptions = new TextDocumentChangeRegistrationOptions()
|
||||
{
|
||||
DocumentSelector = filter,
|
||||
SyncKind = TextDocumentSyncKind.Incremental
|
||||
}
|
||||
},
|
||||
|
@ -52,9 +45,6 @@ internal sealed class CohostEndpointRegistration : IDynamicRegistrationProvider
|
|||
{
|
||||
Method = Methods.TextDocumentDidCloseName,
|
||||
RegisterOptions = new TextDocumentRegistrationOptions()
|
||||
{
|
||||
DocumentSelector = filter
|
||||
}
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ internal class CohostFoldingRangeEndpoint(
|
|||
|
||||
protected override bool RequiresLSPSolution => true;
|
||||
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext)
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext)
|
||||
{
|
||||
if (clientCapabilities.TextDocument?.FoldingRange?.DynamicRegistration is true)
|
||||
{
|
||||
|
@ -49,9 +49,6 @@ internal class CohostFoldingRangeEndpoint(
|
|||
{
|
||||
Method = Methods.TextDocumentFoldingRangeName,
|
||||
RegisterOptions = new FoldingRangeRegistrationOptions()
|
||||
{
|
||||
DocumentSelector = filter
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
|
|
@ -45,14 +45,14 @@ internal sealed class CohostGoToDefinitionEndpoint(
|
|||
|
||||
protected override bool RequiresLSPSolution => true;
|
||||
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext)
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext)
|
||||
{
|
||||
if (clientCapabilities.TextDocument?.Definition?.DynamicRegistration == true)
|
||||
{
|
||||
return [new Registration
|
||||
{
|
||||
Method = Methods.TextDocumentDefinitionName,
|
||||
RegisterOptions = new DefinitionOptions()
|
||||
RegisterOptions = new DefinitionRegistrationOptions()
|
||||
}];
|
||||
}
|
||||
|
||||
|
|
|
@ -41,14 +41,14 @@ internal sealed class CohostGoToImplementationEndpoint(
|
|||
|
||||
protected override bool RequiresLSPSolution => true;
|
||||
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext)
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext)
|
||||
{
|
||||
if (clientCapabilities.TextDocument?.Implementation?.DynamicRegistration == true)
|
||||
{
|
||||
return [new Registration
|
||||
{
|
||||
Method = Methods.TextDocumentImplementationName,
|
||||
RegisterOptions = new ImplementationOptions()
|
||||
RegisterOptions = new ImplementationRegistrationOptions()
|
||||
}];
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Immutable;
|
||||
using System.Composition;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost;
|
||||
using Microsoft.CodeAnalysis.Razor.Remote;
|
||||
using Microsoft.VisualStudio.LanguageServer.ContainedLanguage;
|
||||
using Microsoft.VisualStudio.LanguageServer.Protocol;
|
||||
using RoslynHover = Roslyn.LanguageServer.Protocol.Hover;
|
||||
using RoslynLspFactory = Roslyn.LanguageServer.Protocol.RoslynLspFactory;
|
||||
using VsHover = Microsoft.VisualStudio.LanguageServer.Protocol.Hover;
|
||||
|
||||
namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost;
|
||||
|
||||
#pragma warning disable RS0030 // Do not use banned APIs
|
||||
[Shared]
|
||||
[CohostEndpoint(Methods.TextDocumentHoverName)]
|
||||
[Export(typeof(IDynamicRegistrationProvider))]
|
||||
[ExportCohostStatelessLspService(typeof(CohostHoverEndpoint))]
|
||||
[method: ImportingConstructor]
|
||||
#pragma warning restore RS0030 // Do not use banned APIs
|
||||
internal sealed class CohostHoverEndpoint(
|
||||
IRemoteServiceInvoker remoteServiceInvoker,
|
||||
IHtmlDocumentSynchronizer htmlDocumentSynchronizer,
|
||||
LSPRequestInvoker requestInvoker)
|
||||
: AbstractRazorCohostDocumentRequestHandler<TextDocumentPositionParams, SumType<RoslynHover, VsHover>?>, IDynamicRegistrationProvider
|
||||
{
|
||||
private readonly IRemoteServiceInvoker _remoteServiceInvoker = remoteServiceInvoker;
|
||||
private readonly IHtmlDocumentSynchronizer _htmlDocumentSynchronizer = htmlDocumentSynchronizer;
|
||||
private readonly LSPRequestInvoker _requestInvoker = requestInvoker;
|
||||
|
||||
protected override bool MutatesSolutionState => false;
|
||||
|
||||
protected override bool RequiresLSPSolution => true;
|
||||
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext)
|
||||
{
|
||||
if (clientCapabilities.TextDocument?.Hover?.DynamicRegistration == true)
|
||||
{
|
||||
return [new Registration
|
||||
{
|
||||
Method = Methods.TextDocumentHoverName,
|
||||
RegisterOptions = new HoverRegistrationOptions()
|
||||
}];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
protected override RazorTextDocumentIdentifier? GetRazorTextDocumentIdentifier(TextDocumentPositionParams request)
|
||||
=> request.TextDocument.ToRazorTextDocumentIdentifier();
|
||||
|
||||
protected override Task<SumType<RoslynHover, VsHover>?> HandleRequestAsync(TextDocumentPositionParams request, RazorCohostRequestContext context, CancellationToken cancellationToken)
|
||||
=> HandleRequestAsync(
|
||||
request,
|
||||
context.TextDocument.AssumeNotNull(),
|
||||
cancellationToken);
|
||||
|
||||
private async Task<SumType<RoslynHover, VsHover>?> HandleRequestAsync(TextDocumentPositionParams request, TextDocument razorDocument, CancellationToken cancellationToken)
|
||||
{
|
||||
var position = RoslynLspFactory.CreatePosition(request.Position.ToLinePosition());
|
||||
|
||||
var response = await _remoteServiceInvoker
|
||||
.TryInvokeAsync<IRemoteHoverService, RemoteResponse<RoslynHover?>>(
|
||||
razorDocument.Project.Solution,
|
||||
(service, solutionInfo, cancellationToken) =>
|
||||
service.GetHoverAsync(solutionInfo, razorDocument.Id, position, cancellationToken),
|
||||
cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (response.Result is RoslynHover hover)
|
||||
{
|
||||
return hover;
|
||||
}
|
||||
|
||||
if (response.StopHandling)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return await GetHtmlHoverAsync(request, razorDocument, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task<VsHover?> GetHtmlHoverAsync(TextDocumentPositionParams request, TextDocument razorDocument, CancellationToken cancellationToken)
|
||||
{
|
||||
var htmlDocument = await _htmlDocumentSynchronizer.TryGetSynchronizedHtmlDocumentAsync(razorDocument, cancellationToken).ConfigureAwait(false);
|
||||
if (htmlDocument is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
request.TextDocument = request.TextDocument.WithUri(htmlDocument.Uri);
|
||||
|
||||
var result = await _requestInvoker
|
||||
.ReinvokeRequestOnServerAsync<TextDocumentPositionParams, VsHover?>(
|
||||
htmlDocument.Buffer,
|
||||
Methods.TextDocumentHoverName,
|
||||
RazorLSPConstants.HtmlLanguageServerName,
|
||||
request,
|
||||
cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return result?.Response;
|
||||
}
|
||||
|
||||
internal TestAccessor GetTestAccessor() => new(this);
|
||||
|
||||
internal readonly struct TestAccessor(CohostHoverEndpoint instance)
|
||||
{
|
||||
public Task<SumType<RoslynHover, VsHover>?> HandleRequestAsync(
|
||||
TextDocumentPositionParams request, TextDocument razorDocument, CancellationToken cancellationToken)
|
||||
=> instance.HandleRequestAsync(request, razorDocument, cancellationToken);
|
||||
}
|
||||
}
|
|
@ -30,7 +30,7 @@ internal class CohostInlayHintEndpoint(IRemoteServiceInvoker remoteServiceInvoke
|
|||
|
||||
protected override bool RequiresLSPSolution => true;
|
||||
|
||||
public ImmutableArray<VSLSP.Registration> GetRegistrations(VSLSP.VSInternalClientCapabilities clientCapabilities, VSLSP.DocumentFilter[] filter, RazorCohostRequestContext requestContext)
|
||||
public ImmutableArray<VSLSP.Registration> GetRegistrations(VSLSP.VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext)
|
||||
{
|
||||
if (clientCapabilities.TextDocument?.InlayHint?.DynamicRegistration == true)
|
||||
{
|
||||
|
@ -38,9 +38,6 @@ internal class CohostInlayHintEndpoint(IRemoteServiceInvoker remoteServiceInvoke
|
|||
{
|
||||
Method = Methods.TextDocumentInlayHintName,
|
||||
RegisterOptions = new VSLSP.InlayHintRegistrationOptions()
|
||||
{
|
||||
DocumentSelector = filter
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ internal class CohostLinkedEditingRangeEndpoint(IRemoteServiceInvoker remoteServ
|
|||
|
||||
protected override bool RequiresLSPSolution => true;
|
||||
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext)
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext)
|
||||
{
|
||||
if (clientCapabilities.TextDocument?.LinkedEditingRange?.DynamicRegistration == true)
|
||||
{
|
||||
|
@ -40,9 +40,6 @@ internal class CohostLinkedEditingRangeEndpoint(IRemoteServiceInvoker remoteServ
|
|||
{
|
||||
Method = Methods.TextDocumentLinkedEditingRangeName,
|
||||
RegisterOptions = new LinkedEditingRangeRegistrationOptions()
|
||||
{
|
||||
DocumentSelector = filter
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
|
|
@ -67,14 +67,14 @@ internal class CohostOnAutoInsertEndpoint(
|
|||
|
||||
protected override bool RequiresLSPSolution => true;
|
||||
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext)
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext)
|
||||
{
|
||||
if (clientCapabilities.SupportsVisualStudioExtensions)
|
||||
{
|
||||
return [new Registration
|
||||
{
|
||||
Method = VSInternalMethods.OnAutoInsertName,
|
||||
RegisterOptions = new VSInternalDocumentOnAutoInsertOptions()
|
||||
RegisterOptions = new VSInternalDocumentOnAutoInsertRegistrationOptions()
|
||||
.EnableOnAutoInsert(_triggerCharacters)
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ internal sealed class CohostOnTypeFormattingEndpoint(
|
|||
|
||||
protected override bool RequiresLSPSolution => true;
|
||||
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext)
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext)
|
||||
{
|
||||
if (clientCapabilities.TextDocument?.Formatting?.DynamicRegistration is true)
|
||||
{
|
||||
|
@ -54,9 +54,7 @@ internal sealed class CohostOnTypeFormattingEndpoint(
|
|||
{
|
||||
Method = Methods.TextDocumentOnTypeFormattingName,
|
||||
RegisterOptions = new DocumentOnTypeFormattingRegistrationOptions()
|
||||
{
|
||||
DocumentSelector = filter,
|
||||
}.EnableOnTypeFormattingTriggerCharacters()
|
||||
.EnableOnTypeFormattingTriggerCharacters()
|
||||
}];
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ internal sealed class CohostRangeFormattingEndpoint(
|
|||
|
||||
protected override bool RequiresLSPSolution => true;
|
||||
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext)
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext)
|
||||
{
|
||||
if (clientCapabilities.TextDocument?.Formatting?.DynamicRegistration is true)
|
||||
{
|
||||
|
@ -53,9 +53,6 @@ internal sealed class CohostRangeFormattingEndpoint(
|
|||
{
|
||||
Method = Methods.TextDocumentRangeFormattingName,
|
||||
RegisterOptions = new DocumentRangeFormattingRegistrationOptions()
|
||||
{
|
||||
DocumentSelector = filter
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ internal class CohostRenameEndpoint(
|
|||
|
||||
protected override bool RequiresLSPSolution => true;
|
||||
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext)
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext)
|
||||
{
|
||||
if (clientCapabilities.TextDocument?.Rename?.DynamicRegistration == true)
|
||||
{
|
||||
|
@ -44,7 +44,6 @@ internal class CohostRenameEndpoint(
|
|||
Method = Methods.TextDocumentRenameName,
|
||||
RegisterOptions = new RenameRegistrationOptions()
|
||||
{
|
||||
DocumentSelector = filter,
|
||||
PrepareProvider = false
|
||||
}
|
||||
}];
|
||||
|
|
|
@ -43,7 +43,7 @@ internal sealed class CohostSemanticTokensRangeEndpoint(
|
|||
protected override bool MutatesSolutionState => false;
|
||||
protected override bool RequiresLSPSolution => true;
|
||||
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext)
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext)
|
||||
{
|
||||
if (clientCapabilities.TextDocument?.SemanticTokens?.DynamicRegistration == true)
|
||||
{
|
||||
|
@ -55,9 +55,7 @@ internal sealed class CohostSemanticTokensRangeEndpoint(
|
|||
{
|
||||
Method = Methods.TextDocumentSemanticTokensRangeName,
|
||||
RegisterOptions = new SemanticTokensRegistrationOptions()
|
||||
{
|
||||
DocumentSelector = filter,
|
||||
}.EnableSemanticTokens(_semanticTokensLegendService)
|
||||
.EnableSemanticTokens(_semanticTokensLegendService)
|
||||
}];
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ internal class CohostSignatureHelpEndpoint(
|
|||
|
||||
protected override bool RequiresLSPSolution => true;
|
||||
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext)
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext)
|
||||
{
|
||||
if (clientCapabilities.TextDocument?.SignatureHelp?.DynamicRegistration == true)
|
||||
{
|
||||
|
@ -54,9 +54,7 @@ internal class CohostSignatureHelpEndpoint(
|
|||
{
|
||||
Method = Methods.TextDocumentSignatureHelpName,
|
||||
RegisterOptions = new SignatureHelpRegistrationOptions()
|
||||
{
|
||||
DocumentSelector = filter
|
||||
}.EnableSignatureHelp()
|
||||
.EnableSignatureHelp()
|
||||
}];
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ internal class CohostTextPresentationEndpoint(
|
|||
|
||||
protected override bool RequiresLSPSolution => true;
|
||||
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext)
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext)
|
||||
{
|
||||
if (clientCapabilities.SupportsVisualStudioExtensions)
|
||||
{
|
||||
|
@ -43,9 +43,6 @@ internal class CohostTextPresentationEndpoint(
|
|||
{
|
||||
Method = VSInternalMethods.TextDocumentTextPresentationName,
|
||||
RegisterOptions = new TextDocumentRegistrationOptions()
|
||||
{
|
||||
DocumentSelector = filter
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ internal class CohostUriPresentationEndpoint(
|
|||
|
||||
protected override bool RequiresLSPSolution => true;
|
||||
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext)
|
||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext)
|
||||
{
|
||||
if (clientCapabilities.SupportsVisualStudioExtensions)
|
||||
{
|
||||
|
@ -47,9 +47,6 @@ internal class CohostUriPresentationEndpoint(
|
|||
{
|
||||
Method = VSInternalMethods.TextDocumentUriPresentationName,
|
||||
RegisterOptions = new TextDocumentRegistrationOptions()
|
||||
{
|
||||
DocumentSelector = filter
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
|
|
@ -9,5 +9,5 @@ namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost;
|
|||
|
||||
internal interface IDynamicRegistrationProvider
|
||||
{
|
||||
ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext);
|
||||
ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext);
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ internal class RazorCohostDynamicRegistrationService(
|
|||
Lazy<RazorCohostClientCapabilitiesService> lazyRazorCohostClientCapabilitiesService)
|
||||
: IRazorCohostDynamicRegistrationService
|
||||
{
|
||||
private readonly DocumentFilter[] _filter = [new DocumentFilter()
|
||||
private static readonly DocumentFilter[] s_filter = [new DocumentFilter()
|
||||
{
|
||||
Language = Constants.RazorLanguageName,
|
||||
Pattern = "**/*.{razor,cshtml}"
|
||||
|
@ -50,10 +50,15 @@ internal class RazorCohostDynamicRegistrationService(
|
|||
|
||||
foreach (var provider in _lazyRegistrationProviders)
|
||||
{
|
||||
foreach (var registration in provider.Value.GetRegistrations(clientCapabilities, _filter, requestContext))
|
||||
foreach (var registration in provider.Value.GetRegistrations(clientCapabilities, requestContext))
|
||||
{
|
||||
// We don't unregister anything, so we don't need to do anything interesting with Ids
|
||||
registration.Id = Guid.NewGuid().ToString();
|
||||
if (registration.RegisterOptions is ITextDocumentRegistrationOptions options)
|
||||
{
|
||||
options.DocumentSelector = s_filter;
|
||||
}
|
||||
|
||||
registrations.Add(registration);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,186 @@
|
|||
// 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.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Test.Common;
|
||||
using Microsoft.AspNetCore.Razor.Test.Common.Mef;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost;
|
||||
using Microsoft.CodeAnalysis.Razor.Workspaces;
|
||||
using Microsoft.VisualStudio.LanguageServer.Client;
|
||||
using Microsoft.VisualStudio.LanguageServer.ContainedLanguage;
|
||||
using Microsoft.VisualStudio.LanguageServer.Protocol;
|
||||
using Microsoft.VisualStudio.Razor;
|
||||
using Microsoft.VisualStudio.Razor.LanguageClient.Cohost;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor.Test.Cohost;
|
||||
|
||||
public class CohostEndpointTest(ITestOutputHelper testOutputHelper) : ToolingTestBase(testOutputHelper)
|
||||
{
|
||||
[Fact]
|
||||
public void EndpointsHaveUniqueLSPMethods()
|
||||
{
|
||||
var methods = new Dictionary<string, Type>();
|
||||
|
||||
var endpoints = typeof(CohostColorPresentationEndpoint).Assembly.GetTypes()
|
||||
.Where(t => t.GetCustomAttribute<CohostEndpointAttribute>() != null)
|
||||
.Select(t => (t, t.GetCustomAttribute<CohostEndpointAttribute>().Method));
|
||||
|
||||
foreach (var (endpointType, method) in endpoints)
|
||||
{
|
||||
if (methods.TryGetValue(method, out var existing))
|
||||
{
|
||||
Assert.Fail($"Could not register {endpointType.Name} for {method} because {existing.Name} already has.");
|
||||
}
|
||||
|
||||
methods.Add(method, endpointType);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RegistrationsProvideFilter()
|
||||
{
|
||||
var testComposition = TestComposition.Roslyn
|
||||
.Add(TestComposition.Editor)
|
||||
.AddAssemblies(typeof(CohostLinkedEditingRangeEndpoint).Assembly)
|
||||
.AddAssemblies(typeof(DefaultLSPRequestInvoker).Assembly)
|
||||
.AddAssemblies(typeof(LanguageServerFeatureOptions).Assembly)
|
||||
.AddParts(typeof(TestILanguageServiceBroker2))
|
||||
.AddExcludedPartTypes(typeof(IWorkspaceProvider))
|
||||
.AddParts(typeof(TestWorkspaceProvider))
|
||||
.AddExcludedPartTypes(typeof(ILspEditorFeatureDetector))
|
||||
.AddParts(typeof(TestLspEditorFeatureDetector));
|
||||
|
||||
using var exportProvider = testComposition.ExportProviderFactory.CreateExportProvider();
|
||||
|
||||
var providers = exportProvider.GetExportedValues<IDynamicRegistrationProvider>().ToList();
|
||||
|
||||
// First we verify that the MEF composition above is correct, otherwise this test will be invalid
|
||||
var actualProviders = typeof(CohostLinkedEditingRangeEndpoint).Assembly.GetTypes().Where(t => !t.IsInterface && typeof(IDynamicRegistrationProvider).IsAssignableFrom(t)).ToList();
|
||||
Assert.Equal(actualProviders.OrderBy(a => a.Name).Select(r => r.Name).ToArray(), providers.OrderBy(e => e.GetType().Name).Select(r => r.GetType().Name).ToArray());
|
||||
|
||||
var clientCapabilities = new VSInternalClientCapabilities()
|
||||
{
|
||||
SupportsVisualStudioExtensions = true,
|
||||
TextDocument = new TextDocumentClientCapabilities()
|
||||
{
|
||||
CodeAction = new() { DynamicRegistration = true },
|
||||
CodeLens = new() { DynamicRegistration = true },
|
||||
Completion = new() { DynamicRegistration = true },
|
||||
Definition = new() { DynamicRegistration = true },
|
||||
Diagnostic = new() { DynamicRegistration = true },
|
||||
DocumentHighlight = new() { DynamicRegistration = true },
|
||||
DocumentLink = new() { DynamicRegistration = true },
|
||||
DocumentSymbol = new() { DynamicRegistration = true },
|
||||
FoldingRange = new() { DynamicRegistration = true },
|
||||
Formatting = new() { DynamicRegistration = true },
|
||||
Hover = new() { DynamicRegistration = true },
|
||||
Implementation = new() { DynamicRegistration = true },
|
||||
InlayHint = new() { DynamicRegistration = true },
|
||||
LinkedEditingRange = new() { DynamicRegistration = true },
|
||||
OnTypeFormatting = new() { DynamicRegistration = true },
|
||||
RangeFormatting = new() { DynamicRegistration = true },
|
||||
References = new() { DynamicRegistration = true },
|
||||
Rename = new() { DynamicRegistration = true },
|
||||
SemanticTokens = new() { DynamicRegistration = true },
|
||||
SignatureHelp = new() { DynamicRegistration = true },
|
||||
Synchronization = new() { DynamicRegistration = true },
|
||||
TypeDefinition = new() { DynamicRegistration = true }
|
||||
},
|
||||
};
|
||||
|
||||
foreach (var endpoint in providers)
|
||||
{
|
||||
if (endpoint is CohostSemanticTokensRangeEndpoint)
|
||||
{
|
||||
// We can't currently test this, as the GetRegistrations method calls requestContext.GetRequiredService
|
||||
// and we can't create a request context ourselves
|
||||
continue;
|
||||
}
|
||||
|
||||
var registrations = endpoint.GetRegistrations(clientCapabilities, requestContext: new());
|
||||
|
||||
// If we didn't get any registrations then the test is probably invalid, and we need to update client capabilities above
|
||||
if (registrations.Length == 0)
|
||||
{
|
||||
Assert.Fail($"Did not get any registrations from {endpoint.GetType().Name}. Client capabilities might be wrong?");
|
||||
}
|
||||
|
||||
foreach (var registration in registrations)
|
||||
{
|
||||
var options = registration.RegisterOptions as ITextDocumentRegistrationOptions;
|
||||
if (options is null)
|
||||
{
|
||||
Assert.Fail($"Could not convert registration options from {endpoint.GetType().Name} to {nameof(ITextDocumentRegistrationOptions)}. It was {registration.RegisterOptions?.GetType().Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(ILanguageServiceBroker2))]
|
||||
private class TestILanguageServiceBroker2 : ILanguageServiceBroker2
|
||||
{
|
||||
public IEnumerable<ILanguageClientInstance> ActiveLanguageClients => throw new NotImplementedException();
|
||||
public IEnumerable<Lazy<ILanguageClient, IContentTypeMetadata>> FactoryLanguageClients => throw new NotImplementedException();
|
||||
public IEnumerable<Lazy<ILanguageClient, IContentTypeMetadata>> LanguageClients => throw new NotImplementedException();
|
||||
|
||||
public event EventHandler<LanguageClientLoadedEventArgs> LanguageClientLoaded { add { } remove { } }
|
||||
public event AsyncEventHandler<LanguageClientNotifyEventArgs> ClientNotifyAsync { add { } remove { } }
|
||||
|
||||
public void AddCustomBufferContentTypes(IEnumerable<string> contentTypes) => throw new NotImplementedException();
|
||||
public void AddLanguageClients(IEnumerable<Lazy<ILanguageClient, IContentTypeMetadata>> items) => throw new NotImplementedException();
|
||||
public Task LoadAsync(IContentTypeMetadata contentType, ILanguageClient client) => throw new NotImplementedException();
|
||||
public void Notify<T>(Notification<T> notification, CancellationToken cancellationToken) => throw new NotImplementedException();
|
||||
public Task NotifyAsync(ILanguageClient languageClient, string method, JToken parameters, CancellationToken cancellationToken) => throw new NotImplementedException();
|
||||
public Task OnDidChangeTextDocumentAsync(ITextSnapshot before, ITextSnapshot after, IEnumerable<ITextChange> textChanges) => throw new NotImplementedException();
|
||||
public Task OnDidCloseTextDocumentAsync(ITextSnapshot snapShot) => throw new NotImplementedException();
|
||||
public Task OnDidOpenTextDocumentAsync(ITextSnapshot snapShot) => throw new NotImplementedException();
|
||||
public Task OnDidSaveTextDocumentAsync(ITextDocument document) => throw new NotImplementedException();
|
||||
public void RemoveCustomBufferContentTypes(IEnumerable<string> contentTypes) => throw new NotImplementedException();
|
||||
public void RemoveLanguageClients(IEnumerable<Lazy<ILanguageClient, IContentTypeMetadata>> items) => throw new NotImplementedException();
|
||||
public IAsyncEnumerable<(string client, TResponse? response)> RequestAllAsync<TRequest, TResponse>(Request<TRequest, TResponse> request, CancellationToken cancellationToken) => throw new NotImplementedException();
|
||||
public Task<(ILanguageClient?, JToken?)> RequestAsync(string[] contentTypes, Func<JToken, bool> capabilitiesFilter, string method, JToken parameters, CancellationToken cancellationToken) => throw new NotImplementedException();
|
||||
public Task<(ILanguageClient?, JToken?)> RequestAsync(string[] contentTypes, Func<JToken, bool> capabilitiesFilter, string clientName, string method, JToken parameters, CancellationToken cancellationToken) => throw new NotImplementedException();
|
||||
public Task<ManualInvocationResponse?> RequestAsync(ITextBuffer textBuffer, Func<JToken, bool> capabilitiesFilter, string languageServerName, string method, Func<ITextSnapshot, JToken> parameterFactory, CancellationToken cancellationToken) => throw new NotImplementedException();
|
||||
public Task<JToken?> RequestAsync(ILanguageClient languageClient, string method, JToken parameters, CancellationToken cancellationToken) => throw new NotImplementedException();
|
||||
public Task<TResponse?> RequestAsync<TRequest, TResponse>(Request<TRequest, TResponse> request, CancellationToken cancellationToken) => throw new NotImplementedException();
|
||||
public Task<(ILanguageClient?, TOut)> RequestAsync<TIn, TOut>(string[] contentTypes, Func<ServerCapabilities, bool> capabilitiesFilter, LspRequest<TIn, TOut> method, TIn parameters, CancellationToken cancellationToken) => throw new NotImplementedException();
|
||||
public Task<TOut> RequestAsync<TIn, TOut>(ILanguageClient languageClient, LspRequest<TIn, TOut> method, TIn parameters, CancellationToken cancellationToken) => throw new NotImplementedException();
|
||||
public Task<IEnumerable<(ILanguageClient, JToken?)>> RequestMultipleAsync(string[] contentTypes, Func<JToken, bool> capabilitiesFilter, string method, JToken parameters, CancellationToken cancellationToken) => throw new NotImplementedException();
|
||||
public IAsyncEnumerable<ManualInvocationResponse> RequestMultipleAsync(ITextBuffer textBuffer, Func<JToken, bool> capabilitiesFilter, string method, Func<ITextSnapshot, JToken> parameterFactory, CancellationToken cancellationToken) => throw new NotImplementedException();
|
||||
public Task<IEnumerable<(ILanguageClient, TOut)>> RequestMultipleAsync<TIn, TOut>(string[] contentTypes, Func<ServerCapabilities, bool> capabilitiesFilter, LspRequest<TIn, TOut> method, TIn parameters, CancellationToken cancellationToken) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
[Export(typeof(IWorkspaceProvider))]
|
||||
private class TestWorkspaceProvider : IWorkspaceProvider
|
||||
{
|
||||
public CodeAnalysis.Workspace GetWorkspace() => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
[Export(typeof(ILspEditorFeatureDetector))]
|
||||
private class TestLspEditorFeatureDetector : ILspEditorFeatureDetector
|
||||
{
|
||||
public bool IsLiveShareHost() => throw new NotImplementedException();
|
||||
public bool IsLspEditorEnabled() => throw new NotImplementedException();
|
||||
public bool IsLspEditorSupported(string documentFilePath) => throw new NotImplementedException();
|
||||
public bool IsRemoteClient() => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
[Export(typeof(IRazorSemanticTokensRefreshQueue))]
|
||||
private class TestRazorSemanticTokensRefreshQueue : IRazorSemanticTokensRefreshQueue
|
||||
{
|
||||
public void Initialize(string clientCapabilitiesString) => throw new NotImplementedException();
|
||||
public Task TryEnqueueRefreshComputationAsync(Project project, CancellationToken cancellationToken) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
// 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.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Test.Common;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.ExternalAccess.Razor;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using Microsoft.VisualStudio.LanguageServer.Protocol;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using RoslynHover = Roslyn.LanguageServer.Protocol.Hover;
|
||||
|
||||
namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost;
|
||||
|
||||
using static HoverAssertions;
|
||||
|
||||
public class CohostHoverEndpointTest(ITestOutputHelper testOutputHelper) : CohostEndpointTestBase(testOutputHelper)
|
||||
{
|
||||
[Fact]
|
||||
public async Task Razor()
|
||||
{
|
||||
TestCode code = """
|
||||
<[|PageTi$$tle|]></PageTitle>
|
||||
<div></div>
|
||||
|
||||
@{
|
||||
var myVariable = "Hello";
|
||||
|
||||
var length = myVariable.Length;
|
||||
}
|
||||
""";
|
||||
|
||||
await VerifyHoverAsync(code, async (hover, document) =>
|
||||
{
|
||||
await hover.VerifyRangeAsync(code.Span, document);
|
||||
|
||||
hover.VerifyRawContent(
|
||||
Container(
|
||||
Container(
|
||||
Image,
|
||||
ClassifiedText(
|
||||
Text("Microsoft"),
|
||||
Punctuation("."),
|
||||
Text("AspNetCore"),
|
||||
Punctuation("."),
|
||||
Text("Components"),
|
||||
Punctuation("."),
|
||||
Text("Web"),
|
||||
Punctuation("."),
|
||||
Type("PageTitle")))));
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Html()
|
||||
{
|
||||
TestCode code = """
|
||||
<PageTitle></PageTitle>
|
||||
<div$$></div>
|
||||
|
||||
@{
|
||||
var myVariable = "Hello";
|
||||
|
||||
var length = myVariable.Length;
|
||||
}
|
||||
""";
|
||||
|
||||
var htmlResponse = new VSInternalHover();
|
||||
|
||||
await VerifyHoverAsync(code, htmlResponse, h => Assert.Same(htmlResponse, h));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CSharp()
|
||||
{
|
||||
TestCode code = """
|
||||
<PageTitle></PageTitle>
|
||||
<div></div>
|
||||
|
||||
@{
|
||||
var $$[|myVariable|] = "Hello";
|
||||
|
||||
var length = myVariable.Length;
|
||||
}
|
||||
""";
|
||||
|
||||
await VerifyHoverAsync(code, async (hover, document) =>
|
||||
{
|
||||
await hover.VerifyRangeAsync(code.Span, document);
|
||||
|
||||
hover.VerifyRawContent(
|
||||
Container(
|
||||
Container(
|
||||
Image,
|
||||
ClassifiedText(
|
||||
Punctuation("("),
|
||||
Text("local variable"),
|
||||
Punctuation(")"),
|
||||
WhiteSpace(" "),
|
||||
Keyword("string"),
|
||||
WhiteSpace(" "),
|
||||
LocalName("myVariable")))));
|
||||
});
|
||||
}
|
||||
|
||||
private async Task VerifyHoverAsync(TestCode input, Func<RoslynHover, TextDocument, Task> verifyHover)
|
||||
{
|
||||
var document = await CreateProjectAndRazorDocumentAsync(input.Text);
|
||||
var result = await GetHoverResultAsync(document, input);
|
||||
|
||||
Assert.NotNull(result);
|
||||
var value = result.GetValueOrDefault();
|
||||
|
||||
Assert.True(value.TryGetFirst(out var hover));
|
||||
await verifyHover(hover, document);
|
||||
}
|
||||
|
||||
private async Task VerifyHoverAsync(TestCode input, Hover htmlResponse, Action<Hover?> verifyHover)
|
||||
{
|
||||
var document = await CreateProjectAndRazorDocumentAsync(input.Text);
|
||||
var result = await GetHoverResultAsync(document, input, htmlResponse);
|
||||
|
||||
Assert.NotNull(result);
|
||||
var value = result.GetValueOrDefault();
|
||||
|
||||
Assert.True(value.TryGetSecond(out var hover));
|
||||
verifyHover(hover);
|
||||
}
|
||||
|
||||
private async Task<SumType<RoslynHover, Hover>?> GetHoverResultAsync(TextDocument document, TestCode input, Hover? htmlResponse = null)
|
||||
{
|
||||
var inputText = await document.GetTextAsync(DisposalToken);
|
||||
var linePosition = inputText.GetLinePosition(input.Position);
|
||||
|
||||
var requestInvoker = new TestLSPRequestInvoker([(Methods.TextDocumentHoverName, htmlResponse)]);
|
||||
var endpoint = new CohostHoverEndpoint(RemoteServiceInvoker, TestHtmlDocumentSynchronizer.Instance, requestInvoker);
|
||||
|
||||
var textDocumentPositionParams = new TextDocumentPositionParams
|
||||
{
|
||||
Position = VsLspFactory.CreatePosition(linePosition),
|
||||
TextDocument = new TextDocumentIdentifier { Uri = document.CreateUri() },
|
||||
};
|
||||
|
||||
return await endpoint.GetTestAccessor().HandleRequestAsync(textDocumentPositionParams, document, DisposalToken);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
// 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.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Classification;
|
||||
using Microsoft.CodeAnalysis.Razor.Tooltip;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using Roslyn.LanguageServer.Protocol;
|
||||
using Roslyn.Text.Adornments;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost;
|
||||
|
||||
internal static class HoverAssertions
|
||||
{
|
||||
public static async Task VerifyRangeAsync(this Hover hover, TextSpan expected, TextDocument document)
|
||||
{
|
||||
var text = await document.GetTextAsync();
|
||||
Assert.NotNull(hover.Range);
|
||||
Assert.Equal(text.GetLinePositionSpan(expected), hover.Range.ToLinePositionSpan());
|
||||
}
|
||||
|
||||
public static void VerifyRawContent(this Hover hover, Action<object?> verifier)
|
||||
{
|
||||
var vsHover = Assert.IsType<VSInternalHover>(hover);
|
||||
verifier(vsHover.RawContent);
|
||||
}
|
||||
|
||||
public static Action<object?> Container(params ImmutableArray<Action<object?>> elements)
|
||||
=> o =>
|
||||
{
|
||||
Assert.NotNull(o);
|
||||
var container = Assert.IsType<ContainerElement>(o);
|
||||
|
||||
var allElements = container.Elements.ToArray();
|
||||
Assert.Equal(elements.Length, allElements.Length);
|
||||
|
||||
for (var i = 0; i < elements.Length; i++)
|
||||
{
|
||||
elements[i](allElements[i]);
|
||||
}
|
||||
};
|
||||
|
||||
public static Action<object?> Image
|
||||
=> o =>
|
||||
{
|
||||
Assert.NotNull(o);
|
||||
Assert.IsType<ImageElement>(o);
|
||||
};
|
||||
|
||||
public static Action<object?> ClassifiedText(params ImmutableArray<Action<ClassifiedTextRun>> runs)
|
||||
=> o =>
|
||||
{
|
||||
Assert.NotNull(o);
|
||||
var classifiedText = Assert.IsType<ClassifiedTextElement>(o);
|
||||
|
||||
var allRuns = classifiedText.Runs.ToArray();
|
||||
Assert.Equal(runs.Length, allRuns.Length);
|
||||
|
||||
for (var i = 0; i < runs.Length; i++)
|
||||
{
|
||||
runs[i](allRuns[i]);
|
||||
}
|
||||
};
|
||||
|
||||
public static Action<ClassifiedTextRun> Run(string text, string? classificationTypeName = null)
|
||||
=> run =>
|
||||
{
|
||||
if (classificationTypeName is not null)
|
||||
{
|
||||
Assert.Equal(classificationTypeName, run.ClassificationTypeName);
|
||||
}
|
||||
|
||||
Assert.Equal(text, run.Text);
|
||||
};
|
||||
|
||||
public static Action<ClassifiedTextRun> Keyword(string text)
|
||||
=> Run(text, ClassificationTypeNames.Keyword);
|
||||
|
||||
public static Action<ClassifiedTextRun> LocalName(string text)
|
||||
=> Run(text, ClassificationTypeNames.LocalName);
|
||||
|
||||
public static Action<ClassifiedTextRun> Punctuation(string text)
|
||||
=> Run(text, ClassificationTypeNames.Punctuation);
|
||||
|
||||
public static Action<ClassifiedTextRun> Text(string text)
|
||||
=> Run(text, ClassificationTypeNames.Text);
|
||||
|
||||
public static Action<ClassifiedTextRun> Type(string text)
|
||||
=> Run(text, ClassifiedTagHelperTooltipFactory.TypeClassificationName);
|
||||
|
||||
public static Action<ClassifiedTextRun> WhiteSpace(string text)
|
||||
=> Run(text, ClassificationTypeNames.WhiteSpace);
|
||||
}
|
|
@ -40,22 +40,37 @@ internal static class Assumed
|
|||
public static void Unreachable([CallerFilePath] string? path = null, [CallerLineNumber] int line = 0)
|
||||
=> ThrowInvalidOperation(SR.This_program_location_is_thought_to_be_unreachable, path, line);
|
||||
|
||||
/// <summary>
|
||||
/// Can be called at points that are assumed to be unreachable at runtime.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException"/>
|
||||
[DoesNotReturn]
|
||||
public static void Unreachable(string message, [CallerFilePath] string? path = null, [CallerLineNumber] int line = 0)
|
||||
=> ThrowInvalidOperation(message, path, line);
|
||||
|
||||
/// <summary>
|
||||
/// Can be called at points that are assumed to be unreachable at runtime.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException"/>
|
||||
[DoesNotReturn]
|
||||
public static T Unreachable<T>([CallerFilePath] string? path = null, [CallerLineNumber] int line = 0)
|
||||
{
|
||||
ThrowInvalidOperation(SR.This_program_location_is_thought_to_be_unreachable, path, line);
|
||||
return default;
|
||||
}
|
||||
=> ThrowInvalidOperation<T>(SR.This_program_location_is_thought_to_be_unreachable, path, line);
|
||||
|
||||
/// <summary>
|
||||
/// Can be called at points that are assumed to be unreachable at runtime.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException"/>
|
||||
[DoesNotReturn]
|
||||
public static T Unreachable<T>(string message, [CallerFilePath] string? path = null, [CallerLineNumber] int line = 0)
|
||||
=> ThrowInvalidOperation<T>(message, path, line);
|
||||
|
||||
[DebuggerHidden]
|
||||
[DoesNotReturn]
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void ThrowInvalidOperation(string message, string? path, int line)
|
||||
{
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
=> ThrowHelper.ThrowInvalidOperationException(message + Environment.NewLine + SR.FormatFile_0_Line_1(path, line));
|
||||
|
||||
[DebuggerHidden]
|
||||
[DoesNotReturn]
|
||||
private static T ThrowInvalidOperation<T>(string message, string? path, int line)
|
||||
=> ThrowHelper.ThrowInvalidOperationException<T>(message + Environment.NewLine + SR.FormatFile_0_Line_1(path, line));
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче