summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Milde <daniel.milde@firma.seznam.cz>2021-01-01 22:25:40 +0100
committerDaniel Milde <daniel.milde@firma.seznam.cz>2021-01-01 22:25:40 +0100
commit699affc5bac38922c40e569353db1ea992d10fbd (patch)
tree8d240c85ffd07f25ccac79d91b4d725847e2bdfa
parent9336457b0780efd05d5a4835b2c0a8fd262415f5 (diff)
sorting by name, item count and sizev1.6.0
-rw-r--r--analyze/sort.go19
-rw-r--r--analyze/sort_test.go88
-rw-r--r--cli/cli.go64
-rw-r--r--cli/sort_test.go170
4 files changed, 337 insertions, 4 deletions
diff --git a/analyze/sort.go b/analyze/sort.go
new file mode 100644
index 0000000..8db3dff
--- /dev/null
+++ b/analyze/sort.go
@@ -0,0 +1,19 @@
+package analyze
+
+func (f Files) Len() int { return len(f) }
+func (f Files) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
+func (f Files) Less(i, j int) bool { return f[i].Size > f[j].Size }
+
+// ByItemCount sorts files by item count
+type ByItemCount Files
+
+func (f ByItemCount) Len() int { return len(f) }
+func (f ByItemCount) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
+func (f ByItemCount) Less(i, j int) bool { return f[i].ItemCount > f[j].ItemCount }
+
+// ByName sorts files by name
+type ByName Files
+
+func (f ByName) Len() int { return len(f) }
+func (f ByName) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
+func (f ByName) Less(i, j int) bool { return f[i].Name > f[j].Name }
diff --git a/analyze/sort_test.go b/analyze/sort_test.go
new file mode 100644
index 0000000..269169b
--- /dev/null
+++ b/analyze/sort_test.go
@@ -0,0 +1,88 @@
+package analyze
+
+import (
+ "sort"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestSortBySize(t *testing.T) {
+ files := Files{
+ &File{
+ Size: 1,
+ },
+ &File{
+ Size: 2,
+ },
+ &File{
+ Size: 3,
+ },
+ }
+
+ sort.Sort(files)
+
+ assert.Equal(t, int64(3), files[0].Size)
+ assert.Equal(t, int64(2), files[1].Size)
+ assert.Equal(t, int64(1), files[2].Size)
+}
+
+func TestSortBySizeAsc(t *testing.T) {
+ files := Files{
+ &File{
+ Size: 1,
+ },
+ &File{
+ Size: 2,
+ },
+ &File{
+ Size: 3,
+ },
+ }
+
+ sort.Sort(sort.Reverse(files))
+
+ assert.Equal(t, int64(1), files[0].Size)
+ assert.Equal(t, int64(2), files[1].Size)
+ assert.Equal(t, int64(3), files[2].Size)
+}
+
+func TestSortByItemCount(t *testing.T) {
+ files := Files{
+ &File{
+ ItemCount: 1,
+ },
+ &File{
+ ItemCount: 2,
+ },
+ &File{
+ ItemCount: 3,
+ },
+ }
+
+ sort.Sort(ByItemCount(files))
+
+ assert.Equal(t, 3, files[0].ItemCount)
+ assert.Equal(t, 2, files[1].ItemCount)
+ assert.Equal(t, 1, files[2].ItemCount)
+}
+
+func TestSortByName(t *testing.T) {
+ files := Files{
+ &File{
+ Name: "aa",
+ },
+ &File{
+ Name: "bb",
+ },
+ &File{
+ Name: "cc",
+ },
+ }
+
+ sort.Sort(ByName(files))
+
+ assert.Equal(t, "cc", files[0].Name)
+ assert.Equal(t, "bb", files[1].Name)
+ assert.Equal(t, "aa", files[2].Name)
+}
diff --git a/cli/cli.go b/cli/cli.go
index 7c75e13..e41a2d7 100644
--- a/cli/cli.go
+++ b/cli/cli.go
@@ -19,6 +19,9 @@ const helpText = `
[red]enter, right, l [white]Select directory/device
[red]left, h [white]Go to parent directory
[red]d [white]Delete selected file or directory
+ [red]n [white]Sort by name (asc/desc)
+ [red]s [white]Sort by size (asc/desc)
+ [red]c [white]Sort by items (asc/desc)
`
// UI struct
@@ -36,12 +39,16 @@ type UI struct {
currentDirPath string
askBeforeDelete bool
ignorePaths []string
+ sortBy string
+ sortOrder string
}
// CreateUI creates the whole UI app
func CreateUI(screen tcell.Screen) *UI {
ui := &UI{
askBeforeDelete: true,
+ sortBy: "size",
+ sortOrder: "desc",
}
ui.app = tview.NewApplication()
@@ -174,9 +181,7 @@ func (ui *UI) showDir() {
rowIndex++
}
- sort.Slice(ui.currentDir.Files, func(i, j int) bool {
- return ui.currentDir.Files[i].Size > ui.currentDir.Files[j].Size
- })
+ ui.sortItems()
for i, item := range ui.currentDir.Files {
cell := tview.NewTableCell(formatFileRow(item))
@@ -185,12 +190,40 @@ func (ui *UI) showDir() {
rowIndex++
}
- ui.footer.SetText("Apparent size: " + formatSize(ui.currentDir.Size) + " Items: " + fmt.Sprint(ui.currentDir.ItemCount))
+ ui.footer.SetText(
+ "Apparent size: " +
+ formatSize(ui.currentDir.Size) +
+ " Items: " + fmt.Sprint(ui.currentDir.ItemCount) +
+ " Sorting by: " + ui.sortBy + " " + ui.sortOrder)
ui.table.Select(0, 0)
ui.table.ScrollToBeginning()
ui.app.SetFocus(ui.table)
}
+func (ui *UI) sortItems() {
+ if ui.sortBy == "size" {
+ if ui.sortOrder == "desc" {
+ sort.Sort(ui.currentDir.Files)
+ } else {
+ sort.Sort(sort.Reverse(ui.currentDir.Files))
+ }
+ }
+ if ui.sortBy == "itemCount" {
+ if ui.sortOrder == "desc" {
+ sort.Sort(analyze.ByItemCount(ui.currentDir.Files))
+ } else {
+ sort.Sort(sort.Reverse(analyze.ByItemCount(ui.currentDir.Files)))
+ }
+ }
+ if ui.sortBy == "name" {
+ if ui.sortOrder == "desc" {
+ sort.Sort(analyze.ByName(ui.currentDir.Files))
+ } else {
+ sort.Sort(sort.Reverse(analyze.ByName(ui.currentDir.Files)))
+ }
+ }
+}
+
func (ui *UI) fileItemSelected(row, column int) {
selectedDir := ui.table.GetCell(row, column).GetReference().(*analyze.File)
if !selectedDir.IsDir {
@@ -279,10 +312,33 @@ func (ui *UI) keyPressed(key *tcell.EventKey) *tcell.EventKey {
ui.deleteSelected()
}
break
+ case 's':
+ ui.setSorting("size")
+ break
+ case 'c':
+ ui.setSorting("itemCount")
+ break
+ case 'n':
+ ui.setSorting("name")
+ break
}
return key
}
+func (ui *UI) setSorting(newOrder string) {
+ if newOrder == ui.sortBy {
+ if ui.sortOrder == "asc" {
+ ui.sortOrder = "desc"
+ } else {
+ ui.sortOrder = "asc"
+ }
+ } else {
+ ui.sortBy = newOrder
+ ui.sortOrder = "asc"
+ }
+ ui.showDir()
+}
+
func (ui *UI) updateProgress(progress *analyze.CurrentProgress) {
for {
progress.Mutex.Lock()
diff --git a/cli/sort_test.go b/cli/sort_test.go
new file mode 100644
index 0000000..68f5b37
--- /dev/null
+++ b/cli/sort_test.go
@@ -0,0 +1,170 @@
+package cli
+
+import (
+ "testing"
+ "time"
+
+ "github.com/dundee/gdu/analyze"
+ "github.com/gdamore/tcell/v2"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestSortBySizeAsc(t *testing.T) {
+ fin := analyze.CreateTestDir()
+ defer fin()
+
+ simScreen := tcell.NewSimulationScreen("UTF-8")
+ simScreen.Init()
+ simScreen.SetSize(50, 50)
+
+ ui := CreateUI(simScreen)
+
+ ui.AnalyzePath("test_dir")
+
+ go func() {
+ time.Sleep(100 * time.Millisecond)
+ simScreen.InjectKey(tcell.KeyRune, 'j', 1)
+ time.Sleep(100 * time.Millisecond)
+ simScreen.InjectKey(tcell.KeyRune, 'l', 1)
+ time.Sleep(100 * time.Millisecond)
+ simScreen.InjectKey(tcell.KeyRune, 's', 1)
+ time.Sleep(100 * time.Millisecond)
+ simScreen.InjectKey(tcell.KeyRune, 'q', 1)
+ time.Sleep(100 * time.Millisecond)
+ }()
+
+ ui.StartUILoop()
+
+ dir := ui.currentDir
+
+ assert.Equal(t, "file2", dir.Files[0].Name)
+}
+
+func TestSortByName(t *testing.T) {
+ fin := analyze.CreateTestDir()
+ defer fin()
+
+ simScreen := tcell.NewSimulationScreen("UTF-8")
+ simScreen.Init()
+ simScreen.SetSize(50, 50)
+
+ ui := CreateUI(simScreen)
+
+ ui.AnalyzePath("test_dir")
+
+ go func() {
+ time.Sleep(100 * time.Millisecond)
+ simScreen.InjectKey(tcell.KeyRune, 'j', 1)
+ time.Sleep(100 * time.Millisecond)
+ simScreen.InjectKey(tcell.KeyRune, 'l', 1)
+ time.Sleep(100 * time.Millisecond)
+ simScreen.InjectKey(tcell.KeyRune, 'n', 1)
+ time.Sleep(100 * time.Millisecond)
+ simScreen.InjectKey(tcell.KeyRune, 'q', 1)
+ time.Sleep(100 * time.Millisecond)
+ }()
+
+ ui.StartUILoop()
+
+ dir := ui.currentDir
+
+ assert.Equal(t, "file2", dir.Files[0].Name)
+}
+
+func TestSortByNameDesc(t *testing.T) {
+ fin := analyze.CreateTestDir()
+ defer fin()
+
+ simScreen := tcell.NewSimulationScreen("UTF-8")
+ simScreen.Init()
+ simScreen.SetSize(50, 50)
+
+ ui := CreateUI(simScreen)
+
+ ui.AnalyzePath("test_dir")
+
+ go func() {
+ time.Sleep(100 * time.Millisecond)
+ simScreen.InjectKey(tcell.KeyRune, 'j', 1)
+ time.Sleep(100 * time.Millisecond)
+ simScreen.InjectKey(tcell.KeyRune, 'l', 1)
+ time.Sleep(100 * time.Millisecond)
+ simScreen.InjectKey(tcell.KeyRune, 'n', 1)
+ time.Sleep(100 * time.Millisecond)
+ simScreen.InjectKey(tcell.KeyRune, 'n', 1)
+ time.Sleep(100 * time.Millisecond)
+ simScreen.InjectKey(tcell.KeyRune, 'q', 1)
+ time.Sleep(100 * time.Millisecond)
+ }()
+
+ ui.StartUILoop()
+
+ dir := ui.currentDir
+
+ assert.Equal(t, "subnested", dir.Files[0].Name)
+}
+
+func TestSortByItemCount(t *testing.T) {
+ fin := analyze.CreateTestDir()
+ defer fin()
+
+ simScreen := tcell.NewSimulationScreen("UTF-8")
+ simScreen.Init()
+ simScreen.SetSize(50, 50)
+
+ ui := CreateUI(simScreen)
+
+ ui.AnalyzePath("test_dir")
+
+ go func() {
+ time.Sleep(100 * time.Millisecond)
+ simScreen.InjectKey(tcell.KeyRune, 'j', 1)
+ time.Sleep(100 * time.Millisecond)
+ simScreen.InjectKey(tcell.KeyRune, 'l', 1)
+ time.Sleep(100 * time.Millisecond)
+ simScreen.InjectKey(tcell.KeyRune, 'c', 1)
+ time.Sleep(100 * time.Millisecond)
+ simScreen.InjectKey(tcell.KeyRune, 'q', 1)
+ time.Sleep(100 * time.Millisecond)
+ }()
+
+ ui.StartUILoop()
+
+ dir := ui.currentDir
+
+ assert.Equal(t, "file2", dir.Files[0].Name)
+}
+
+func TestSortByItemCountDesc(t *testing.T) {
+ // fin := analyze.CreateTestDir()
+ // defer fin()
+ analyze.CreateTestDir()
+
+ simScreen := tcell.NewSimulationScreen("UTF-8")
+ simScreen.Init()
+ simScreen.SetSize(50, 50)
+
+ ui := CreateUI(simScreen)
+
+ ui.AnalyzePath("test_dir")
+
+ go func() {
+ time.Sleep(100 * time.Millisecond)
+ simScreen.InjectKey(tcell.KeyRune, 'j', 1)
+ time.Sleep(100 * time.Millisecond)
+ simScreen.InjectKey(tcell.KeyRune, 'l', 1)
+ time.Sleep(100 * time.Millisecond)
+ simScreen.InjectKey(tcell.KeyRune, 'c', 1)
+ time.Sleep(100 * time.Millisecond)
+ simScreen.InjectKey(tcell.KeyRune, 'c', 1)
+ time.Sleep(100 * time.Millisecond)
+ simScreen.InjectKey(tcell.KeyRune, 'q', 1)
+ time.Sleep(100 * time.Millisecond)
+ }()
+
+ ui.StartUILoop()
+
+ dir := ui.currentDir
+
+ assert.Equal(t, "subnested", dir.Files[0].Name)
+}