File: UnusedReferences\Dialog\UnusedReferencesTableProvider.DataSource.cs
Web Access
Project: src\src\VisualStudio\Core\Def\Microsoft.VisualStudio.LanguageServices_pxr0p0dn_wpftmp.csproj (Microsoft.VisualStudio.LanguageServices)
// 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.IO;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.UnusedReferences;
using Microsoft.VisualStudio.Shell.TableManager;
 
namespace Microsoft.VisualStudio.LanguageServices.Implementation.UnusedReferences.Dialog;
 
internal partial class UnusedReferencesTableProvider
{
    internal class UnusedReferencesDataSource : ITableDataSource
    {
        public const string Name = nameof(UnusedReferencesDataSource);
 
        public string SourceTypeIdentifier => Name;
        public string Identifier => Name;
        public string? DisplayName => null;
 
        private ImmutableList<SinkManager> _managers = ImmutableList<SinkManager>.Empty;
        private ImmutableArray<UnusedReferencesEntry> _currentEntries = ImmutableArray<UnusedReferencesEntry>.Empty;
 
        public IDisposable Subscribe(ITableDataSink sink)
        {
            return new SinkManager(this, sink);
        }
 
        public void AddTableData(Solution solution, string projectFilePath, ImmutableArray<ReferenceUpdate> referenceUpdates)
        {
            var solutionName = Path.GetFileName(solution.FilePath);
            var project = solution.Projects.First(project => projectFilePath.Equals(project.FilePath, StringComparison.OrdinalIgnoreCase));
            var entries = referenceUpdates
                .Select(update => new UnusedReferencesEntry(solutionName, project.Name, project.Language, update))
                .ToImmutableArray();
 
            foreach (var manager in _managers)
            {
                manager.Sink.AddEntries(entries);
            }
 
            _currentEntries = _currentEntries.AddRange(entries);
        }
 
        public void RemoveAllTableData()
        {
            foreach (var manager in _managers)
            {
                manager.Sink.RemoveAllEntries();
            }
 
            _currentEntries = ImmutableArray<UnusedReferencesEntry>.Empty;
        }
 
        internal void AddSinkManager(SinkManager manager)
        {
            _managers = _managers.Add(manager);
 
            manager.Sink.AddEntries(_currentEntries);
        }
 
        internal void RemoveSinkManager(SinkManager manager)
        {
            _managers = _managers.Remove(manager);
        }
 
        internal sealed class SinkManager : IDisposable
        {
            internal readonly UnusedReferencesDataSource UnusedReferencesDataSource;
            internal readonly ITableDataSink Sink;
 
            internal SinkManager(UnusedReferencesDataSource unusedReferencesDataSource, ITableDataSink sink)
            {
                UnusedReferencesDataSource = unusedReferencesDataSource;
                Sink = sink;
 
                UnusedReferencesDataSource.AddSinkManager(this);
            }
 
            public void Dispose()
            {
                UnusedReferencesDataSource.RemoveSinkManager(this);
            }
        }
 
        internal class UnusedReferencesEntry : ITableEntry
        {
            public string SolutionName { get; }
            public string ProjectName { get; }
            public string Language { get; }
            public ReferenceUpdate ReferenceUpdate { get; }
 
            public object Identity => ReferenceUpdate;
 
            public UnusedReferencesEntry(string solutionName, string projectName, string language, ReferenceUpdate referenceUpdate)
            {
                SolutionName = solutionName;
                ProjectName = projectName;
                Language = language;
                ReferenceUpdate = referenceUpdate;
            }
 
            public bool TryGetValue(string keyName, out object? content)
            {
                content = null;
 
                switch (keyName)
                {
                    case UnusedReferencesTableKeyNames.SolutionName:
                        content = SolutionName;
                        break;
                    case UnusedReferencesTableKeyNames.ProjectName:
                        content = ProjectName;
                        break;
                    case UnusedReferencesTableKeyNames.Language:
                        content = Language;
                        break;
                    case UnusedReferencesTableKeyNames.ReferenceType:
                        content = ReferenceUpdate.ReferenceInfo.ReferenceType;
                        break;
                    case UnusedReferencesTableKeyNames.ReferenceName:
                        // For Project and Assembly references, use the file name instead of overwhelming the user with the full path.
                        content = ReferenceUpdate.ReferenceInfo.ReferenceType != ReferenceType.Package
                            ? Path.GetFileName(ReferenceUpdate.ReferenceInfo.ItemSpecification)
                            : ReferenceUpdate.ReferenceInfo.ItemSpecification;
                        break;
                    case UnusedReferencesTableKeyNames.UpdateAction:
                        content = ReferenceUpdate.Action;
                        break;
                }
 
                return content != null;
            }
 
            public bool CanSetValue(string keyName)
            {
                return keyName == UnusedReferencesTableKeyNames.UpdateAction;
            }
 
            public bool TrySetValue(string keyName, object content)
            {
                if (keyName != UnusedReferencesTableKeyNames.UpdateAction || content is not UpdateAction action)
                {
                    return false;
                }
 
                ReferenceUpdate.Action = action;
                return true;
            }
        }
    }
}