зеркало из https://github.com/dotnet/razor.git
Single Server FAR (#8014)
This commit is contained in:
Родитель
b1ae68ef62
Коммит
d1bfa89ca5
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче