File: QuickInfo\AbstractEmbeddedLanguageQuickInfoProvider.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;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.EmbeddedLanguages;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Shared.Extensions;
 
namespace Microsoft.CodeAnalysis.QuickInfo;
 
internal abstract class AbstractEmbeddedLanguageQuickInfoProvider : CommonQuickInfoProvider
{
    private readonly EmbeddedLanguageProviderFeatureService _embeddedLanguageProviderFeature;
 
    public AbstractEmbeddedLanguageQuickInfoProvider(
        string languageName,
        EmbeddedLanguageInfo info,
        ISyntaxKinds syntaxKinds,
        IEnumerable<Lazy<IEmbeddedLanguageQuickInfoProvider, EmbeddedLanguageMetadata>> allServices)
    {
        _embeddedLanguageProviderFeature = new EmbeddedLanguageProviderFeatureService(languageName, info, syntaxKinds, allServices);
    }
 
    protected override async Task<QuickInfoItem?> BuildQuickInfoAsync(QuickInfoContext context, SyntaxToken token)
    {
        if (!_embeddedLanguageProviderFeature.SyntaxTokenKinds.Contains(token.RawKind))
            return null;
 
        var semanticModel = await context.Document.GetRequiredSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
 
        var quickInfoProviders = _embeddedLanguageProviderFeature.GetServices(semanticModel, token, context.CancellationToken);
        foreach (var quickInfoProvider in quickInfoProviders)
        {
            // If this service added values then need to check the other ones.
            var result = quickInfoProvider.Value.GetQuickInfo(context, semanticModel, token);
            if (result != null)
                return result;
        }
 
        return null;
    }
 
    protected override Task<QuickInfoItem?> BuildQuickInfoAsync(CommonQuickInfoContext context, SyntaxToken token)
    {
        // Not implemented as this entrypoint appears to be dead code.
        throw new NotImplementedException();
    }
 
    /// <summary>
    /// A derivation of <see cref="AbstractEmbeddedLanguageFeatureService{TService}"/> so we can fetch providers. Normally, our providers implement an interface,
    /// and the combined provider directly inherits from <see cref="AbstractEmbeddedLanguageFeatureService{TService}"/>. Unfortunately Quick Info is a bit different:
    /// there is a class (not an interface) and a private base class that also defines some logic that we need to reuse. Since we don't
    /// have multiple inheritance, we'll create a separate class here and delegate to the protected methods. We can remove this if we
    /// switch Quick Info over to a pattern like the rest of our features.
    /// </summary>
    private class EmbeddedLanguageProviderFeatureService :
        AbstractEmbeddedLanguageFeatureService<IEmbeddedLanguageQuickInfoProvider>
    {
        public EmbeddedLanguageProviderFeatureService(string languageName, EmbeddedLanguageInfo info, ISyntaxKinds syntaxKinds, IEnumerable<Lazy<IEmbeddedLanguageQuickInfoProvider, EmbeddedLanguageMetadata>> allServices)
            : base(languageName, info, syntaxKinds, allServices)
        {
        }
 
        public new ImmutableArray<Lazy<IEmbeddedLanguageQuickInfoProvider, EmbeddedLanguageMetadata>> GetServices(
            SemanticModel semanticModel,
            SyntaxToken token,
            CancellationToken cancellationToken)
        {
            return base.GetServices(semanticModel, token, cancellationToken);
        }
 
        public new HashSet<int> SyntaxTokenKinds => base.SyntaxTokenKinds;
    }
}