diff --git a/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerItem/AnalyzerItem.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerItem/AnalyzerItem.cs index f076af2f03f5b..1af836885084e 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerItem/AnalyzerItem.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerItem/AnalyzerItem.cs @@ -9,83 +9,42 @@ using Microsoft.VisualStudio.Imaging; using Microsoft.VisualStudio.Imaging.Interop; -namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer +namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer; + +internal partial class AnalyzerItem( + AnalyzersFolderItem analyzersFolder, + AnalyzerReference analyzerReference, + IContextMenuController contextMenuController) : BaseItem(GetNameText(analyzerReference)) { - internal partial class AnalyzerItem : BaseItem - { - private readonly AnalyzersFolderItem _analyzersFolder; - private readonly AnalyzerReference _analyzerReference; - private readonly IContextMenuController _contextMenuController; + public AnalyzersFolderItem AnalyzersFolder { get; } = analyzersFolder; + public AnalyzerReference AnalyzerReference { get; } = analyzerReference; + public override IContextMenuController ContextMenuController { get; } = contextMenuController; - public AnalyzerItem(AnalyzersFolderItem analyzersFolder, AnalyzerReference analyzerReference, IContextMenuController contextMenuController) - : base(GetNameText(analyzerReference)) - { - _analyzersFolder = analyzersFolder; - _analyzerReference = analyzerReference; - _contextMenuController = contextMenuController; - } + public override ImageMoniker IconMoniker => KnownMonikers.CodeInformation; - public override ImageMoniker IconMoniker - { - get - { - return KnownMonikers.CodeInformation; - } - } + public override ImageMoniker OverlayIconMoniker + => this.AnalyzerReference is UnresolvedAnalyzerReference + ? KnownMonikers.OverlayWarning + : default; - public override ImageMoniker OverlayIconMoniker - { - get - { - if (_analyzerReference is UnresolvedAnalyzerReference) - { - return KnownMonikers.OverlayWarning; - } - else - { - return default; - } - } - } + public override object GetBrowseObject() + => new BrowseObject(this); - public override object GetBrowseObject() - { - return new BrowseObject(this); - } + /// + /// Remove this AnalyzerItem from it's folder. + /// + public void Remove() + => this.AnalyzersFolder.RemoveAnalyzer(this.AnalyzerReference.FullPath); - public AnalyzerReference AnalyzerReference - { - get { return _analyzerReference; } - } - - public override IContextMenuController ContextMenuController - { - get { return _contextMenuController; } - } - - public AnalyzersFolderItem AnalyzersFolder - { - get { return _analyzersFolder; } - } - - /// - /// Remove this AnalyzerItem from it's folder. - /// - public void Remove() + private static string GetNameText(AnalyzerReference analyzerReference) + { + if (analyzerReference is UnresolvedAnalyzerReference) { - _analyzersFolder.RemoveAnalyzer(_analyzerReference.FullPath); + return analyzerReference.FullPath; } - - private static string GetNameText(AnalyzerReference analyzerReference) + else { - if (analyzerReference is UnresolvedAnalyzerReference) - { - return analyzerReference.FullPath; - } - else - { - return analyzerReference.Display; - } + return analyzerReference.Display; } } } diff --git a/src/VisualStudio/Core/Impl/SolutionExplorer/BaseItem.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/BaseItem.cs index eb4e8e26026af..1d6e7acd2c146 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/BaseItem.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/BaseItem.cs @@ -33,11 +33,11 @@ internal abstract class BaseItem : { public virtual event PropertyChangedEventHandler PropertyChanged { add { } remove { } } - private readonly string _name; + protected readonly string Name; public BaseItem(string name) { - _name = name; + Name = name; } public IEnumerable Children => []; @@ -61,9 +61,9 @@ public BaseItem(string name) public virtual ImageMoniker StateIconMoniker => default; public string? StateToolTipText => null; public override string ToString() => Text; - public string Text => _name; + public string Text => Name; public object? ToolTipContent => null; - public string ToolTipText => _name; + public string ToolTipText => Name; private static readonly HashSet s_supportedPatterns = [ diff --git a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/BaseDiagnosticAndGeneratorItemSource.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/BaseDiagnosticAndGeneratorItemSource.cs index ef558ad59cd77..480ad20740cd3 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/BaseDiagnosticAndGeneratorItemSource.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/BaseDiagnosticAndGeneratorItemSource.cs @@ -123,7 +123,7 @@ private BulkObservableCollection CreateDiagnosticAndGeneratorItems(Pro collection.AddRange( AnalyzerReference.GetGenerators(language) - .Select(g => new SourceGeneratorItem(projectId, g, AnalyzerReference))); + .Select(g => new SourceGeneratorItem(projectId, SourceGeneratorIdentity.Create(g, AnalyzerReference), AnalyzerReference.FullPath))); return collection; } diff --git a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/CpsDiagnosticItemSource.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/CpsDiagnosticItemSource.cs index 41eb9357fd5c9..8c5a577299f15 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/CpsDiagnosticItemSource.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/CpsDiagnosticItemSource.cs @@ -11,112 +11,119 @@ using Microsoft.Internal.VisualStudio.PlatformUI; using Microsoft.VisualStudio.Shell; -namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer +namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer; + +internal sealed partial class CpsDiagnosticItemSource : BaseDiagnosticAndGeneratorItemSource, INotifyPropertyChanged { - internal partial class CpsDiagnosticItemSource : BaseDiagnosticAndGeneratorItemSource, INotifyPropertyChanged + private readonly IVsHierarchyItem _item; + private readonly string _projectDirectoryPath; + + /// + /// The analyzer reference that has been found. Once it's been assigned a non-null value, it'll never be assigned null again. + /// + private AnalyzerReference? _analyzerReference; + + public event PropertyChangedEventHandler? PropertyChanged; + + public CpsDiagnosticItemSource( + Workspace workspace, + string projectPath, + ProjectId projectId, + IVsHierarchyItem item, + IAnalyzersCommandHandler commandHandler, + IDiagnosticAnalyzerService analyzerService) + : base(workspace, projectId, commandHandler, analyzerService) { - private readonly IVsHierarchyItem _item; - private readonly string _projectDirectoryPath; - - /// - /// The analyzer reference that has been found. Once it's been assigned a non-null value, it'll never be assigned null again. - /// - private AnalyzerReference? _analyzerReference; - - public event PropertyChangedEventHandler? PropertyChanged; + _item = item; + _projectDirectoryPath = Path.GetDirectoryName(projectPath); - public CpsDiagnosticItemSource(Workspace workspace, string projectPath, ProjectId projectId, IVsHierarchyItem item, IAnalyzersCommandHandler commandHandler, IDiagnosticAnalyzerService analyzerService) - : base(workspace, projectId, commandHandler, analyzerService) + _analyzerReference = TryGetAnalyzerReference(Workspace.CurrentSolution); + if (_analyzerReference == null) { - _item = item; - _projectDirectoryPath = Path.GetDirectoryName(projectPath); - - _analyzerReference = TryGetAnalyzerReference(Workspace.CurrentSolution); - if (_analyzerReference == null) + // The ProjectId that was given to us was found by enumerating the list of projects in the solution, + // thus the project must have already been added to the workspace at some point. As long as the project + // is still there, we're going to assume the reason we don't have the reference yet is because while we + // have a project, we don't have all the references added yet. We'll wait until we see the reference and + // then connect to it. + if (workspace.CurrentSolution.ContainsProject(projectId)) { - // The ProjectId that was given to us was found by enumerating the list of projects in the solution, thus the project must have already - // been added to the workspace at some point. As long as the project is still there, we're going to assume the reason we don't have the reference - // yet is because while we have a project, we don't have all the references added yet. We'll wait until we see the reference and then connect to it. - if (workspace.CurrentSolution.ContainsProject(projectId)) - { - Workspace.WorkspaceChanged += OnWorkspaceChangedLookForAnalyzer; - item.PropertyChanged += IVsHierarchyItem_PropertyChanged; + Workspace.WorkspaceChanged += OnWorkspaceChangedLookForAnalyzer; + item.PropertyChanged += IVsHierarchyItem_PropertyChanged; - // Now that we've subscribed, check once more in case we missed the event - var analyzerReference = TryGetAnalyzerReference(Workspace.CurrentSolution); + // Now that we've subscribed, check once more in case we missed the event + var analyzerReference = TryGetAnalyzerReference(Workspace.CurrentSolution); - if (analyzerReference != null) - { - _analyzerReference = analyzerReference; - UnsubscribeFromEvents(); - } + if (analyzerReference != null) + { + _analyzerReference = analyzerReference; + UnsubscribeFromEvents(); } } } + } - private void UnsubscribeFromEvents() - { - Workspace.WorkspaceChanged -= OnWorkspaceChangedLookForAnalyzer; - _item.PropertyChanged -= IVsHierarchyItem_PropertyChanged; - } + private void UnsubscribeFromEvents() + { + Workspace.WorkspaceChanged -= OnWorkspaceChangedLookForAnalyzer; + _item.PropertyChanged -= IVsHierarchyItem_PropertyChanged; + } - private void IVsHierarchyItem_PropertyChanged(object sender, PropertyChangedEventArgs e) + private void IVsHierarchyItem_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + // IVsHierarchyItem implements ISupportDisposalNotification, which allows us to know when it's been removed + if (e.PropertyName == nameof(ISupportDisposalNotification.IsDisposed)) { - // IVsHierarchyItem implements ISupportDisposalNotification, which allows us to know when it's been removed - if (e.PropertyName == nameof(ISupportDisposalNotification.IsDisposed)) - { - UnsubscribeFromEvents(); - } + UnsubscribeFromEvents(); } + } - public IContextMenuController DiagnosticItemContextMenuController => CommandHandler.DiagnosticContextMenuController; + public IContextMenuController DiagnosticItemContextMenuController => CommandHandler.DiagnosticContextMenuController; - public override object SourceItem => _item; + public override object SourceItem => _item; - public override AnalyzerReference? AnalyzerReference => _analyzerReference; + public override AnalyzerReference? AnalyzerReference => _analyzerReference; - private void OnWorkspaceChangedLookForAnalyzer(object sender, WorkspaceChangeEventArgs e) + private void OnWorkspaceChangedLookForAnalyzer(object sender, WorkspaceChangeEventArgs e) + { + // If the project has gone away in this change, it's not coming back, so we can stop looking at this point + if (!e.NewSolution.ContainsProject(ProjectId)) { - // If the project has gone away in this change, it's not coming back, so we can stop looking at this point - if (!e.NewSolution.ContainsProject(ProjectId)) - { - UnsubscribeFromEvents(); - return; - } + UnsubscribeFromEvents(); + return; + } - // Was this a change to our project, or a global change? - if (e.ProjectId == ProjectId || - e.Kind == WorkspaceChangeKind.SolutionChanged) + // Was this a change to our project, or a global change? + if (e.ProjectId == ProjectId || + e.Kind == WorkspaceChangeKind.SolutionChanged) + { + var analyzerReference = TryGetAnalyzerReference(e.NewSolution); + if (analyzerReference != null) { - var analyzerReference = TryGetAnalyzerReference(e.NewSolution); - if (analyzerReference != null) - { - _analyzerReference = analyzerReference; - UnsubscribeFromEvents(); + _analyzerReference = analyzerReference; + UnsubscribeFromEvents(); - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(HasItems))); - } + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(HasItems))); } } + } - private AnalyzerReference? TryGetAnalyzerReference(Solution solution) - { - var project = solution.GetProject(ProjectId); - - if (project == null) - { - return null; - } + private AnalyzerReference? TryGetAnalyzerReference(Solution solution) + { + var project = solution.GetProject(ProjectId); - var canonicalName = _item.CanonicalName; - var analyzerFilePath = CpsUtilities.ExtractAnalyzerFilePath(_projectDirectoryPath, canonicalName); + if (project == null) + { + return null; + } - if (string.IsNullOrEmpty(analyzerFilePath)) - { - return null; - } + var canonicalName = _item.CanonicalName; + var analyzerFilePath = CpsUtilities.ExtractAnalyzerFilePath(_projectDirectoryPath, canonicalName); - return project.AnalyzerReferences.FirstOrDefault(r => string.Equals(r.FullPath, analyzerFilePath, StringComparison.OrdinalIgnoreCase)); + if (string.IsNullOrEmpty(analyzerFilePath)) + { + return null; } + + return project.AnalyzerReferences.FirstOrDefault(r => string.Equals(r.FullPath, analyzerFilePath, StringComparison.OrdinalIgnoreCase)); } } diff --git a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/CpsDiagnosticItemSourceProvider.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/CpsDiagnosticItemSourceProvider.cs index b4720a60d12de..dbc662bb2739a 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/CpsDiagnosticItemSourceProvider.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/CpsDiagnosticItemSourceProvider.cs @@ -7,162 +7,162 @@ using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.Internal.VisualStudio.PlatformUI; using Microsoft.VisualStudio.ProjectSystem; using Microsoft.VisualStudio.ProjectSystem.Properties; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Utilities; -namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer +namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer; + +using OrderAttribute = Microsoft.VisualStudio.Utilities.OrderAttribute; +using Workspace = Microsoft.CodeAnalysis.Workspace; + +[Export(typeof(IAttachedCollectionSourceProvider))] +[Name(nameof(CpsDiagnosticItemSourceProvider))] +[Order] +[AppliesToProject($"({ProjectCapabilities.CSharp} | {ProjectCapabilities.VB}) & {ProjectCapabilities.Cps}")] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CpsDiagnosticItemSourceProvider( + IThreadingContext threadingContext, + [Import(typeof(AnalyzersCommandHandler))] IAnalyzersCommandHandler commandHandler, + IDiagnosticAnalyzerService diagnosticAnalyzerService, + VisualStudioWorkspace workspace, + IAsynchronousOperationListenerProvider listenerProvider) + : AttachedCollectionSourceProvider { - using OrderAttribute = Microsoft.VisualStudio.Utilities.OrderAttribute; - using Workspace = Microsoft.CodeAnalysis.Workspace; - - [Export(typeof(IAttachedCollectionSourceProvider))] - [Name(nameof(CpsDiagnosticItemSourceProvider))] - [Order] - [AppliesToProject($"({ProjectCapabilities.CSharp} | {ProjectCapabilities.VB}) & {ProjectCapabilities.Cps}")] - internal sealed class CpsDiagnosticItemSourceProvider : AttachedCollectionSourceProvider - { - private readonly IAnalyzersCommandHandler _commandHandler; - private readonly IDiagnosticAnalyzerService _diagnosticAnalyzerService; - private readonly Workspace _workspace; - - private IHierarchyItemToProjectIdMap? _projectMap; - - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CpsDiagnosticItemSourceProvider( - [Import(typeof(AnalyzersCommandHandler))] IAnalyzersCommandHandler commandHandler, - IDiagnosticAnalyzerService diagnosticAnalyzerService, - VisualStudioWorkspace workspace) - { - _commandHandler = commandHandler; - _diagnosticAnalyzerService = diagnosticAnalyzerService; - _workspace = workspace; - } + private readonly IThreadingContext _threadingContext = threadingContext; + private readonly IAnalyzersCommandHandler _commandHandler = commandHandler; + private readonly IDiagnosticAnalyzerService _diagnosticAnalyzerService = diagnosticAnalyzerService; + private readonly Workspace _workspace = workspace; + private readonly IAsynchronousOperationListenerProvider _listenerProvider = listenerProvider; - protected override IAttachedCollectionSource? CreateCollectionSource(IVsHierarchyItem? item, string relationshipName) + private IHierarchyItemToProjectIdMap? _projectMap; + + protected override IAttachedCollectionSource? CreateCollectionSource(IVsHierarchyItem? item, string relationshipName) + { + if (item?.HierarchyIdentity?.NestedHierarchy != null && + !item.IsDisposed && + relationshipName == KnownRelationships.Contains) { - if (item?.HierarchyIdentity?.NestedHierarchy != null && - !item.IsDisposed && - relationshipName == KnownRelationships.Contains) + if (NestedHierarchyHasProjectTreeCapability(item, "AnalyzerDependency")) { - if (NestedHierarchyHasProjectTreeCapability(item, "AnalyzerDependency")) + var projectRootItem = FindProjectRootItem(item, out var targetFrameworkMoniker); + if (projectRootItem != null) { - var projectRootItem = FindProjectRootItem(item, out var targetFrameworkMoniker); - if (projectRootItem != null) + var hierarchyMapper = TryGetProjectMap(); + if (hierarchyMapper != null && + hierarchyMapper.TryGetProjectId(projectRootItem, targetFrameworkMoniker, out var projectId)) { - var hierarchyMapper = TryGetProjectMap(); - if (hierarchyMapper != null && - hierarchyMapper.TryGetProjectId(projectRootItem, targetFrameworkMoniker, out var projectId)) + var hierarchy = projectRootItem.HierarchyIdentity.NestedHierarchy; + var itemId = projectRootItem.HierarchyIdentity.NestedItemID; + if (hierarchy.GetCanonicalName(itemId, out var projectCanonicalName) == VSConstants.S_OK) { - var hierarchy = projectRootItem.HierarchyIdentity.NestedHierarchy; - var itemId = projectRootItem.HierarchyIdentity.NestedItemID; - if (hierarchy.GetCanonicalName(itemId, out var projectCanonicalName) == VSConstants.S_OK) - { - return new CpsDiagnosticItemSource(_workspace, projectCanonicalName, projectId, item, _commandHandler, _diagnosticAnalyzerService); - } + return new CpsDiagnosticItemSource( + _workspace, projectCanonicalName, projectId, item, _commandHandler, _diagnosticAnalyzerService); } } } } - - return null; } - /// - /// Starting at the given item, walks up the tree to find the item representing the project root. - /// If the item is located under a target-framework specific node, the corresponding - /// TargetFrameworkMoniker will be found as well. - /// - private static IVsHierarchyItem? FindProjectRootItem(IVsHierarchyItem item, out string? targetFrameworkMoniker) + return null; + } + + /// + /// Starting at the given item, walks up the tree to find the item representing the project root. + /// If the item is located under a target-framework specific node, the corresponding + /// TargetFrameworkMoniker will be found as well. + /// + private static IVsHierarchyItem? FindProjectRootItem(IVsHierarchyItem item, out string? targetFrameworkMoniker) + { + targetFrameworkMoniker = null; + + for (var parent = item; parent != null; parent = parent.Parent) { - targetFrameworkMoniker = null; + targetFrameworkMoniker ??= GetTargetFrameworkMoniker(parent); - for (var parent = item; parent != null; parent = parent.Parent) + if (NestedHierarchyHasProjectTreeCapability(parent, "ProjectRoot")) { - targetFrameworkMoniker ??= GetTargetFrameworkMoniker(parent); - - if (NestedHierarchyHasProjectTreeCapability(parent, "ProjectRoot")) - { - return parent; - } + return parent; } - - return null; } - /// - /// Given an item determines if it represents a particular target framework. - /// If so, it returns the corresponding TargetFrameworkMoniker. - /// - private static string? GetTargetFrameworkMoniker(IVsHierarchyItem item) - { - if (TryGetFlags(item, out var flags) && - flags.Contains("TargetNode")) - { - const string prefix = "$TFM:"; - var flag = flags.FirstOrDefault(f => f.StartsWith(prefix)); + return null; + } - return flag?.Substring(prefix.Length); - } + /// + /// Given an item determines if it represents a particular target framework. + /// If so, it returns the corresponding TargetFrameworkMoniker. + /// + private static string? GetTargetFrameworkMoniker(IVsHierarchyItem item) + { + if (TryGetFlags(item, out var flags) && + flags.Contains("TargetNode")) + { + const string prefix = "$TFM:"; + var flag = flags.FirstOrDefault(f => f.StartsWith(prefix)); - return null; + return flag?.Substring(prefix.Length); } - private static bool NestedHierarchyHasProjectTreeCapability(IVsHierarchyItem item, string capability) - { - if (TryGetFlags(item, out var flags)) - return flags.Contains(capability); + return null; + } - return false; - } + private static bool NestedHierarchyHasProjectTreeCapability(IVsHierarchyItem item, string capability) + { + if (TryGetFlags(item, out var flags)) + return flags.Contains(capability); + + return false; + } - public static bool TryGetFlags(IVsHierarchyItem item, out ProjectTreeFlags flags) + public static bool TryGetFlags(IVsHierarchyItem item, out ProjectTreeFlags flags) + { + if (item.HierarchyIdentity.IsRoot) { - if (item.HierarchyIdentity.IsRoot) + if (item.HierarchyIdentity.NestedHierarchy is IVsBrowseObjectContext { UnconfiguredProject.Services.ProjectTreeService.CurrentTree.Tree: { } tree }) { - if (item.HierarchyIdentity.NestedHierarchy is IVsBrowseObjectContext { UnconfiguredProject.Services.ProjectTreeService.CurrentTree.Tree: { } tree }) - { - flags = tree.Flags; - return true; - } + flags = tree.Flags; + return true; } - else + } + else + { + var itemId = item.HierarchyIdentity.ItemID; + + // Browse objects are created lazily, and we want to avoid creating them when possible. + // This method is typically invoked for every hierarchy item in the tree, via Solution Explorer APIs. + // Rather than create a browse object for every node, we find the project root node and use that. + // In this way, we only ever create one browse object per project. + var root = item; + while (!root.HierarchyIdentity.IsRoot) { - var itemId = item.HierarchyIdentity.ItemID; - - // Browse objects are created lazily, and we want to avoid creating them when possible. - // This method is typically invoked for every hierarchy item in the tree, via Solution Explorer APIs. - // Rather than create a browse object for every node, we find the project root node and use that. - // In this way, we only ever create one browse object per project. - var root = item; - while (!root.HierarchyIdentity.IsRoot) - { - root = root.Parent; - } + root = root.Parent; + } - if (root.HierarchyIdentity.NestedHierarchy is IVsBrowseObjectContext { UnconfiguredProject.Services.ProjectTreeService.CurrentTree.Tree: { } tree }) + if (root.HierarchyIdentity.NestedHierarchy is IVsBrowseObjectContext { UnconfiguredProject.Services.ProjectTreeService.CurrentTree.Tree: { } tree }) + { + if (tree?.TryFind((IntPtr)itemId, out var subtree) == true) { - if (tree?.TryFind((IntPtr)itemId, out var subtree) == true) - { - flags = subtree.Flags; - return true; - } + flags = subtree.Flags; + return true; } } - - flags = default; - return false; } - private IHierarchyItemToProjectIdMap? TryGetProjectMap() - { - _projectMap ??= _workspace.Services.GetService(); + flags = default; + return false; + } - return _projectMap; - } + private IHierarchyItemToProjectIdMap? TryGetProjectMap() + { + _projectMap ??= _workspace.Services.GetService(); + + return _projectMap; } } diff --git a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/DiagnosticItem.BrowseObject.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/DiagnosticItem.BrowseObject.cs index 59d281e2075bf..1b0b3c71760c5 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/DiagnosticItem.BrowseObject.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/DiagnosticItem.BrowseObject.cs @@ -105,12 +105,7 @@ public string Tags [BrowseObjectDisplayName(nameof(SolutionExplorerShim.Effective_severity))] public string EffectiveSeverity - { - get - { - return MapReportDiagnosticToText(_diagnosticItem.EffectiveSeverity); - } - } + => MapReportDiagnosticToText(_diagnosticItem.EffectiveSeverity); public override string GetClassName() { diff --git a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/DiagnosticItem.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/DiagnosticItem.cs index 5a6eed07e4a9f..f2b62fd23e1a2 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/DiagnosticItem.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/DiagnosticItem.cs @@ -2,6 +2,7 @@ // 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.ComponentModel; using System.Threading; using System.Threading.Tasks; @@ -12,81 +13,95 @@ using Microsoft.Internal.VisualStudio.PlatformUI; using Microsoft.VisualStudio.Imaging; using Microsoft.VisualStudio.Imaging.Interop; +using Roslyn.Utilities; -namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer +namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer; + +internal sealed partial class DiagnosticItem( + ProjectId projectId, + AnalyzerReference analyzerReference, + DiagnosticDescriptor descriptor, + ReportDiagnostic effectiveSeverity, + IAnalyzersCommandHandler commandHandler) + : BaseItem(descriptor.Id + ": " + descriptor.Title), IEquatable { - internal partial class DiagnosticItem : BaseItem - { - private readonly AnalyzerReference _analyzerReference; - private readonly IAnalyzersCommandHandler _commandHandler; + private readonly AnalyzerReference _analyzerReference = analyzerReference; + private readonly IAnalyzersCommandHandler _commandHandler = commandHandler; - public ProjectId ProjectId { get; } - public DiagnosticDescriptor Descriptor { get; } - public ReportDiagnostic EffectiveSeverity { get; private set; } + public ProjectId ProjectId { get; } = projectId; + public DiagnosticDescriptor Descriptor { get; } = descriptor; + public ReportDiagnostic EffectiveSeverity { get; private set; } = effectiveSeverity; - public override event PropertyChangedEventHandler? PropertyChanged; + public override event PropertyChangedEventHandler? PropertyChanged; - public DiagnosticItem(ProjectId projectId, AnalyzerReference analyzerReference, DiagnosticDescriptor descriptor, ReportDiagnostic effectiveSeverity, IAnalyzersCommandHandler commandHandler) - : base(descriptor.Id + ": " + descriptor.Title) - { - ProjectId = projectId; - _analyzerReference = analyzerReference; - Descriptor = descriptor; - EffectiveSeverity = effectiveSeverity; - _commandHandler = commandHandler; - } + public override ImageMoniker IconMoniker + => MapEffectiveSeverityToIconMoniker(this.EffectiveSeverity); - public override ImageMoniker IconMoniker - => MapEffectiveSeverityToIconMoniker(EffectiveSeverity); + public override IContextMenuController ContextMenuController => _commandHandler.DiagnosticContextMenuController; - public override IContextMenuController ContextMenuController => _commandHandler.DiagnosticContextMenuController; + public override object GetBrowseObject() + => new BrowseObject(this); - public override object GetBrowseObject() + internal void UpdateEffectiveSeverity(ReportDiagnostic newEffectiveSeverity) + { + if (EffectiveSeverity != newEffectiveSeverity) { - return new BrowseObject(this); + EffectiveSeverity = newEffectiveSeverity; + + NotifyPropertyChanged(nameof(EffectiveSeverity)); + NotifyPropertyChanged(nameof(IconMoniker)); } + } - internal void UpdateEffectiveSeverity(ReportDiagnostic newEffectiveSeverity) + private static ImageMoniker MapEffectiveSeverityToIconMoniker(ReportDiagnostic effectiveSeverity) + => effectiveSeverity switch { - if (EffectiveSeverity != newEffectiveSeverity) - { - EffectiveSeverity = newEffectiveSeverity; + ReportDiagnostic.Error => KnownMonikers.CodeErrorRule, + ReportDiagnostic.Warn => KnownMonikers.CodeWarningRule, + ReportDiagnostic.Info => KnownMonikers.CodeInformationRule, + ReportDiagnostic.Hidden => KnownMonikers.CodeHiddenRule, + ReportDiagnostic.Suppress => KnownMonikers.CodeSuppressedRule, + _ => default, + }; + + private void NotifyPropertyChanged(string propertyName) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } - NotifyPropertyChanged(nameof(EffectiveSeverity)); - NotifyPropertyChanged(nameof(IconMoniker)); - } - } + internal void SetRuleSetSeverity(ReportDiagnostic value, string pathToRuleSet) + { + var ruleSetDocument = XDocument.Load(pathToRuleSet); - private static ImageMoniker MapEffectiveSeverityToIconMoniker(ReportDiagnostic effectiveSeverity) - => effectiveSeverity switch - { - ReportDiagnostic.Error => KnownMonikers.CodeErrorRule, - ReportDiagnostic.Warn => KnownMonikers.CodeWarningRule, - ReportDiagnostic.Info => KnownMonikers.CodeInformationRule, - ReportDiagnostic.Hidden => KnownMonikers.CodeHiddenRule, - ReportDiagnostic.Suppress => KnownMonikers.CodeSuppressedRule, - _ => default, - }; - - private void NotifyPropertyChanged(string propertyName) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } + ruleSetDocument.SetSeverity(_analyzerReference.Display, Descriptor.Id, value); - internal void SetRuleSetSeverity(ReportDiagnostic value, string pathToRuleSet) - { - var ruleSetDocument = XDocument.Load(pathToRuleSet); + ruleSetDocument.Save(pathToRuleSet); + } - ruleSetDocument.SetSeverity(_analyzerReference.Display, Descriptor.Id, value); + internal Task GetSolutionWithUpdatedAnalyzerConfigSeverityAsync(ReportDiagnostic value, Project project, CancellationToken cancellationToken) + { + var effectiveSeverity = value.ToDiagnosticSeverity() ?? Descriptor.DefaultSeverity; + var diagnostic = Diagnostic.Create(Descriptor, Location.None, effectiveSeverity, additionalLocations: null, properties: null); + return ConfigurationUpdater.ConfigureSeverityAsync(value, diagnostic, project, cancellationToken); + } - ruleSetDocument.Save(pathToRuleSet); - } + public override int GetHashCode() + => Hash.Combine(this.Name, + Hash.Combine(this.ProjectId, + Hash.Combine(this.Descriptor.GetHashCode(), (int)this.EffectiveSeverity))); - internal Task GetSolutionWithUpdatedAnalyzerConfigSeverityAsync(ReportDiagnostic value, Project project, CancellationToken cancellationToken) - { - var effectiveSeverity = value.ToDiagnosticSeverity() ?? Descriptor.DefaultSeverity; - var diagnostic = Diagnostic.Create(Descriptor, Location.None, effectiveSeverity, additionalLocations: null, properties: null); - return ConfigurationUpdater.ConfigureSeverityAsync(value, diagnostic, project, cancellationToken); - } + public override bool Equals(object obj) + => Equals(obj as DiagnosticItem); + + public bool Equals(DiagnosticItem? other) + { + if (this == other) + return true; + + return other != null && + this.Name == other.Name && + this.ProjectId == other.ProjectId && + this.Descriptor.Equals(other.Descriptor) && + this.EffectiveSeverity == other.EffectiveSeverity; } } diff --git a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/LegacyDiagnosticItemSource.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/LegacyDiagnosticItemSource.cs index 611598537081e..e90f43a88cefe 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/LegacyDiagnosticItemSource.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/LegacyDiagnosticItemSource.cs @@ -4,19 +4,26 @@ using Microsoft.CodeAnalysis.Diagnostics; -namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer +namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer; + +internal sealed partial class LegacyDiagnosticItemSource : BaseDiagnosticAndGeneratorItemSource { - internal partial class LegacyDiagnosticItemSource : BaseDiagnosticAndGeneratorItemSource + private readonly AnalyzerItem _item; + + public LegacyDiagnosticItemSource( + AnalyzerItem item, + IAnalyzersCommandHandler commandHandler, + IDiagnosticAnalyzerService diagnosticAnalyzerService) + : base( + item.AnalyzersFolder.Workspace, + item.AnalyzersFolder.ProjectId, + commandHandler, + diagnosticAnalyzerService) { - private readonly AnalyzerItem _item; + _item = item; + } - public LegacyDiagnosticItemSource(AnalyzerItem item, IAnalyzersCommandHandler commandHandler, IDiagnosticAnalyzerService diagnosticAnalyzerService) - : base(item.AnalyzersFolder.Workspace, item.AnalyzersFolder.ProjectId, commandHandler, diagnosticAnalyzerService) - { - _item = item; - } + public override object SourceItem => _item; - public override object SourceItem => _item; - public override AnalyzerReference AnalyzerReference => _item.AnalyzerReference; - } + public override AnalyzerReference? AnalyzerReference => _item.AnalyzerReference; } diff --git a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/LegacyDiagnosticItemSourceProvider.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/LegacyDiagnosticItemSourceProvider.cs index c48962188a1f3..6ef52d112ac9f 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/LegacyDiagnosticItemSourceProvider.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/LegacyDiagnosticItemSourceProvider.cs @@ -5,40 +5,33 @@ using System; using System.ComponentModel.Composition; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.Internal.VisualStudio.PlatformUI; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Utilities; -namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer +namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer; + +[Export(typeof(IAttachedCollectionSourceProvider))] +[Name(nameof(LegacyDiagnosticItemSourceProvider))] +[Order] +[AppliesToProject("(CSharp | VB) & !CPS")] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class LegacyDiagnosticItemSourceProvider( + [Import(typeof(AnalyzersCommandHandler))] IAnalyzersCommandHandler commandHandler, + IDiagnosticAnalyzerService diagnosticAnalyzerService) : AttachedCollectionSourceProvider { - [Export(typeof(IAttachedCollectionSourceProvider))] - [Name(nameof(LegacyDiagnosticItemSourceProvider))] - [Order] - [AppliesToProject("(CSharp | VB) & !CPS")] - internal sealed class LegacyDiagnosticItemSourceProvider : AttachedCollectionSourceProvider + protected override IAttachedCollectionSource? CreateCollectionSource(AnalyzerItem item, string relationshipName) { - private readonly IAnalyzersCommandHandler _commandHandler; - private readonly IDiagnosticAnalyzerService _diagnosticAnalyzerService; - - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public LegacyDiagnosticItemSourceProvider( - [Import(typeof(AnalyzersCommandHandler))] IAnalyzersCommandHandler commandHandler, - IDiagnosticAnalyzerService diagnosticAnalyzerService) + if (relationshipName == KnownRelationships.Contains) { - _commandHandler = commandHandler; - _diagnosticAnalyzerService = diagnosticAnalyzerService; + return new LegacyDiagnosticItemSource( + item, commandHandler, diagnosticAnalyzerService); } - protected override IAttachedCollectionSource? CreateCollectionSource(AnalyzerItem item, string relationshipName) - { - if (relationshipName == KnownRelationships.Contains) - { - return new LegacyDiagnosticItemSource(item, _commandHandler, _diagnosticAnalyzerService); - } - - return null; - } + return null; } } diff --git a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/SourceGeneratorItem.BrowseObject.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/SourceGeneratorItem.BrowseObject.cs index 27939c54c48e2..0949019cea990 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/SourceGeneratorItem.BrowseObject.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/SourceGeneratorItem.BrowseObject.cs @@ -4,27 +4,19 @@ using Microsoft.VisualStudio.Shell; -namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer +namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer; + +internal sealed partial class SourceGeneratorItem { - internal sealed partial class SourceGeneratorItem + internal sealed class BrowseObject(SourceGeneratorItem sourceGeneratorItem) : LocalizableProperties { - internal sealed class BrowseObject : LocalizableProperties - { - private readonly SourceGeneratorItem _sourceGeneratorItem; - - public BrowseObject(SourceGeneratorItem sourceGeneratorItem) - { - _sourceGeneratorItem = sourceGeneratorItem; - } - - [BrowseObjectDisplayName(nameof(SolutionExplorerShim.Type_Name))] - public string TypeName => _sourceGeneratorItem.Identity.TypeName; + [BrowseObjectDisplayName(nameof(SolutionExplorerShim.Type_Name))] + public string TypeName => sourceGeneratorItem.Identity.TypeName; - [BrowseObjectDisplayName(nameof(SolutionExplorerShim.Path))] - public string? Path => _sourceGeneratorItem.AnalyzerReference.FullPath; + [BrowseObjectDisplayName(nameof(SolutionExplorerShim.Path))] + public string? Path => sourceGeneratorItem._path; - public override string GetClassName() => SolutionExplorerShim.Source_Generator_Properties; - public override string GetComponentName() => TypeName; - } + public override string GetClassName() => SolutionExplorerShim.Source_Generator_Properties; + public override string GetComponentName() => TypeName; } } diff --git a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/SourceGeneratorItem.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/SourceGeneratorItem.cs index 2728157932efc..d7a8ce4a0d773 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/SourceGeneratorItem.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/SourceGeneratorItem.cs @@ -2,33 +2,47 @@ // 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 Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.VisualStudio.Imaging; using Microsoft.VisualStudio.Imaging.Interop; +using Roslyn.Utilities; -namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer +namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer; + +internal sealed partial class SourceGeneratorItem( + ProjectId projectId, + SourceGeneratorIdentity identity, + string? path) : BaseItem(identity.TypeName), IEquatable { - internal sealed partial class SourceGeneratorItem : BaseItem + public ProjectId ProjectId { get; } = projectId; + public SourceGeneratorIdentity Identity { get; } = identity; + private readonly string? _path = path; + + // TODO: do we need an icon for our use? + public override ImageMoniker IconMoniker + => KnownMonikers.Process; + + public override object GetBrowseObject() + => new BrowseObject(this); + + public override int GetHashCode() + => Hash.Combine(this.Name, + Hash.Combine(this.ProjectId, + Hash.Combine(_path, this.Identity.GetHashCode()))); + + public override bool Equals(object obj) + => Equals(obj as SourceGeneratorItem); + + public bool Equals(SourceGeneratorItem? other) { - public ProjectId ProjectId { get; } - public SourceGeneratorIdentity Identity { get; } - public AnalyzerReference AnalyzerReference { get; } - - public SourceGeneratorItem(ProjectId projectId, ISourceGenerator generator, AnalyzerReference analyzerReference) - : base(name: SourceGeneratorIdentity.GetGeneratorTypeName(generator)) - { - ProjectId = projectId; - Identity = SourceGeneratorIdentity.Create(generator, analyzerReference); - AnalyzerReference = analyzerReference; - } - - // TODO: do we need an icon for our use? - public override ImageMoniker IconMoniker => KnownMonikers.Process; - - public override object GetBrowseObject() - { - return new BrowseObject(this); - } + if (this == other) + return true; + + return other != null && + this.Name == other.Name && + this.ProjectId == other.ProjectId && + this.Identity == other.Identity && + _path == other._path; } } diff --git a/src/VisualStudio/Core/Test/SolutionExplorer/CpsDiagnosticItemSourceTests.vb b/src/VisualStudio/Core/Test/SolutionExplorer/CpsDiagnosticItemSourceTests.vb index 4f75fb6c2e803..7c33159685a6e 100644 --- a/src/VisualStudio/Core/Test/SolutionExplorer/CpsDiagnosticItemSourceTests.vb +++ b/src/VisualStudio/Core/Test/SolutionExplorer/CpsDiagnosticItemSourceTests.vb @@ -5,18 +5,21 @@ Imports System.Collections.Immutable Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.Editor.[Shared].Utilities Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces +Imports Microsoft.CodeAnalysis.[Shared].TestHooks Imports Microsoft.CodeAnalysis.Test.Utilities Imports Microsoft.Internal.VisualStudio.PlatformUI Imports Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer Imports Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Framework Imports Microsoft.VisualStudio.Shell +Imports Roslyn.Test.Utilities Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.SolutionExplorer Public Class CpsDiagnosticItemSourceTests - - Public Sub AnalyzerHasDiagnostics() + + Public Async Function AnalyzerHasDiagnostics() As Task Dim workspaceXml = @@ -35,6 +38,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.SolutionExplorer Const analyzerPath = "C:\Analyzer.dll" workspace.OnAnalyzerReferenceAdded(project.Id, New TestAnalyzerReferenceByLanguage(analyzers, analyzerPath)) + Dim listenerProvider = workspace.GetService(Of IAsynchronousOperationListenerProvider) Dim source As IAttachedCollectionSource = New CpsDiagnosticItemSource( workspace, project.FilePath, @@ -43,9 +47,13 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.SolutionExplorer New FakeAnalyzersCommandHandler, workspace.GetService(Of IDiagnosticAnalyzerService)) Assert.True(source.HasItems) + + Dim waiter = DirectCast(listenerProvider.GetListener(FeatureAttribute.SourceGenerators), IAsynchronousOperationWaiter) + Await waiter.ExpeditedWaitAsync() + Dim diagnostic = Assert.IsAssignableFrom(Of ITreeDisplayItem)(Assert.Single(source.Items)) Assert.Contains(IDEDiagnosticIds.UseAutoPropertyDiagnosticId, diagnostic.Text) End Using - End Sub + End Function End Class End Namespace diff --git a/src/VisualStudio/Core/Test/SolutionExplorer/SourceGeneratorItemTests.vb b/src/VisualStudio/Core/Test/SolutionExplorer/SourceGeneratorItemTests.vb index 74145221a2051..8634c1210b3b5 100644 --- a/src/VisualStudio/Core/Test/SolutionExplorer/SourceGeneratorItemTests.vb +++ b/src/VisualStudio/Core/Test/SolutionExplorer/SourceGeneratorItemTests.vb @@ -19,7 +19,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.SolutionExplorer Public Class SourceGeneratorItemTests - Public Sub SourceGeneratorsListed() + Public Async Function SourceGeneratorsListed() As Task Dim workspaceXml = @@ -30,13 +30,14 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.SolutionExplorer Dim projectId = workspace.Projects.Single().Id Dim source = CreateItemSourceForAnalyzerReference(workspace, projectId) + Await WaitForGeneratorsAndItemSourcesAsync(workspace) Assert.True(source.HasItems) Dim generatorItem = Assert.IsAssignableFrom(Of SourceGeneratorItem)(Assert.Single(source.Items)) Assert.Equal(GetType(GenerateFileForEachAdditionalFileWithContentsCommented).FullName, generatorItem.Text) End Using - End Sub + End Function Public Async Function PlaceholderItemCreateIfGeneratorProducesNoFiles() As Task @@ -49,6 +50,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.SolutionExplorer Using workspace = EditorTestWorkspace.Create(workspaceXml) Dim projectId = workspace.Projects.Single().Id Dim source = CreateItemSourceForAnalyzerReference(workspace, projectId) + Await WaitForGeneratorsAndItemSourcesAsync(workspace) Dim generatorItem = Assert.IsAssignableFrom(Of SourceGeneratorItem)(Assert.Single(source.Items)) Dim generatorFilesItemSource = CreateSourceGeneratedFilesItemSource(workspace, generatorItem) @@ -74,6 +76,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.SolutionExplorer Using workspace = EditorTestWorkspace.Create(workspaceXml) Dim projectId = workspace.Projects.Single().Id Dim source = CreateItemSourceForAnalyzerReference(workspace, projectId) + Await WaitForGeneratorsAndItemSourcesAsync(workspace) Dim generatorItem = Assert.IsAssignableFrom(Of SourceGeneratorItem)(Assert.Single(source.Items)) Dim generatorFilesItemSource = CreateSourceGeneratedFilesItemSource(workspace, generatorItem) @@ -108,6 +111,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.SolutionExplorer Using workspace = EditorTestWorkspace.Create(workspaceXml) Dim projectId = workspace.Projects.Single().Id Dim source = CreateItemSourceForAnalyzerReference(workspace, projectId) + Await WaitForGeneratorsAndItemSourcesAsync(workspace) Dim generatorItem = Assert.IsAssignableFrom(Of SourceGeneratorItem)(Assert.Single(source.Items)) Dim generatorFilesItemSource = CreateSourceGeneratedFilesItemSource(workspace, generatorItem) @@ -142,6 +146,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.SolutionExplorer Dim projectId = workspace.Projects.Single().Id Dim source = CreateItemSourceForAnalyzerReference(workspace, projectId) + Await WaitForGeneratorsAndItemSourcesAsync(workspace) Dim generatorItem = Assert.IsAssignableFrom(Of SourceGeneratorItem)(Assert.Single(source.Items)) Dim generatorFilesItemSource = CreateSourceGeneratedFilesItemSource(workspace, generatorItem) @@ -182,6 +187,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.SolutionExplorer Dim projectId = workspace.Projects.Single().Id Dim source = CreateItemSourceForAnalyzerReference(workspace, projectId) + Await WaitForGeneratorsAndItemSourcesAsync(workspace) Dim generatorItem = Assert.IsAssignableFrom(Of SourceGeneratorItem)(Assert.Single(source.Items)) Dim generatorFilesItemSource = CreateSourceGeneratedFilesItemSource(workspace, generatorItem) @@ -234,6 +240,8 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.SolutionExplorer Using workspace = EditorTestWorkspace.Create(workspaceXml) Dim projectId = workspace.Projects.Single().Id Dim source = CreateItemSourceForAnalyzerReference(workspace, projectId) + Await WaitForGeneratorsAndItemSourcesAsync(workspace) + Dim generatorItem = Assert.IsAssignableFrom(Of SourceGeneratorItem)(Assert.Single(source.Items)) Dim generatorFilesItemSource = CreateSourceGeneratedFilesItemSource(workspace, generatorItem) diff --git a/src/Workspaces/Core/Portable/SourceGeneration/IRemoteSourceGenerationService.cs b/src/Workspaces/Core/Portable/SourceGeneration/IRemoteSourceGenerationService.cs index fffa94b324236..ce1a05a2ca1d8 100644 --- a/src/Workspaces/Core/Portable/SourceGeneration/IRemoteSourceGenerationService.cs +++ b/src/Workspaces/Core/Portable/SourceGeneration/IRemoteSourceGenerationService.cs @@ -8,12 +8,13 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.SourceGeneration; [DataContract] -internal readonly record struct SourceGeneratedDocmentInfo( +internal readonly record struct SourceGeneratedDocumentInfo( [property: DataMember(Order = 0)] SourceGeneratedDocumentIdentity DocumentIdentity, [property: DataMember(Order = 1)] SourceGeneratedDocumentContentIdentity ContentIdentity, [property: DataMember(Order = 2)] DateTime GenerationDateTime); @@ -30,7 +31,7 @@ internal interface IRemoteSourceGenerationService /// Controls if the caller wants frozen source generator documents /// included in the result, or if only the most underlying generated documents (produced by the real compiler should be included. - ValueTask> GetSourceGeneratedDocumentInfoAsync( + ValueTask> GetSourceGeneratedDocumentInfoAsync( Checksum solutionChecksum, ProjectId projectId, bool withFrozenSourceGeneratedDocuments, CancellationToken cancellationToken); /// diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratorIdentity.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratorIdentity.cs index cf815501c688a..91818767b01f9 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratorIdentity.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratorIdentity.cs @@ -32,7 +32,4 @@ public static SourceGeneratorIdentity Create(ISourceGenerator generator, Analyze return new SourceGeneratorIdentity( assemblyName.Name!, analyzerReference.FullPath, assemblyName.Version!, generatorType.FullName!); } - - public static string GetGeneratorTypeName(ISourceGenerator generator) - => generator.GetGeneratorType().FullName!; } diff --git a/src/Workspaces/Remote/ServiceHub/Services/SourceGeneration/RemoteSourceGenerationService.cs b/src/Workspaces/Remote/ServiceHub/Services/SourceGeneration/RemoteSourceGenerationService.cs index f1cd430234c82..cffdf6bca2b0b 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/SourceGeneration/RemoteSourceGenerationService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/SourceGeneration/RemoteSourceGenerationService.cs @@ -29,7 +29,7 @@ protected override IRemoteSourceGenerationService CreateService(in ServiceConstr => new RemoteSourceGenerationService(arguments); } - public ValueTask> GetSourceGeneratedDocumentInfoAsync( + public ValueTask> GetSourceGeneratedDocumentInfoAsync( Checksum solutionChecksum, ProjectId projectId, bool withFrozenSourceGeneratedDocuments, CancellationToken cancellationToken) { return RunServiceAsync(solutionChecksum, async solution => @@ -38,7 +38,7 @@ public ValueTask> GetSourceGeneratedD var documentStates = await solution.CompilationState.GetSourceGeneratedDocumentStatesAsync( project.State, withFrozenSourceGeneratedDocuments, cancellationToken).ConfigureAwait(false); - var result = new FixedSizeArrayBuilder(documentStates.States.Count); + var result = new FixedSizeArrayBuilder(documentStates.States.Count); foreach (var (id, state) in documentStates.States) { Contract.ThrowIfFalse(id.IsSourceGenerated);