Fixes https://github.com/dotnet/razor/issues/10689
Roslyn side: https://github.com/dotnet/roslyn/pull/74730

Looks good, despite the Roslyn side having VS deps for images:

![image](https://github.com/user-attachments/assets/44d2ac8d-f7c1-46ec-9572-15bfd91bed28)
This commit is contained in:
David Wengier 2024-08-21 13:34:46 +10:00 коммит произвёл GitHub
Родитель d38698fa24 687a473d0d
Коммит 2723187736
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
14 изменённых файлов: 459 добавлений и 143 удалений

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

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

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

@ -53,25 +53,25 @@
<MicrosoftSourceBuildIntermediatearcadePackageVersion>9.0.0-beta.24352.2</MicrosoftSourceBuildIntermediatearcadePackageVersion>
<MicrosoftDotNetXliffTasksPackageVersion>1.0.0-beta.23475.1</MicrosoftDotNetXliffTasksPackageVersion>
<MicrosoftSourceBuildIntermediatexlifftasksPackageVersion>1.0.0-beta.23475.1</MicrosoftSourceBuildIntermediatexlifftasksPackageVersion>
<MicrosoftNetCompilersToolsetPackageVersion>4.12.0-1.24379.11</MicrosoftNetCompilersToolsetPackageVersion>
<MicrosoftCommonLanguageServerProtocolFrameworkPackageVersion>4.12.0-1.24379.11</MicrosoftCommonLanguageServerProtocolFrameworkPackageVersion>
<MicrosoftCodeAnalysisExternalAccessRazorPackageVersion>4.12.0-1.24379.11</MicrosoftCodeAnalysisExternalAccessRazorPackageVersion>
<MicrosoftCodeAnalysisCommonPackageVersion>4.12.0-1.24379.11</MicrosoftCodeAnalysisCommonPackageVersion>
<MicrosoftCodeAnalysisCSharpPackageVersion>4.12.0-1.24379.11</MicrosoftCodeAnalysisCSharpPackageVersion>
<MicrosoftCodeAnalysisCSharpEditorFeaturesPackageVersion>4.12.0-1.24379.11</MicrosoftCodeAnalysisCSharpEditorFeaturesPackageVersion>
<MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>4.12.0-1.24379.11</MicrosoftCodeAnalysisCSharpFeaturesPackageVersion>
<MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>4.12.0-1.24379.11</MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>
<MicrosoftCodeAnalysisEditorFeaturesPackageVersion>4.12.0-1.24379.11</MicrosoftCodeAnalysisEditorFeaturesPackageVersion>
<MicrosoftCodeAnalysisEditorFeaturesCommonPackageVersion>4.12.0-1.24379.11</MicrosoftCodeAnalysisEditorFeaturesCommonPackageVersion>
<MicrosoftCodeAnalysisEditorFeaturesTextPackageVersion>4.12.0-1.24379.11</MicrosoftCodeAnalysisEditorFeaturesTextPackageVersion>
<MicrosoftCodeAnalysisEditorFeaturesWpfPackageVersion>4.12.0-1.24379.11</MicrosoftCodeAnalysisEditorFeaturesWpfPackageVersion>
<MicrosoftCodeAnalysisRemoteServiceHubPackageVersion>4.12.0-1.24379.11</MicrosoftCodeAnalysisRemoteServiceHubPackageVersion>
<MicrosoftCodeAnalysisTestUtilitiesPackageVersion>4.12.0-1.24379.11</MicrosoftCodeAnalysisTestUtilitiesPackageVersion>
<MicrosoftCodeAnalysisVisualBasicWorkspacesPackageVersion>4.12.0-1.24379.11</MicrosoftCodeAnalysisVisualBasicWorkspacesPackageVersion>
<MicrosoftCodeAnalysisWorkspacesCommonPackageVersion>4.12.0-1.24379.11</MicrosoftCodeAnalysisWorkspacesCommonPackageVersion>
<MicrosoftCodeAnalysisWorkspacesMSBuildPackageVersion>4.12.0-1.24379.11</MicrosoftCodeAnalysisWorkspacesMSBuildPackageVersion>
<MicrosoftSourceBuildIntermediateroslynPackageVersion>4.12.0-1.24379.11</MicrosoftSourceBuildIntermediateroslynPackageVersion>
<MicrosoftVisualStudioLanguageServicesPackageVersion>4.12.0-1.24379.11</MicrosoftVisualStudioLanguageServicesPackageVersion>
<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>
<!--
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.

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

@ -25,6 +25,7 @@
<ServiceHubService Include="Microsoft.VisualStudio.Razor.SignatureHelp" ClassName="Microsoft.CodeAnalysis.Remote.Razor.RemoteSignatureHelpService+Factory" />
<ServiceHubService Include="Microsoft.VisualStudio.Razor.DocumentHighlight" ClassName="Microsoft.CodeAnalysis.Remote.Razor.RemoteDocumentHighlightService+Factory" />
<ServiceHubService Include="Microsoft.VisualStudio.Razor.InlayHint" ClassName="Microsoft.CodeAnalysis.Remote.Razor.RemoteInlayHintService+Factory" />
<ServiceHubService Include="Microsoft.VisualStudio.Razor.DocumentSymbol" ClassName="Microsoft.CodeAnalysis.Remote.Razor.RemoteDocumentSymbolService+Factory" />
<ServiceHubService Include="Microsoft.VisualStudio.Razor.GoToDefinition" ClassName="Microsoft.CodeAnalysis.Remote.Razor.RemoteGoToDefinitionService+Factory" />
<ServiceHubService Include="Microsoft.VisualStudio.Razor.Rename" ClassName="Microsoft.CodeAnalysis.Remote.Razor.RemoteRenameService+Factory" />
</ItemGroup>

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

@ -10,7 +10,9 @@ using Microsoft.AspNetCore.Razor.LanguageServer.EndpointContracts;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol;
using Microsoft.CodeAnalysis.Razor.Protocol.DocumentSymbols;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Microsoft.VisualStudio.Text.Tagging;
@ -18,21 +20,14 @@ using Microsoft.VisualStudio.Text.Tagging;
namespace Microsoft.AspNetCore.Razor.LanguageServer.DocumentSymbols;
[RazorLanguageServerEndpoint(Methods.TextDocumentDocumentSymbolName)]
internal class DocumentSymbolEndpoint : IRazorRequestHandler<DocumentSymbolParams, SumType<DocumentSymbol[], SymbolInformation[]>?>, ICapabilitiesProvider
internal class DocumentSymbolEndpoint(
IClientConnection clientConnection,
IDocumentSymbolService documentSymbolService,
LanguageServerFeatureOptions languageServerFeatureOptions) : IRazorRequestHandler<DocumentSymbolParams, SumType<DocumentSymbol[], SymbolInformation[]>?>, ICapabilitiesProvider
{
private readonly IClientConnection _clientConnection;
private readonly IDocumentMappingService _documentMappingService;
private readonly LanguageServerFeatureOptions _languageServerFeatureOptions;
public DocumentSymbolEndpoint(
IClientConnection clientConnection,
IDocumentMappingService documentMappingService,
LanguageServerFeatureOptions languageServerFeatureOptions)
{
_clientConnection = clientConnection ?? throw new ArgumentNullException(nameof(clientConnection));
_documentMappingService = documentMappingService ?? throw new ArgumentNullException(nameof(documentMappingService));
_languageServerFeatureOptions = languageServerFeatureOptions;
}
private readonly IClientConnection _clientConnection = clientConnection;
private readonly IDocumentSymbolService _documentSymbolService = documentSymbolService;
private readonly LanguageServerFeatureOptions _languageServerFeatureOptions = languageServerFeatureOptions;
public bool MutatesSolutionState => false;
@ -76,74 +71,6 @@ internal class DocumentSymbolEndpoint : IRazorRequestHandler<DocumentSymbolParam
var codeDocument = await documentContext.GetCodeDocumentAsync(cancellationToken).ConfigureAwait(false);
var csharpDocument = codeDocument.GetCSharpDocument();
if (symbols.TryGetFirst(out var documentSymbols))
{
return RemapDocumentSymbols(csharpDocument, documentSymbols);
}
else if (symbols.TryGetSecond(out var symbolInformations))
{
using var _ = ListPool<SymbolInformation>.GetPooledObject(out var mappedSymbols);
foreach (var symbolInformation in symbolInformations)
{
if (_documentMappingService.TryMapToHostDocumentRange(csharpDocument, symbolInformation.Location.Range, out var newRange))
{
symbolInformation.Location.Range = newRange;
symbolInformation.Location.Uri = documentContext.Uri;
mappedSymbols.Add(symbolInformation);
}
}
return mappedSymbols.ToArray();
}
else
{
Debug.Fail("Unsupported response type");
throw new InvalidOperationException();
}
}
private DocumentSymbol[]? RemapDocumentSymbols(RazorCSharpDocument csharpDocument, DocumentSymbol[]? documentSymbols)
{
if (documentSymbols is null)
{
return null;
}
using var _ = ListPool<DocumentSymbol>.GetPooledObject(out var mappedSymbols);
foreach (var documentSymbol in documentSymbols)
{
if (TryRemapRanges(csharpDocument, documentSymbol))
{
documentSymbol.Children = RemapDocumentSymbols(csharpDocument, documentSymbol.Children);
mappedSymbols.Add(documentSymbol);
}
else if (documentSymbol.Children is [_, ..] &&
RemapDocumentSymbols(csharpDocument, documentSymbol.Children) is [_, ..] mappedChildren)
{
// This range didn't map, but some/all of its children did, so we promote them to this level so we don't
// lose any information.
mappedSymbols.AddRange(mappedChildren);
}
}
return mappedSymbols.ToArray();
bool TryRemapRanges(RazorCSharpDocument csharpDocument, DocumentSymbol documentSymbol)
{
if (_documentMappingService.TryMapToHostDocumentRange(csharpDocument, documentSymbol.Range, out var newRange) &&
_documentMappingService.TryMapToHostDocumentRange(csharpDocument, documentSymbol.SelectionRange, out var newSelectionRange))
{
documentSymbol.Range = newRange;
documentSymbol.SelectionRange = newSelectionRange;
return true;
}
return false;
}
return _documentSymbolService.GetDocumentSymbols(documentContext.Uri, csharpDocument, symbols);
}
}

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

