Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup in VS generator UI code. #74448

Merged
merged 6 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,83 +9,42 @@
using Microsoft.VisualStudio.Imaging;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

view with whitespace off.

using Microsoft.VisualStudio.Imaging.Interop;

namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer
namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FSNs and primary constructors.


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);
}
/// <summary>
/// Remove this AnalyzerItem from it's folder.
/// </summary>
public void Remove()
=> this.AnalyzersFolder.RemoveAnalyzer(this.AnalyzerReference.FullPath);

public AnalyzerReference AnalyzerReference
CyrusNajmabadi marked this conversation as resolved.
Show resolved Hide resolved
{
get { return _analyzerReference; }
}

public override IContextMenuController ContextMenuController
{
get { return _contextMenuController; }
}

public AnalyzersFolderItem AnalyzersFolder
{
get { return _analyzersFolder; }
}

/// <summary>
/// Remove this AnalyzerItem from it's folder.
/// </summary>
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;
}
}
}
8 changes: 4 additions & 4 deletions src/VisualStudio/Core/Impl/SolutionExplorer/BaseItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ internal abstract class BaseItem :
{
public virtual event PropertyChangedEventHandler PropertyChanged { add { } remove { } }

private readonly string _name;
protected readonly string Name;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

part of the equality contract the derived tuypes check.


public BaseItem(string name)
{
_name = name;
Name = name;
}

public IEnumerable<string> Children => [];
Expand All @@ -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<Type> s_supportedPatterns =
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ private BulkObservableCollection<BaseItem> 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)));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved SGItem to be pure data.


return collection;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/// <summary>
/// The analyzer reference that has been found. Once it's been assigned a non-null value, it'll never be assigned null again.
/// </summary>
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;

/// <summary>
/// The analyzer reference that has been found. Once it's been assigned a non-null value, it'll never be assigned null again.
/// </summary>
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));
}
}
Loading
Loading