File: Completion\AggregateCompletionItemResolver.cs
Web Access
Project: src\src\Razor\src\Razor\src\Microsoft.CodeAnalysis.Razor.Workspaces\Microsoft.CodeAnalysis.Razor.Workspaces.csproj (Microsoft.CodeAnalysis.Razor.Workspaces)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CodeAnalysis.Razor.Tooltip;
 
namespace Microsoft.CodeAnalysis.Razor.Completion;
 
internal class AggregateCompletionItemResolver(IEnumerable<CompletionItemResolver> completionItemResolvers, ILoggerFactory loggerFactory)
{
    private readonly ImmutableArray<CompletionItemResolver> _completionItemResolvers = [.. completionItemResolvers];
    private readonly ILogger _logger = loggerFactory.GetOrCreateLogger<AggregateCompletionItemResolver>();
 
    public async Task<VSInternalCompletionItem?> ResolveAsync(
        VSInternalCompletionItem item,
        VSInternalCompletionList containingCompletionList,
        ICompletionResolveContext originalRequestContext,
        VSInternalClientCapabilities clientCapabilities,
        IComponentAvailabilityService componentAvailabilityService,
        CancellationToken cancellationToken)
    {
        using var completionItemResolverTasks = new PooledArrayBuilder<Task<VSInternalCompletionItem?>>(_completionItemResolvers.Length);
 
        foreach (var completionItemResolver in _completionItemResolvers)
        {
            try
            {
                var task = completionItemResolver.ResolveAsync(item, containingCompletionList, originalRequestContext, clientCapabilities, componentAvailabilityService, cancellationToken);
                completionItemResolverTasks.Add(task);
            }
            catch (Exception ex) when (ex is not TaskCanceledException)
            {
                _logger.LogError(ex, $"Resolving completion item failed synchronously unexpectedly.");
            }
        }
 
        // We don't currently handle merging completion items because it's very rare for more than one resolution to take place.
        // Instead we'll prioritized the last completion item resolved.
        VSInternalCompletionItem? lastResolved = null;
        foreach (var completionItemResolverTask in completionItemResolverTasks)
        {
            try
            {
                var resolvedCompletionItem = await completionItemResolverTask.ConfigureAwait(false);
                if (resolvedCompletionItem is not null)
                {
                    lastResolved = resolvedCompletionItem;
                }
 
                cancellationToken.ThrowIfCancellationRequested();
            }
            catch (Exception ex) when (ex is not OperationCanceledException)
            {
                _logger.LogError(ex, $"Resolving completion item failed unexpectedly.");
            }
        }
 
        return lastResolved;
    }
}