Repository: dotnet/roslyn
File: src/VisualStudio/Core/Def/TaskList/VisualStudioDiagnosticIdCache.cs (C#)

Code before:
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.Threading;

namespace Microsoft.VisualStudio.LanguageServices.TaskList;

[ExportWorkspaceServiceFactory(typeof(VisualStudioDiagnosticIdCache)), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class DiagnosticAnalyzerServiceFactory(
    [Import(AllowDefault = true)] IAsynchronousOperationListenerProvider? listenerProvider) : IWorkspaceServiceFactory
{
    public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices)
    {
        return new VisualStudioDiagnosticIdCache(
            workspaceServices.Workspace,
            listenerProvider);
    }
}

internal class VisualStudioDiagnosticIdCache : IWorkspaceService
{
    // This dictionary maps ProjectIds to a descriptor map.
    private readonly ConcurrentDictionary<ProjectId, ImmutableHashSet<string>> _projectIdToDiagnosticIdsCache = [];
    private readonly AsyncBatchingWorkQueue<ProjectId> _projectDescriptorRefreshQueue;

    private readonly Workspace _workspace;
    private readonly IDiagnosticAnalyzerService _analyzerService;
    private readonly IAsynchronousOperationListener _listener;

    public VisualStudioDiagnosticIdCache(Workspace workspace, IAsynchronousOperationListenerProvider? listenerProvider)
    {
        _workspace = workspace;
        _analyzerService = workspace.Services.GetRequiredService<IDiagnosticAnalyzerService>();
        _listener = listenerProvider?.GetListener(FeatureAttribute.DiagnosticService) ?? AsynchronousOperationListenerProvider.NullListener;

        _projectDescriptorRefreshQueue = new AsyncBatchingWorkQueue<ProjectId>(
            delay: DelayTimeSpan.Short,
            processBatchAsync: RefreshCachedDiagnosticIdsAsync,
            equalityComparer: EqualityComparer<ProjectId>.Default,
            asyncListener: _listener,
            cancellationToken: CancellationToken.None);

        _workspace.RegisterWorkspaceChangedHandler(WorkspaceChanged);

        foreach (var projectId in _workspace.CurrentSolution.ProjectIds)
        {
            _projectDescriptorRefreshQueue.AddWork(projectId);

Review comment (suggestion):
```suggestion
    // This dictionary maps ProjectIds to a descriptor list.
```

Code after:
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.Threading;

namespace Microsoft.VisualStudio.LanguageServices.TaskList;

[ExportWorkspaceServiceFactory(typeof(VisualStudioDiagnosticIdCache)), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class VisualStudioDiagnosticIdCacheFactory(
    IThreadingContext threadingContext,
    IAsynchronousOperationListenerProvider listenerProvider) : IWorkspaceServiceFactory
{
    public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices)
    {
        return new VisualStudioDiagnosticIdCache(
            workspaceServices.Workspace,
            threadingContext,
            listenerProvider);
    }
}

internal class VisualStudioDiagnosticIdCache : IWorkspaceService
{
    // This dictionary maps ProjectIds to a descriptor list.
    private readonly ConcurrentDictionary<ProjectId, ImmutableHashSet<string>> _projectIdToDiagnosticIdsCache = [];
    private readonly AsyncBatchingWorkQueue<ProjectId> _projectDescriptorRefreshQueue;

    private readonly Workspace _workspace;
    private readonly IDiagnosticAnalyzerService _analyzerService;
    private readonly IAsynchronousOperationListener _listener;

    public VisualStudioDiagnosticIdCache(
        Workspace workspace,
        IThreadingContext threadingContext,
        IAsynchronousOperationListenerProvider listenerProvider)
    {
        _workspace = workspace;
        _analyzerService = workspace.Services.GetRequiredService<IDiagnosticAnalyzerService>();
        _listener = listenerProvider.GetListener(FeatureAttribute.DiagnosticService);

        _projectDescriptorRefreshQueue = new AsyncBatchingWorkQueue<ProjectId>(
            delay: DelayTimeSpan.Short,
            processBatchAsync: RefreshCachedDiagnosticIdsAsync,
            equalityComparer: EqualityComparer<ProjectId>.Default,
            asyncListener: _listener,
            cancellationToken: threadingContext.DisposalToken);