@ -27,6 +27,7 @@ using Microsoft.AspNetCore.Razor.Telemetry;
using Microsoft.CodeAnalysis.Razor.FoldingRanges;
using Microsoft.CodeAnalysis.Razor.GoToDefinition;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CodeAnalysis.Razor.Protocol.DocumentSymbols;
using Microsoft.CodeAnalysis.Razor.Rename;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.CommonLanguageServerProtocol.Framework;
@ -196,6 +197,9 @@ internal partial class RazorLanguageServer : SystemTextJsonLanguageServer<RazorR
services.AddHandlerWithCapabilities<InlayHintEndpoint>();
services.AddHandler<InlayHintResolveEndpoint>();
services.AddHandlerWithCapabilities<DocumentSymbolEndpoint>();
services.AddSingleton<IDocumentSymbolService, DocumentSymbolService>();
services.AddHandlerWithCapabilities<DocumentColorEndpoint>();
services.AddHandler<ColorPresentationEndpoint>();
}
@ -207,7 +211,6 @@ internal partial class RazorLanguageServer : SystemTextJsonLanguageServer<RazorR
services.AddHandlerWithCapabilities<ValidateBreakpointRangeEndpoint>();
services.AddHandlerWithCapabilities<FindAllReferencesEndpoint>();
services.AddHandlerWithCapabilities<ProjectContextsEndpoint>();
services.AddHandlerWithCapabilities<DocumentSymbolEndpoint>();
services.AddHandlerWithCapabilities<MapCodeEndpoint>();
}
}

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

