diff --git a/internal/sortfiles/sortfiles.go b/internal/sortfiles/sortfiles.go index 7c0ad9c..975f55e 100644 --- a/internal/sortfiles/sortfiles.go +++ b/internal/sortfiles/sortfiles.go @@ -94,9 +94,9 @@ func ForRenamingAndUndo(changes file.Changes, revert bool) { }) } -// EnforceHierarchicalOrder ensures all files in the same directory are sorted +// Hierarchically ensures all files in the same directory are sorted // before children directories. -func EnforceHierarchicalOrder(changes file.Changes) { +func Hierarchically(changes file.Changes) { slices.SortStableFunc(changes, func(a, b *file.Change) int { lenA, lenB := len(a.BaseDir), len(b.BaseDir) if lenA == lenB { @@ -242,9 +242,8 @@ func Changes( changes file.Changes, conf *config.Config, ) { - // TODO: EnforceHierarchicalOrder should be the default sort? if conf.SortPerDir { - EnforceHierarchicalOrder(changes) + Hierarchically(changes) } //nolint:exhaustive // default sort not needed diff --git a/internal/sortfiles/sortfiles_test/sortfiles_test.go b/internal/sortfiles/sortfiles_test/sortfiles_test.go index f649e35..1a72af5 100644 --- a/internal/sortfiles/sortfiles_test/sortfiles_test.go +++ b/internal/sortfiles/sortfiles_test/sortfiles_test.go @@ -104,7 +104,7 @@ func TestSortFiles_EnforceHierarchicalOrder(t *testing.T) { t.Run(tc.Name, func(t *testing.T) { unsorted := sortTest(t, tc.Unsorted) - sortfiles.EnforceHierarchicalOrder(unsorted) + sortfiles.Hierarchically(unsorted) testutil.CompareSourcePath(t, tc.Sorted, unsorted) }) diff --git a/replace/replace.go b/replace/replace.go index f8671d8..4c319da 100644 --- a/replace/replace.go +++ b/replace/replace.go @@ -71,8 +71,10 @@ func replaceMatches( return nil, err } - if vars.IndexMatches() > 0 { - sortfiles.EnforceHierarchicalOrder(matches) + // If using indexes without an explicit sort, ensure that the files + // are arranged hierarchically + if vars.IndexMatches() > 0 && conf.Sort == config.SortDefault { + sortfiles.Hierarchically(matches) } var pairs int diff --git a/replace/replace_test/replace_test.go b/replace/replace_test/replace_test.go index 30daae5..3bda6e3 100644 --- a/replace/replace_test/replace_test.go +++ b/replace/replace_test/replace_test.go @@ -91,22 +91,6 @@ func TestReplace(t *testing.T) { }, Args: []string{"-f", "budget", "-r", "forecast", "-l", "-2"}, }, - { - Name: "replace the first 2 matches in reverse", - Changes: file.Changes{ - { - Source: "budget_budget_budget_2023.xlsx", - }, - { - Source: "budget_2024.xlsx", - }, - }, - Want: []string{ - "budget_forecast_forecast_2023.xlsx", - "forecast_2024.xlsx", - }, - Args: []string{"-f", "budget", "-r", "forecast", "-l", "-2"}, - }, { Name: "rename with capture variables", Changes: file.Changes{ @@ -271,6 +255,76 @@ func TestReplace(t *testing.T) { "pictures", }, }, + { + Name: "retain file order with an explicit sort", + Changes: file.Changes{ + { + BaseDir: "testdata/dir1", + Source: "doc.txt", + }, + { + BaseDir: "testdata/dir1", + Source: "file.md", + }, + { + BaseDir: "testdata", + Source: "audio.mp3", + }, + { + BaseDir: "testdata", + Source: "binary.mp3", + }, + }, + Want: []string{ + "testdata/dir1/001.txt", + "testdata/dir1/002.md", + "testdata/003.mp3", + "testdata/004.mp3", + }, + Args: []string{ + "-f", + `.*\.(txt|md|mp3)`, + "-r", + "{%03d}{ext}", + "-R", + "--sort", + "size", + }, + }, + { + Name: "without an explicit sort, arrange hierarchically", + Changes: file.Changes{ + { + BaseDir: "testdata/dir1", + Source: "doc.txt", + }, + { + BaseDir: "testdata/dir1", + Source: "file.md", + }, + { + BaseDir: "testdata", + Source: "audio.mp3", + }, + { + BaseDir: "testdata", + Source: "binary.mp3", + }, + }, + Want: []string{ + "testdata/001.mp3", + "testdata/002.mp3", + "testdata/dir1/003.txt", + "testdata/dir1/004.md", + }, + Args: []string{ + "-f", + `.*\.(txt|md|mp3)`, + "-r", + "{%03d}{ext}", + "-R", + }, + }, } replaceTest(t, testCases)