diff --git a/src/Commands/CompareRevisions.cs b/src/Commands/CompareRevisions.cs index 7b4a496de..fe7430bc6 100644 --- a/src/Commands/CompareRevisions.cs +++ b/src/Commands/CompareRevisions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text.RegularExpressions; +using SourceGit.ViewModels; namespace SourceGit.Commands { @@ -39,7 +40,8 @@ public CompareRevisions(string repo, string start, string end, string path) foreach (var line in lines) ParseLine(line); - _changes.Sort((l, r) => string.Compare(l.Path, r.Path, StringComparison.Ordinal)); + _changes.Sort((l, r) => string.Compare(l.Path, r.Path, + Preferences.Instance.GetPreferredListComparisonType())); return _changes; } diff --git a/src/Commands/QueryLocalChanges.cs b/src/Commands/QueryLocalChanges.cs index 788ed6172..f9af4f07c 100644 --- a/src/Commands/QueryLocalChanges.cs +++ b/src/Commands/QueryLocalChanges.cs @@ -159,6 +159,8 @@ public QueryLocalChanges(string repo, bool includeUntracked = true) outs.Add(change); } + outs.Sort((l, r) => string.Compare(l.Path, r.Path, + ViewModels.Preferences.Instance.GetPreferredListComparisonType())); return outs; } } diff --git a/src/Models/Commit.cs b/src/Models/Commit.cs index ced7597ec..79c346bfc 100644 --- a/src/Models/Commit.cs +++ b/src/Models/Commit.cs @@ -113,7 +113,8 @@ public void ParseDecorators(string data) if (l.Type != r.Type) return (int)l.Type - (int)r.Type; else - return string.Compare(l.Name, r.Name, StringComparison.Ordinal); + return string.Compare(l.Name, r.Name, + ViewModels.Preferences.Instance.GetPreferredListComparisonType()); }); } } diff --git a/src/Models/NumericSort.cs b/src/Models/NumericSort.cs index ed5002e62..8ba485571 100644 --- a/src/Models/NumericSort.cs +++ b/src/Models/NumericSort.cs @@ -1,4 +1,6 @@ -namespace SourceGit.Models +using System; + +namespace SourceGit.Models { public static class NumericSort { @@ -10,58 +12,51 @@ public static int Compare(string s1, string s2) int marker1 = 0; int marker2 = 0; - char[] tmp1 = new char[len1]; - char[] tmp2 = new char[len2]; + char[] tmp = new char[Math.Max(len1, len2)]; while (marker1 < len1 && marker2 < len2) { char c1 = s1[marker1]; char c2 = s2[marker2]; - int loc1 = 0; - int loc2 = 0; bool isDigit1 = char.IsDigit(c1); bool isDigit2 = char.IsDigit(c2); if (isDigit1 != isDigit2) return c1.CompareTo(c2); - do - { - tmp1[loc1] = c1; - loc1++; - marker1++; + int subLen1 = GetCoherentSubstringLength(s1, len1, marker1, isDigit1, ref tmp); + int subLen2 = GetCoherentSubstringLength(s2, len2, marker2, isDigit2, ref tmp); - if (marker1 < len1) - c1 = s1[marker1]; - else - break; - } while (char.IsDigit(c1) == isDigit1); + string sub1 = s1.Substring(marker1, subLen1); + string sub2 = s2.Substring(marker2, subLen2); - do - { - tmp2[loc2] = c2; - loc2++; - marker2++; + marker1 += subLen1; + marker2 += subLen2; - if (marker2 < len2) - c2 = s2[marker2]; - else - break; - } while (char.IsDigit(c2) == isDigit2); - - string sub1 = new string(tmp1, 0, loc1); - string sub2 = new string(tmp2, 0, loc2); int result; if (isDigit1) - result = loc1 == loc2 ? string.CompareOrdinal(sub1, sub2) : loc1 - loc2; + { + // NOTE: We don't strip leading zeroes before comparing substring digits/lengths - should we? + result = (subLen1 == subLen2) ? string.CompareOrdinal(sub1, sub2) : (subLen1 - subLen2); + } else - result = string.CompareOrdinal(sub1, sub2); - + { + result = string.Compare(sub1, sub2, + ViewModels.Preferences.Instance.GetPreferredListComparisonType()); + } if (result != 0) return result; } return len1 - len2; } + + private static int GetCoherentSubstringLength(string s, int len, int start, bool isDigit, ref char[] tmp) + { + int num = 1; + while (start + num < len && char.IsDigit(s[start + num]) == isDigit) + num++; + return num; + } } } diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index afdff5da7..d534fbf47 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -491,6 +491,7 @@ Use monospace font only in text editor Theme Theme Overrides + Use case-insensitive sorting in lists Use fixed tab width in titlebar Use native window frame DIFF/MERGE TOOL diff --git a/src/ViewModels/Preferences.cs b/src/ViewModels/Preferences.cs index e41e046ef..84172343c 100644 --- a/src/ViewModels/Preferences.cs +++ b/src/ViewModels/Preferences.cs @@ -89,6 +89,20 @@ public bool OnlyUseMonoFontInEditor } } + public bool UseCaseInsensitiveSortingInLists + { + get => _useCaseInsensitiveSortingInLists; + set + { + if (SetProperty(ref _useCaseInsensitiveSortingInLists, value) && !_isLoading) + { + var launcher = App.GetLauncher(); + if (launcher.ActivePage.Data is ViewModels.Repository repo) + repo.RefreshAll(); + } + } + } + public bool UseSystemWindowFrame { get => Native.OS.UseSystemWindowFrame; @@ -432,6 +446,12 @@ public Workspace GetActiveWorkspace() return first; } + public StringComparison GetPreferredListComparisonType() + { + return Preferences.Instance.UseCaseInsensitiveSortingInLists ? + StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; + } + public void AddNode(RepositoryNode node, RepositoryNode to, bool save) { var collection = to == null ? RepositoryNodes : to.SubNodes; @@ -449,7 +469,7 @@ public void SortNodes(List collection) if (l.IsRepository != r.IsRepository) return l.IsRepository ? 1 : -1; - return string.Compare(l.Name, r.Name, StringComparison.Ordinal); + return string.Compare(l.Name, r.Name, GetPreferredListComparisonType()); }); } @@ -669,6 +689,7 @@ private bool RemoveInvalidRepositoriesRecursive(List collection) private string _defaultFontFamily = string.Empty; private string _monospaceFontFamily = string.Empty; private bool _onlyUseMonoFontInEditor = true; + private bool _useCaseInsensitiveSortingInLists = false; private double _defaultFontSize = 13; private double _editorFontSize = 13; private int _editorTabWidth = 4; diff --git a/src/ViewModels/StashesPage.cs b/src/ViewModels/StashesPage.cs index dec8ea6bf..99bcce094 100644 --- a/src/ViewModels/StashesPage.cs +++ b/src/ViewModels/StashesPage.cs @@ -71,7 +71,8 @@ public Models.Stash SelectedStash changes.Add(c); if (needSort) - changes.Sort((l, r) => string.Compare(l.Path, r.Path, StringComparison.Ordinal)); + changes.Sort((l, r) => string.Compare(l.Path, r.Path, + Preferences.Instance.GetPreferredListComparisonType())); } Dispatcher.UIThread.Invoke(() => diff --git a/src/ViewModels/WorkingCopy.cs b/src/ViewModels/WorkingCopy.cs index b7dee5c9c..09ebc6f63 100644 --- a/src/ViewModels/WorkingCopy.cs +++ b/src/ViewModels/WorkingCopy.cs @@ -1778,16 +1778,11 @@ private bool IsChanged(List old, List cur) if (old.Count != cur.Count) return true; - var oldMap = new Dictionary(); - foreach (var c in old) - oldMap.Add(c.Path, c); - - foreach (var c in cur) + for (int idx = 0; idx < old.Count; idx++) { - if (!oldMap.TryGetValue(c.Path, out var o)) - return true; - - if (o.Index != c.Index || o.WorkTree != c.WorkTree) + var o = old[idx]; + var c = cur[idx]; + if (o.Path != c.Path || o.Index != c.Index || o.WorkTree != c.WorkTree) return true; } diff --git a/src/Views/Preferences.axaml b/src/Views/Preferences.axaml index beb228b65..45b6253d4 100644 --- a/src/Views/Preferences.axaml +++ b/src/Views/Preferences.axaml @@ -153,7 +153,7 @@ - + + + - { - if (l.IsFolder == r.IsFolder) - return string.Compare(l.Name, r.Name, StringComparison.Ordinal); - return l.IsFolder ? -1 : 1; - }); + SortNodes(_tree); var topTree = new List(); MakeRows(topTree, _tree, 0); @@ -341,12 +336,7 @@ private void OnRowsSelectionChanged(object sender, SelectionChangedEventArgs _) foreach (var obj in objects) node.Children.Add(new ViewModels.RevisionFileTreeNode() { Backend = obj }); - node.Children.Sort((l, r) => - { - if (l.IsFolder == r.IsFolder) - return Models.NumericSort.Compare(l.Name, r.Name); - return l.IsFolder ? -1 : 1; - }); + SortNodes(node.Children); return node.Children; } @@ -365,6 +355,16 @@ private void MakeRows(List rows, List nodes) + { + nodes.Sort((l, r) => + { + if (l.IsFolder == r.IsFolder) + return Models.NumericSort.Compare(l.Name, r.Name); + return l.IsFolder ? -1 : 1; + }); + } + private List _tree = []; private AvaloniaList _rows = []; private bool _disableSelectionChangingEvent = false;