This commit is contained in:
Andrew Hall 2022-12-15 16:54:18 -08:00 коммит произвёл GitHub
Родитель b1ae68ef62
Коммит d1bfa89ca5
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
10 изменённых файлов: 324 добавлений и 0 удалений

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

@ -46,4 +46,6 @@ internal static class RazorLanguageServerCustomMessageTargets
public const string RazorValidateBreakpointRangeName = "razor/validateBreakpointRange";
public const string RazorPullDiagnosticEndpointName = "razor/pullDiagnostics";
public const string RazorReferencesEndpointName = "razor/references";
}

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

@ -0,0 +1,15 @@
// 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.Text;
using Microsoft.CommonLanguageServerProtocol.Framework;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer.EndpointContracts;
[LanguageServerEndpoint(Methods.TextDocumentReferencesName)]
internal interface IVSFindAllReferencesEndpoint : IRazorRequestHandler<ReferenceParamsBridge, VSInternalReferenceItem[]?>, IRegistrationExtension
{
}

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

@ -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 System.Collections.Generic;
using System.Text;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer.EndpointContracts;
internal class ReferenceParamsBridge : ReferenceParams, ITextDocumentPositionParams
{
}

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

@ -0,0 +1,157 @@
// 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.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.Common;
using Microsoft.AspNetCore.Razor.LanguageServer.EndpointContracts;
using Microsoft.AspNetCore.Razor.LanguageServer.Extensions;
using Microsoft.AspNetCore.Razor.LanguageServer.Protocol;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Microsoft.VisualStudio.Text.Adornments;
namespace Microsoft.AspNetCore.Razor.LanguageServer.FindAllReferences;
internal class FindAllReferencesEndpoint : AbstractRazorDelegatingEndpoint<ReferenceParamsBridge, VSInternalReferenceItem[]>, IVSFindAllReferencesEndpoint
{
private readonly LanguageServerFeatureOptions _featureOptions;
private readonly RazorDocumentMappingService _documentMappingService;
public FindAllReferencesEndpoint(
LanguageServerFeatureOptions languageServerFeatureOptions,
RazorDocumentMappingService documentMappingService,
ClientNotifierServiceBase languageServer,
ILoggerFactory loggerFactory,
LanguageServerFeatureOptions featureOptions)
: base(languageServerFeatureOptions, documentMappingService, languageServer, loggerFactory.CreateLogger<FindAllReferencesEndpoint>())
{
_featureOptions = featureOptions;
_documentMappingService = documentMappingService ?? throw new ArgumentNullException(nameof(documentMappingService));
}
public RegistrationExtensionResult GetRegistration(VSInternalClientCapabilities clientCapabilities)
{
const string AssociatedServerCapability = "referencesProvider";
var registrationOptions = new ReferenceOptions()
{
// https://github.com/dotnet/razor/issues/8033
WorkDoneProgress = false,
};
return new RegistrationExtensionResult(AssociatedServerCapability, new SumType<bool, ReferenceOptions>(registrationOptions));
}
protected override string CustomMessageTarget => RazorLanguageServerCustomMessageTargets.RazorReferencesEndpointName;
protected override Task<IDelegatedParams?> CreateDelegatedParamsAsync(ReferenceParamsBridge request, RazorRequestContext requestContext, Projection projection, CancellationToken cancellationToken)
{
// HTML doesn't need to do FAR
if (projection.LanguageKind != RazorLanguageKind.CSharp)
{
return Task.FromResult<IDelegatedParams?>(null);
}
var documentContext = requestContext.GetRequiredDocumentContext();
return Task.FromResult<IDelegatedParams?>(new DelegatedPositionParams(
documentContext.Identifier,
projection.Position,
projection.LanguageKind));
}
protected override async Task<VSInternalReferenceItem[]> HandleDelegatedResponseAsync(VSInternalReferenceItem[] delegatedResponse, ReferenceParamsBridge originalRequest, RazorRequestContext requestContext, Projection projection, CancellationToken cancellationToken)
{
var remappedLocations = new List<VSInternalReferenceItem>();
foreach (var referenceItem in delegatedResponse)
{
if (referenceItem?.Location is null || referenceItem.Text is null)
{
continue;
}
// Temporary fix for codebehind leaking through
// Revert when https://github.com/dotnet/aspnetcore/issues/22512 is resolved
referenceItem.DefinitionText = FilterReferenceDisplayText(referenceItem.DefinitionText);
referenceItem.Text = FilterReferenceDisplayText(referenceItem.Text);
// Indicates the reference item is directly available in the code
referenceItem.Origin = VSInternalItemOrigin.Exact;
if (!_featureOptions.IsVirtualCSharpFile(referenceItem.Location.Uri) &&
!_featureOptions.IsVirtualHtmlFile(referenceItem.Location.Uri))
{
// This location doesn't point to a virtual file. No need to remap.
remappedLocations.Add(referenceItem);
continue;
}
var (itemUri, mappedRange) = await _documentMappingService.MapFromProjectedDocumentRangeAsync(referenceItem.Location.Uri, referenceItem.Location.Range, cancellationToken);
referenceItem.Location.Uri = itemUri;
referenceItem.DisplayPath = itemUri.AbsolutePath;
referenceItem.Location.Range = mappedRange;
remappedLocations.Add(referenceItem);
}
return remappedLocations.ToArray();
}
/// <summary>
/// If the reference text is showing a generated identifier (such as "__o =") this
/// fixes it to be what the actual reference display would look like to a user.
/// See https://github.com/dotnet/razor/issues/4611 for more details on what this fixes
/// </summary>
private static object? FilterReferenceDisplayText(object? referenceText)
{
const string CodeBehindObjectPrefix = "__o = ";
const string CodeBehindBackingFieldSuffix = "k__BackingField";
if (referenceText is string text)
{
if (text.StartsWith(CodeBehindObjectPrefix, StringComparison.Ordinal))
{
return text
.Substring(CodeBehindObjectPrefix.Length, text.Length - CodeBehindObjectPrefix.Length - 1); // -1 for trailing `;`
}
return text.Replace(CodeBehindBackingFieldSuffix, string.Empty);
}
if (referenceText is ClassifiedTextElement textElement &&
FilterReferenceClassifiedRuns(textElement.Runs.ToArray()))
{
var filteredRuns = textElement.Runs.Skip(4); // `__o`, ` `, `=`, ` `
filteredRuns = filteredRuns.Take(filteredRuns.Count() - 1); // Trailing `;`
return new ClassifiedTextElement(filteredRuns);
}
return referenceText;
}
private static bool FilterReferenceClassifiedRuns(IReadOnlyList<ClassifiedTextRun> runs)
{
if (runs.Count < 5)
{
return false;
}
return VerifyRunMatches(runs[0], "field name", "__o") &&
VerifyRunMatches(runs[1], "text", " ") &&
VerifyRunMatches(runs[2], "operator", "=") &&
VerifyRunMatches(runs[3], "text", " ") &&
VerifyRunMatches(runs[runs.Count - 1], "punctuation", ";");
static bool VerifyRunMatches(ClassifiedTextRun run, string expectedClassificationType, string expectedText)
{
return run.ClassificationTypeName == expectedClassificationType &&
run.Text == expectedText;
}
}
}

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

