@@ -48,7 +48,7 @@ public class EndpointHandler<TRequest, TResponse> : EndpointHandler
4848 {
4949 private readonly CompositionHost _host ;
5050 private readonly IPredicateHandler _languagePredicateHandler ;
51- private readonly Lazy < Task < Dictionary < string , ExportHandler < TRequest , TResponse > > > > _exports ;
51+ private readonly Lazy < Task < Dictionary < string , ExportHandler < TRequest , TResponse > [ ] > > > _exports ;
5252 private readonly OmniSharpWorkspace _workspace ;
5353 private readonly bool _hasLanguageProperty ;
5454 private readonly bool _hasFileNameProperty ;
@@ -71,10 +71,10 @@ public EndpointHandler(IPredicateHandler languagePredicateHandler, CompositionHo
7171 _canBeAggregated = typeof ( IAggregateResponse ) . IsAssignableFrom ( metadata . ResponseType ) ;
7272 _updateBufferHandler = updateBufferHandler ;
7373
74- _exports = new Lazy < Task < Dictionary < string , ExportHandler < TRequest , TResponse > > > > ( ( ) => LoadExportHandlers ( handlers ) ) ;
74+ _exports = new Lazy < Task < Dictionary < string , ExportHandler < TRequest , TResponse > [ ] > > > ( ( ) => LoadExportHandlers ( handlers ) ) ;
7575 }
7676
77- private Task < Dictionary < string , ExportHandler < TRequest , TResponse > > > LoadExportHandlers ( IEnumerable < Lazy < IRequestHandler , OmniSharpRequestHandlerMetadata > > handlers )
77+ private Task < Dictionary < string , ExportHandler < TRequest , TResponse > [ ] > > LoadExportHandlers ( IEnumerable < Lazy < IRequestHandler , OmniSharpRequestHandlerMetadata > > handlers )
7878 {
7979 var interfaceHandlers = handlers
8080 . Select ( export => new RequestHandlerExportHandler < TRequest , TResponse > ( export . Metadata . Language , ( IRequestHandler < TRequest , TResponse > ) export . Value ) )
@@ -84,9 +84,13 @@ private Task<Dictionary<string, ExportHandler<TRequest, TResponse>>> LoadExportH
8484 . Select ( plugin => new PluginExportHandler < TRequest , TResponse > ( EndpointName , plugin ) )
8585 . Cast < ExportHandler < TRequest , TResponse > > ( ) ;
8686
87+ // Group handlers by language and sort each group for consistency
8788 return Task . FromResult ( interfaceHandlers
88- . Concat ( plugins )
89- . ToDictionary ( export => export . Language ) ) ;
89+ . Concat ( plugins )
90+ . GroupBy ( export => export . Language , StringComparer . OrdinalIgnoreCase )
91+ . ToDictionary (
92+ group => group . Key ,
93+ group => group . OrderBy ( g => g ) . ToArray ( ) ) ) ;
9094 }
9195
9296 public string EndpointName { get ; }
@@ -142,18 +146,88 @@ private Task<object> HandleLanguageRequest(string language, TRequest request, Re
142146 {
143147 if ( ! string . IsNullOrEmpty ( language ) )
144148 {
145- return HandleSingleRequest ( language , request , packet ) ;
149+ return HandleRequestForLanguage ( language , request , packet ) ;
146150 }
147151
148152 return HandleAllRequest ( request , packet ) ;
149153 }
150154
151- private async Task < object > HandleSingleRequest ( string language , TRequest request , RequestPacket packet )
155+ private async Task < IAggregateResponse > AggregateResponsesFromLanguageHandlers ( ExportHandler < TRequest , TResponse > [ ] handlers , TRequest request )
156+ {
157+ if ( ! _canBeAggregated )
158+ {
159+ throw new NotSupportedException ( $ "Must be able to aggregate responses from all handlers for { EndpointName } ") ;
160+ }
161+
162+ IAggregateResponse aggregateResponse = null ;
163+
164+ if ( handlers . Length == 1 )
165+ {
166+ var response = handlers [ 0 ] . Handle ( request ) ;
167+ return ( IAggregateResponse ) await response ;
168+ }
169+ else
170+ {
171+ var responses = new List < Task < TResponse > > ( ) ;
172+ foreach ( var handler in handlers )
173+ {
174+ responses . Add ( handler . Handle ( request ) ) ;
175+ }
176+
177+ foreach ( IAggregateResponse response in await Task . WhenAll ( responses ) )
178+ {
179+ if ( aggregateResponse != null )
180+ {
181+ aggregateResponse = aggregateResponse . Merge ( response ) ;
182+ }
183+ else
184+ {
185+ aggregateResponse = response ;
186+ }
187+ }
188+ }
189+
190+ return aggregateResponse ;
191+ }
192+
193+ private async Task < object > GetFirstNotEmptyResponseFromHandlers ( ExportHandler < TRequest , TResponse > [ ] handlers , TRequest request )
194+ {
195+ var responses = new List < Task < TResponse > > ( ) ;
196+ foreach ( var handler in handlers )
197+ {
198+ responses . Add ( handler . Handle ( request ) ) ;
199+ }
200+
201+ foreach ( object response in await Task . WhenAll ( responses ) )
202+ {
203+ var canBeEmptyResponse = response as ICanBeEmptyResponse ;
204+ if ( canBeEmptyResponse != null )
205+ {
206+ if ( ! canBeEmptyResponse . IsEmpty )
207+ {
208+ return response ;
209+ }
210+ }
211+ else if ( response != null )
212+ {
213+ return response ;
214+ }
215+ }
216+
217+ return null ;
218+ }
219+
220+ private async Task < object > HandleRequestForLanguage ( string language , TRequest request , RequestPacket packet )
152221 {
153222 var exports = await _exports . Value ;
154- if ( exports . TryGetValue ( language , out var handler ) )
223+ if ( exports . TryGetValue ( language , out var handlers ) )
155224 {
156- return await handler . Handle ( request ) ;
225+ if ( _canBeAggregated )
226+ {
227+ return await AggregateResponsesFromLanguageHandlers ( handlers , request ) ;
228+ }
229+
230+ return await GetFirstNotEmptyResponseFromHandlers ( handlers , request ) ;
157231 }
158232
159233 throw new NotSupportedException ( $ "{ language } does not support { EndpointName } ") ;
@@ -169,11 +243,10 @@ private async Task<object> HandleAllRequest(TRequest request, RequestPacket pack
169243 var exports = await _exports . Value ;
170244
171245 IAggregateResponse aggregateResponse = null ;
172-
173- var responses = new List < Task < TResponse > > ( ) ;
174- foreach ( var handler in exports . Values )
246+ var responses = new List < Task < IAggregateResponse > > ( ) ;
247+ foreach ( var export in exports )
175248 {
176- responses . Add ( handler . Handle ( request ) ) ;
249+ responses . Add ( AggregateResponsesFromLanguageHandlers ( export . Value , request ) ) ;
177250 }
178251
179252 foreach ( IAggregateResponse exportResponse in await Task . WhenAll ( responses ) )
0 commit comments