/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/LanguageServer/Protocol/Handler/TextDocumentContent/AbstractTextDocumentContentRefreshQueue.cs
122 строки
5 KB
David Barbet
Review feedback
17 апр 2026, 01:23
17 апр 2026, 01:23
43bb3d6
Код
Авторство
О чём код?
// 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 System; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Threading; using Roslyn.LanguageServer.Protocol; using StreamJsonRpc; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.TextDocumentContent; /// <summary> /// Abstract refresh queue for text document content providers. Subclasses specify which URI scheme they handle /// and implement custom change detection logic via <see cref="AbstractRefreshQueue.OnLspSolutionChanged"/>. /// Refresh notifications are sent for any open document matching the specified scheme. /// </summary> internal abstract class AbstractTextDocumentContentRefreshQueue : IOnInitialized, ILspService, IDisposable { private readonly IAsynchronousOperationListener _asyncListener; private readonly CancellationTokenSource _disposalTokenSource = new(); private readonly LspWorkspaceRegistrationService _lspWorkspaceRegistrationService; private readonly LspWorkspaceManager _lspWorkspaceManager; private readonly IClientLanguageServerManager _notificationManager; private readonly AsyncBatchingWorkQueue _refreshQueue; public AbstractTextDocumentContentRefreshQueue( IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider, LspWorkspaceRegistrationService lspWorkspaceRegistrationService, LspWorkspaceManager lspWorkspaceManager, IClientLanguageServerManager notificationManager) { _lspWorkspaceRegistrationService = lspWorkspaceRegistrationService; _lspWorkspaceManager = lspWorkspaceManager; _notificationManager = notificationManager; _asyncListener = asynchronousOperationListenerProvider.GetListener(FeatureAttribute.Workspace); // Batch up workspace notifications so that we only send a notification to refresh virtual files // every 2 seconds - long enough to avoid spamming the client with notifications, but short enough to refresh // the virtual files relatively frequently. _refreshQueue = _refreshQueue = new AsyncBatchingWorkQueue( delay: DelayTimeSpan.Idle, processBatchAsync: RefreshVirtualDocumentsAsync, asyncListener: _asyncListener, _disposalTokenSource.Token); } public async Task OnInitializedAsync(ClientCapabilities clientCapabilities, RequestContext context, CancellationToken cancellationToken) { if (clientCapabilities.Workspace?.TextDocumentContent == null) { return; } // After we have initialized we can start listening for workspace changes. _lspWorkspaceRegistrationService.LspSolutionChanged += OnLspSolutionChanged; } private void OnLspSolutionChanged(object? sender, WorkspaceChangeEventArgs e) { var asyncToken = _asyncListener.BeginAsyncOperation($"{nameof(AbstractTextDocumentContentRefreshQueue)}.{nameof(OnLspSolutionChanged)}"); _ = OnLspSolutionChangedAsync(e) .CompletesAsyncOperation(asyncToken) .ReportNonFatalErrorUnlessCancelledAsync(_disposalTokenSource.Token); } protected async Task OnLspSolutionChangedAsync(WorkspaceChangeEventArgs e) { var shouldQueue = await ShouldEnqueueRefreshNotificationAsync(e, _disposalTokenSource.Token).ConfigureAwait(false); if (shouldQueue) { _refreshQueue.AddWork(); } } protected abstract Task<bool> ShouldEnqueueRefreshNotificationAsync(WorkspaceChangeEventArgs e, CancellationToken cancellationToken); /// <summary> /// The scheme that this queue is responsible for. /// </summary> protected abstract string Scheme { get; } private async ValueTask RefreshVirtualDocumentsAsync( CancellationToken cancellationToken) { var trackedDocuments = _lspWorkspaceManager.GetTrackedLspText(); foreach (var kvp in trackedDocuments) { var uri = kvp.Key; if (uri.ParsedUri is { } parsedUri && parsedUri.Scheme == Scheme) { try { await _notificationManager.SendRequestAsync( Methods.WorkspaceTextDocumentContentRefreshName, new TextDocumentContentRefreshParams { Uri = uri }, cancellationToken).ConfigureAwait(false); } catch (Exception ex) when (ex is ObjectDisposedException or ConnectionLostException) { // Connection may be lost during shutdown. return; } } } } public void Dispose() { _lspWorkspaceRegistrationService.LspSolutionChanged -= OnLspSolutionChanged; _disposalTokenSource.Cancel(); _disposalTokenSource.Dispose(); } }