@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Razor.LanguageServer.DocumentColor;
using Microsoft.AspNetCore.Razor.LanguageServer.DocumentHighlighting;
using Microsoft.AspNetCore.Razor.LanguageServer.EndpointContracts;
using Microsoft.AspNetCore.Razor.LanguageServer.Extensions;
using Microsoft.AspNetCore.Razor.LanguageServer.FindAllReferences;
using Microsoft.AspNetCore.Razor.LanguageServer.Folding;
using Microsoft.AspNetCore.Razor.LanguageServer.Implementation;
using Microsoft.AspNetCore.Razor.LanguageServer.LinkedEditingRange;
@ -153,6 +154,7 @@ internal class RazorLanguageServer : AbstractLanguageServer<RazorRequestContext>
services.AddRegisteringHandler<DocumentColorEndpoint>();
services.AddRegisteringHandler<FoldingRangeEndpoint>();
services.AddRegisteringHandler<ValidateBreakpointRangeEndpoint>();
services.AddRegisteringHandler<FindAllReferencesEndpoint>();
}
}

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

@ -1092,6 +1092,9 @@ internal class DefaultRazorLanguageServerCustomMessageTarget : RazorLanguageServ
public override Task<ImplementationResult> ImplementationAsync(DelegatedPositionParams request, CancellationToken cancellationToken)
=> DelegateTextDocumentPositionRequestAsync<ImplementationResult>(request, Methods.TextDocumentImplementationName, cancellationToken);
public override Task<VSInternalReferenceItem[]?> ReferencesAsync(DelegatedPositionParams request, CancellationToken cancellationToken)
=> DelegateTextDocumentPositionRequestAsync<VSInternalReferenceItem[]>(request, Methods.TextDocumentReferencesName, cancellationToken);
public override async Task<RazorPullDiagnosticResponse?> DiagnosticsAsync(DelegatedDiagnosticParams request, CancellationToken cancellationToken)
{
var csharpTask = Task.Run(() => GetVirtualDocumentPullDiagnosticsAsync<CSharpVirtualDocumentSnapshot>(request.HostDocument, RazorLSPConstants.RazorCSharpLanguageServerName, cancellationToken), cancellationToken);

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

@ -113,6 +113,7 @@ internal class InitializeHandler : IRequestHandler<InitializeParams, InitializeR
_initializeResult.Capabilities.DocumentHighlightProvider = false;
_initializeResult.Capabilities.SignatureHelpProvider = null;
_initializeResult.Capabilities.ImplementationProvider = false;
_initializeResult.Capabilities.ReferencesProvider = false;
((VSInternalServerCapabilities)_initializeResult.Capabilities).OnAutoInsertProvider = null;
((VSInternalServerCapabilities)_initializeResult.Capabilities).SupportsDiagnosticRequests = false;

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

@ -125,4 +125,7 @@ internal abstract class RazorLanguageServerCustomMessageTarget
[JsonRpcMethod(RazorLanguageServerCustomMessageTargets.RazorPullDiagnosticEndpointName, UseSingleObjectParameterDeserialization = true)]
public abstract Task<RazorPullDiagnosticResponse?> DiagnosticsAsync(DelegatedDiagnosticParams request, CancellationToken cancellationToken);
[JsonRpcMethod(RazorLanguageServerCustomMessageTargets.RazorReferencesEndpointName, UseSingleObjectParameterDeserialization = true)]
public abstract Task<VSInternalReferenceItem[]?> ReferencesAsync(DelegatedPositionParams request, CancellationToken cancellationToken);
}

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

