Skip to content

Commit

Permalink
Sorting like Xcode. Fixes #615, with exceptions.
Browse files Browse the repository at this point in the history
  • Loading branch information
Coeur committed Apr 25, 2019
1 parent b48b020 commit b7ee6ed
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 3 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
* Updated latest SDK versions for the release of Xcode 10.2.
[Samuel Giddins](https://github.com/segiddins)

* Sort by name like Xcode does.
[Antoine Cœur](https://github.com/Coeur)
[#677](https://github.com/CocoaPods/Xcodeproj/pull/677)

##### Bug Fixes

* Use modern localization identifier 'en' for the development region.
Expand Down
6 changes: 3 additions & 3 deletions lib/xcodeproj/project/object/group.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require 'xcodeproj/project/object/helpers/groupable_helper'
require 'xcodeproj/project/object/helpers/file_references_factory'
require 'xcodeproj/project/object/helpers/sort_helper'

module Xcodeproj
class Project
Expand Down Expand Up @@ -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, '.*'))
if result.zero?
File.extname(x.display_name.downcase) <=> File.extname(y.display_name.downcase)
XcodeSortString.new(File.extname(x.display_name)) <=> XcodeSortString.new(File.extname(y.display_name))
else
result
end
Expand Down
34 changes: 34 additions & 0 deletions lib/xcodeproj/project/object/helpers/sort_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
module Xcodeproj
# Wrapper for a string that sorts by name like Xcode does.
# @example
# arrayOfFilenames.sort_by { |s| XcodeSortString.new(s) }
class XcodeSortString
include Comparable
attr_reader :str, :scrubbed, :ints_and_strings, :i_s_pattern

def initialize(str)
@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 }
# 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
end

def <=>(other)
if i_s_pattern.start_with?(other.i_s_pattern) || other.i_s_pattern.start_with?(i_s_pattern)
compare = ints_and_strings <=> other.ints_and_strings
if compare != 0
# we sort naturally
compare
else
# equality, we sort reverse raw
-(str <=> other.str)
end
else
# type mismatch, we sort alphabetically
scrubbed <=> other.scrubbed
end
end
end
end
52 changes: 52 additions & 0 deletions spec/project/object/helpers/sort_helper_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
require File.expand_path('../../../../spec_helper', __FILE__)

module ProjectSpecs
describe Xcodeproj::XcodeSortString do
before do
@helper = XcodeSortString
end

#-------------------------------------------------------------------------#

describe 'In general' do
it 'sorts names like Xcode' do
unsorted_names = [
# spaces comparison (test disabled: not properly supported)
# ' a', 'a ',
# basic mix
'a1', '1a',
# pure integers
'1', '2', '10', '01',
# multi integers
'0.1.1', '0.1.2', '0.1.10', '0.1.01',
# equal integers
'A1B001', 'A01B1',
# case comparison
'a', 'A'
]
sorted = unsorted_names.sort_by { |s| @helper.new(s) }
# order given by Xcode 10.2 "Sort by Name" on macOS 10.14.4, English as primary language
sorted.should == [
# ' a',
'0.1.1',
'0.1.01',
'0.1.2',
'0.1.10',
'1',
'01',
'1a',
'2',
'10',
'a',
'A',
# 'a ',
'a1',
'A1B001',
'A01B1',
]
end
end

#-------------------------------------------------------------------------#
end
end

0 comments on commit b7ee6ed

Please sign in to comment.