|
6 | 6 | using System.Collections.Generic; |
7 | 7 | using System.Collections.Immutable; |
8 | 8 | using System.Composition; |
9 | | -using System.Linq; |
10 | | -using System.Runtime.CompilerServices; |
11 | 9 | using System.Threading; |
12 | | -using System.Threading.Tasks; |
13 | | -using Microsoft.CodeAnalysis; |
14 | | -using Microsoft.CodeAnalysis.ErrorReporting; |
15 | 10 | using Microsoft.CodeAnalysis.ExternalAccess.Copilot.Completion; |
16 | 11 | using Microsoft.CodeAnalysis.Host.Mef; |
17 | | -using Microsoft.VisualStudio.Threading; |
| 12 | +using Microsoft.CodeAnalysis.Shared.Utilities; |
18 | 13 |
|
19 | 14 | namespace Microsoft.CodeAnalysis.ExternalAccess.Copilot.Internal.Completion; |
20 | 15 |
|
21 | | -[Shared] |
22 | | -[Export(typeof(ICSharpCopilotContextProviderService))] |
23 | | -internal sealed class CSharpContextProviderService : ICSharpCopilotContextProviderService |
| 16 | +[Export(typeof(ICSharpCopilotContextProviderService)), Shared] |
| 17 | +[method: ImportingConstructor] |
| 18 | +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] |
| 19 | +internal sealed class CSharpContextProviderService([ImportMany] IEnumerable<IContextProvider> providers) |
| 20 | + : ICSharpCopilotContextProviderService |
24 | 21 | { |
25 | | - // Exposed for testing |
26 | | - public ImmutableArray<IContextProvider> Providers { get; } |
27 | | - |
28 | | - [ImportingConstructor] |
29 | | - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] |
30 | | - public CSharpContextProviderService([ImportMany] IEnumerable<IContextProvider> providers) |
31 | | - { |
32 | | - Providers = providers.ToImmutableArray(); |
33 | | - } |
34 | | - |
35 | | - public async IAsyncEnumerable<IContextItem> GetContextItemsAsync(Document document, int position, IReadOnlyDictionary<string, object> activeExperiments, [EnumeratorCancellation] CancellationToken cancellationToken) |
36 | | - { |
37 | | - var queue = new AsyncQueue<IContextItem>(); |
38 | | - var tasks = this.Providers.Select(provider => Task.Run(async () => |
39 | | - { |
40 | | - try |
41 | | - { |
42 | | - await provider.ProvideContextItemsAsync(document, position, activeExperiments, ProvideItemsAsync, cancellationToken).ConfigureAwait(false); |
43 | | - } |
44 | | - catch (Exception exception) when (FatalError.ReportAndCatchUnlessCanceled(exception, ErrorSeverity.General)) |
45 | | - { |
46 | | - } |
47 | | - }, |
48 | | - cancellationToken)); |
49 | | - |
50 | | - // Let all providers run in parallel in the background, so we can steam results as they come in. |
51 | | - // Complete the queue when all providers are done. |
52 | | - _ = Task.WhenAll(tasks) |
53 | | - .ContinueWith((_, __) => queue.Complete(), |
54 | | - null, |
55 | | - cancellationToken, |
56 | | - TaskContinuationOptions.ExecuteSynchronously, |
57 | | - TaskScheduler.Default); |
58 | | - |
59 | | - while (true) |
60 | | - { |
61 | | - IContextItem item; |
62 | | - try |
63 | | - { |
64 | | - item = await queue.DequeueAsync(cancellationToken).ConfigureAwait(false); |
65 | | - } |
66 | | - catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested) |
67 | | - { |
68 | | - // Dequeue is cancelled because the queue is empty and completed, we can break out of the loop. |
69 | | - break; |
70 | | - } |
71 | | - |
72 | | - yield return item; |
73 | | - } |
74 | | - |
75 | | - ValueTask ProvideItemsAsync(ImmutableArray<IContextItem> items, CancellationToken cancellationToken) |
76 | | - { |
77 | | - foreach (var item in items) |
78 | | - { |
79 | | - queue.Enqueue(item); |
80 | | - } |
81 | | - |
82 | | - return default; |
83 | | - } |
84 | | - } |
| 22 | + private readonly ImmutableArray<IContextProvider> _providers = [.. providers]; |
| 23 | + |
| 24 | + public IAsyncEnumerable<IContextItem> GetContextItemsAsync(Document document, int position, IReadOnlyDictionary<string, object> activeExperiments, CancellationToken cancellationToken) |
| 25 | + => ProducerConsumer<IContextItem>.RunParallelStreamAsync( |
| 26 | + _providers, |
| 27 | + static async (provider, callback, args, cancellationToken) => |
| 28 | + await provider.ProvideContextItemsAsync( |
| 29 | + args.document, args.position, args.activeExperiments, |
| 30 | + (items, cancellationToken) => |
| 31 | + { |
| 32 | + foreach (var item in items) |
| 33 | + callback(item); |
| 34 | + |
| 35 | + return default; |
| 36 | + }, cancellationToken).ConfigureAwait(false), |
| 37 | + args: (document, position, activeExperiments), |
| 38 | + cancellationToken); |
85 | 39 | } |
0 commit comments