File: SolutionExplorer\AnalyzerItemTracker.cs
Web Access
Project: src\src\VisualStudio\Core\Impl\Microsoft.VisualStudio.LanguageServices.Implementation.csproj (Microsoft.VisualStudio.LanguageServices.Implementation)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections.Immutable;
using System.ComponentModel.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Roslyn.Utilities;
 
namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer;
 
/// <summary>
/// This class listens to selection change events, and tracks which, if any, of our
/// <see cref="AnalyzerItem"/> or <see cref="AnalyzersFolderItem"/> is selected.
/// </summary>
[Export]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class AnalyzerItemsTracker(IThreadingContext threadingContext) : IVsSelectionEvents
{
    private readonly IThreadingContext _threadingContext = threadingContext;
 
    private IVsMonitorSelection? _vsMonitorSelection = null;
    private uint _selectionEventsCookie = 0;
 
    public event EventHandler? SelectedHierarchyItemChanged;
 
    public async Task RegisterAsync(IAsyncServiceProvider serviceProvider, CancellationToken cancellationToken)
    {
        _vsMonitorSelection ??= await serviceProvider.GetServiceAsync<SVsShellMonitorSelection, IVsMonitorSelection>(throwOnFailure: false, cancellationToken).ConfigureAwait(false);
        await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
        _vsMonitorSelection?.AdviseSelectionEvents(this, out _selectionEventsCookie);
    }
 
    public void Unregister()
    {
        _vsMonitorSelection?.UnadviseSelectionEvents(_selectionEventsCookie);
    }
 
    public IVsHierarchy? SelectedHierarchy { get; private set; }
    public uint SelectedItemId { get; private set; } = VSConstants.VSITEMID_NIL;
    public AnalyzersFolderItem? SelectedFolder { get; private set; }
    public ImmutableArray<AnalyzerItem> SelectedAnalyzerItems { get; private set; } = [];
    public ImmutableArray<DiagnosticItem> SelectedDiagnosticItems { get; private set; } = [];
 
    int IVsSelectionEvents.OnCmdUIContextChanged(uint dwCmdUICookie, int fActive)
    {
        return VSConstants.S_OK;
    }
 
    int IVsSelectionEvents.OnElementValueChanged(uint elementid, object varValueOld, object varValueNew)
    {
        return VSConstants.S_OK;
    }
 
    int IVsSelectionEvents.OnSelectionChanged(
        IVsHierarchy pHierOld,
        uint itemidOld,
        IVsMultiItemSelect pMISOld,
        ISelectionContainer pSCOld,
        IVsHierarchy pHierNew,
        uint itemidNew,
        IVsMultiItemSelect pMISNew,
        ISelectionContainer pSCNew)
    {
        var oldSelectedHierarchy = this.SelectedHierarchy;
        var oldSelectedItemId = this.SelectedItemId;
 
        this.SelectedHierarchy = pHierNew;
        this.SelectedItemId = itemidNew;
 
        var selectedObjects = GetSelectedObjects(pSCNew);
 
        this.SelectedAnalyzerItems = [.. selectedObjects
            .OfType<AnalyzerItem.BrowseObject>()
            .Select(b => b.AnalyzerItem)];
 
        this.SelectedFolder = selectedObjects
            .OfType<AnalyzersFolderItem.BrowseObject>()
            .Select(b => b.Folder)
            .FirstOrDefault();
 
        this.SelectedDiagnosticItems = [.. selectedObjects
            .OfType<DiagnosticItem.BrowseObject>()
            .Select(b => b.DiagnosticItem)];
 
        if (!object.ReferenceEquals(oldSelectedHierarchy, this.SelectedHierarchy) ||
            oldSelectedItemId != this.SelectedItemId)
        {
            this.SelectedHierarchyItemChanged?.Invoke(this, EventArgs.Empty);
        }
 
        return VSConstants.S_OK;
    }
 
    private static object[] GetSelectedObjects(ISelectionContainer? selectionContainer)
    {
        if (selectionContainer == null)
        {
            return [];
        }
 
        if (selectionContainer.CountObjects((uint)Constants.GETOBJS_SELECTED, out var selectedObjectCount) < 0 || selectedObjectCount == 0)
        {
            return [];
        }
 
        var selectedObjects = new object[selectedObjectCount];
        if (selectionContainer.GetObjects((uint)Constants.GETOBJS_SELECTED, selectedObjectCount, selectedObjects) < 0)
        {
            return [];
        }
 
        return selectedObjects;
    }
}