|
// 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.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Extensions;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Shared.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())
{
try
{
if (!extensionManager.IsDisabled(provider))
{
var context = new QuickInfoContext(document, position, options, cancellationToken);
var info = await provider.GetQuickInfoAsync(context).ConfigureAwait(false);
if (info != null)
{
return info;
}
}
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception e) when (extensionManager.CanHandleException(provider, e))
{
extensionManager.HandleException(provider, e);
}
}
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>())
{
try
{
if (!extensionManager.IsDisabled(provider))
{
var context = new CommonQuickInfoContext(_services.SolutionServices, semanticModel, position, options, cancellationToken);
var info = await provider.GetQuickInfoAsync(context).ConfigureAwait(false);
if (info != null)
{
return info;
}
}
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception e) when (extensionManager.CanHandleException(provider, e))
{
extensionManager.HandleException(provider, e);
}
}
return null;
}
}
}
|