Needs https://github.com/dotnet/roslyn/pull/74978
Fixes https://github.com/dotnet/razor/issues/10695
Part of https://github.com/dotnet/razor/issues/9519

Pretty straightforward. A tiny bit of code moved to be shared with Go To
Def, but that's it.
This commit is contained in:
David Wengier 2024-09-06 13:24:56 +10:00 коммит произвёл GitHub
Родитель 459e9d6d9e 70f110d19d
Коммит 49e727b90e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
12 изменённых файлов: 544 добавлений и 80 удалений

Просмотреть файл

@ -11,82 +11,82 @@
<Sha>6bcf90f99d13da86c5e9753a6f34b6484673d0a0</Sha>
<SourceBuild RepoName="source-build-reference-packages" ManagedOnly="true" />
</Dependency>
<Dependency Name="Microsoft.Net.Compilers.Toolset" Version="4.12.0-2.24419.3">
<Dependency Name="Microsoft.Net.Compilers.Toolset" Version="4.12.0-3.24454.5">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>0ec44d9775c80a6861b811d8af637ee36e3315e1</Sha>
<Sha>9f86520c46f67d2a8a59af189f8fd87e35c574bb</Sha>
</Dependency>
<Dependency Name="Microsoft.CommonLanguageServerProtocol.Framework" Version="4.12.0-2.24419.3">
<Dependency Name="Microsoft.CommonLanguageServerProtocol.Framework" Version="4.12.0-3.24454.5">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>0ec44d9775c80a6861b811d8af637ee36e3315e1</Sha>
<Sha>9f86520c46f67d2a8a59af189f8fd87e35c574bb</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.ExternalAccess.Razor" Version="4.12.0-2.24419.3">
<Dependency Name="Microsoft.CodeAnalysis.ExternalAccess.Razor" Version="4.12.0-3.24454.5">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>0ec44d9775c80a6861b811d8af637ee36e3315e1</Sha>
<Sha>9f86520c46f67d2a8a59af189f8fd87e35c574bb</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.Common" Version="4.12.0-2.24419.3">
<Dependency Name="Microsoft.CodeAnalysis.Common" Version="4.12.0-3.24454.5">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>0ec44d9775c80a6861b811d8af637ee36e3315e1</Sha>
<Sha>9f86520c46f67d2a8a59af189f8fd87e35c574bb</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.CSharp" Version="4.12.0-2.24419.3">
<Dependency Name="Microsoft.CodeAnalysis.CSharp" Version="4.12.0-3.24454.5">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>0ec44d9775c80a6861b811d8af637ee36e3315e1</Sha>
<Sha>9f86520c46f67d2a8a59af189f8fd87e35c574bb</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.CSharp.EditorFeatures" Version="4.12.0-2.24419.3">
<Dependency Name="Microsoft.CodeAnalysis.CSharp.EditorFeatures" Version="4.12.0-3.24454.5">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>0ec44d9775c80a6861b811d8af637ee36e3315e1</Sha>
<Sha>9f86520c46f67d2a8a59af189f8fd87e35c574bb</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.CSharp.Features" Version="4.12.0-2.24419.3">
<Dependency Name="Microsoft.CodeAnalysis.CSharp.Features" Version="4.12.0-3.24454.5">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>0ec44d9775c80a6861b811d8af637ee36e3315e1</Sha>
<Sha>9f86520c46f67d2a8a59af189f8fd87e35c574bb</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.12.0-2.24419.3">
<Dependency Name="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.12.0-3.24454.5">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>0ec44d9775c80a6861b811d8af637ee36e3315e1</Sha>
<Sha>9f86520c46f67d2a8a59af189f8fd87e35c574bb</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.EditorFeatures" Version="4.12.0-2.24419.3">
<Dependency Name="Microsoft.CodeAnalysis.EditorFeatures" Version="4.12.0-3.24454.5">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>0ec44d9775c80a6861b811d8af637ee36e3315e1</Sha>
<Sha>9f86520c46f67d2a8a59af189f8fd87e35c574bb</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.EditorFeatures.Common" Version="4.12.0-2.24419.3">
<Dependency Name="Microsoft.CodeAnalysis.EditorFeatures.Common" Version="4.12.0-3.24454.5">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>0ec44d9775c80a6861b811d8af637ee36e3315e1</Sha>
<Sha>9f86520c46f67d2a8a59af189f8fd87e35c574bb</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.EditorFeatures.Text" Version="4.12.0-2.24419.3">
<Dependency Name="Microsoft.CodeAnalysis.EditorFeatures.Text" Version="4.12.0-3.24454.5">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>0ec44d9775c80a6861b811d8af637ee36e3315e1</Sha>
<Sha>9f86520c46f67d2a8a59af189f8fd87e35c574bb</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.EditorFeatures.Wpf" Version="4.12.0-2.24419.3">
<Dependency Name="Microsoft.CodeAnalysis.EditorFeatures.Wpf" Version="4.12.0-3.24454.5">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>0ec44d9775c80a6861b811d8af637ee36e3315e1</Sha>
<Sha>9f86520c46f67d2a8a59af189f8fd87e35c574bb</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.Remote.ServiceHub" Version="4.12.0-2.24419.3">
<Dependency Name="Microsoft.CodeAnalysis.Remote.ServiceHub" Version="4.12.0-3.24454.5">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>0ec44d9775c80a6861b811d8af637ee36e3315e1</Sha>
<Sha>9f86520c46f67d2a8a59af189f8fd87e35c574bb</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.VisualBasic.Workspaces" Version="4.12.0-2.24419.3">
<Dependency Name="Microsoft.CodeAnalysis.VisualBasic.Workspaces" Version="4.12.0-3.24454.5">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>0ec44d9775c80a6861b811d8af637ee36e3315e1</Sha>
<Sha>9f86520c46f67d2a8a59af189f8fd87e35c574bb</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.12.0-2.24419.3">
<Dependency Name="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.12.0-3.24454.5">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>0ec44d9775c80a6861b811d8af637ee36e3315e1</Sha>
<Sha>9f86520c46f67d2a8a59af189f8fd87e35c574bb</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.12.0-2.24419.3">
<Dependency Name="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.12.0-3.24454.5">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>0ec44d9775c80a6861b811d8af637ee36e3315e1</Sha>
<Sha>9f86520c46f67d2a8a59af189f8fd87e35c574bb</Sha>
</Dependency>
<Dependency Name="Microsoft.VisualStudio.LanguageServices" Version="4.12.0-2.24419.3">
<Dependency Name="Microsoft.VisualStudio.LanguageServices" Version="4.12.0-3.24454.5">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>0ec44d9775c80a6861b811d8af637ee36e3315e1</Sha>
<Sha>9f86520c46f67d2a8a59af189f8fd87e35c574bb</Sha>
</Dependency>
<Dependency Name="Microsoft.CodeAnalysis.Test.Utilities" Version="4.12.0-2.24419.3">
<Dependency Name="Microsoft.CodeAnalysis.Test.Utilities" Version="4.12.0-3.24454.5">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>0ec44d9775c80a6861b811d8af637ee36e3315e1</Sha>
<Sha>9f86520c46f67d2a8a59af189f8fd87e35c574bb</Sha>
</Dependency>
<!-- Intermediate is necessary for source build. -->
<Dependency Name="Microsoft.SourceBuild.Intermediate.roslyn" Version="4.12.0-2.24419.3">
<Dependency Name="Microsoft.SourceBuild.Intermediate.roslyn" Version="4.12.0-3.24454.5">
<Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>0ec44d9775c80a6861b811d8af637ee36e3315e1</Sha>
<Sha>9f86520c46f67d2a8a59af189f8fd87e35c574bb</Sha>
<SourceBuild RepoName="roslyn" ManagedOnly="true" />
</Dependency>
</ProductDependencies>