@ -0,0 +1,88 @@
// 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.Diagnostics;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.CodeAnalysis.Razor.Protocol.DocumentSymbols;
internal class DocumentSymbolService(IDocumentMappingService documentMappingService) : IDocumentSymbolService
{
private readonly IDocumentMappingService _documentMappingService = documentMappingService;
public SumType<DocumentSymbol[], SymbolInformation[]>? GetDocumentSymbols(Uri razorDocumentUri, RazorCSharpDocument csharpDocument, SumType<DocumentSymbol[], SymbolInformation[]> csharpSymbols)
{
if (csharpSymbols.TryGetFirst(out var documentSymbols))
{
return RemapDocumentSymbols(csharpDocument, documentSymbols);
}
else if (csharpSymbols.TryGetSecond(out var symbolInformations))
{
using var _ = ListPool<SymbolInformation>.GetPooledObject(out var mappedSymbols);
foreach (var symbolInformation in symbolInformations)
{
if (_documentMappingService.TryMapToHostDocumentRange(csharpDocument, symbolInformation.Location.Range, out var newRange))
{
symbolInformation.Location.Range = newRange;
symbolInformation.Location.Uri = razorDocumentUri;
mappedSymbols.Add(symbolInformation);
}
}
return mappedSymbols.ToArray();
}
else
{
Debug.Fail("Unsupported response type");
throw new InvalidOperationException();
}
}
private DocumentSymbol[]? RemapDocumentSymbols(RazorCSharpDocument csharpDocument, DocumentSymbol[]? documentSymbols)
{
if (documentSymbols is null)
{
return null;
}
using var _ = ListPool<DocumentSymbol>.GetPooledObject(out var mappedSymbols);
foreach (var documentSymbol in documentSymbols)
{
if (TryRemapRanges(csharpDocument, documentSymbol))
{
documentSymbol.Children = RemapDocumentSymbols(csharpDocument, documentSymbol.Children);
mappedSymbols.Add(documentSymbol);
}
else if (documentSymbol.Children is [_, ..] &&
RemapDocumentSymbols(csharpDocument, documentSymbol.Children) is [_, ..] mappedChildren)
{
// This range didn't map, but some/all of its children did, so we promote them to this level so we don't
// lose any information.
mappedSymbols.AddRange(mappedChildren);
}
}
return mappedSymbols.ToArray();
bool TryRemapRanges(RazorCSharpDocument csharpDocument, DocumentSymbol documentSymbol)
{
if (_documentMappingService.TryMapToHostDocumentRange(csharpDocument, documentSymbol.Range, out var newRange) &&
_documentMappingService.TryMapToHostDocumentRange(csharpDocument, documentSymbol.SelectionRange, out var newSelectionRange))
{
documentSymbol.Range = newRange;
documentSymbol.SelectionRange = newSelectionRange;
return true;
}
return false;
}
}
}

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

@ -0,0 +1,13 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.CodeAnalysis.Razor.Protocol.DocumentSymbols;
internal interface IDocumentSymbolService
{
SumType<DocumentSymbol[], SymbolInformation[]>? GetDocumentSymbols(Uri razorDocumentUri, RazorCSharpDocument csharpDocument, SumType<DocumentSymbol[], SymbolInformation[]> csharpSymbols);
}

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

@ -0,0 +1,14 @@
// 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 Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.CodeAnalysis.Razor.Remote;
internal interface IRemoteDocumentSymbolService : IRemoteJsonService
{
ValueTask<SumType<DocumentSymbol[], SymbolInformation[]>?> GetDocumentSymbolsAsync(JsonSerializableRazorPinnedSolutionInfoWrapper solutionInfo, JsonSerializableDocumentId documentId, bool useHierarchicalSymbols, CancellationToken cancellationToken);
}

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

@ -29,6 +29,7 @@ internal static class RazorServices
(typeof(IRemoteGoToDefinitionService), null),
(typeof(IRemoteSignatureHelpService), null),
(typeof(IRemoteInlayHintService), null),
(typeof(IRemoteDocumentSymbolService), null),
(typeof(IRemoteRenameService), null),
];

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

@ -0,0 +1,14 @@
// 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 Microsoft.CodeAnalysis.Razor.DocumentMapping;
using Microsoft.CodeAnalysis.Razor.Protocol.DocumentSymbols;
namespace Microsoft.CodeAnalysis.Remote.Razor.DocumentSymbols;
[Export(typeof(IDocumentSymbolService)), Shared]
[method: ImportingConstructor]
internal class OOPDocumentSymbolService(IDocumentMappingService documentMappingService) : DocumentSymbolService(documentMappingService)
{
}

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

