зеркало из 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>
|
<Sha>839cdfb0ecca5e0be3dbccd926e7651ef50fdf10</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<!-- Intermediate is necessary for source build. -->
|
<!-- 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>
|
<Uri>https://github.com/dotnet/source-build-reference-packages</Uri>
|
||||||
<Sha>ccd0927e3823fb178c7151594f5d2eaba81bba81</Sha>
|
<Sha>136e43e45e20bd58bf86eeabba0a0fa7e1a4b3ae</Sha>
|
||||||
<SourceBuild RepoName="source-build-reference-packages" ManagedOnly="true" />
|
<SourceBuild RepoName="source-build-reference-packages" ManagedOnly="true" />
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Net.Compilers.Toolset" Version="4.13.0-2.24554.8">
|
<Dependency Name="Microsoft.Net.Compilers.Toolset" Version="4.13.0-2.24554.8">
|
||||||
|
|
|
@ -49,7 +49,7 @@
|
||||||
<PropertyGroup Label="Automated">
|
<PropertyGroup Label="Automated">
|
||||||
<MicrosoftNETCoreBrowserDebugHostTransportPackageVersion>6.0.2-servicing.22064.6</MicrosoftNETCoreBrowserDebugHostTransportPackageVersion>
|
<MicrosoftNETCoreBrowserDebugHostTransportPackageVersion>6.0.2-servicing.22064.6</MicrosoftNETCoreBrowserDebugHostTransportPackageVersion>
|
||||||
<MicrosoftNETCorePlatformsPackageVersion>6.0.1</MicrosoftNETCorePlatformsPackageVersion>
|
<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>
|
<MicrosoftSourceBuildIntermediatearcadePackageVersion>9.0.0-beta.24516.2</MicrosoftSourceBuildIntermediatearcadePackageVersion>
|
||||||
<MicrosoftDotNetXliffTasksPackageVersion>1.0.0-beta.23475.1</MicrosoftDotNetXliffTasksPackageVersion>
|
<MicrosoftDotNetXliffTasksPackageVersion>1.0.0-beta.23475.1</MicrosoftDotNetXliffTasksPackageVersion>
|
||||||
<MicrosoftSourceBuildIntermediatexlifftasksPackageVersion>1.0.0-beta.23475.1</MicrosoftSourceBuildIntermediatexlifftasksPackageVersion>
|
<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.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.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.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.Completion" ClassName="Microsoft.CodeAnalysis.Remote.Razor.RemoteCompletionService+Factory" />
|
||||||
<ServiceHubService Include="Microsoft.VisualStudio.Razor.CodeActions" ClassName="Microsoft.CodeAnalysis.Remote.Razor.RemoteCodeActionsService+Factory" />
|
<ServiceHubService Include="Microsoft.VisualStudio.Razor.CodeActions" ClassName="Microsoft.CodeAnalysis.Remote.Razor.RemoteCodeActionsService+Factory" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -130,7 +130,6 @@ internal partial class RazorLanguageServer : SystemTextJsonLanguageServer<RazorR
|
||||||
services.AddDocumentManagementServices(featureOptions);
|
services.AddDocumentManagementServices(featureOptions);
|
||||||
services.AddFormattingServices(featureOptions);
|
services.AddFormattingServices(featureOptions);
|
||||||
services.AddOptionsServices(_lspOptions);
|
services.AddOptionsServices(_lspOptions);
|
||||||
services.AddHoverServices();
|
|
||||||
services.AddTextDocumentServices(featureOptions);
|
services.AddTextDocumentServices(featureOptions);
|
||||||
|
|
||||||
if (!featureOptions.UseRazorCohostServer)
|
if (!featureOptions.UseRazorCohostServer)
|
||||||
|
@ -157,6 +156,9 @@ internal partial class RazorLanguageServer : SystemTextJsonLanguageServer<RazorR
|
||||||
services.AddSingleton<IRazorFoldingRangeProvider, UsingsFoldingRangeProvider>();
|
services.AddSingleton<IRazorFoldingRangeProvider, UsingsFoldingRangeProvider>();
|
||||||
|
|
||||||
services.AddSingleton<IFoldingRangeService, FoldingRangeService>();
|
services.AddSingleton<IFoldingRangeService, FoldingRangeService>();
|
||||||
|
|
||||||
|
// Hover
|
||||||
|
services.AddHoverServices();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Other
|
// 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.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
|
||||||
using Microsoft.VisualStudio.LanguageServer.Protocol;
|
using Microsoft.VisualStudio.LanguageServer.Protocol;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor.Hover;
|
namespace Microsoft.CodeAnalysis.Razor.Hover;
|
||||||
|
@ -10,16 +9,8 @@ internal readonly record struct HoverDisplayOptions(MarkupKind MarkupKind, bool
|
||||||
{
|
{
|
||||||
public static HoverDisplayOptions From(ClientCapabilities clientCapabilities)
|
public static HoverDisplayOptions From(ClientCapabilities clientCapabilities)
|
||||||
{
|
{
|
||||||
var markupKind = MarkupKind.PlainText;
|
var markupKind = clientCapabilities.GetMarkupKind();
|
||||||
|
var supportsVisualStudioExtensions = clientCapabilities.SupportsVisualStudioExtensions();
|
||||||
// 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;
|
|
||||||
|
|
||||||
return new(markupKind, 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(IRemoteClientInitializationService), null),
|
||||||
(typeof(IRemoteGoToDefinitionService), null),
|
(typeof(IRemoteGoToDefinitionService), null),
|
||||||
|
(typeof(IRemoteHoverService), null),
|
||||||
(typeof(IRemoteSignatureHelpService), null),
|
(typeof(IRemoteSignatureHelpService), null),
|
||||||
(typeof(IRemoteInlayHintService), null),
|
(typeof(IRemoteInlayHintService), null),
|
||||||
(typeof(IRemoteDocumentSymbolService), 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;
|
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)
|
if (clientCapabilities.SupportsVisualStudioExtensions)
|
||||||
{
|
{
|
||||||
|
@ -40,9 +40,6 @@ internal class CohostDocumentColorEndpoint(
|
||||||
{
|
{
|
||||||
Method = Methods.TextDocumentDocumentColorName,
|
Method = Methods.TextDocumentDocumentColorName,
|
||||||
RegisterOptions = new DocumentColorRegistrationOptions()
|
RegisterOptions = new DocumentColorRegistrationOptions()
|
||||||
{
|
|
||||||
DocumentSelector = filter
|
|
||||||
}
|
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ internal sealed class CohostDocumentCompletionEndpoint(
|
||||||
|
|
||||||
protected override bool RequiresLSPSolution => true;
|
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)
|
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
|
ResolveProvider = false, // TODO - change to true when Resolve is implemented
|
||||||
TriggerCharacters = CompletionTriggerAndCommitCharacters.AllTriggerCharacters,
|
TriggerCharacters = CompletionTriggerAndCommitCharacters.AllTriggerCharacters,
|
||||||
DocumentSelector = filter,
|
|
||||||
AllCommitCharacters = CompletionTriggerAndCommitCharacters.AllCommitCharacters
|
AllCommitCharacters = CompletionTriggerAndCommitCharacters.AllCommitCharacters
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
|
@ -188,7 +187,7 @@ internal sealed class CohostDocumentCompletionEndpoint(
|
||||||
completionContext.TriggerCharacter);
|
completionContext.TriggerCharacter);
|
||||||
}
|
}
|
||||||
|
|
||||||
return combinedCompletionList;
|
return combinedCompletionList;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<VSInternalCompletionList?> GetHtmlCompletionListAsync(
|
private async Task<VSInternalCompletionList?> GetHtmlCompletionListAsync(
|
||||||
|
@ -219,7 +218,7 @@ internal sealed class CohostDocumentCompletionEndpoint(
|
||||||
return rewrittenResponse;
|
return rewrittenResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static T? ToVsLSP<T>(object source) where T : class
|
private static T? ToVsLSP<T>(object source) where T : class
|
||||||
{
|
{
|
||||||
// This is, to say the least, not ideal. In future we're going to normalize on to Roslyn LSP types, and this can go.
|
// This is, to say the least, not ideal. In future we're going to normalize on to Roslyn LSP types, and this can go.
|
||||||
var options = new JsonSerializerOptions();
|
var options = new JsonSerializerOptions();
|
||||||
|
|
|
@ -45,7 +45,7 @@ internal sealed class CohostDocumentFormattingEndpoint(
|
||||||
|
|
||||||
protected override bool RequiresLSPSolution => true;
|
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)
|
if (clientCapabilities.TextDocument?.Formatting?.DynamicRegistration is true)
|
||||||
{
|
{
|
||||||
|
@ -53,9 +53,6 @@ internal sealed class CohostDocumentFormattingEndpoint(
|
||||||
{
|
{
|
||||||
Method = Methods.TextDocumentFormattingName,
|
Method = Methods.TextDocumentFormattingName,
|
||||||
RegisterOptions = new DocumentFormattingRegistrationOptions()
|
RegisterOptions = new DocumentFormattingRegistrationOptions()
|
||||||
{
|
|
||||||
DocumentSelector = filter
|
|
||||||
}
|
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ internal class CohostDocumentHighlightEndpoint(
|
||||||
|
|
||||||
protected override bool RequiresLSPSolution => true;
|
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)
|
if (clientCapabilities.SupportsVisualStudioExtensions)
|
||||||
{
|
{
|
||||||
|
@ -46,9 +46,6 @@ internal class CohostDocumentHighlightEndpoint(
|
||||||
{
|
{
|
||||||
Method = Methods.TextDocumentDocumentHighlightName,
|
Method = Methods.TextDocumentDocumentHighlightName,
|
||||||
RegisterOptions = new DocumentHighlightRegistrationOptions()
|
RegisterOptions = new DocumentHighlightRegistrationOptions()
|
||||||
{
|
|
||||||
DocumentSelector = filter
|
|
||||||
}
|
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ internal class CohostDocumentPullDiagnosticsEndpoint(
|
||||||
|
|
||||||
protected override bool RequiresLSPSolution => true;
|
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)
|
// TODO: if (clientCapabilities.TextDocument?.Diagnostic?.DynamicRegistration is true)
|
||||||
{
|
{
|
||||||
|
@ -59,7 +59,6 @@ internal class CohostDocumentPullDiagnosticsEndpoint(
|
||||||
Method = VSInternalMethods.DocumentPullDiagnosticName,
|
Method = VSInternalMethods.DocumentPullDiagnosticName,
|
||||||
RegisterOptions = new VSInternalDiagnosticRegistrationOptions()
|
RegisterOptions = new VSInternalDiagnosticRegistrationOptions()
|
||||||
{
|
{
|
||||||
DocumentSelector = filter,
|
|
||||||
DiagnosticKinds = [VSInternalDiagnosticKind.Syntax]
|
DiagnosticKinds = [VSInternalDiagnosticKind.Syntax]
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
|
|
|
@ -31,7 +31,7 @@ internal sealed class CohostDocumentSpellCheckEndpoint(
|
||||||
|
|
||||||
protected override bool RequiresLSPSolution => true;
|
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)
|
if (clientCapabilities.SupportsVisualStudioExtensions)
|
||||||
{
|
{
|
||||||
|
@ -39,9 +39,6 @@ internal sealed class CohostDocumentSpellCheckEndpoint(
|
||||||
{
|
{
|
||||||
Method = VSInternalMethods.TextDocumentSpellCheckableRangesName,
|
Method = VSInternalMethods.TextDocumentSpellCheckableRangesName,
|
||||||
RegisterOptions = new TextDocumentRegistrationOptions()
|
RegisterOptions = new TextDocumentRegistrationOptions()
|
||||||
{
|
|
||||||
DocumentSelector = filter
|
|
||||||
}
|
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ internal class CohostDocumentSymbolEndpoint(IRemoteServiceInvoker remoteServiceI
|
||||||
|
|
||||||
protected override bool RequiresLSPSolution => true;
|
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)
|
if (clientCapabilities.TextDocument?.DocumentSymbol?.DynamicRegistration == true)
|
||||||
{
|
{
|
||||||
|
@ -40,9 +40,6 @@ internal class CohostDocumentSymbolEndpoint(IRemoteServiceInvoker remoteServiceI
|
||||||
{
|
{
|
||||||
Method = Methods.TextDocumentDocumentSymbolName,
|
Method = Methods.TextDocumentDocumentSymbolName,
|
||||||
RegisterOptions = new DocumentSymbolRegistrationOptions()
|
RegisterOptions = new DocumentSymbolRegistrationOptions()
|
||||||
{
|
|
||||||
DocumentSelector = filter
|
|
||||||
}
|
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost;
|
||||||
#pragma warning restore RS0030 // Do not use banned APIs
|
#pragma warning restore RS0030 // Do not use banned APIs
|
||||||
internal sealed class CohostEndpointRegistration : IDynamicRegistrationProvider
|
internal sealed class CohostEndpointRegistration : IDynamicRegistrationProvider
|
||||||
{
|
{
|
||||||
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext)
|
public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext)
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
// Project Context, for the nav bar
|
// Project Context, for the nav bar
|
||||||
|
@ -26,25 +26,18 @@ internal sealed class CohostEndpointRegistration : IDynamicRegistrationProvider
|
||||||
{
|
{
|
||||||
Method = VSMethods.GetProjectContextsName,
|
Method = VSMethods.GetProjectContextsName,
|
||||||
RegisterOptions = new TextDocumentRegistrationOptions()
|
RegisterOptions = new TextDocumentRegistrationOptions()
|
||||||
{
|
|
||||||
DocumentSelector = filter
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
// DidOpen, DidChange, DidClose, for document synchronization
|
// DidOpen, DidChange, DidClose, for document synchronization
|
||||||
new Registration
|
new Registration
|
||||||
{
|
{
|
||||||
Method = Methods.TextDocumentDidOpenName,
|
Method = Methods.TextDocumentDidOpenName,
|
||||||
RegisterOptions = new TextDocumentRegistrationOptions()
|
RegisterOptions = new TextDocumentRegistrationOptions()
|
||||||
{
|
|
||||||
DocumentSelector = filter
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
new Registration
|
new Registration
|
||||||
{
|
{
|
||||||
Method = Methods.TextDocumentDidChangeName,
|
Method = Methods.TextDocumentDidChangeName,
|
||||||
RegisterOptions = new TextDocumentChangeRegistrationOptions()
|
RegisterOptions = new TextDocumentChangeRegistrationOptions()
|
||||||
{
|
{
|
||||||
DocumentSelector = filter,
|
|
||||||
SyncKind = TextDocumentSyncKind.Incremental
|
SyncKind = TextDocumentSyncKind.Incremental
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -52,9 +45,6 @@ internal sealed class CohostEndpointRegistration : IDynamicRegistrationProvider
|
||||||
{
|
{
|
||||||
Method = Methods.TextDocumentDidCloseName,
|
Method = Methods.TextDocumentDidCloseName,
|
||||||
RegisterOptions = new TextDocumentRegistrationOptions()
|
RegisterOptions = new TextDocumentRegistrationOptions()
|
||||||
{
|
|
||||||
DocumentSelector = filter
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ internal class CohostFoldingRangeEndpoint(
|
||||||
|
|
||||||
protected override bool RequiresLSPSolution => true;
|
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)
|
if (clientCapabilities.TextDocument?.FoldingRange?.DynamicRegistration is true)
|
||||||
{
|
{
|
||||||
|
@ -49,9 +49,6 @@ internal class CohostFoldingRangeEndpoint(
|
||||||
{
|
{
|
||||||
Method = Methods.TextDocumentFoldingRangeName,
|
Method = Methods.TextDocumentFoldingRangeName,
|
||||||
RegisterOptions = new FoldingRangeRegistrationOptions()
|
RegisterOptions = new FoldingRangeRegistrationOptions()
|
||||||
{
|
|
||||||
DocumentSelector = filter
|
|
||||||
}
|
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,14 +45,14 @@ internal sealed class CohostGoToDefinitionEndpoint(
|
||||||
|
|
||||||
protected override bool RequiresLSPSolution => true;
|
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)
|
if (clientCapabilities.TextDocument?.Definition?.DynamicRegistration == true)
|
||||||
{
|
{
|
||||||
return [new Registration
|
return [new Registration
|
||||||
{
|
{
|
||||||
Method = Methods.TextDocumentDefinitionName,
|
Method = Methods.TextDocumentDefinitionName,
|
||||||
RegisterOptions = new DefinitionOptions()
|
RegisterOptions = new DefinitionRegistrationOptions()
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,14 +41,14 @@ internal sealed class CohostGoToImplementationEndpoint(
|
||||||
|
|
||||||
protected override bool RequiresLSPSolution => true;
|
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)
|
if (clientCapabilities.TextDocument?.Implementation?.DynamicRegistration == true)
|
||||||
{
|
{
|
||||||
return [new Registration
|
return [new Registration
|
||||||
{
|
{
|
||||||
Method = Methods.TextDocumentImplementationName,
|
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;
|
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)
|
if (clientCapabilities.TextDocument?.InlayHint?.DynamicRegistration == true)
|
||||||
{
|
{
|
||||||
|
@ -38,9 +38,6 @@ internal class CohostInlayHintEndpoint(IRemoteServiceInvoker remoteServiceInvoke
|
||||||
{
|
{
|
||||||
Method = Methods.TextDocumentInlayHintName,
|
Method = Methods.TextDocumentInlayHintName,
|
||||||
RegisterOptions = new VSLSP.InlayHintRegistrationOptions()
|
RegisterOptions = new VSLSP.InlayHintRegistrationOptions()
|
||||||
{
|
|
||||||
DocumentSelector = filter
|
|
||||||
}
|
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ internal class CohostLinkedEditingRangeEndpoint(IRemoteServiceInvoker remoteServ
|
||||||
|
|
||||||
protected override bool RequiresLSPSolution => true;
|
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)
|
if (clientCapabilities.TextDocument?.LinkedEditingRange?.DynamicRegistration == true)
|
||||||
{
|
{
|
||||||
|
@ -40,9 +40,6 @@ internal class CohostLinkedEditingRangeEndpoint(IRemoteServiceInvoker remoteServ
|
||||||
{
|
{
|
||||||
Method = Methods.TextDocumentLinkedEditingRangeName,
|
Method = Methods.TextDocumentLinkedEditingRangeName,
|
||||||
RegisterOptions = new LinkedEditingRangeRegistrationOptions()
|
RegisterOptions = new LinkedEditingRangeRegistrationOptions()
|
||||||
{
|
|
||||||
DocumentSelector = filter
|
|
||||||
}
|
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,14 +67,14 @@ internal class CohostOnAutoInsertEndpoint(
|
||||||
|
|
||||||
protected override bool RequiresLSPSolution => true;
|
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)
|
if (clientCapabilities.SupportsVisualStudioExtensions)
|
||||||
{
|
{
|
||||||
return [new Registration
|
return [new Registration
|
||||||
{
|
{
|
||||||
Method = VSInternalMethods.OnAutoInsertName,
|
Method = VSInternalMethods.OnAutoInsertName,
|
||||||
RegisterOptions = new VSInternalDocumentOnAutoInsertOptions()
|
RegisterOptions = new VSInternalDocumentOnAutoInsertRegistrationOptions()
|
||||||
.EnableOnAutoInsert(_triggerCharacters)
|
.EnableOnAutoInsert(_triggerCharacters)
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ internal sealed class CohostOnTypeFormattingEndpoint(
|
||||||
|
|
||||||
protected override bool RequiresLSPSolution => true;
|
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)
|
if (clientCapabilities.TextDocument?.Formatting?.DynamicRegistration is true)
|
||||||
{
|
{
|
||||||
|
@ -54,9 +54,7 @@ internal sealed class CohostOnTypeFormattingEndpoint(
|
||||||
{
|
{
|
||||||
Method = Methods.TextDocumentOnTypeFormattingName,
|
Method = Methods.TextDocumentOnTypeFormattingName,
|
||||||
RegisterOptions = new DocumentOnTypeFormattingRegistrationOptions()
|
RegisterOptions = new DocumentOnTypeFormattingRegistrationOptions()
|
||||||
{
|
.EnableOnTypeFormattingTriggerCharacters()
|
||||||
DocumentSelector = filter,
|
|
||||||
}.EnableOnTypeFormattingTriggerCharacters()
|
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ internal sealed class CohostRangeFormattingEndpoint(
|
||||||
|
|
||||||
protected override bool RequiresLSPSolution => true;
|
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)
|
if (clientCapabilities.TextDocument?.Formatting?.DynamicRegistration is true)
|
||||||
{
|
{
|
||||||
|
@ -53,9 +53,6 @@ internal sealed class CohostRangeFormattingEndpoint(
|
||||||
{
|
{
|
||||||
Method = Methods.TextDocumentRangeFormattingName,
|
Method = Methods.TextDocumentRangeFormattingName,
|
||||||
RegisterOptions = new DocumentRangeFormattingRegistrationOptions()
|
RegisterOptions = new DocumentRangeFormattingRegistrationOptions()
|
||||||
{
|
|
||||||
DocumentSelector = filter
|
|
||||||
}
|
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ internal class CohostRenameEndpoint(
|
||||||
|
|
||||||
protected override bool RequiresLSPSolution => true;
|
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)
|
if (clientCapabilities.TextDocument?.Rename?.DynamicRegistration == true)
|
||||||
{
|
{
|
||||||
|
@ -44,7 +44,6 @@ internal class CohostRenameEndpoint(
|
||||||
Method = Methods.TextDocumentRenameName,
|
Method = Methods.TextDocumentRenameName,
|
||||||
RegisterOptions = new RenameRegistrationOptions()
|
RegisterOptions = new RenameRegistrationOptions()
|
||||||
{
|
{
|
||||||
DocumentSelector = filter,
|
|
||||||
PrepareProvider = false
|
PrepareProvider = false
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
|
|
|
@ -43,7 +43,7 @@ internal sealed class CohostSemanticTokensRangeEndpoint(
|
||||||
protected override bool MutatesSolutionState => false;
|
protected override bool MutatesSolutionState => false;
|
||||||
protected override bool RequiresLSPSolution => true;
|
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)
|
if (clientCapabilities.TextDocument?.SemanticTokens?.DynamicRegistration == true)
|
||||||
{
|
{
|
||||||
|
@ -55,9 +55,7 @@ internal sealed class CohostSemanticTokensRangeEndpoint(
|
||||||
{
|
{
|
||||||
Method = Methods.TextDocumentSemanticTokensRangeName,
|
Method = Methods.TextDocumentSemanticTokensRangeName,
|
||||||
RegisterOptions = new SemanticTokensRegistrationOptions()
|
RegisterOptions = new SemanticTokensRegistrationOptions()
|
||||||
{
|
.EnableSemanticTokens(_semanticTokensLegendService)
|
||||||
DocumentSelector = filter,
|
|
||||||
}.EnableSemanticTokens(_semanticTokensLegendService)
|
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ internal class CohostSignatureHelpEndpoint(
|
||||||
|
|
||||||
protected override bool RequiresLSPSolution => true;
|
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)
|
if (clientCapabilities.TextDocument?.SignatureHelp?.DynamicRegistration == true)
|
||||||
{
|
{
|
||||||
|
@ -54,9 +54,7 @@ internal class CohostSignatureHelpEndpoint(
|
||||||
{
|
{
|
||||||
Method = Methods.TextDocumentSignatureHelpName,
|
Method = Methods.TextDocumentSignatureHelpName,
|
||||||
RegisterOptions = new SignatureHelpRegistrationOptions()
|
RegisterOptions = new SignatureHelpRegistrationOptions()
|
||||||
{
|
.EnableSignatureHelp()
|
||||||
DocumentSelector = filter
|
|
||||||
}.EnableSignatureHelp()
|
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ internal class CohostTextPresentationEndpoint(
|
||||||
|
|
||||||
protected override bool RequiresLSPSolution => true;
|
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)
|
if (clientCapabilities.SupportsVisualStudioExtensions)
|
||||||
{
|
{
|
||||||
|
@ -43,9 +43,6 @@ internal class CohostTextPresentationEndpoint(
|
||||||
{
|
{
|
||||||
Method = VSInternalMethods.TextDocumentTextPresentationName,
|
Method = VSInternalMethods.TextDocumentTextPresentationName,
|
||||||
RegisterOptions = new TextDocumentRegistrationOptions()
|
RegisterOptions = new TextDocumentRegistrationOptions()
|
||||||
{
|
|
||||||
DocumentSelector = filter
|
|
||||||
}
|
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ internal class CohostUriPresentationEndpoint(
|
||||||
|
|
||||||
protected override bool RequiresLSPSolution => true;
|
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)
|
if (clientCapabilities.SupportsVisualStudioExtensions)
|
||||||
{
|
{
|
||||||
|
@ -47,9 +47,6 @@ internal class CohostUriPresentationEndpoint(
|
||||||
{
|
{
|
||||||
Method = VSInternalMethods.TextDocumentUriPresentationName,
|
Method = VSInternalMethods.TextDocumentUriPresentationName,
|
||||||
RegisterOptions = new TextDocumentRegistrationOptions()
|
RegisterOptions = new TextDocumentRegistrationOptions()
|
||||||
{
|
|
||||||
DocumentSelector = filter
|
|
||||||
}
|
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,5 +9,5 @@ namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost;
|
||||||
|
|
||||||
internal interface IDynamicRegistrationProvider
|
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)
|
Lazy<RazorCohostClientCapabilitiesService> lazyRazorCohostClientCapabilitiesService)
|
||||||
: IRazorCohostDynamicRegistrationService
|
: IRazorCohostDynamicRegistrationService
|
||||||
{
|
{
|
||||||
private readonly DocumentFilter[] _filter = [new DocumentFilter()
|
private static readonly DocumentFilter[] s_filter = [new DocumentFilter()
|
||||||
{
|
{
|
||||||
Language = Constants.RazorLanguageName,
|
Language = Constants.RazorLanguageName,
|
||||||
Pattern = "**/*.{razor,cshtml}"
|
Pattern = "**/*.{razor,cshtml}"
|
||||||
|
@ -50,10 +50,15 @@ internal class RazorCohostDynamicRegistrationService(
|
||||||
|
|
||||||
foreach (var provider in _lazyRegistrationProviders)
|
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
|
// We don't unregister anything, so we don't need to do anything interesting with Ids
|
||||||
registration.Id = Guid.NewGuid().ToString();
|
registration.Id = Guid.NewGuid().ToString();
|
||||||
|
if (registration.RegisterOptions is ITextDocumentRegistrationOptions options)
|
||||||
|
{
|
||||||
|
options.DocumentSelector = s_filter;
|
||||||
|
}
|
||||||
|
|
||||||
registrations.Add(registration);
|
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)
|
public static void Unreachable([CallerFilePath] string? path = null, [CallerLineNumber] int line = 0)
|
||||||
=> ThrowInvalidOperation(SR.This_program_location_is_thought_to_be_unreachable, path, line);
|
=> 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>
|
/// <summary>
|
||||||
/// Can be called at points that are assumed to be unreachable at runtime.
|
/// Can be called at points that are assumed to be unreachable at runtime.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <exception cref="InvalidOperationException"/>
|
/// <exception cref="InvalidOperationException"/>
|
||||||
[DoesNotReturn]
|
[DoesNotReturn]
|
||||||
public static T Unreachable<T>([CallerFilePath] string? path = null, [CallerLineNumber] int line = 0)
|
public static T Unreachable<T>([CallerFilePath] string? path = null, [CallerLineNumber] int line = 0)
|
||||||
{
|
=> ThrowInvalidOperation<T>(SR.This_program_location_is_thought_to_be_unreachable, path, line);
|
||||||
ThrowInvalidOperation(SR.This_program_location_is_thought_to_be_unreachable, path, line);
|
|
||||||
return default;
|
/// <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]
|
[DebuggerHidden]
|
||||||
[DoesNotReturn]
|
[DoesNotReturn]
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
|
||||||
private static void ThrowInvalidOperation(string message, string? path, int line)
|
private static void ThrowInvalidOperation(string message, string? path, int line)
|
||||||
{
|
=> ThrowHelper.ThrowInvalidOperationException(message + Environment.NewLine + SR.FormatFile_0_Line_1(path, line));
|
||||||
throw new InvalidOperationException(message);
|
|
||||||
}
|
[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));
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче