Comparable helper to compare by components#37894
Conversation
| } | ||
|
|
||
| return this.CompletionItem.IsPreferredItem().CompareTo(other.CompletionItem.IsPreferredItem()); | ||
| return IComparableHelper.CompareTo( |
There was a problem hiding this comment.
I'm a little concerned about the extra allocations here. Could this somehow end up in some hot path during completion and cause perf regression? #Resolved
There was a problem hiding this comment.
yeah, agreed. i don't see how this wouldn't be a problem. #Resolved
| { | ||
| internal static class IComparableHelper | ||
| { | ||
| public static int CompareTo<T>(T first, T second, params Func<T, IComparable>[] comparingComponents) |
There was a problem hiding this comment.
not certin we want this. will this be double allocation (the function, and the array?). Or do you intend to cache that all callside.
| internal static class IComparableHelper | ||
| { | ||
| public static int CompareTo<T>(T first, T second, params Func<T, IComparable>[] comparingComponents) | ||
| { |
There was a problem hiding this comment.
why add to compiler layer? just have in IDE layer.
| ? f.CompletionItem.Rules.MatchPriority | ||
| : MatchPriority.Default, | ||
| f => f.CompletionItem.FilterText.GetCaseSensitivePrefixLength(f.FilterText), | ||
| f => f.CompletionItem.IsPreferredItem()); |
There was a problem hiding this comment.
this is a cool concept. However, we should only do this allocation once.
|
Thank you, @genlu and @CyrusNajmabadi ! |
|
@genlu and @CyrusNajmabadi please review #Resolved |
CyrusNajmabadi
left a comment
There was a problem hiding this comment.
Seems fine :) Though i would prefer to see this used in more places to justify the code change. other examples might be: CodeFixService.GetFixesAsync, VirtualTreePoint.CompareTo, AddImport.Reference.CompareTo, CompletionHelper.CompareItems, MethodExtractor.Compare, WrapItemsAction.SortActions, MemberDeclarationsOrganizer.Comparer, ModifiersOranizer.Comparer, CompareNameParts, NameSyntaxComparer, TokenComparer.
If we can really show this being used in a lot of places, hte need for a helper becomes more apparent.
Comment
Submit general feedback without explicit approval.
|
I have applied the approach to multiple scenarios.
#Resolved |
| : MatchPriority.Default; | ||
|
|
||
| yield return completionItem.FilterText.GetCaseSensitivePrefixLength(filterResult.FilterText); | ||
| yield return completionItem.IsPreferredItem(); |
There was a problem hiding this comment.
definitely concerned. i think allocations in these paths are not ok. I think you should have to overloads. One that takes just an ImmutableArray<IComparable>s. Another that takes an ImmutableArray<IComparableWithState> and a state arg. That way this can be allocated once, and you can call the helper from here passing along filterResult as an arg that can feed through.
Does that make sense? #Resolved
There was a problem hiding this comment.
Using immutable arrays, we exclude algorithms with variable length for comparison paths like https://github.com/dotnet/roslyn/pull/37894/files#diff-322fd889d857cd92d680c3f841a26094R43 #Resolved
There was a problem hiding this comment.
What is IComparableWithState? #Resolved
There was a problem hiding this comment.
somehting you would have to define, but it would allow you to avoid captures by effectively stating that the delegate can be passed extra data. that way you can statically create the array of delegates, but pass data it needs dynamically. #Resolved
There was a problem hiding this comment.
Thank you, @CyrusNajmabadi ! Hopefully implemented ComparerWithState.
@CyrusNajmabadi and @genlu please take a look. #Resolved
| (p, b) => !b && !p.IsCaseSensitive, | ||
| // Consider a match to be better if it was successful without stripping punctuation | ||
| // versus a match that had to strip punctuation to succeed. | ||
| (p, b) => p._punctuationStripped); |
There was a problem hiding this comment.
fwiw, i'm reviewing this but not actually confirming the lambdas match the original code. i hope you've done that. #Resolved
| IReadOnlyList<string> names1, IReadOnlyList<string> names2, | ||
| bool placeSystemNamespaceFirst) | ||
| IReadOnlyList<string> names1, IReadOnlyList<string> names2, | ||
| bool placeSystemNamespaceFirst) |
|
|
||
| namespace Roslyn.Utilities | ||
| { | ||
| internal delegate int ComparisonWithState<in T, in S>(T x, T y, S state); |
There was a problem hiding this comment.
this type makes sense to me. #Resolved
| /// </summary> | ||
| /// <typeparam name="T">Comparing type</typeparam> | ||
| /// <typeparam name="S">State</typeparam> | ||
| internal class ComparerWithState<T, S> |
There was a problem hiding this comment.
not certain if these classes are needed? can't you make one bit ComparisonWithState out of N individual ones? #Resolved
| => new ComparerWithState<T>((t1, t2, state) => comparingMethod(t1).CompareTo(comparingMethod(t2))); | ||
| } | ||
|
|
||
| internal class ComparerWithState |
There was a problem hiding this comment.
static class.
i can understand these helpers to build up a final comparer-thingy out of the individual pieces. I don't quite get the need the for the above classes. #Resolved
| } | ||
| => ComparerWithState.CompareTo(this, other, s_comparers); | ||
|
|
||
| private readonly static ImmutableArray<Func<FilterResult, IComparable>> s_comparers = |
There was a problem hiding this comment.
Could we keep the original comments? #Resolved
A helper to compare objects by a series of components. If the first component are different, return the comparison. Otherwise, proceed with the second component and so on.