From b44660ac3da2f8e651372c40ae803782bddea283 Mon Sep 17 00:00:00 2001 From: Robert Liebowitz Date: Sat, 28 Oct 2017 03:00:11 -0400 Subject: [PATCH] Consider case when sorting strings This makes sorting flags and other sections consistent with how most command line tools function, by placing both flags `-A` and `-a` before a flag `-B`. --- category.go | 2 +- command.go | 2 +- flag.go | 2 +- sort.go | 29 +++++++++++++++++++++++++++++ sort_test.go | 30 ++++++++++++++++++++++++++++++ 5 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 sort.go create mode 100644 sort_test.go diff --git a/category.go b/category.go index 1a60550..bf3c73c 100644 --- a/category.go +++ b/category.go @@ -10,7 +10,7 @@ type CommandCategory struct { } func (c CommandCategories) Less(i, j int) bool { - return c[i].Name < c[j].Name + return lexicographicLess(c[i].Name, c[j].Name) } func (c CommandCategories) Len() int { diff --git a/command.go b/command.go index 502fc9f..be8f8f0 100644 --- a/command.go +++ b/command.go @@ -73,7 +73,7 @@ func (c CommandsByName) Len() int { } func (c CommandsByName) Less(i, j int) bool { - return c[i].Name < c[j].Name + return lexicographicLess(c[i].Name, c[j].Name) } func (c CommandsByName) Swap(i, j int) { diff --git a/flag.go b/flag.go index b17f5b9..53fb8bb 100644 --- a/flag.go +++ b/flag.go @@ -53,7 +53,7 @@ func (f FlagsByName) Len() int { } func (f FlagsByName) Less(i, j int) bool { - return f[i].GetName() < f[j].GetName() + return lexicographicLess(f[i].GetName(), f[j].GetName()) } func (f FlagsByName) Swap(i, j int) { diff --git a/sort.go b/sort.go new file mode 100644 index 0000000..23d1c2f --- /dev/null +++ b/sort.go @@ -0,0 +1,29 @@ +package cli + +import "unicode" + +// lexicographicLess compares strings alphabetically considering case. +func lexicographicLess(i, j string) bool { + iRunes := []rune(i) + jRunes := []rune(j) + + lenShared := len(iRunes) + if lenShared > len(jRunes) { + lenShared = len(jRunes) + } + + for index := 0; index < lenShared; index++ { + ir := iRunes[index] + jr := jRunes[index] + + if lir, ljr := unicode.ToLower(ir), unicode.ToLower(jr); lir != ljr { + return lir < ljr + } + + if ir != jr { + return ir < jr + } + } + + return i < j +} diff --git a/sort_test.go b/sort_test.go new file mode 100644 index 0000000..662ef9b --- /dev/null +++ b/sort_test.go @@ -0,0 +1,30 @@ +package cli + +import "testing" + +var lexicographicLessTests = []struct { + i string + j string + expected bool +}{ + {"", "a", true}, + {"a", "", false}, + {"a", "a", false}, + {"a", "A", false}, + {"A", "a", true}, + {"aa", "a", false}, + {"a", "aa", true}, + {"a", "b", true}, + {"a", "B", true}, + {"A", "b", true}, + {"A", "B", true}, +} + +func TestLexicographicLess(t *testing.T) { + for _, test := range lexicographicLessTests { + actual := lexicographicLess(test.i, test.j) + if test.expected != actual { + t.Errorf(`expected string "%s" to come before "%s"`, test.i, test.j) + } + } +}