@ -0,0 +1,106 @@
// 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.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.EndpointContracts;
using Microsoft.AspNetCore.Razor.LanguageServer.Extensions;
using Microsoft.CodeAnalysis.Razor.Workspaces.Extensions;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Microsoft.VisualStudio.Threading;
using Xunit;
using Xunit.Abstractions;
namespace Microsoft.AspNetCore.Razor.LanguageServer.FindAllReferences;
public class FindReferencesEndpointTest : SingleServerDelegatingEndpointTestBase
{
public FindReferencesEndpointTest(ITestOutputHelper testOutput) : base(testOutput)
{
}
[Fact]
public Task FindCSharpReferences()
=> VerifyCSharpFindAllReferencesAsyncAsync("""
@{
const string [|$$S|] = "";
string M()
{
return [|S|];
}
string N()
{
return [|S|];
}
}
<p>@[|S|]</p>
""");
private async Task VerifyCSharpFindAllReferencesAsyncAsync(string input)
{
// Arrange
TestFileMarkupParser.GetPositionAndSpans(input, out var output, out int cursorPosition, out ImmutableArray<TextSpan> expectedSpans);
var codeDocument = CreateCodeDocument(output);
var razorFilePath = "C:/path/to/file.razor";
await CreateLanguageServerAsync(codeDocument, razorFilePath);
var endpoint = new FindAllReferencesEndpoint(
LanguageServerFeatureOptions, DocumentMappingService, LanguageServer, LoggerFactory, LanguageServerFeatureOptions);
var sourceText = codeDocument.GetSourceText();
sourceText.GetLineAndOffset(cursorPosition, out var line, out var offset);
var completedTokenSource = new CancellationTokenSource();
var progressToken = new ProgressWithCompletion<object>((val) =>
{
var results = Assert.IsType<VSInternalReferenceItem[]>(val);
completedTokenSource.CancelAfter(0);
});
var request = new ReferenceParamsBridge
{
Context = new ReferenceContext()
{
IncludeDeclaration = true
},
PartialResultToken = progressToken,
TextDocument = new TextDocumentIdentifier
{
Uri = new Uri(razorFilePath)
},
Position = new Position(line, offset)
};
var documentContext = await DocumentContextFactory.TryCreateAsync(request.TextDocument.Uri, DisposalToken);
var requestContext = CreateRazorRequestContext(documentContext);
// Act
var result = await endpoint.HandleRequestAsync(request, requestContext, DisposalToken);
// Assert
Assert.NotNull(result);
Assert.Equal(expectedSpans.Length, result.Length);
var i = 0;
foreach (var referenceItem in result.OrderBy(l => l.Location.Range.Start.Line))
{
Assert.Equal(new Uri(razorFilePath), referenceItem.Location.Uri);
var expectedRange = expectedSpans[i].AsRange(codeDocument.GetSourceText());
Assert.Equal(expectedRange, referenceItem.Location.Range);
i++;
}
}
}

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

