-
Notifications
You must be signed in to change notification settings - Fork 463
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sorting like Xcode. Fixes #615. #677
base: master
Are you sure you want to change the base?
Conversation
af8c682
to
b7ee6ed
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are the performance implications of this change?
@@ -437,10 +438,9 @@ def sort(options = nil) | |||
next groups_position == :above ? 1 : -1 | |||
end | |||
end | |||
|
|||
result = File.basename(x.display_name.downcase, '.*') <=> File.basename(y.display_name.downcase, '.*') | |||
result = XcodeSortString.new(File.basename(x.display_name, '.*')) <=> XcodeSortString.new(File.basename(y.display_name, '.*')) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should XcodeSortString
internally handle sorting lexicographically by basename and then extname ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I think this situation is just to handle identical filenames in different folders: let's keep the fallback independent from the sort helper..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've tested this situation with Xcode, and Xcode does not sort at all when filenames are identical: it will simply keep the existing order. That supports that we shouldn't include this behavior in XcodeSortString
itself, and we should simply keep it externally like that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@segiddins actually, I misunderstood extname
for "extended name" (aka fullpath) when actually it's a function to get the "file extension". So yes, you're right, there is something to fix here. I'm looking at it.
describe 'In general' do | ||
it 'sorts names like Xcode' do | ||
unsorted_names = [ | ||
# spaces comparison (test disabled: not properly supported) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this be handled?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, if someone figures out how to do it. It's not straight forward like simply stripping the spaces, it's more about handling three kinds of patterns instead of two: ints, strings, spaces. And all the possible comparisons (int vs space, string vs space, space vs space). If the Xcode sorting was documented, it would be easier.
# `Integer`+`rescue`: credit to https://stackoverflow.com/a/39025521 | ||
@ints_and_strings = @scrubbed.scan(/\d+|\D+/).map { |s| begin Integer(s, 10); rescue ArgumentError; s end } | ||
# comparing patterns: credit to https://rosettacode.org/wiki/Natural_sorting#Ruby | ||
@i_s_pattern = @ints_and_strings.map { |el| el.is_a?(Integer) ? :i : :s }.join |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you please give this ivar a more descriptive name?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's the "integers and strings pattern". I'll try to rename it.
compare | ||
else | ||
# equality, we sort reverse raw | ||
-(str <=> other.str) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to sort in reverse order, you can do other.str <=> str
with no need for negation
@str = str | ||
@scrubbed = str.downcase | ||
# `Integer`+`rescue`: credit to https://stackoverflow.com/a/39025521 | ||
@ints_and_strings = @scrubbed.scan(/\d+|\D+/).map { |s| begin Integer(s, 10); rescue ArgumentError; s end } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we do this without having to swallow exceptions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, absolutely, by adding a regex match: { |s| /d/ === s ? Integer(s, 10) : s }
.
I do not know which one is the most performant, but I thought an exception may be faster than a regex match.
I'll change it.
[edit: actually, it doesn't give the same results, strange]
The filenames are split on their digits part, then the sort is made on those collections: the cost is probably quite low. We may do a performance test, but project filenames are generally short (often less than 50 chars) and don't often have more than 1 digit part. So I bet it would be at worst sorting twice slower than before. |
c8a13c9
to
ca7b41d
Compare
@segiddins I've now solved all my known test cases. |
@dnkoutso Would you like to review this PR as well? |
OK, I figured out something worth mentioning: Solving the unicode sort would most likely require [edit] |
Mostly concerned with performance implications here. Large projects can take a big hit. Can you simulate a scenario with many many files and benchmark it? |
a113a92
to
3ebffad
Compare
@dnkoutso I was busy solving some edge cases, and I figured out that Xcode sorting is broken for Unicode: http://www.openradar.me/radar?id=5012044621283328 I also figured out, from https://travis-ci.org/CocoaPods/Xcodeproj/builds/533316561 that ruby 2.4 and newer will sort Unicode strings differently than in ruby 2.3 and older. Lastly, while macOS 10.14.5 is bundled with ruby 2.3.7p456, the upcoming macOS 10.15 will be bundled with ruby 2.6.0 according to Google: https://www.google.com/search?q=ruby+macOS+%2210.15%22. All in all, we should wait for macOS 10.15 beta in a couple of weeks and see if Xcode sorting is affected by the newer version of ruby. As for performances, I suggest generating 10,000 UDID (easy mix of letters and digits) and measuring sorting that data with a plain |
sg! |
any updates here? Looking to make a release for Xcode 11 this week. |
require 'benchmark'
require 'securerandom'
array = (1..10000).map { SecureRandom.uuid }
array1 = array.dup
array2 = array.dup
Benchmark.bmbm do |x|
x.report("downcase") { array1.sort! do |x, y|; x.downcase <=> y.downcase; end }
x.report("XcodeSortString") { array2.sort! do |x, y|; Xcodeproj::XcodeSortString.new(x) <=> Xcodeproj::XcodeSortString.new(y); end }
end output:
lol ... I guess we need to give the choice between a fast sort and an accurate sort. Another issue is that we're using Benchmark.bmbm do |x|
x.report("downcase") { array1.sort_by! do |x|; x.downcase; end }
x.report("XcodeSortString") { array2.sort_by! do |x|; Xcodeproj::XcodeSortString.new(x); end }
end
|
If we can maintain the performance or make it negligible we can land this! |
48dfac8
to
864d6ce
Compare
I just found issues in sorting symbols... so it's not ready to merge. |
I'm not fully familiar with Ruby (apart from a Podfile/Podspec), so advices are welcome.