// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.Navigation;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.FindReferences
{
internal abstract partial class AbstractFindReferencesService
{
///
/// Forwards IFindReferencesProgress calls to a FindRefrencesContext instance.
///
private class ProgressAdapter : ForegroundThreadAffinitizedObject, IStreamingFindReferencesProgress
{
private readonly Solution _solution;
private readonly IFindUsagesContext _context;
///
/// 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 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.
///
private readonly ConcurrentDictionary _definitionToItem =
new ConcurrentDictionary(MetadataUnifyingEquivalenceComparer.Instance);
private readonly Func _definitionFactory;
public ProgressAdapter(Solution solution, IFindUsagesContext context)
{
_solution = solution;
_context = context;
_definitionFactory = s => s.ToDefinitionItem(solution);
}
// Do nothing functions. The streaming far service doesn't care about
// any of these.
public Task OnStartedAsync() => SpecializedTasks.EmptyTask;
public Task OnCompletedAsync() => SpecializedTasks.EmptyTask;
public Task OnFindInDocumentStartedAsync(Document document) => SpecializedTasks.EmptyTask;
public Task OnFindInDocumentCompletedAsync(Document document) => SpecializedTasks.EmptyTask;
// Simple context forwarding functions.
public Task ReportProgressAsync(int current, int maximum) =>
_context.ReportProgressAsync(current, maximum);
// 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 DefinitionItem GetDefinitionItem(SymbolAndProjectId definition)
{
return _definitionToItem.GetOrAdd(definition.Symbol, _definitionFactory);
}
public Task OnDefinitionFoundAsync(SymbolAndProjectId definition)
{
return _context.OnDefinitionFoundAsync(GetDefinitionItem(definition));
}
public async Task OnReferenceFoundAsync(SymbolAndProjectId definition, ReferenceLocation location)
{
// Ignore duplicate locations. We don't want to clutter the UI with them.
if (location.IsDuplicateReferenceLocation)
{
return;
}
var referenceItem = location.TryCreateSourceReferenceItem(
GetDefinitionItem(definition));
if (referenceItem != null)
{
await _context.OnReferenceFoundAsync(referenceItem).ConfigureAwait(false);
}
}
public async Task CallThirdPartyExtensionsAsync()
{
var factory = _solution.Workspace.Services.GetService();
foreach (var definition in _definitionToItem.Keys)
{
var item = factory.GetThirdPartyDefinitionItem(_solution, definition);
if (item != null)
{
// ConfigureAwait(true) because we want to come back on the
// same thread after calling into extensions.
await _context.OnDefinitionFoundAsync(item).ConfigureAwait(true);
}
}
}
}
}
}