File: QuickInfo\QuickInfoServiceWithProviders.cs
Web Access
Project: src\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.
 
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Extensions;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.QuickInfo;
 
/// <summary>
/// Base class for <see cref="QuickInfoService"/>'s that delegate to <see cref="QuickInfoProvider"/>'s.
/// </summary>
internal abstract class QuickInfoServiceWithProviders : QuickInfoService
{
    private readonly LanguageServices _services;
    private ImmutableArray<QuickInfoProvider> _providers;
 
    protected QuickInfoServiceWithProviders(LanguageServices services)
    {
        _services = services;
    }
 
    private ImmutableArray<QuickInfoProvider> GetProviders()
    {
        if (_providers.IsDefault)
        {
            var mefExporter = _services.SolutionServices.ExportProvider;
 
            var providers = ExtensionOrderer
                .Order(mefExporter.GetExports<QuickInfoProvider, QuickInfoProviderMetadata>()
                    .Where(lz => lz.Metadata.Language == _services.Language))
                .Select(lz => lz.Value)
                .ToImmutableArray();
 
            ImmutableInterlocked.InterlockedCompareExchange(ref _providers, providers, default);
        }
 
        return _providers;
    }
 
    internal override async Task<QuickInfoItem?> GetQuickInfoAsync(Document document, int position, SymbolDescriptionOptions options, CancellationToken cancellationToken)
    {
        var extensionManager = _services.SolutionServices.GetRequiredService<IExtensionManager>();
 
        // returns the first non-empty quick info found (based on provider order)
        foreach (var provider in GetProviders())
        {
            var info = await extensionManager.PerformFunctionAsync(
                provider,
                cancellationToken =>
                {
                    var context = new QuickInfoContext(document, position, options, cancellationToken);
 
                    return provider.GetQuickInfoAsync(context);
                },
                defaultValue: null,
                cancellationToken).ConfigureAwait(false);
            if (info != null)
                return info;
        }
 
        return null;
    }
 
    internal async Task<QuickInfoItem?> GetQuickInfoAsync(SemanticModel semanticModel, int position, SymbolDescriptionOptions options, CancellationToken cancellationToken)
    {
        var extensionManager = _services.SolutionServices.GetRequiredService<IExtensionManager>();
 
        // returns the first non-empty quick info found (based on provider order)
        foreach (var provider in GetProviders().OfType<CommonQuickInfoProvider>())
        {
            var info = await extensionManager.PerformFunctionAsync(
                provider,
                cancellationToken =>
                {
                    var context = new CommonQuickInfoContext(_services.SolutionServices, semanticModel, position, options, cancellationToken);
 
                    return provider.GetQuickInfoAsync(context);
                },
                defaultValue: null,
                cancellationToken).ConfigureAwait(false);
            if (info != null)
                return info;
        }
 
        return null;
    }
}