@@ -176,18 +176,20 @@ int ComputeHashCode()
176
176
}
177
177
}
178
178
179
- public override string ToString ( )
180
- {
181
- string ? [ ] features = new [ ] {
182
- $ "type={ this . DataContextType . ToCode ( ) } ",
183
- this . ServerSideOnly ? "server-side-only" : null ,
184
- this . NamespaceImports . Any ( ) ? "imports=[" + string . Join ( ", " , this . NamespaceImports ) + "]" : null ,
185
- this . ExtensionParameters . Any ( ) ? "ext=[" + string . Join ( ", " , this . ExtensionParameters . Select ( e => e . Identifier + ": " + e . ParameterType . CSharpName ) ) + "]" : null ,
186
- this . BindingPropertyResolvers . Any ( ) ? "resolvers=[" + string . Join ( ", " , this . BindingPropertyResolvers . Select ( s => s . Method ) ) + "]" : null ,
187
- this . Parent != null ? "par=[" + string . Join ( ", " , this . Parents ( ) . Select ( p => p . ToCode ( stripNamespace : true ) ) ) + "]" : null
188
- } ;
189
- return "(" + features . Where ( a => a != null ) . StringJoin ( ", " ) + ")" ;
190
- }
179
+ private string ? [ ] ToStringFeatures ( ) => [
180
+ $ "type={ this . DataContextType . ToCode ( ) } ",
181
+ this . ServerSideOnly ? "server-side-only" : null ,
182
+ this . NamespaceImports . Any ( ) ? "imports=[" + string . Join ( ", " , this . NamespaceImports ) + "]" : null ,
183
+ this . ExtensionParameters . Any ( ) ? "ext=[" + string . Join ( ", " , this . ExtensionParameters . Select ( e => e . Identifier + ": " + e . ParameterType . CSharpName ) ) + "]" : null ,
184
+ this . BindingPropertyResolvers . Any ( ) ? "resolvers=[" + string . Join ( ", " , this . BindingPropertyResolvers . Select ( s => s . Method ) ) + "]" : null ,
185
+ this . Parent != null ? "par=[" + string . Join ( ", " , this . Parents ( ) . Select ( p => p . ToCode ( stripNamespace : true ) ) ) + "]" : null
186
+ ] ;
187
+
188
+ public override string ToString ( ) =>
189
+ "(" + ToStringFeatures ( ) . WhereNotNull ( ) . StringJoin ( ", " ) + ")" ;
190
+
191
+ private string ToStringWithoutParent ( ) =>
192
+ ToStringFeatures ( ) [ ..^ 1 ] . WhereNotNull ( ) . StringJoin ( ", " ) ;
191
193
192
194
193
195
//private static ConditionalWeakTable<DataContextStack, DataContextStack> internCache = new ConditionalWeakTable<DataContextStack, DataContextStack>();
@@ -212,7 +214,7 @@ public static DataContextStack CreateCollectionElement(Type elementType,
212
214
bool serverSideOnly = false )
213
215
{
214
216
var indexParameters = new CollectionElementDataContextChangeAttribute ( 0 ) . GetExtensionParameters ( new ResolvedTypeDescriptor ( elementType . MakeArrayType ( ) ) ) ;
215
- extensionParameters = extensionParameters is null ? indexParameters . ToArray ( ) : extensionParameters . Concat ( indexParameters ) . ToArray ( ) ;
217
+ extensionParameters = [ .. ( extensionParameters ?? [ ] ) , .. indexParameters ] ;
216
218
return DataContextStack . Create (
217
219
elementType , parent ,
218
220
imports : imports ,
@@ -221,5 +223,139 @@ public static DataContextStack CreateCollectionElement(Type elementType,
221
223
serverSideOnly : serverSideOnly
222
224
) ;
223
225
}
226
+
227
+ private static int Difference ( DataContextStack a , DataContextStack b )
228
+ {
229
+ if ( a == b ) return 0 ;
230
+
231
+ var result = 0 ;
232
+ if ( a . DataContextType != b . DataContextType )
233
+ result += 6 ;
234
+
235
+ if ( a . DataContextType . Namespace != b . DataContextType . Namespace )
236
+ result += 2 ;
237
+
238
+ if ( a . DataContextType . Name != b . DataContextType . Name )
239
+ result += 2 ;
240
+
241
+ result += CompareSets ( a . NamespaceImports , b . NamespaceImports ) ;
242
+
243
+ result += CompareSets ( a . ExtensionParameters , b . ExtensionParameters ) ;
244
+
245
+ result += CompareSets ( a . BindingPropertyResolvers , b . BindingPropertyResolvers ) ;
246
+
247
+ if ( a . Parent != b . Parent )
248
+ result += 1 ;
249
+
250
+ return result ;
251
+
252
+
253
+ static int CompareSets < T > ( IEnumerable < T > a , IEnumerable < T > b )
254
+ {
255
+ return a . Union ( b ) . Count ( ) - a . Intersect ( b ) . Count ( ) ;
256
+ }
257
+ }
258
+
259
+ public static ( string a , string b ) [ ] [ ] CompareStacksMessage ( DataContextStack a , DataContextStack b )
260
+ {
261
+ var alignment = StringSimilarity . SequenceAlignment < DataContextStack > (
262
+ a . EnumerableItems ( ) . ToArray ( ) . AsSpan ( ) , b . EnumerableItems ( ) . ToArray ( ) . AsSpan ( ) ,
263
+ Difference ,
264
+ gapCost : 10 ) ;
265
+
266
+ return alignment . Select ( pair => {
267
+ return CompareMessage ( pair . a , pair . b ) ;
268
+ } ) . ToArray ( ) ;
269
+ }
270
+
271
+ /// <summary> Provides a formatted string for two DataContextStacks with aligned fragments used for highlighting. Does not include the parent context. </summary>
272
+ public static ( string a , string b ) [ ] CompareMessage ( DataContextStack ? a , DataContextStack ? b )
273
+ {
274
+ if ( a == null || b == null ) return new [ ] { ( a ? . ToStringWithoutParent ( ) ?? "(missing)" , b ? . ToStringWithoutParent ( ) ?? "(missing)" ) } ;
275
+
276
+ var result = new List < ( string , string ) > ( ) ;
277
+
278
+ void same ( string str ) => result . Add ( ( str , str ) ) ;
279
+ void different ( string ? a , string ? b ) => result . Add ( ( a ?? "" , b ?? "" ) ) ;
280
+
281
+ same ( "type=" ) ;
282
+ if ( a . DataContextType == b . DataContextType )
283
+ same ( a . DataContextType . ToCode ( stripNamespace : true ) ) ;
284
+ else
285
+ {
286
+ different ( a . DataContextType . Namespace , b . DataContextType . Namespace ) ;
287
+ same ( "." ) ;
288
+ different ( a . DataContextType . ToCode ( stripNamespace : true ) , b . DataContextType . ToCode ( stripNamespace : true ) ) ;
289
+ }
290
+
291
+ if ( a . ServerSideOnly || b . ServerSideOnly )
292
+ {
293
+ same ( ", " ) ;
294
+ different ( a . ServerSideOnly ? "server-side-only" : "" , b . ServerSideOnly ? "server-side-only" : "" ) ;
295
+ }
296
+
297
+ if ( a . NamespaceImports . Any ( ) || b . NamespaceImports . Any ( ) )
298
+ {
299
+ same ( ", imports=[" ) ;
300
+ var importsAligned = StringSimilarity . SequenceAlignment (
301
+ a . NamespaceImports . AsSpan ( ) , b . NamespaceImports . AsSpan ( ) ,
302
+ ( a , b ) => a . Equals ( b ) ? 0 :
303
+ a . Namespace == b . Namespace || a . Alias == b . Alias ? 1 :
304
+ 3 ,
305
+ gapCost : 2 ) ;
306
+ foreach ( var ( i , ( aImport , bImport ) ) in importsAligned . Indexed ( ) )
307
+ {
308
+ if ( i > 0 )
309
+ same ( ", " ) ;
310
+
311
+ different ( aImport . ToString ( ) , bImport . ToString ( ) ) ;
312
+ }
313
+
314
+ same ( "]" ) ;
315
+ }
316
+
317
+ if ( a . ExtensionParameters . Any ( ) || b . ExtensionParameters . Any ( ) )
318
+ {
319
+ same ( ", ext=[" ) ;
320
+ var extAligned = StringSimilarity . SequenceAlignment (
321
+ a . ExtensionParameters . AsSpan ( ) , b . ExtensionParameters . AsSpan ( ) ,
322
+ ( a , b ) => a . Equals ( b ) ? 0 :
323
+ a . Identifier == b . Identifier ? 1 :
324
+ 3 ,
325
+ gapCost : 2 ) ;
326
+ foreach ( var ( i , ( aExt , bExt ) ) in extAligned . Indexed ( ) )
327
+ {
328
+ if ( i > 0 )
329
+ same ( ", " ) ;
330
+
331
+ if ( Equals ( aExt , bExt ) )
332
+ same ( aExt ! . Identifier ) ;
333
+ else if ( aExt is null )
334
+ different ( "" , bExt ! . Identifier + ": " + bExt . ParameterType . CSharpName ) ;
335
+ else if ( bExt is null )
336
+ different ( aExt . Identifier + ": " + aExt . ParameterType . CSharpName , "" ) ;
337
+ else
338
+ {
339
+ different ( aExt . Identifier , bExt . Identifier ) ;
340
+ same ( ": " ) ;
341
+ if ( aExt . ParameterType . IsEqualTo ( bExt . ParameterType ) )
342
+ same ( aExt . ParameterType . CSharpName ) ;
343
+ else
344
+ different ( aExt . ParameterType . CSharpFullName , bExt . ParameterType . CSharpFullName ) ;
345
+
346
+ if ( aExt . Identifier == bExt . Identifier && aExt . GetType ( ) != bExt . GetType ( ) )
347
+ {
348
+ same ( " (" ) ;
349
+ different ( aExt . GetType ( ) . ToCode ( ) , bExt . GetType ( ) . ToCode ( ) ) ;
350
+ same ( ")" ) ;
351
+ }
352
+ }
353
+ }
354
+
355
+ same ( "]" ) ;
356
+ }
357
+
358
+ return result . ToArray ( ) ;
359
+ }
224
360
}
225
361
}
0 commit comments