@ -0,0 +1,59 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.ExternalAccess.Razor;
using Microsoft.CodeAnalysis.Razor.Protocol.DocumentSymbols;
using Microsoft.CodeAnalysis.Razor.Remote;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.CodeAnalysis.Remote.Razor.ProjectSystem;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using ExternalHandlers = Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost.Handlers;
namespace Microsoft.CodeAnalysis.Remote.Razor;
internal sealed partial class RemoteDocumentSymbolService(in ServiceArgs args) : RazorDocumentServiceBase(in args), IRemoteDocumentSymbolService
{
internal sealed class Factory : FactoryBase<IRemoteDocumentSymbolService>
{
protected override IRemoteDocumentSymbolService CreateService(in ServiceArgs args)
=> new RemoteDocumentSymbolService(in args);
}
private readonly IDocumentSymbolService _documentSymbolService = args.ExportProvider.GetExportedValue<IDocumentSymbolService>();
private readonly IFilePathService _filePathService = args.ExportProvider.GetExportedValue<IFilePathService>();
public ValueTask<SumType<DocumentSymbol[], SymbolInformation[]>?> GetDocumentSymbolsAsync(JsonSerializableRazorPinnedSolutionInfoWrapper solutionInfo, JsonSerializableDocumentId razorDocumentId, bool useHierarchicalSymbols, CancellationToken cancellationToken)
=> RunServiceAsync(
solutionInfo,
razorDocumentId,
context => GetDocumentSymbolsAsync(context, useHierarchicalSymbols, cancellationToken),
cancellationToken);
private async ValueTask<SumType<DocumentSymbol[], SymbolInformation[]>?> GetDocumentSymbolsAsync(RemoteDocumentContext context, bool useHierarchicalSymbols, CancellationToken cancellationToken)
{
var generatedDocument = await context.Snapshot.GetGeneratedDocumentAsync().ConfigureAwait(false);
var csharpSymbols = await ExternalHandlers.DocumentSymbols.GetDocumentSymbolsAsync(generatedDocument, useHierarchicalSymbols, cancellationToken).ConfigureAwait(false);
var codeDocument = await context.GetCodeDocumentAsync(cancellationToken).ConfigureAwait(false);
var csharpDocument = codeDocument.GetCSharpDocument();
// 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();
foreach (var converter in RazorServiceDescriptorsWrapper.GetLspConverters())
{
options.Converters.Add(converter);
}
var vsCSharpSymbols = JsonSerializer.Deserialize<SumType<DocumentSymbol[], SymbolInformation[]>?>(JsonSerializer.SerializeToDocument(csharpSymbols), options);
if (vsCSharpSymbols is not { } convertedSymbols)
{
return null;
}
return _documentSymbolService.GetDocumentSymbols(context.Uri, csharpDocument, convertedSymbols);
}
}

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