@ -14,6 +14,7 @@ using Microsoft.AspNetCore.Razor.Test.Common;
using Microsoft.AspNetCore.Razor.Test.Common.Mef;
using Microsoft.CodeAnalysis.Razor.Workspaces;
using Microsoft.CodeAnalysis.Razor.Workspaces.Extensions;
using Microsoft.VisualStudio.Editor.Razor;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Moq;
using Xunit;
@ -106,12 +107,33 @@ public abstract class SingleServerDelegatingEndpointTestBase : LanguageServerTes
RazorLanguageServerCustomMessageTargets.RazorRenameEndpointName => await HandleRenameAsync(@params),
RazorLanguageServerCustomMessageTargets.RazorOnAutoInsertEndpointName => await HandleOnAutoInsertAsync(@params),
RazorLanguageServerCustomMessageTargets.RazorValidateBreakpointRangeName => await HandleValidateBreakpointRangeAsync(@params),
RazorLanguageServerCustomMessageTargets.RazorReferencesEndpointName => await HandleReferencesAsync(@params),
_ => throw new NotImplementedException($"I don't know how to handle the '{method}' method.")
};
return (TResponse)result;
}
private async Task<VSInternalReferenceItem[]> HandleReferencesAsync<TParams>(TParams @params)
{
var delegatedParams = Assert.IsType<DelegatedPositionParams>(@params);
var delegatedRequest = new TextDocumentPositionParams()
{
TextDocument = new TextDocumentIdentifier()
{
Uri = _csharpDocumentUri
},
Position = delegatedParams.ProjectedPosition
};
var result = await _csharpServer.ExecuteRequestAsync<TextDocumentPositionParams, VSInternalReferenceItem[]>(
Methods.TextDocumentReferencesName,
delegatedRequest,
_cancellationToken);
return result;
}
private async Task<DefinitionResult?> HandleDefinitionAsync<T>(T @params)
{
var delegatedParams = Assert.IsType<DelegatedPositionParams>(@params);