/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ProjectInitializationHandler.cs
103 строки
5 KB
David Barbet
Convert language server project/workspace creation stack into LSP services
28 май 2026, 20:24
28 май 2026, 20:24
dd8e404
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.LanguageServer.BrokeredServices.Services; using Microsoft.CodeAnalysis.LanguageServer.BrokeredServices.Services.Definitions; using Microsoft.CodeAnalysis.LanguageServer.LanguageServer; using Microsoft.CodeAnalysis.LanguageServer.Telemetry; using Microsoft.Extensions.Logging; using Microsoft.ServiceHub.Framework; using StreamJsonRpc; namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; internal sealed class ProjectInitializationHandler : IDisposable { internal const string ProjectInitializationCompleteName = "workspace/projectInitializationComplete"; private readonly IServiceBroker _serviceBroker; private readonly ServiceBrokerClient _serviceBrokerClient; private readonly ILogger _logger; private readonly TaskCompletionSource _serviceAvailable = new(); private readonly ProjectInitializationCompleteObserver _projectInitializationCompleteObserver; private IDisposable? _subscription; public ProjectInitializationHandler(IClientLanguageServerManager clientLanguageServerManager, IServiceBroker serviceBroker, ILoggerFactory loggerFactory) { _serviceBroker = serviceBroker; _serviceBroker.AvailabilityChanged += AvailabilityChanged; _serviceBrokerClient = new ServiceBrokerClient(_serviceBroker, joinableTaskFactory: null); _logger = loggerFactory.CreateLogger<ProjectInitializationHandler>(); _projectInitializationCompleteObserver = new ProjectInitializationCompleteObserver(clientLanguageServerManager, _logger); } public static async ValueTask SendProjectInitializationCompleteNotificationAsync(IClientLanguageServerManager clientLanguageServerManager) { await clientLanguageServerManager.SendNotificationAsync(ProjectInitializationCompleteName, CancellationToken.None); } public async Task SubscribeToInitializationCompleteAsync(CancellationToken cancellationToken) { // Use the ServiceBrokerClient so that we actually hold onto the instance of the service to prevent it from being disposed of until we're shutting down. var didSubscribe = await TrySubscribeAsync(cancellationToken); if (!didSubscribe) { // Service might be null the first time we try to access it - wait for it to become available on the remote side. await _serviceAvailable.Task; didSubscribe = await TrySubscribeAsync(cancellationToken); Contract.ThrowIfFalse(didSubscribe, $"Unable to subscribe to {Descriptors.RemoteProjectInitializationStatusService.Moniker}"); } } private async Task<bool> TrySubscribeAsync(CancellationToken cancellationToken) { using var rental = await _serviceBrokerClient.GetProxyAsync<IProjectInitializationStatusService>(Descriptors.RemoteProjectInitializationStatusService, cancellationToken); if (rental.Proxy is not null) { _subscription = await rental.Proxy.SubscribeInitializationCompletionAsync(_projectInitializationCompleteObserver, cancellationToken); return true; } return false; } private void AvailabilityChanged(object? sender, BrokeredServicesChangedEventArgs e) { if (e.ImpactedServices.Contains(Descriptors.RemoteProjectInitializationStatusService.Moniker)) _serviceAvailable.SetResult(); } public void Dispose() { _serviceBroker.AvailabilityChanged -= AvailabilityChanged; _subscription?.Dispose(); _serviceBrokerClient.Dispose(); } internal sealed class ProjectInitializationCompleteObserver(IClientLanguageServerManager clientLanguageServerManager, ILogger logger) : IObserver<ProjectInitializationCompletionState> { [JsonRpcMethod("onCompleted")] public void OnCompleted() { // NoOp - OnNext is the only method that will be called upon completion of initial project load. } [JsonRpcMethod("onError", UseSingleObjectParameterDeserialization = true)] public void OnError(Exception error) { logger.LogError(error, "Devkit project initialization observer failed"); } [JsonRpcMethod("onNext", UseSingleObjectParameterDeserialization = true)] public void OnNext(ProjectInitializationCompletionState value) { logger.LogDebug("Devkit project initialization completed"); VSCodeRequestTelemetryLogger.ReportProjectInitializationComplete(); _ = SendProjectInitializationCompleteNotificationAsync(clientLanguageServerManager).AsTask().ReportNonFatalErrorAsync(); } } }