@ -0,0 +1,75 @@
// 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.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost;
#pragma warning disable RS0030 // Do not use banned APIs
[Shared]
[CohostEndpoint(Methods.TextDocumentDocumentSymbolName)]
[Export(typeof(IDynamicRegistrationProvider))]
[ExportCohostStatelessLspService(typeof(CohostDocumentSymbolEndpoint))]
[method: ImportingConstructor]
#pragma warning restore RS0030 // Do not use banned APIs
internal class CohostDocumentSymbolEndpoint(IRemoteServiceInvoker remoteServiceInvoker)
: AbstractRazorCohostDocumentRequestHandler<DocumentSymbolParams, SumType<DocumentSymbol[], SymbolInformation[]>?>, IDynamicRegistrationProvider
{
private readonly IRemoteServiceInvoker _remoteServiceInvoker = remoteServiceInvoker;
private bool _useHierarchicalSymbols;
protected override bool MutatesSolutionState => false;
protected override bool RequiresLSPSolution => true;
public Registration? GetRegistration(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext)
{
if (clientCapabilities.TextDocument?.DocumentSymbol?.DynamicRegistration == true)
{
_useHierarchicalSymbols = clientCapabilities.TextDocument.DocumentSymbol.HierarchicalDocumentSymbolSupport;
return new Registration
{
Method = Methods.TextDocumentDocumentSymbolName,
RegisterOptions = new DocumentSymbolRegistrationOptions()
{
DocumentSelector = filter
}
};
}
return null;
}
protected override RazorTextDocumentIdentifier? GetRazorTextDocumentIdentifier(DocumentSymbolParams request)
=> request.TextDocument.ToRazorTextDocumentIdentifier();
protected override Task<SumType<DocumentSymbol[], SymbolInformation[]>?> HandleRequestAsync(DocumentSymbolParams request, RazorCohostRequestContext context, CancellationToken cancellationToken)
=> HandleRequestAsync(context.TextDocument.AssumeNotNull(), _useHierarchicalSymbols, cancellationToken);
private async Task<SumType<DocumentSymbol[], SymbolInformation[]>?> HandleRequestAsync(TextDocument razorDocument, bool useHierarchicalSymbols, CancellationToken cancellationToken)
{
// Normally we could remove the await here, but in this case it neatly converts from ValueTask to Task for us,
// and more importantly this method is essentially a public API entry point (via LSP) so having it appear in
// call stacks is desirable
return await _remoteServiceInvoker.TryInvokeAsync<IRemoteDocumentSymbolService, SumType<DocumentSymbol[], SymbolInformation[]>?>(
razorDocument.Project.Solution,
(service, solutionInfo, cancellationToken) => service.GetDocumentSymbolsAsync(solutionInfo, razorDocument.Id, useHierarchicalSymbols, cancellationToken),
cancellationToken).ConfigureAwait(false);
}
internal TestAccessor GetTestAccessor() => new(this);
internal readonly struct TestAccessor(CohostDocumentSymbolEndpoint instance)
{
public Task<SumType<DocumentSymbol[], SymbolInformation[]>?> HandleRequestAsync(TextDocument razorDocument, bool useHierarchicalSymbols, CancellationToken cancellationToken)
=> instance.HandleRequestAsync(razorDocument, useHierarchicalSymbols, cancellationToken);
}
}

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

