|
// 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.
#nullable disable
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Workspaces.ProjectSystem;
using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.TaskList
{
// exporting both Abstract and HostDiagnosticUpdateSource is just to make testing easier.
// use HostDiagnosticUpdateSource when abstract one is not needed for testing purpose
[Export(typeof(AbstractHostDiagnosticUpdateSource))]
[Export(typeof(HostDiagnosticUpdateSource))]
internal sealed class HostDiagnosticUpdateSource : AbstractHostDiagnosticUpdateSource, IProjectSystemDiagnosticSource
{
private readonly Lazy<VisualStudioWorkspace> _workspace;
private readonly object _gate = new();
private readonly Dictionary<ProjectId, HashSet<object>> _diagnosticMap = new();
[ImportingConstructor]
[SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")]
public HostDiagnosticUpdateSource(Lazy<VisualStudioWorkspace> workspace, IDiagnosticUpdateSourceRegistrationService registrationService)
{
_workspace = workspace;
registrationService.Register(this);
}
public override Workspace Workspace
{
get
{
return _workspace.Value;
}
}
private void RaiseDiagnosticsCreatedForProject(ProjectId projectId, object key, IEnumerable<DiagnosticData> items)
{
var args = DiagnosticsUpdatedArgs.DiagnosticsCreated(
CreateId(projectId, key),
Workspace,
solution: null,
projectId: projectId,
documentId: null,
diagnostics: items.AsImmutableOrEmpty());
RaiseDiagnosticsUpdated(args);
}
private void RaiseDiagnosticsRemovedForProject(ProjectId projectId, object key)
{
var args = DiagnosticsUpdatedArgs.DiagnosticsRemoved(
CreateId(projectId, key),
Workspace,
solution: null,
projectId: projectId,
documentId: null);
RaiseDiagnosticsUpdated(args);
}
private object CreateId(ProjectId projectId, object key) => Tuple.Create(this, projectId, key);
public void UpdateDiagnosticsForProject(ProjectId projectId, object key, IEnumerable<DiagnosticData> items)
{
Contract.ThrowIfNull(projectId);
Contract.ThrowIfNull(key);
Contract.ThrowIfNull(items);
lock (_gate)
{
_diagnosticMap.GetOrAdd(projectId, id => new HashSet<object>()).Add(key);
}
RaiseDiagnosticsCreatedForProject(projectId, key, items);
}
public void ClearAllDiagnosticsForProject(ProjectId projectId)
{
Contract.ThrowIfNull(projectId);
HashSet<object> projectDiagnosticKeys;
lock (_gate)
{
if (_diagnosticMap.TryGetValue(projectId, out projectDiagnosticKeys))
{
_diagnosticMap.Remove(projectId);
}
}
if (projectDiagnosticKeys != null)
{
foreach (var key in projectDiagnosticKeys)
{
RaiseDiagnosticsRemovedForProject(projectId, key);
}
}
ClearAnalyzerDiagnostics(projectId);
}
public void ClearDiagnosticsForProject(ProjectId projectId, object key)
{
Contract.ThrowIfNull(projectId);
Contract.ThrowIfNull(key);
var raiseEvent = false;
lock (_gate)
{
if (_diagnosticMap.TryGetValue(projectId, out var projectDiagnosticKeys))
{
raiseEvent = projectDiagnosticKeys.Remove(key);
}
}
if (raiseEvent)
{
RaiseDiagnosticsRemovedForProject(projectId, key);
}
}
public DiagnosticData CreateAnalyzerLoadFailureDiagnostic(AnalyzerLoadFailureEventArgs e, string fullPath, ProjectId projectId, string language)
{
return DocumentAnalysisExecutor.CreateAnalyzerLoadFailureDiagnostic(e, fullPath, projectId, language);
}
}
}
|