Просмотреть файл

@ -53,25 +53,25 @@
<MicrosoftSourceBuildIntermediatearcadePackageVersion>9.0.0-beta.24453.1</MicrosoftSourceBuildIntermediatearcadePackageVersion>
<MicrosoftDotNetXliffTasksPackageVersion>1.0.0-beta.23475.1</MicrosoftDotNetXliffTasksPackageVersion>
<MicrosoftSourceBuildIntermediatexlifftasksPackageVersion>1.0.0-beta.23475.1</MicrosoftSourceBuildIntermediatexlifftasksPackageVersion>
<MicrosoftNetCompilersToolsetPackageVersion>4.12.0-2.24419.3</MicrosoftNetCompilersToolsetPackageVersion>
<MicrosoftCommonLanguageServerProtocolFrameworkPackageVersion>4.12.0-2.24419.3</MicrosoftCommonLanguageServerProtocolFrameworkPackageVersion>
<MicrosoftCodeAnalysisExternalAccessRazorPackageVersion>4.12.0-2.24419.3</MicrosoftCodeAnalysisExternalAccessRazorPackageVersion>
<MicrosoftCodeAnalysisCommonPackageVersion>4.12.0-2.24419.3</MicrosoftCodeAnalysisCommonPackageVersion>
<MicrosoftCodeAnalysisCSharpPackageVersion>4.12.0-2.24419.3</MicrosoftCodeAnalysisCSharpPackageVersion>
<MicrosoftCodeAnalysisCSharpEditorFeaturesPackageVersion>4.12.0-2.24419.3</MicrosoftCodeAnalysisCSharpEditorFeaturesPackageVersion>
<MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>4.12.0-2.24419.3</MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>
<MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>4.12.0-2.24419.3</MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>
<MicrosoftCodeAnalysisEditorFeaturesPackageVersion>4.12.0-2.24419.3</MicrosoftCodeAnalysisEditorFeaturesPackageVersion>
<MicrosoftCodeAnalysisEditorFeaturesCommonPackageVersion>4.12.0-2.24419.3</MicrosoftCodeAnalysisEditorFeaturesCommonPackageVersion>
<MicrosoftCodeAnalysisEditorFeaturesTextPackageVersion>4.12.0-2.24419.3</MicrosoftCodeAnalysisEditorFeaturesTextPackageVersion>
<MicrosoftCodeAnalysisEditorFeaturesWpfPackageVersion>4.12.0-2.24419.3</MicrosoftCodeAnalysisEditorFeaturesWpfPackageVersion>
<MicrosoftCodeAnalysisRemoteServiceHubPackageVersion>4.12.0-2.24419.3</MicrosoftCodeAnalysisRemoteServiceHubPackageVersion>
<MicrosoftCodeAnalysisTestUtilitiesPackageVersion>4.12.0-2.24419.3</MicrosoftCodeAnalysisTestUtilitiesPackageVersion>
<MicrosoftCodeAnalysisVisualBasicWorkspacesPackageVersion>4.12.0-2.24419.3</MicrosoftCodeAnalysisVisualBasicWorkspacesPackageVersion>
<MicrosoftCodeAnalysisWorkspacesCommonPackageVersion>4.12.0-2.24419.3</MicrosoftCodeAnalysisWorkspacesCommonPackageVersion>
<MicrosoftCodeAnalysisWorkspacesMSBuildPackageVersion>4.12.0-2.24419.3</MicrosoftCodeAnalysisWorkspacesMSBuildPackageVersion>
<MicrosoftSourceBuildIntermediateroslynPackageVersion>4.12.0-2.24419.3</MicrosoftSourceBuildIntermediateroslynPackageVersion>
<MicrosoftVisualStudioLanguageServicesPackageVersion>4.12.0-2.24419.3</MicrosoftVisualStudioLanguageServicesPackageVersion>
<MicrosoftNetCompilersToolsetPackageVersion>4.12.0-3.24454.5</MicrosoftNetCompilersToolsetPackageVersion>
<MicrosoftCommonLanguageServerProtocolFrameworkPackageVersion>4.12.0-3.24454.5</MicrosoftCommonLanguageServerProtocolFrameworkPackageVersion>
<MicrosoftCodeAnalysisExternalAccessRazorPackageVersion>4.12.0-3.24454.5</MicrosoftCodeAnalysisExternalAccessRazorPackageVersion>
<MicrosoftCodeAnalysisCommonPackageVersion>4.12.0-3.24454.5</MicrosoftCodeAnalysisCommonPackageVersion>
<MicrosoftCodeAnalysisCSharpPackageVersion>4.12.0-3.24454.5</MicrosoftCodeAnalysisCSharpPackageVersion>
<MicrosoftCodeAnalysisCSharpEditorFeaturesPackageVersion>4.12.0-3.24454.5</MicrosoftCodeAnalysisCSharpEditorFeaturesPackageVersion>
<MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>4.12.0-3.24454.5</MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>
<MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>4.12.0-3.24454.5</MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>
<MicrosoftCodeAnalysisEditorFeaturesPackageVersion>4.12.0-3.24454.5</MicrosoftCodeAnalysisEditorFeaturesPackageVersion>
<MicrosoftCodeAnalysisEditorFeaturesCommonPackageVersion>4.12.0-3.24454.5</MicrosoftCodeAnalysisEditorFeaturesCommonPackageVersion>
<MicrosoftCodeAnalysisEditorFeaturesTextPackageVersion>4.12.0-3.24454.5</MicrosoftCodeAnalysisEditorFeaturesTextPackageVersion>
<MicrosoftCodeAnalysisEditorFeaturesWpfPackageVersion>4.12.0-3.24454.5</MicrosoftCodeAnalysisEditorFeaturesWpfPackageVersion>
<MicrosoftCodeAnalysisRemoteServiceHubPackageVersion>4.12.0-3.24454.5</MicrosoftCodeAnalysisRemoteServiceHubPackageVersion>
<MicrosoftCodeAnalysisTestUtilitiesPackageVersion>4.12.0-3.24454.5</MicrosoftCodeAnalysisTestUtilitiesPackageVersion>
<MicrosoftCodeAnalysisVisualBasicWorkspacesPackageVersion>4.12.0-3.24454.5</MicrosoftCodeAnalysisVisualBasicWorkspacesPackageVersion>
<MicrosoftCodeAnalysisWorkspacesCommonPackageVersion>4.12.0-3.24454.5</MicrosoftCodeAnalysisWorkspacesCommonPackageVersion>
<MicrosoftCodeAnalysisWorkspacesMSBuildPackageVersion>4.12.0-3.24454.5</MicrosoftCodeAnalysisWorkspacesMSBuildPackageVersion>
<MicrosoftSourceBuildIntermediateroslynPackageVersion>4.12.0-3.24454.5</MicrosoftSourceBuildIntermediateroslynPackageVersion>
<MicrosoftVisualStudioLanguageServicesPackageVersion>4.12.0-3.24454.5</MicrosoftVisualStudioLanguageServicesPackageVersion>
<!--
Exception - Microsoft.Extensions.ObjectPool and System.Collections.Immutable packages are not updated by automation,
but are present in Version.Details.xml for source-build PVP flow. See the comment in Version.Details.xml for more information.

