зеркало из https://github.com/dotnet/razor.git
Move boiler plate logging and error handling into the service
This commit is contained in:
Родитель
8de1a27532
Коммит
36eeb135c4
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.ExternalAccess.Razor;
|
||||
|
@ -9,5 +10,5 @@ namespace Microsoft.CodeAnalysis.Razor.Remote;
|
|||
|
||||
internal interface IRemoteClientProvider
|
||||
{
|
||||
Task<RazorRemoteHostClient?> TryGetClientAsync(CancellationToken cancellationToken);
|
||||
ValueTask<TResult?> TryInvokeAsync<TService, TResult>(Solution solution, Func<TService, RazorPinnedSolutionInfoWrapper, CancellationToken, ValueTask<TResult>> invocation, CancellationToken cancellationToken) where TService : class;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// 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.Composition;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -35,7 +34,7 @@ internal class CohostLinkedEditingRangeEndpoint(IRemoteClientProvider remoteClie
|
|||
protected override bool RequiresLSPSolution => true;
|
||||
|
||||
public Registration? GetRegistration(VSInternalClientCapabilities clientCapabilities, DocumentFilter[] filter, RazorCohostRequestContext requestContext)
|
||||
{
|
||||
{
|
||||
if (clientCapabilities.TextDocument?.LinkedEditingRange?.DynamicRegistration == true)
|
||||
{
|
||||
return new Registration
|
||||
|
@ -58,35 +57,18 @@ internal class CohostLinkedEditingRangeEndpoint(IRemoteClientProvider remoteClie
|
|||
{
|
||||
var razorDocument = context.TextDocument.AssumeNotNull();
|
||||
|
||||
var remoteClient = await _remoteClientProvider.TryGetClientAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (remoteClient is null)
|
||||
{
|
||||
_logger.LogWarning($"Couldn't get remote client");
|
||||
return null;
|
||||
}
|
||||
var linkedRanges = await _remoteClientProvider.TryInvokeAsync<IRemoteLinkedEditingRangeService, LinePositionSpan[]?>(
|
||||
razorDocument.Project.Solution,
|
||||
(service, solutionInfo, cancellationToken) => service.GetRangesAsync(solutionInfo, razorDocument.Id, request.Position.ToLinePosition(), cancellationToken),
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
try
|
||||
if (linkedRanges is [{ } span1, { } span2])
|
||||
{
|
||||
var data = await remoteClient.TryInvokeAsync<IRemoteLinkedEditingRangeService, LinePositionSpan[]?>(
|
||||
razorDocument.Project.Solution,
|
||||
(service, solutionInfo, cancellationToken) => service.GetRangesAsync(solutionInfo, razorDocument.Id, request.Position.ToLinePosition(), cancellationToken),
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (data.Value is { } linkedRanges && linkedRanges.Length == 2)
|
||||
return new LinkedEditingRanges
|
||||
{
|
||||
var ranges = new Range[2] { linkedRanges[0].ToRange(), linkedRanges[1].ToRange() };
|
||||
|
||||
return new LinkedEditingRanges
|
||||
{
|
||||
Ranges = ranges,
|
||||
WordPattern = LinkedEditingRangeHelper.WordPattern
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"Error calling remote");
|
||||
return null;
|
||||
Ranges = [span1.ToRange(), span2.ToRange()],
|
||||
WordPattern = LinkedEditingRangeHelper.WordPattern
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
@ -73,40 +73,24 @@ internal sealed class CohostSemanticTokensRangeEndpoint(
|
|||
var razorDocument = context.TextDocument.AssumeNotNull();
|
||||
var span = request.Range.ToLinePositionSpan();
|
||||
|
||||
var remoteClient = await _remoteClientProvider.TryGetClientAsync(cancellationToken).ConfigureAwait(false);
|
||||
var colorBackground = _clientSettingsManager.GetClientSettings().AdvancedSettings.ColorBackground;
|
||||
|
||||
if (remoteClient is null)
|
||||
var correlationId = Guid.NewGuid();
|
||||
using var _ = _telemetryReporter.TrackLspRequest(Methods.TextDocumentSemanticTokensRangeName, RazorLSPConstants.CohostLanguageServerName, correlationId);
|
||||
|
||||
var tokens = await _remoteClientProvider.TryInvokeAsync<IRemoteSemanticTokensService, int[]?>(
|
||||
razorDocument.Project.Solution,
|
||||
(service, solutionInfo, cancellationToken) => service.GetSemanticTokensDataAsync(solutionInfo, razorDocument.Id, span, colorBackground, correlationId, cancellationToken),
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (tokens is not null)
|
||||
{
|
||||
_logger.LogWarning($"Couldn't get remote client");
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var colorBackground = _clientSettingsManager.GetClientSettings().AdvancedSettings.ColorBackground;
|
||||
|
||||
var correlationId = Guid.NewGuid();
|
||||
using var _ = _telemetryReporter.TrackLspRequest(Methods.TextDocumentSemanticTokensRangeName, RazorLSPConstants.CohostLanguageServerName, correlationId);
|
||||
|
||||
var data = await remoteClient.TryInvokeAsync<IRemoteSemanticTokensService, int[]?>(
|
||||
razorDocument.Project.Solution,
|
||||
(service, solutionInfo, cancellationToken) => service.GetSemanticTokensDataAsync(solutionInfo, razorDocument.Id, span, colorBackground, correlationId, cancellationToken),
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (data.Value is not { } tokens)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new SemanticTokens
|
||||
{
|
||||
Data = tokens
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"Error calling remote");
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,29 +66,20 @@ internal class CohostUriPresentationEndpoint(
|
|||
{
|
||||
var razorDocument = context.TextDocument.AssumeNotNull();
|
||||
|
||||
var remoteClient = await _remoteClientProvider.TryGetClientAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (remoteClient is null)
|
||||
{
|
||||
_logger.LogWarning($"Couldn't get remote client");
|
||||
return null;
|
||||
}
|
||||
var data = await _remoteClientProvider.TryInvokeAsync<IRemoteUriPresentationService, TextChange?>(
|
||||
razorDocument.Project.Solution,
|
||||
(service, solutionInfo, cancellationToken) => service.GetPresentationAsync(solutionInfo, razorDocument.Id, request.Range.ToLinePositionSpan(), request.Uris, cancellationToken),
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
try
|
||||
// If we got a response back, then either Razor or C# wants to do something with this, so we're good to go
|
||||
if (data is { } textChange)
|
||||
{
|
||||
var data = await remoteClient.TryInvokeAsync<IRemoteUriPresentationService, TextChange?>(
|
||||
razorDocument.Project.Solution,
|
||||
(service, solutionInfo, cancellationToken) => service.GetPresentationAsync(solutionInfo, razorDocument.Id, request.Range.ToLinePositionSpan(), request.Uris, cancellationToken),
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
var sourceText = await razorDocument.GetTextAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// If we got a response back, then either Razor or C# wants to do something with this, so we're good to go
|
||||
if (data.Value is { } textChange)
|
||||
return new WorkspaceEdit
|
||||
{
|
||||
var sourceText = await razorDocument.GetTextAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return new WorkspaceEdit
|
||||
DocumentChanges = new TextDocumentEdit[]
|
||||
{
|
||||
DocumentChanges = new TextDocumentEdit[]
|
||||
{
|
||||
new TextDocumentEdit
|
||||
{
|
||||
TextDocument = new()
|
||||
|
@ -97,14 +88,8 @@ internal class CohostUriPresentationEndpoint(
|
|||
},
|
||||
Edits = [textChange.ToTextEdit(sourceText)]
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"Error calling remote");
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// If we didn't get anything from Razor or Roslyn, lets ask Html what they want to do
|
||||
|
|
|
@ -28,25 +28,11 @@ internal sealed class HtmlDocumentPublisher(
|
|||
private readonly TrackingLSPDocumentManager _documentManager = documentManager as TrackingLSPDocumentManager ?? throw new InvalidOperationException("Expected TrackingLSPDocumentManager");
|
||||
private readonly ILogger _logger = loggerFactory.GetOrCreateLogger<HtmlDocumentPublisher>();
|
||||
|
||||
public async Task<string?> GetHtmlSourceFromOOPAsync(TextDocument document, CancellationToken cancellationToken)
|
||||
public Task<string?> GetHtmlSourceFromOOPAsync(TextDocument document, CancellationToken cancellationToken)
|
||||
{
|
||||
var client = await _remoteClientProvider.TryGetClientAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (client is null)
|
||||
{
|
||||
_logger.LogError($"Couldn't get remote client for html document generation for {document.FilePath}. Html document contents will be stale");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var htmlText = await client.TryInvokeAsync<IRemoteHtmlDocumentService, string?>(document.Project.Solution,
|
||||
return _remoteClientProvider.TryInvokeAsync<IRemoteHtmlDocumentService, string>(document.Project.Solution,
|
||||
(service, solutionInfo, ct) => service.GetHtmlDocumentTextAsync(solutionInfo, document.Id, ct),
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return htmlText.Value;
|
||||
cancellationToken).AsTask();
|
||||
}
|
||||
|
||||
public async Task PublishAsync(TextDocument document, string htmlText, CancellationToken cancellationToken)
|
||||
|
|
|
@ -68,14 +68,6 @@ internal class OutOfProcTagHelperResolver(
|
|||
|
||||
protected virtual async ValueTask<ImmutableArray<TagHelperDescriptor>> ResolveTagHelpersOutOfProcessAsync(Project workspaceProject, IProjectSnapshot projectSnapshot, CancellationToken cancellationToken)
|
||||
{
|
||||
var remoteClient = await _remoteClientProvider.TryGetClientAsync(cancellationToken);
|
||||
|
||||
if (remoteClient is null)
|
||||
{
|
||||
// Could not resolve
|
||||
return default;
|
||||
}
|
||||
|
||||
if (!_resultCache.TryGetId(workspaceProject.Id, out var lastResultId))
|
||||
{
|
||||
lastResultId = -1;
|
||||
|
@ -83,19 +75,19 @@ internal class OutOfProcTagHelperResolver(
|
|||
|
||||
var projectHandle = new ProjectSnapshotHandle(workspaceProject.Id, projectSnapshot.Configuration, projectSnapshot.RootNamespace);
|
||||
|
||||
var deltaResult = await remoteClient.TryInvokeAsync<IRemoteTagHelperProviderService, TagHelperDeltaResult>(
|
||||
var deltaResult = await _remoteClientProvider.TryInvokeAsync<IRemoteTagHelperProviderService, TagHelperDeltaResult>(
|
||||
workspaceProject.Solution,
|
||||
(service, solutionInfo, innerCancellationToken) =>
|
||||
service.GetTagHelpersDeltaAsync(solutionInfo, projectHandle, lastResultId, innerCancellationToken),
|
||||
cancellationToken);
|
||||
|
||||
if (!deltaResult.HasValue)
|
||||
if (deltaResult is null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
// Apply the delta we received to any cached checksums for the current project.
|
||||
var checksums = ProduceChecksumsFromDelta(workspaceProject.Id, lastResultId, deltaResult.Value);
|
||||
var checksums = ProduceChecksumsFromDelta(workspaceProject.Id, lastResultId, deltaResult);
|
||||
|
||||
using var _1 = ArrayBuilderPool<TagHelperDescriptor>.GetPooledObject(out var tagHelpers);
|
||||
using var _2 = ArrayBuilderPool<Checksum>.GetPooledObject(out var checksumsToFetch);
|
||||
|
@ -118,18 +110,18 @@ internal class OutOfProcTagHelperResolver(
|
|||
if (checksumsToFetch.Count > 0)
|
||||
{
|
||||
// There are checksums that we don't have cached tag helpers for, so we need to fetch them from OOP.
|
||||
var fetchResult = await remoteClient.TryInvokeAsync<IRemoteTagHelperProviderService, FetchTagHelpersResult>(
|
||||
var fetchResult = await _remoteClientProvider.TryInvokeAsync<IRemoteTagHelperProviderService, FetchTagHelpersResult>(
|
||||
workspaceProject.Solution,
|
||||
(service, solutionInfo, innerCancellationToken) =>
|
||||
service.FetchTagHelpersAsync(solutionInfo, projectHandle, checksumsToFetch.DrainToImmutable(), innerCancellationToken),
|
||||
cancellationToken);
|
||||
|
||||
if (!fetchResult.HasValue)
|
||||
if (fetchResult is null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
var fetchedTagHelpers = fetchResult.Value.TagHelpers;
|
||||
var fetchedTagHelpers = fetchResult.TagHelpers;
|
||||
if (fetchedTagHelpers.IsEmpty)
|
||||
{
|
||||
// If we didn't receive any tag helpers, something catastrophic happened in the Roslyn OOP
|
||||
|
|
|
@ -1,10 +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;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Telemetry;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.ExternalAccess.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.Logging;
|
||||
using Microsoft.CodeAnalysis.Razor.Protocol;
|
||||
using Microsoft.CodeAnalysis.Razor.Remote;
|
||||
using Microsoft.CodeAnalysis.Razor.SemanticTokens;
|
||||
|
@ -18,17 +22,52 @@ internal sealed class RemoteClientProvider(
|
|||
IWorkspaceProvider workspaceProvider,
|
||||
LanguageServerFeatureOptions languageServerFeatureOptions,
|
||||
IClientCapabilitiesService clientCapabilitiesService,
|
||||
ISemanticTokensLegendService semanticTokensLegendService)
|
||||
ISemanticTokensLegendService semanticTokensLegendService,
|
||||
ITelemetryReporter telemetryReporter,
|
||||
ILoggerFactory loggerFactory)
|
||||
: IRemoteClientProvider
|
||||
{
|
||||
private readonly IWorkspaceProvider _workspaceProvider = workspaceProvider;
|
||||
private readonly LanguageServerFeatureOptions _languageServerFeatureOptions = languageServerFeatureOptions;
|
||||
private readonly IClientCapabilitiesService _clientCapabilitiesService = clientCapabilitiesService;
|
||||
private readonly ISemanticTokensLegendService _semanticTokensLegendService = semanticTokensLegendService;
|
||||
private readonly ITelemetryReporter _telemetryReporter = telemetryReporter;
|
||||
private readonly ILogger _logger = loggerFactory.GetOrCreateLogger<RemoteClientProvider>();
|
||||
|
||||
private bool _isInitialized;
|
||||
private bool _isLSPInitialized;
|
||||
|
||||
public async Task<RazorRemoteHostClient?> TryGetClientAsync(CancellationToken cancellationToken)
|
||||
public async ValueTask<TResult?> TryInvokeAsync<TService, TResult>(Solution solution, Func<TService, RazorPinnedSolutionInfoWrapper, CancellationToken, ValueTask<TResult>> invocation, CancellationToken cancellationToken)
|
||||
where TService : class
|
||||
{
|
||||
var client = await TryGetClientAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (client is null)
|
||||
{
|
||||
_logger.LogError($"Couldn't get remote client for {typeof(TService).Name} service");
|
||||
_telemetryReporter.ReportEvent("OOPClientFailure", Severity.Normal, new Property("service", typeof(TService).FullName));
|
||||
return default;
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var result = await client.TryInvokeAsync(solution, invocation, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"Error calling remote method for {typeof(TService).Name} service, invocation: ${invocation.ToString()}");
|
||||
_telemetryReporter.ReportFault(ex, "Exception calling remote method for {service}", typeof(TService).FullName);
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<RazorRemoteHostClient?> TryGetClientAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var workspace = _workspaceProvider.GetWorkspace();
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче