From bedf640d3d76e7fe700b6873f417e977e482f398 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 18 Jul 2024 08:14:34 -0700 Subject: [PATCH 1/5] Comment formatting Formatting In progress In progress In progress In progress Host side' git pus Host side' git pus Host side' git pus Host side' git pus Host side Remote side Accessibility Tests Cast In progress' UI thread Tests Make private Make private Docs Simplify Clear the items Docs fix Reversinos Reversinos revert revert revert revert --- .../AnalyzerItem/AnalyzerItem.cs | 106 ++++---- .../Core/Impl/SolutionExplorer/BaseItem.cs | 8 +- .../BaseDiagnosticAndGeneratorItemSource.cs | 2 +- .../DiagnosticItem/CpsDiagnosticItemSource.cs | 163 +++++++------ .../CpsDiagnosticItemSourceProvider.cs | 228 +++++++++--------- .../DiagnosticItem.BrowseObject.cs | 7 +- .../DiagnosticItem/DiagnosticItem.cs | 130 +++++----- .../LegacyDiagnosticItemSource.cs | 29 ++- .../LegacyDiagnosticItemSourceProvider.cs | 43 ++-- .../SourceGeneratorItem.BrowseObject.cs | 28 +-- .../DiagnosticItem/SourceGeneratorItem.cs | 54 +++-- .../CpsDiagnosticItemSourceTests.vb | 14 +- .../SourceGeneratorItemTests.vb | 12 +- .../IRemoteSourceGenerationService.cs | 5 +- .../Solution/SourceGeneratorIdentity.cs | 3 - .../RemoteSourceGenerationService.cs | 4 +- 16 files changed, 429 insertions(+), 407 deletions(-) diff --git a/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerItem/AnalyzerItem.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerItem/AnalyzerItem.cs index f076af2f03f5b..67044d8d9cd40 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerItem/AnalyzerItem.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerItem/AnalyzerItem.cs @@ -9,83 +9,73 @@ 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; + private readonly AnalyzersFolderItem _analyzersFolder = analyzersFolder; + private readonly IContextMenuController _contextMenuController = contextMenuController; + + public AnalyzerReference AnalyzerReference { get; } = analyzerReference; - public AnalyzerItem(AnalyzersFolderItem analyzersFolder, AnalyzerReference analyzerReference, IContextMenuController contextMenuController) - : base(GetNameText(analyzerReference)) + public override ImageMoniker IconMoniker + { + get { - _analyzersFolder = analyzersFolder; - _analyzerReference = analyzerReference; - _contextMenuController = contextMenuController; + return KnownMonikers.CodeInformation; } + } - public override ImageMoniker IconMoniker + public override ImageMoniker OverlayIconMoniker + { + get { - get + if (this.AnalyzerReference is UnresolvedAnalyzerReference) { - return KnownMonikers.CodeInformation; + return KnownMonikers.OverlayWarning; } - } - - public override ImageMoniker OverlayIconMoniker - { - get + else { - if (_analyzerReference is UnresolvedAnalyzerReference) - { - return KnownMonikers.OverlayWarning; - } - else - { - return default; - } + return default; } } + } - public override object GetBrowseObject() - { - return new BrowseObject(this); - } + public override object GetBrowseObject() + { + return new BrowseObject(this); + } - public AnalyzerReference AnalyzerReference - { - get { return _analyzerReference; } - } + public override IContextMenuController ContextMenuController + { + get { return _contextMenuController; } + } - public override IContextMenuController ContextMenuController - { - get { return _contextMenuController; } - } + public AnalyzersFolderItem AnalyzersFolder + { + get { return _analyzersFolder; } + } - public AnalyzersFolderItem AnalyzersFolder - { - get { return _analyzersFolder; } - } + /// + /// Remove this AnalyzerItem from it's folder. + /// + public void Remove() + { + _analyzersFolder.RemoveAnalyzer(this.AnalyzerReference.FullPath); + } - /// - /// 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..37187d8cc7a93 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/DiagnosticItem.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/DiagnosticItem.cs @@ -2,91 +2,105 @@ // 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.ObjectModel; using System.ComponentModel; using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; +using ICSharpCode.Decompiler.IL; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes.Configuration; using Microsoft.CodeAnalysis.Diagnostics; 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) + { + 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..3033ec47263c3 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/SourceGeneratorItem.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/SourceGeneratorItem.cs @@ -2,33 +2,43 @@ // 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); - } + 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); From 1eda82fb4160a131773cb492c3be12fd1536b086 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 18 Jul 2024 14:23:32 -0700 Subject: [PATCH 2/5] Simplify more --- .../AnalyzerItem/AnalyzerItem.cs | 47 ++++--------------- 1 file changed, 8 insertions(+), 39 deletions(-) diff --git a/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerItem/AnalyzerItem.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerItem/AnalyzerItem.cs index 67044d8d9cd40..1af836885084e 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerItem/AnalyzerItem.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerItem/AnalyzerItem.cs @@ -16,56 +16,25 @@ internal partial class AnalyzerItem( AnalyzerReference analyzerReference, IContextMenuController contextMenuController) : BaseItem(GetNameText(analyzerReference)) { - private readonly AnalyzersFolderItem _analyzersFolder = analyzersFolder; - private readonly IContextMenuController _contextMenuController = contextMenuController; - + public AnalyzersFolderItem AnalyzersFolder { get; } = analyzersFolder; public AnalyzerReference AnalyzerReference { get; } = analyzerReference; + public override IContextMenuController ContextMenuController { get; } = contextMenuController; - public override ImageMoniker IconMoniker - { - get - { - return KnownMonikers.CodeInformation; - } - } + public override ImageMoniker IconMoniker => KnownMonikers.CodeInformation; public override ImageMoniker OverlayIconMoniker - { - get - { - if (this.AnalyzerReference is UnresolvedAnalyzerReference) - { - return KnownMonikers.OverlayWarning; - } - else - { - return default; - } - } - } + => this.AnalyzerReference is UnresolvedAnalyzerReference + ? KnownMonikers.OverlayWarning + : default; public override object GetBrowseObject() - { - return new BrowseObject(this); - } - - public override IContextMenuController ContextMenuController - { - get { return _contextMenuController; } - } - - public AnalyzersFolderItem AnalyzersFolder - { - get { return _analyzersFolder; } - } + => new BrowseObject(this); /// /// Remove this AnalyzerItem from it's folder. /// public void Remove() - { - _analyzersFolder.RemoveAnalyzer(this.AnalyzerReference.FullPath); - } + => this.AnalyzersFolder.RemoveAnalyzer(this.AnalyzerReference.FullPath); private static string GetNameText(AnalyzerReference analyzerReference) { From 1b597598772c18fd9d2224f5049abae2b4124556 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 18 Jul 2024 14:24:07 -0700 Subject: [PATCH 3/5] remove --- .../Core/Impl/SolutionExplorer/DiagnosticItem/DiagnosticItem.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/DiagnosticItem.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/DiagnosticItem.cs index 37187d8cc7a93..4ea2e66ddad0d 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/DiagnosticItem.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/DiagnosticItem.cs @@ -3,12 +3,10 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.ObjectModel; using System.ComponentModel; using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; -using ICSharpCode.Decompiler.IL; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes.Configuration; using Microsoft.CodeAnalysis.Diagnostics; From cf4c87e6669cda7b2f005ee39baa1fa12240eb99 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 18 Jul 2024 14:26:14 -0700 Subject: [PATCH 4/5] Fast path --- .../Impl/SolutionExplorer/DiagnosticItem/DiagnosticItem.cs | 3 +++ .../SolutionExplorer/DiagnosticItem/SourceGeneratorItem.cs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/DiagnosticItem.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/DiagnosticItem.cs index 4ea2e66ddad0d..f2b62fd23e1a2 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/DiagnosticItem.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/DiagnosticItem.cs @@ -95,6 +95,9 @@ public override bool Equals(object obj) public bool Equals(DiagnosticItem? other) { + if (this == other) + return true; + return other != null && this.Name == other.Name && this.ProjectId == other.ProjectId && diff --git a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/SourceGeneratorItem.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/SourceGeneratorItem.cs index 3033ec47263c3..ea0fb3248d5b8 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/SourceGeneratorItem.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/SourceGeneratorItem.cs @@ -35,6 +35,9 @@ public override bool Equals(object obj) public bool Equals(SourceGeneratorItem? other) { + if (this == other) + return true; + return other != null && this.Name == other.Name && this.ProjectId == other.ProjectId && From 0632dc5d26546e4805032a28ee0e463ad91dd383 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 18 Jul 2024 22:29:29 -0700 Subject: [PATCH 5/5] Wrapping --- .../SolutionExplorer/DiagnosticItem/SourceGeneratorItem.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/SourceGeneratorItem.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/SourceGeneratorItem.cs index ea0fb3248d5b8..d7a8ce4a0d773 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/SourceGeneratorItem.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/SourceGeneratorItem.cs @@ -20,7 +20,8 @@ internal sealed partial class SourceGeneratorItem( private readonly string? _path = path; // TODO: do we need an icon for our use? - public override ImageMoniker IconMoniker => KnownMonikers.Process; + public override ImageMoniker IconMoniker + => KnownMonikers.Process; public override object GetBrowseObject() => new BrowseObject(this);