Просмотреть файл

@ -30,5 +30,6 @@
<ServiceHubService Include="Microsoft.VisualStudio.Razor.Rename" ClassName="Microsoft.CodeAnalysis.Remote.Razor.RemoteRenameService+Factory" />
<ServiceHubService Include="Microsoft.VisualStudio.Razor.AutoInsert" ClassName="Microsoft.CodeAnalysis.Remote.Razor.RemoteAutoInsertService+Factory" />
<ServiceHubService Include="Microsoft.VisualStudio.Razor.Formatting" ClassName="Microsoft.CodeAnalysis.Remote.Razor.RemoteFormattingService+Factory" />
<ServiceHubService Include="Microsoft.VisualStudio.Razor.GoToImplementation" ClassName="Microsoft.CodeAnalysis.Remote.Razor.RemoteGoToImplementationService+Factory" />
</ItemGroup>
</Project>

Просмотреть файл

@ -184,10 +184,10 @@ internal partial class RazorLanguageServer : SystemTextJsonLanguageServer<RazorR
// Transient because it should only be used once and I'm hoping it doesn't stick around.
services.AddTransient<IOnInitialized>(sp => sp.GetRequiredService<RazorConfigurationEndpoint>());
services.AddHandlerWithCapabilities<ImplementationEndpoint>();
if (!featureOptions.UseRazorCohostServer)
{
services.AddHandlerWithCapabilities<ImplementationEndpoint>();
services.AddSingleton<IRazorComponentDefinitionService, RazorComponentDefinitionService>();
services.AddHandlerWithCapabilities<DefinitionEndpoint>();

Просмотреть файл

@ -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 RoslynLocation = Roslyn.LanguageServer.Protocol.Location;
using RoslynPosition = Roslyn.LanguageServer.Protocol.Position;
namespace Microsoft.CodeAnalysis.Razor.Remote;
internal interface IRemoteGoToImplementationService : IRemoteJsonService
{
ValueTask<RemoteResponse<RoslynLocation[]?>> GetImplementationAsync(
JsonSerializableRazorPinnedSolutionInfoWrapper solutionInfo,
JsonSerializableDocumentId razorDocumentId,
RoslynPosition position,
CancellationToken cancellationToken);
}

Просмотреть файл

@ -33,6 +33,7 @@ internal static class RazorServices
(typeof(IRemoteInlayHintService), null),
(typeof(IRemoteDocumentSymbolService), null),
(typeof(IRemoteRenameService), null),
(typeof(IRemoteGoToImplementationService), null),
];
private const string ComponentName = "Razor";

