File: FindSymbols\StreamingProgressCollector.cs
Web Access
Project: ..\..\..\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces)
// 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.
 
#nullable disable
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.FindSymbols
{
    /// <summary>
    /// Collects all the <see cref="ISymbol"/> definitions and <see cref="ReferenceLocation"/> 
    /// references that are reported independently and packages them up into the final list
    /// of <see cref="ReferencedSymbol" />.  This is used by the old non-streaming Find-References
    /// APIs to return all the results at the end of the operation, as opposed to broadcasting
    /// the results as they are found.
    /// </summary>
    internal class StreamingProgressCollector : IStreamingFindReferencesProgress
    {
        private readonly object _gate = new();
        private readonly IStreamingFindReferencesProgress _underlyingProgress;
 
        private readonly Dictionary<ISymbol, List<ReferenceLocation>> _symbolToLocations = new();
 
        public IStreamingProgressTracker ProgressTracker => _underlyingProgress.ProgressTracker;
 
        public StreamingProgressCollector()
            : this(NoOpStreamingFindReferencesProgress.Instance)
        {
        }
 
        public StreamingProgressCollector(
            IStreamingFindReferencesProgress underlyingProgress)
        {
            _underlyingProgress = underlyingProgress;
        }
 
        public ImmutableArray<ReferencedSymbol> GetReferencedSymbols()
        {
            lock (_gate)
            {
                using var _ = ArrayBuilder<ReferencedSymbol>.GetInstance(out var result);
                foreach (var (symbol, locations) in _symbolToLocations)
                    result.Add(new ReferencedSymbol(symbol, locations.ToImmutableArray()));
 
                return result.ToImmutable();
            }
        }
 
        public ValueTask OnStartedAsync(CancellationToken cancellationToken) => _underlyingProgress.OnStartedAsync(cancellationToken);
        public ValueTask OnCompletedAsync(CancellationToken cancellationToken) => _underlyingProgress.OnCompletedAsync(cancellationToken);
 
        public ValueTask OnFindInDocumentCompletedAsync(Document document, CancellationToken cancellationToken) => _underlyingProgress.OnFindInDocumentCompletedAsync(document, cancellationToken);
        public ValueTask OnFindInDocumentStartedAsync(Document document, CancellationToken cancellationToken) => _underlyingProgress.OnFindInDocumentStartedAsync(document, cancellationToken);
 
        public ValueTask OnDefinitionFoundAsync(SymbolGroup group, CancellationToken cancellationToken)
        {
            try
            {
                lock (_gate)
                {
                    foreach (var definition in group.Symbols)
                        _symbolToLocations[definition] = new List<ReferenceLocation>();
                }
 
                return _underlyingProgress.OnDefinitionFoundAsync(group, cancellationToken);
            }
            catch (Exception ex) when (FatalError.ReportAndPropagateUnlessCanceled(ex, cancellationToken))
            {
                throw ExceptionUtilities.Unreachable();
            }
        }
 
        public ValueTask OnReferenceFoundAsync(SymbolGroup group, ISymbol definition, ReferenceLocation location, CancellationToken cancellationToken)
        {
            lock (_gate)
            {
                _symbolToLocations[definition].Add(location);
            }
 
            return _underlyingProgress.OnReferenceFoundAsync(group, definition, location, cancellationToken);
        }
    }
}