@ -8,6 +8,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.DocumentSymbols;
using Microsoft.AspNetCore.Razor.Test.Common.Workspaces;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Protocol.DocumentSymbols;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.LanguageServer.Protocol;
@ -83,7 +84,8 @@ public class DocumentSymbolEndpointTest(ITestOutputHelper testOutput) : SingleSe
// This test requires the SingleServerSupport to be disabled
Assert.False(TestLanguageServerFeatureOptions.Instance.SingleServerSupport);
var endpoint = new DocumentSymbolEndpoint(languageServer, DocumentMappingService, TestLanguageServerFeatureOptions.Instance);
var documentSymbolService = new DocumentSymbolService(DocumentMappingService);
var endpoint = new DocumentSymbolEndpoint(languageServer, documentSymbolService, TestLanguageServerFeatureOptions.Instance);
var serverCapabilities = new VSInternalServerCapabilities();
var clientCapabilities = new VSInternalClientCapabilities();
@ -102,7 +104,8 @@ public class DocumentSymbolEndpointTest(ITestOutputHelper testOutput) : SingleSe
var languageServer = await CreateLanguageServerAsync(codeDocument, razorFilePath,
capabilitiesUpdater: c => c.TextDocument!.DocumentSymbol = new DocumentSymbolSetting() { HierarchicalDocumentSymbolSupport = hierarchical });
var endpoint = new DocumentSymbolEndpoint(languageServer, DocumentMappingService, TestLanguageServerFeatureOptions.Instance);
var documentSymbolService = new DocumentSymbolService(DocumentMappingService);
var endpoint = new DocumentSymbolEndpoint(languageServer, documentSymbolService, TestLanguageServerFeatureOptions.Instance);
var request = new DocumentSymbolParams()
{

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

@ -0,0 +1,118 @@
// 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.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Xunit;
using Xunit.Abstractions;
namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost;
public class CohostDocumentSymbolEndpointTest(ITestOutputHelper testOutput) : CohostEndpointTestBase(testOutput)
{
[Theory]
[CombinatorialData]
public Task DocumentSymbols_CSharpClassWithMethods(bool hierarchical)
=> VerifyDocumentSymbolsAsync(
"""
@functions {
class {|SomeProject.File1.C:C|}
{
private void {|HandleString(string s):HandleString|}(string s)
{
s += "Hello";
}
private void {|M(int i):M|}(int i)
{
i++;
}
private string {|ObjToString(object o):ObjToString|}(object o)
{
return o.ToString();
}
}
}
""", hierarchical);
[Theory]
[CombinatorialData]
public Task DocumentSymbols_CSharpMethods(bool hierarchical)
=> VerifyDocumentSymbolsAsync(
"""
@functions {
private void {|HandleString(string s):HandleString|}(string s)
{
s += "Hello";
}
private void {|M(int i):M|}(int i)
{
i++;
}
private string {|ObjToString(object o):ObjToString|}(object o)
{
return o.ToString();
}
}
""", hierarchical);
private async Task VerifyDocumentSymbolsAsync(string input, bool hierarchical = false)
{
TestFileMarkupParser.GetSpans(input, out input, out ImmutableDictionary<string, ImmutableArray<TextSpan>> spansDict);
var document = CreateProjectAndRazorDocument(input);
var endpoint = new CohostDocumentSymbolEndpoint(RemoteServiceInvoker);
var result = await endpoint.GetTestAccessor().HandleRequestAsync(document, hierarchical, DisposalToken);
if (hierarchical)
{
var documentSymbols = result.Value.First;
var sourceText = SourceText.From(input);
var seen = 0;
VerifyDocumentSymbols(spansDict, documentSymbols, sourceText, ref seen);
Assert.Equal(spansDict.Values.Count(), seen);
}
else
{
var symbolsInformations = result.Value.Second;
Assert.Equal(spansDict.Values.Count(), symbolsInformations.Length);
var sourceText = SourceText.From(input);
foreach (var symbolInformation in symbolsInformations)
{
Assert.True(spansDict.TryGetValue(symbolInformation.Name, out var spans), $"Expected {symbolInformation.Name} to be in test provided markers");
var expectedRange = sourceText.GetRange(Assert.Single(spans));
Assert.Equal(expectedRange, symbolInformation.Location.Range);
}
}
}
private static void VerifyDocumentSymbols(ImmutableDictionary<string, ImmutableArray<TextSpan>> spansDict, DocumentSymbol[] documentSymbols, SourceText sourceText, ref int seen)
{
foreach (var symbol in documentSymbols)
{
seen++;
Assert.True(spansDict.TryGetValue(symbol.Detail.AssumeNotNull(), out var spans), $"Expected {symbol.Detail} to be in test provided markers");
var expectedRange = sourceText.GetRange(Assert.Single(spans));
Assert.Equal(expectedRange, symbol.SelectionRange);
if (symbol.Children is not null)
{
VerifyDocumentSymbols(spansDict, symbol.Children, sourceText, ref seen);
}
}
}
}