Просмотреть файл

@ -19,7 +19,6 @@ using static Microsoft.CodeAnalysis.Razor.Remote.RemoteResponse<Roslyn.LanguageS
using ExternalHandlers = Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost.Handlers;
using RoslynLocation = Roslyn.LanguageServer.Protocol.Location;
using RoslynPosition = Roslyn.LanguageServer.Protocol.Position;
using VsPosition = Microsoft.VisualStudio.LanguageServer.Protocol.Position;
namespace Microsoft.CodeAnalysis.Remote.Razor;
@ -58,20 +57,7 @@ internal sealed class RemoteGoToDefinitionService(in ServiceArgs args) : RazorDo
return NoFurtherHandling;
}
var positionInfo = GetPositionInfo(codeDocument, hostDocumentIndex);
if (positionInfo.LanguageKind == RazorLanguageKind.Html)
{
// Sometimes Html can actually be mapped to C#, like for example component attributes, which map to
// C# properties, even though they appear entirely in a Html context. Since remapping is pretty cheap
// it's easier to just try mapping, and see what happens, rather than checking for specific syntax nodes.
if (DocumentMappingService.TryMapToGeneratedDocumentPosition(codeDocument.GetCSharpDocument(), positionInfo.HostDocumentIndex, out VsPosition? csharpPosition, out _))
{
// We're just gonna pretend this mapped perfectly normally onto C#. Moving this logic to the actual position info
// calculating code is possible, but could have untold effects, so opt-in is better (for now?)
positionInfo = positionInfo with { LanguageKind = RazorLanguageKind.CSharp, Position = csharpPosition };
}
}
var positionInfo = GetPositionInfo(codeDocument, hostDocumentIndex, preferCSharpOverHtml: true);
if (positionInfo.LanguageKind is RazorLanguageKind.Html or RazorLanguageKind.Razor)
{

Просмотреть файл

@ -0,0 +1,110 @@
// 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.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.CodeAnalysis.ExternalAccess.Razor;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.CodeAnalysis.Razor.Remote;
using Microsoft.CodeAnalysis.Remote.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Remote.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Roslyn.LanguageServer.Protocol;
using static Microsoft.CodeAnalysis.Razor.Remote.RemoteResponse<Roslyn.LanguageServer.Protocol.Location[]?>;
using ExternalHandlers = Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost.Handlers;
using RoslynLocation = Roslyn.LanguageServer.Protocol.Location;
using RoslynPosition = Roslyn.LanguageServer.Protocol.Position;
namespace Microsoft.CodeAnalysis.Remote.Razor;
internal sealed class RemoteGoToImplementationService(in ServiceArgs args) : RazorDocumentServiceBase(in args), IRemoteGoToImplementationService
{
internal sealed class Factory : FactoryBase<IRemoteGoToImplementationService>
{
protected override IRemoteGoToImplementationService CreateService(in ServiceArgs args)
=> new RemoteGoToImplementationService(in args);
}
protected override IDocumentPositionInfoStrategy DocumentPositionInfoStrategy => PreferAttributeNameDocumentPositionInfoStrategy.Instance;
public ValueTask<RemoteResponse<RoslynLocation[]?>> GetImplementationAsync(
JsonSerializableRazorPinnedSolutionInfoWrapper solutionInfo,
JsonSerializableDocumentId documentId,
RoslynPosition position,
CancellationToken cancellationToken)
=> RunServiceAsync(
solutionInfo,
documentId,
context => GetImplementationAsync(context, position, cancellationToken),
cancellationToken);
private async ValueTask<RemoteResponse<RoslynLocation[]?>> GetImplementationAsync(
RemoteDocumentContext context,
RoslynPosition position,
CancellationToken cancellationToken)
{
var codeDocument = await context.GetCodeDocumentAsync(cancellationToken).ConfigureAwait(false);
if (!codeDocument.Source.Text.TryGetAbsoluteIndex(position, out var hostDocumentIndex))
{
return NoFurtherHandling;
}
var positionInfo = GetPositionInfo(codeDocument, hostDocumentIndex, preferCSharpOverHtml: true);
if (positionInfo.LanguageKind is RazorLanguageKind.Razor)
{
return NoFurtherHandling;
}
if (positionInfo.LanguageKind is RazorLanguageKind.Html)
{
return CallHtml;
}
if (!DocumentMappingService.TryMapToGeneratedDocumentPosition(codeDocument.GetCSharpDocument(), positionInfo.HostDocumentIndex, out var mappedPosition, out _))
{
// If we can't map to the generated C# file, we're done.
return NoFurtherHandling;
}
// Finally, call into C#.
var generatedDocument = await context.Snapshot.GetGeneratedDocumentAsync().ConfigureAwait(false);
var locations = await ExternalHandlers.GoToImplementation
.FindImplementationsAsync(
generatedDocument,
mappedPosition,
supportsVisualStudioExtensions: true,
cancellationToken)
.ConfigureAwait(false);
if (locations is null and not [])
{
// C# didn't return anything, so we're done.
return NoFurtherHandling;
}
// Map the C# locations back to the Razor file.
using var mappedLocations = new PooledArrayBuilder<RoslynLocation>(locations.Length);
foreach (var location in locations)
{
var (uri, range) = location;
var (mappedDocumentUri, mappedRange) = await DocumentMappingService
.MapToHostDocumentUriAndRangeAsync(context.Snapshot, uri, range.ToLinePositionSpan(), cancellationToken)
.ConfigureAwait(false);
var mappedLocation = RoslynLspFactory.CreateLocation(mappedDocumentUri, mappedRange);
mappedLocations.Add(mappedLocation);
}
return Results(mappedLocations.ToArray());
}
}

Просмотреть файл

@ -7,6 +7,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.ExternalAccess.Razor;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.CodeAnalysis.Remote.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.LanguageServer.Protocol;
@ -24,11 +25,34 @@ internal abstract class RazorDocumentServiceBase(in ServiceArgs args) : RazorBro
protected virtual IDocumentPositionInfoStrategy DocumentPositionInfoStrategy { get; } = DefaultDocumentPositionInfoStrategy.Instance;
protected DocumentPositionInfo GetPositionInfo(RazorCodeDocument codeDocument, int hostDocumentIndex)
=> GetPositionInfo(codeDocument, hostDocumentIndex, preferCSharpOverHtml: false);
protected DocumentPositionInfo GetPositionInfo(RazorCodeDocument codeDocument, int hostDocumentIndex, bool preferCSharpOverHtml)
{
return DocumentPositionInfoStrategy.GetPositionInfo(DocumentMappingService, codeDocument, hostDocumentIndex);
var positionInfo = DocumentPositionInfoStrategy.GetPositionInfo(DocumentMappingService, codeDocument, hostDocumentIndex);
if (preferCSharpOverHtml && positionInfo.LanguageKind == RazorLanguageKind.Html)
{
// Sometimes Html can actually be mapped to C#, like for example component attributes, which map to
// C# properties, even though they appear entirely in a Html context. Since remapping is pretty cheap
// it's easier to just try mapping, and see what happens, rather than checking for specific syntax nodes.
if (DocumentMappingService.TryMapToGeneratedDocumentPosition(codeDocument.GetCSharpDocument(), positionInfo.HostDocumentIndex, out VsPosition? csharpPosition, out _))
{
// We're just gonna pretend this mapped perfectly normally onto C#. Moving this logic to the actual position info
// calculating code is possible, but could have untold effects, so opt-in is better (for now?)
// TODO: Not using a with operator here because it doesn't work in OOP for some reason.
positionInfo = new DocumentPositionInfo(RazorLanguageKind.CSharp, csharpPosition, positionInfo.HostDocumentIndex);
}
}
return positionInfo;
}
protected bool TryGetDocumentPositionInfo(RazorCodeDocument codeDocument, RoslynPosition position, out DocumentPositionInfo positionInfo)
=> TryGetDocumentPositionInfo(codeDocument, position, preferCSharpOverHtml: false, out positionInfo);
protected bool TryGetDocumentPositionInfo(RazorCodeDocument codeDocument, RoslynPosition position, bool preferCSharpOverHtml, out DocumentPositionInfo positionInfo)
{
if (!codeDocument.Source.Text.TryGetAbsoluteIndex(position, out var hostDocumentIndex))
{
@ -36,11 +60,14 @@ internal abstract class RazorDocumentServiceBase(in ServiceArgs args) : RazorBro
return false;
}
positionInfo = GetPositionInfo(codeDocument, hostDocumentIndex);
positionInfo = GetPositionInfo(codeDocument, hostDocumentIndex, preferCSharpOverHtml);
return true;
}
protected bool TryGetDocumentPositionInfo(RazorCodeDocument codeDocument, VsPosition position, out DocumentPositionInfo positionInfo)
=> TryGetDocumentPositionInfo(codeDocument, position, preferCSharpOverHtml: false, out positionInfo);
protected bool TryGetDocumentPositionInfo(RazorCodeDocument codeDocument, VsPosition position, bool preferCSharpOverHtml, out DocumentPositionInfo positionInfo)
{
if (!codeDocument.Source.Text.TryGetAbsoluteIndex(position, out var hostDocumentIndex))
{
@ -48,7 +75,7 @@ internal abstract class RazorDocumentServiceBase(in ServiceArgs args) : RazorBro
return false;
}
positionInfo = GetPositionInfo(codeDocument, hostDocumentIndex);
positionInfo = GetPositionInfo(codeDocument, hostDocumentIndex, preferCSharpOverHtml);
return true;
}

Просмотреть файл

@ -0,0 +1,153 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
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.CodeAnalysis.Razor.Workspaces;
using Microsoft.VisualStudio.LanguageServer.ContainedLanguage;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using RoslynLspFactory = Roslyn.LanguageServer.Protocol.RoslynLspFactory;
using RoslynLspLocation = Roslyn.LanguageServer.Protocol.Location;
using VsLspLocation = Microsoft.VisualStudio.LanguageServer.Protocol.Location;
namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost;
#pragma warning disable RS0030 // Do not use banned APIs
[Shared]
[CohostEndpoint(Methods.TextDocumentImplementationName)]
[Export(typeof(IDynamicRegistrationProvider))]
[ExportCohostStatelessLspService(typeof(CohostGoToImplementationEndpoint))]
[method: ImportingConstructor]
#pragma warning restore RS0030 // Do not use banned APIs
internal sealed class CohostGoToImplementationEndpoint(
IRemoteServiceInvoker remoteServiceInvoker,
IHtmlDocumentSynchronizer htmlDocumentSynchronizer,
LSPRequestInvoker requestInvoker,
IFilePathService filePathService)
: AbstractRazorCohostDocumentRequestHandler<TextDocumentPositionParams, SumType<RoslynLspLocation[], VsLspLocation[], VSInternalReferenceItem[]>?>, IDynamicRegistrationProvider
{
private readonly IRemoteServiceInvoker _remoteServiceInvoker = remoteServiceInvoker;
private readonly IHtmlDocumentSynchronizer _htmlDocumentSynchronizer = htmlDocumentSynchronizer;
private readonly LSPRequestInvoker _requestInvoker = requestInvoker;
private readonly IFilePathService _filePathService = filePathService;
protected override bool MutatesSolutionState => false;
protected override bool RequiresLSPSolution => true;
public Registration? GetRegistration(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext)
{
if (clientCapabilities.TextDocument?.Implementation?.DynamicRegistration == true)
{
return new Registration
{
Method = Methods.TextDocumentImplementationName,
RegisterOptions = new ImplementationOptions()
};
}
return null;
}
protected override RazorTextDocumentIdentifier? GetRazorTextDocumentIdentifier(TextDocumentPositionParams request)
=> request.TextDocument.ToRazorTextDocumentIdentifier();
protected override Task<SumType<RoslynLspLocation[], VsLspLocation[], VSInternalReferenceItem[]>?> HandleRequestAsync(TextDocumentPositionParams request, RazorCohostRequestContext context, CancellationToken cancellationToken)
=> HandleRequestAsync(
request,
context.TextDocument.AssumeNotNull(),
cancellationToken);
private async Task<SumType<RoslynLspLocation[], VsLspLocation[], VSInternalReferenceItem[]>?> HandleRequestAsync(TextDocumentPositionParams request, TextDocument razorDocument, CancellationToken cancellationToken)
{
var position = RoslynLspFactory.CreatePosition(request.Position.ToLinePosition());
var response = await _remoteServiceInvoker
.TryInvokeAsync<IRemoteGoToImplementationService, RemoteResponse<RoslynLspLocation[]?>>(
razorDocument.Project.Solution,
(service, solutionInfo, cancellationToken) =>
service.GetImplementationAsync(solutionInfo, razorDocument.Id, position, cancellationToken),
cancellationToken)
.ConfigureAwait(false);
if (response.Result is RoslynLspLocation[] locations)
{
return locations;
}
if (response.StopHandling)
{
return null;
}
return await GetHtmlImplementationsAsync(request, razorDocument, cancellationToken).ConfigureAwait(false);
}
private async Task<SumType<RoslynLspLocation[], VsLspLocation[], VSInternalReferenceItem[]>?> GetHtmlImplementationsAsync(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, SumType<VsLspLocation[], VSInternalReferenceItem[]>?>(
htmlDocument.Buffer,
Methods.TextDocumentImplementationName,
RazorLSPConstants.HtmlLanguageServerName,
request,
cancellationToken)
.ConfigureAwait(false);
if (result is not { Response: { } response })
{
return null;
}
if (response.TryGetFirst(out var locations))
{
foreach (var location in locations)
{
RemapVirtualHtmlUri(location);
}
return locations;
}
else if (response.TryGetSecond(out var referenceItems))
{
foreach (var referenceItem in referenceItems)
{
RemapVirtualHtmlUri(referenceItem.Location);
}
return referenceItems;
}
return null;
}
private void RemapVirtualHtmlUri(VsLspLocation location)
{
if (_filePathService.IsVirtualHtmlFile(location.Uri))
{
location.Uri = _filePathService.GetRazorDocumentUri(location.Uri);
}
}
internal TestAccessor GetTestAccessor() => new(this);
internal readonly struct TestAccessor(CohostGoToImplementationEndpoint instance)
{
public Task<SumType<RoslynLspLocation[], VsLspLocation[], VSInternalReferenceItem[]>?> HandleRequestAsync(
TextDocumentPositionParams request, TextDocument razorDocument, CancellationToken cancellationToken)
=> instance.HandleRequestAsync(request, razorDocument, cancellationToken);
}
}

Просмотреть файл

@ -216,17 +216,18 @@ public abstract partial class SingleServerDelegatingEndpointTestBase
private Task<VSInternalReferenceItem[]> HandleReferencesAsync<TParams>(TParams @params)
{
var delegatedParams = Assert.IsType<DelegatedPositionParams>(@params);
var delegatedRequest = new TextDocumentPositionParams()
var delegatedRequest = new ReferenceParams()
{
TextDocument = new VSTextDocumentIdentifier()
{
Uri = _csharpDocumentUri,
ProjectContext = delegatedParams.Identifier.TextDocumentIdentifier.GetProjectContext(),
},
Position = delegatedParams.ProjectedPosition
Position = delegatedParams.ProjectedPosition,
Context = new ReferenceContext()
};
return _csharpServer.ExecuteRequestAsync<TextDocumentPositionParams, VSInternalReferenceItem[]>(
return _csharpServer.ExecuteRequestAsync<ReferenceParams, VSInternalReferenceItem[]>(
Methods.TextDocumentReferencesName,
delegatedRequest,
_cancellationToken);

Просмотреть файл

@ -0,0 +1,166 @@
// 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.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Test.Common;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.ExternalAccess.Razor;
using Microsoft.CodeAnalysis.Remote.Razor;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Xunit;
using Xunit.Abstractions;
using LspLocation = Microsoft.VisualStudio.LanguageServer.Protocol.Location;
using RoslynLspExtensions = Roslyn.LanguageServer.Protocol.RoslynLspExtensions;
namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost;
public class CohostGoToImplementationEndpointTest(ITestOutputHelper testOutputHelper) : CohostEndpointTestBase(testOutputHelper)
{
[Fact]
public async Task CSharp_Method()
{
var input = """
<div></div>
@{
var x = Ge$$tX();
}
@code
{
void [|GetX|]()
{
}
}
""";
await VerifyCSharpGoToImplementationAsync(input);
}
[Fact]
public async Task CSharp_Field()
{
var input = """
<div></div>
@{
var x = GetX();
}
@code
{
private string [|_name|];
string GetX()
{
return _na$$me;
}
}
""";
await VerifyCSharpGoToImplementationAsync(input);
}
[Fact]
public async Task CSharp_Multiple()
{
var input = """
<div></div>
@code
{
class [|Base|] { }
class [|Derived1|] : Base { }
class [|Derived2|] : Base { }
void M(Ba$$se b)
{
}
}
""";
await VerifyCSharpGoToImplementationAsync(input);
}
[Fact]
public async Task Html()
{
// This really just validates Uri remapping, the actual response is largely arbitrary
TestCode input = """
<div></div>
<script>
function [|foo|]() {
f$$oo();
}
</script>
""";
var document = CreateProjectAndRazorDocument(input.Text);
var inputText = await document.GetTextAsync(DisposalToken);
var htmlResponse = new SumType<LspLocation[], VSInternalReferenceItem[]>?(new LspLocation[]
{
new LspLocation
{
Uri = new Uri(document.CreateUri(), document.Name + FeatureOptions.HtmlVirtualDocumentSuffix),
Range = inputText.GetRange(input.Span),
},
});
var requestInvoker = new TestLSPRequestInvoker([(Methods.TextDocumentImplementationName, htmlResponse)]);
await VerifyGoToImplementationResultAsync(input, document, requestInvoker);
}
private async Task VerifyCSharpGoToImplementationAsync(TestCode input)
{
var document = CreateProjectAndRazorDocument(input.Text);
var requestInvoker = new TestLSPRequestInvoker();
await VerifyGoToImplementationResultAsync(input, document, requestInvoker);
}
private async Task VerifyGoToImplementationResultAsync(TestCode input, TextDocument document, TestLSPRequestInvoker requestInvoker)
{
var inputText = await document.GetTextAsync(DisposalToken);
var filePathService = new RemoteFilePathService(FeatureOptions);
var endpoint = new CohostGoToImplementationEndpoint(RemoteServiceInvoker, TestHtmlDocumentSynchronizer.Instance, requestInvoker, filePathService);
var position = inputText.GetPosition(input.Position);
var textDocumentPositionParams = new TextDocumentPositionParams
{
Position = position,
TextDocument = new TextDocumentIdentifier { Uri = document.CreateUri() },
};
var result = await endpoint.GetTestAccessor().HandleRequestAsync(textDocumentPositionParams, document, DisposalToken);
if (result.Value.TryGetFirst(out var roslynLocations))
{
var expected = input.Spans.Select(s => inputText.GetRange(s).ToLinePositionSpan()).OrderBy(r => r.Start.Line).ToArray();
var actual = roslynLocations.Select(l => RoslynLspExtensions.ToLinePositionSpan(l.Range)).OrderBy(r => r.Start.Line).ToArray();
Assert.Equal(expected, actual);
Assert.All(roslynLocations, l => l.Uri.Equals(document.CreateUri()));
}
else if (result.Value.TryGetSecond(out var vsLocations))
{
var expected = input.Spans.Select(s => inputText.GetRange(s).ToLinePositionSpan()).OrderBy(r => r.Start.Line).ToArray();
var actual = vsLocations.Select(l => l.Range.ToLinePositionSpan()).OrderBy(r => r.Start.Line).ToArray();
Assert.Equal(expected, actual);
Assert.All(vsLocations, l => l.Uri.Equals(document.CreateUri()));
}
else
{
Assert.Fail($"Unsupported result type: {result.Value.GetType()}");
}
}
}