File: FindUsages\AbstractFindUsagesService.ProgressAdapter.cs
Web Access
Project: ..\..\..\src\Features\Core\Portable\Microsoft.CodeAnalysis.Features.csproj (Microsoft.CodeAnalysis.Features)
// 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.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Classification;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.Navigation;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.FindUsages
{
    internal abstract partial class AbstractFindUsagesService
    {
        /// <summary>
        /// Forwards <see cref="IStreamingFindLiteralReferencesProgress"/> calls to an
        /// <see cref="IFindUsagesContext"/> instance.
        /// </summary>
        private sealed class FindLiteralsProgressAdapter : IStreamingFindLiteralReferencesProgress
        {
            private readonly IFindUsagesContext _context;
            private readonly DefinitionItem _definition;
 
            public IStreamingProgressTracker ProgressTracker
                => _context.ProgressTracker;
 
            public FindLiteralsProgressAdapter(
                IFindUsagesContext context, DefinitionItem definition)
            {
                _context = context;
                _definition = definition;
            }
 
            public async ValueTask OnReferenceFoundAsync(Document document, TextSpan span, CancellationToken cancellationToken)
            {
                var options = await _context.GetOptionsAsync(document.Project.Language, cancellationToken).ConfigureAwait(false);
 
                var documentSpan = await ClassifiedSpansAndHighlightSpanFactory.GetClassifiedDocumentSpanAsync(
                    document, span, options.ClassificationOptions, cancellationToken).ConfigureAwait(false);
 
                await _context.OnReferenceFoundAsync(
                    new SourceReferenceItem(_definition, documentSpan, SymbolUsageInfo.None), cancellationToken).ConfigureAwait(false);
            }
        }
 
        /// <summary>
        /// Forwards IFindReferencesProgress calls to an IFindUsagesContext instance.
        /// </summary>
        private sealed class FindReferencesProgressAdapter : IStreamingFindReferencesProgress
        {
            private readonly Solution _solution;
            private readonly IFindUsagesContext _context;
            private readonly FindReferencesSearchOptions _options;
 
            /// <summary>
            /// We will hear about definition symbols many times while performing FAR.  We'll
            /// here about it first when the FAR engine discovers the symbol, and then for every
            /// reference it finds to the symbol.  However, we only want to create and pass along
            /// a single instance of <see cref="INavigableItem" /> for that definition no matter
            /// how many times we see it.
            /// 
            /// This dictionary allows us to make that mapping once and then keep it around for
            /// all future callbacks.
            /// </summary>
            private readonly Dictionary<SymbolGroup, DefinitionItem> _definitionToItem = new();
 
            private readonly SemaphoreSlim _gate = new(initialCount: 1);
 
            public IStreamingProgressTracker ProgressTracker
                => _context.ProgressTracker;
 
            public FindReferencesProgressAdapter(
                Solution solution, IFindUsagesContext context, FindReferencesSearchOptions options)
            {
                _solution = solution;
                _context = context;
                _options = options;
            }
 
            // Do nothing functions.  The streaming far service doesn't care about
            // any of these.
            public ValueTask OnStartedAsync(CancellationToken cancellationToken) => default;
            public ValueTask OnCompletedAsync(CancellationToken cancellationToken) => default;
            public ValueTask OnFindInDocumentStartedAsync(Document document, CancellationToken cancellationToken) => default;
            public ValueTask OnFindInDocumentCompletedAsync(Document document, CancellationToken cancellationToken) => default;
 
            // More complicated forwarding functions.  These need to map from the symbols
            // used by the FAR engine to the INavigableItems used by the streaming FAR 
            // feature.
 
            private async ValueTask<DefinitionItem> GetDefinitionItemAsync(SymbolGroup group, CancellationToken cancellationToken)
            {
                using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
                {
                    if (!_definitionToItem.TryGetValue(group, out var definitionItem))
                    {
                        definitionItem = await group.ToClassifiedDefinitionItemAsync(
                            _context,
                            _solution,
                            _options,
                            isPrimary: _definitionToItem.Count == 0,
                            includeHiddenLocations: false,
                            cancellationToken).ConfigureAwait(false);
 
                        _definitionToItem[group] = definitionItem;
                    }
 
                    return definitionItem;
                }
            }
 
            public async ValueTask OnDefinitionFoundAsync(SymbolGroup group, CancellationToken cancellationToken)
            {
                var definitionItem = await GetDefinitionItemAsync(group, cancellationToken).ConfigureAwait(false);
                await _context.OnDefinitionFoundAsync(definitionItem, cancellationToken).ConfigureAwait(false);
            }
 
            public async ValueTask OnReferenceFoundAsync(SymbolGroup group, ISymbol definition, ReferenceLocation location, CancellationToken cancellationToken)
            {
                var definitionItem = await GetDefinitionItemAsync(group, cancellationToken).ConfigureAwait(false);
                var referenceItem = await location.TryCreateSourceReferenceItemAsync(
                    _context,
                    definitionItem,
                    includeHiddenLocations: false,
                    cancellationToken).ConfigureAwait(false);
 
                if (referenceItem != null)
                    await _context.OnReferenceFoundAsync(referenceItem, cancellationToken).ConfigureAwait(false);
            }
        }
    }
}