You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
NaturalStringComparer has no ignore-case option, so File2.txt and README.md sort ahead of build3.log and file1.txt #66
NaturalStringComparer compares text chunks with string.CompareOrdinal. Every uppercase letter therefore sorts before every lowercase one. The comparer exists mostly to sort file and folder names, and in those names case is usually an accident. A capitalised name drops into a separate block ahead of all the lowercase names, and its numbers stop interleaving with the rest.
The class has no constructor, option or static instance that changes this. To work around it, a caller has to normalise case themselves, which allocates on every comparison and loses the original strings' tie-break order.
Expected with an ignore-case option:build3.log, Build20.log, file1.txt, File2.txt, file10.txt, README.md. This is the order Windows Explorer and Finder show.
The existing test Compare_SimpleStrings_SortsAlphabetically says "should be case-sensitive by default". That reads as a default, not a deliberate exclusion, so this proposal keeps it.
Why it matters
Natural sort is used almost entirely for names that people read: files, versions, tags and menu entries. When a stray capital letter moves an item to the top of one of those lists, the user sees it as a bug.
Suggested fix and acceptance criteria
Add an opt-in ignore-case mode and keep the current behaviour as the default. For example, a NaturalStringComparer(bool ignoreCase) constructor plus static Ordinal and OrdinalIgnoreCase instances.
In that mode, compare text chunks with StringComparison.OrdinalIgnoreCase, using either MemoryExtensions.CompareTo over spans or string.Compare(x, xi, y, yi, len, StringComparison.OrdinalIgnoreCase). Keep it allocation-free: Compare_AllocatesNothing must still pass with the option on.
The number-vs-text branch stays consistent with the case-folded text comparison. Today it compares the digit against the raw first character, and folding can change where that character sits relative to digits.
The transitivity/antisymmetry fuzz is re-run with the option on.
Tests:
The repro above produces the expected order.
The default instance behaves exactly as it does today.
What's wrong
NaturalStringComparercompares text chunks withstring.CompareOrdinal. Every uppercase letter therefore sorts before every lowercase one. The comparer exists mostly to sort file and folder names, and in those names case is usually an accident. A capitalised name drops into a separate block ahead of all the lowercase names, and its numbers stop interleaving with the rest.The class has no constructor, option or static instance that changes this. To work around it, a caller has to normalise case themselves, which allocates on every comparison and loses the original strings' tie-break order.
Repro
Build20.log, File2.txt, README.md, build3.log, file1.txt, file10.txtbuild3.log, Build20.log, file1.txt, File2.txt, file10.txt, README.md. This is the order Windows Explorer and Finder show.The existing test
Compare_SimpleStrings_SortsAlphabeticallysays "should be case-sensitive by default". That reads as a default, not a deliberate exclusion, so this proposal keeps it.Why it matters
Natural sort is used almost entirely for names that people read: files, versions, tags and menu entries. When a stray capital letter moves an item to the top of one of those lists, the user sees it as a bug.
Suggested fix and acceptance criteria
NaturalStringComparer(bool ignoreCase)constructor plus staticOrdinalandOrdinalIgnoreCaseinstances.StringComparison.OrdinalIgnoreCase, using eitherMemoryExtensions.CompareToover spans orstring.Compare(x, xi, y, yi, len, StringComparison.OrdinalIgnoreCase). Keep it allocation-free:Compare_AllocatesNothingmust still pass with the option on.SortedSetbuilt with the ignore-case comparer keepsfile1andFile1as two separate entries (after [patch] Break natural-order ties ordinally so distinct strings never compare equal #65).