From a3ad561932dab74114e6ed41cb9475dc57b74082 Mon Sep 17 00:00:00 2001 From: Daniel Milde Date: Sun, 21 Apr 2024 01:09:34 +0200 Subject: [PATCH] feat: ignore item --- tui/format.go | 14 ++++---- tui/format_test.go | 35 +++++++++++++++++--- tui/keys.go | 23 +++++++++++++ tui/keys_test.go | 81 ++++++++++++++++++++++++++++++++++++++++++++++ tui/show.go | 36 +++++++++++++++------ tui/tui.go | 3 ++ tui/tui_test.go | 4 +-- 7 files changed, 173 insertions(+), 23 deletions(-) diff --git a/tui/format.go b/tui/format.go index c0913e556..84ad967fc 100644 --- a/tui/format.go +++ b/tui/format.go @@ -9,10 +9,12 @@ import ( "github.com/rivo/tview" ) -func (ui *UI) formatFileRow(item fs.Item, maxUsage int64, maxSize int64, marked bool) string { +func (ui *UI) formatFileRow(item fs.Item, maxUsage int64, maxSize int64, marked, ignored bool) string { var part int - if ui.ShowApparentSize { + if ignored { + part = 0 + } else if ui.ShowApparentSize { part = int(float64(item.GetSize()) / float64(maxSize) * 100.0) } else { part = int(float64(item.GetUsage()) / float64(maxUsage) * 100.0) @@ -20,7 +22,7 @@ func (ui *UI) formatFileRow(item fs.Item, maxUsage int64, maxSize int64, marked row := string(item.GetFlag()) - if ui.UseColors && !marked { + if ui.UseColors && !marked && !ignored { row += "[#e67100::b]" } else { row += "[::b]" @@ -39,7 +41,7 @@ func (ui *UI) formatFileRow(item fs.Item, maxUsage int64, maxSize int64, marked } if ui.showItemCount { - if ui.UseColors && !marked { + if ui.UseColors && !marked && !ignored { row += "[#e67100::b]" } else { row += "[::b]" @@ -48,7 +50,7 @@ func (ui *UI) formatFileRow(item fs.Item, maxUsage int64, maxSize int64, marked } if ui.showMtime { - if ui.UseColors && !marked { + if ui.UseColors && !marked && !ignored { row += "[#e67100::b]" } else { row += "[::b]" @@ -69,7 +71,7 @@ func (ui *UI) formatFileRow(item fs.Item, maxUsage int64, maxSize int64, marked } if item.IsDir() { - if ui.UseColors && !marked { + if ui.UseColors && !marked && !ignored { row += "[#3498db::b]/" } else { row += "[::b]/" diff --git a/tui/format_test.go b/tui/format_test.go index 97f8d1d1e..7ffab6c5a 100644 --- a/tui/format_test.go +++ b/tui/format_test.go @@ -75,7 +75,7 @@ func TestEscapeName(t *testing.T) { Usage: 10, } - assert.Contains(t, ui.formatFileRow(file, file.GetUsage(), file.GetSize(), false), "Aaa [red[] bbb") + assert.Contains(t, ui.formatFileRow(file, file.GetUsage(), file.GetSize(), false, false), "Aaa [red[] bbb") } func TestMarked(t *testing.T) { @@ -99,8 +99,33 @@ func TestMarked(t *testing.T) { Usage: 10, } - assert.Contains(t, ui.formatFileRow(file, file.GetUsage(), file.GetSize(), true), "✓ Aaa") - assert.Contains(t, ui.formatFileRow(file, file.GetUsage(), file.GetSize(), false), "[##########] Aaa") + assert.Contains(t, ui.formatFileRow(file, file.GetUsage(), file.GetSize(), true, false), "✓ Aaa") + assert.Contains(t, ui.formatFileRow(file, file.GetUsage(), file.GetSize(), false, false), "[##########] Aaa") +} + +func TestIgnored(t *testing.T) { + simScreen := testapp.CreateSimScreen() + defer simScreen.Fini() + + app := testapp.CreateMockedApp(true) + ui := CreateUI(app, simScreen, &bytes.Buffer{}, false, false, false, false, false) + ui.ignoredRows[0] = struct{}{} + ui.useOldSizeBar = true + + dir := &analyze.Dir{ + File: &analyze.File{ + Usage: 10, + }, + } + + file := &analyze.File{ + Name: "Aaa", + Parent: dir, + Usage: 10, + } + + assert.Contains(t, ui.formatFileRow(file, file.GetUsage(), file.GetSize(), false, true), "[ ] Aaa") + assert.Contains(t, ui.formatFileRow(file, file.GetUsage(), file.GetSize(), false, false), "[##########] Aaa") } func TestSizeBar(t *testing.T) { @@ -122,7 +147,7 @@ func TestSizeBar(t *testing.T) { Usage: 10, } - assert.Contains(t, ui.formatFileRow(file, file.GetUsage(), file.GetSize(), false), "██████████▏Aaa") + assert.Contains(t, ui.formatFileRow(file, file.GetUsage(), file.GetSize(), false, false), "██████████▏Aaa") } func TestOldSizeBar(t *testing.T) { @@ -146,5 +171,5 @@ func TestOldSizeBar(t *testing.T) { Usage: 10, } - assert.Contains(t, ui.formatFileRow(file, dir.GetUsage(), dir.GetSize(), false), "[##### ] Aaa") + assert.Contains(t, ui.formatFileRow(file, dir.GetUsage(), dir.GetSize(), false, false), "[##### ] Aaa") } diff --git a/tui/keys.go b/tui/keys.go index 25dd5574c..c28ceb5fb 100644 --- a/tui/keys.go +++ b/tui/keys.go @@ -260,6 +260,8 @@ func (ui *UI) handleMainActions(key *tcell.EventKey) *tcell.EventKey { return nil case ' ': ui.handleMark() + case 'I': + ui.ignoreItem() } return key } @@ -324,3 +326,24 @@ func (ui *UI) handleMark() { ui.fileItemMarked(row) } + +func (ui *UI) ignoreItem() { + if ui.currentDir == nil { + return + } + // do not allow ignoring parent dir + row, column := ui.table.GetSelection() + selectedFile, ok := ui.table.GetCell(row, column).GetReference().(fs.Item) + if !ok || selectedFile == ui.currentDir.GetParent() { + return + } + + if _, ok := ui.ignoredRows[row]; ok { + delete(ui.ignoredRows, row) + } else { + ui.ignoredRows[row] = struct{}{} + } + ui.showDir() + // select next row if possible + ui.table.Select(min(row+1, ui.table.GetRowCount()-1), 0) +} diff --git a/tui/keys_test.go b/tui/keys_test.go index 66889999d..cb7baa820 100644 --- a/tui/keys_test.go +++ b/tui/keys_test.go @@ -364,6 +364,18 @@ func TestMarkEmpty(t *testing.T) { assert.NotNil(t, key) } +func TestIgnoreEmpty(t *testing.T) { + simScreen := testapp.CreateSimScreen() + defer simScreen.Fini() + + app := testapp.CreateMockedApp(true) + ui := CreateUI(app, simScreen, &bytes.Buffer{}, false, true, false, false, false) + ui.done = make(chan struct{}) + + key := ui.keyPressed(tcell.NewEventKey(tcell.KeyRune, 'I', 0)) + assert.NotNil(t, key) +} + func TestDelete(t *testing.T) { fin := testdir.CreateTestDir() defer fin() @@ -528,6 +540,36 @@ func TestMarkParent(t *testing.T) { assert.Equal(t, len(ui.markedRows), 0) } +func TestIgnoreParent(t *testing.T) { + fin := testdir.CreateTestDir() + defer fin() + simScreen := testapp.CreateSimScreen() + defer simScreen.Fini() + + app := testapp.CreateMockedApp(true) + ui := CreateUI(app, simScreen, &bytes.Buffer{}, false, true, false, false, false) + ui.done = make(chan struct{}) + ui.askBeforeDelete = false + err := ui.AnalyzePath("test_dir", nil) + assert.Nil(t, err) + + <-ui.done // wait for analyzer + + for _, f := range ui.app.(*testapp.MockedApp).GetUpdateDraws() { + f() + } + + assert.Equal(t, "test_dir", ui.currentDir.GetName()) + assert.Equal(t, 1, ui.table.GetRowCount()) + + ui.table.Select(0, 0) + + ui.keyPressed(tcell.NewEventKey(tcell.KeyRight, 'l', 0)) + ui.keyPressed(tcell.NewEventKey(tcell.KeyRune, 'I', 0)) + + assert.Equal(t, len(ui.ignoredRows), 0) +} + func TestEmptyDir(t *testing.T) { fin := testdir.CreateTestDir() defer fin() @@ -604,6 +646,45 @@ func TestMarkedEmptyDir(t *testing.T) { assert.NoDirExists(t, "test_dir/nested/subnested") } +func TestIgnoreDir(t *testing.T) { + fin := testdir.CreateTestDir() + defer fin() + simScreen := testapp.CreateSimScreen() + defer simScreen.Fini() + + app := testapp.CreateMockedApp(true) + ui := CreateUI(app, simScreen, &bytes.Buffer{}, false, true, false, false, false) + ui.done = make(chan struct{}) + ui.askBeforeDelete = false + err := ui.AnalyzePath("test_dir", nil) + assert.Nil(t, err) + + <-ui.done // wait for analyzer + + for _, f := range ui.app.(*testapp.MockedApp).GetUpdateDraws() { + f() + } + + assert.Equal(t, "test_dir", ui.currentDir.GetName()) + + assert.Equal(t, 1, ui.table.GetRowCount()) + + ui.table.Select(0, 0) + + ui.keyPressed(tcell.NewEventKey(tcell.KeyRight, 'l', 0)) // into nested + assert.Equal(t, 3, ui.table.GetRowCount()) + + ui.table.Select(1, 0) // subnested + + ui.keyPressed(tcell.NewEventKey(tcell.KeyRune, 'I', 0)) // ignore subnested + + row, _ := ui.table.GetSelection() + assert.Equal(t, 2, row) // selection moves to next row + + ui.table.Select(1, 0) + ui.keyPressed(tcell.NewEventKey(tcell.KeyRune, 'I', 0)) // unignore subnested +} + func TestEmptyFile(t *testing.T) { fin := testdir.CreateTestDir() defer fin() diff --git a/tui/show.go b/tui/show.go index 39923a1ae..0beeeff68 100644 --- a/tui/show.go +++ b/tui/show.go @@ -31,6 +31,7 @@ Item under cursor: [::b]d [white:black:-]Delete file or directory [::b]e [white:black:-]Empty file or directory [::b]space [white:black:-]Mark file or directory for deletion + [::b]I [white:black:-]Ignore file or directory [::b]v [white:black:-]Show content of file [::b]o [white:black:-]Open file or directory in external program [::b]i [white:black:-]Show info about item @@ -87,18 +88,27 @@ func (ui *UI) showDir() { unlock := ui.currentDir.RLock() defer unlock() - if ui.ShowRelativeSize { - for _, item := range ui.currentDir.GetFiles() { + i := rowIndex + maxUsage = 0 + maxSize = 0 + for _, item := range ui.currentDir.GetFiles() { + if _, ignored := ui.ignoredRows[i]; ignored { + i++ + continue + } + + if ui.ShowRelativeSize { if item.GetUsage() > maxUsage { maxUsage = item.GetUsage() } if item.GetSize() > maxSize { maxSize = item.GetSize() } + } else { + maxSize += item.GetSize() + maxUsage += item.GetUsage() } - } else { - maxUsage = ui.currentDir.GetUsage() - maxSize = ui.currentDir.GetSize() + i++ } for i, item := range ui.currentDir.GetFiles() { @@ -109,15 +119,21 @@ func (ui *UI) showDir() { continue } - totalUsage += item.GetUsage() - totalSize += item.GetSize() - itemCount += item.GetItemCount() + _, ignored := ui.ignoredRows[rowIndex] + + if !ignored { + totalUsage += item.GetUsage() + totalSize += item.GetSize() + itemCount += item.GetItemCount() + } _, marked := ui.markedRows[rowIndex] - cell := tview.NewTableCell(ui.formatFileRow(item, maxUsage, maxSize, marked)) + cell := tview.NewTableCell(ui.formatFileRow(item, maxUsage, maxSize, marked, ignored)) cell.SetReference(ui.currentDir.GetFiles()[i]) - if marked { + if ignored { + cell.SetStyle(tcell.Style{}.Foreground(tview.Styles.SecondaryTextColor)) + } else if marked { cell.SetStyle(tcell.Style{}.Foreground(tview.Styles.PrimaryTextColor)) cell.SetBackgroundColor(tview.Styles.ContrastBackgroundColor) } else { diff --git a/tui/tui.go b/tui/tui.go index 57edb985e..a737866b9 100644 --- a/tui/tui.go +++ b/tui/tui.go @@ -61,6 +61,7 @@ type UI struct { useOldSizeBar bool defaultSortBy string defaultSortOrder string + ignoredRows map[int]struct{} markedRows map[int]struct{} exportName string noDelete bool @@ -115,6 +116,7 @@ func CreateUI( currentItemNameMaxLen: 70, defaultSortBy: "size", defaultSortOrder: "desc", + ignoredRows: make(map[int]struct{}), markedRows: make(map[int]struct{}), exportName: "export.json", noDelete: false, @@ -282,6 +284,7 @@ func (ui *UI) fileItemSelected(row, column int) { ui.currentDir = selectedDir ui.hideFilterInput() ui.markedRows = make(map[int]struct{}) + ui.ignoredRows = make(map[int]struct{}) ui.showDir() if origDir.GetParent() != nil && selectedDir.GetName() == origDir.GetParent().GetName() { diff --git a/tui/tui_test.go b/tui/tui_test.go index abf3d9556..46821ecde 100644 --- a/tui/tui_test.go +++ b/tui/tui_test.go @@ -97,7 +97,7 @@ func TestHelp(t *testing.T) { b, _, _ := simScreen.GetContents() - cells := b[456 : 456+9] + cells := b[507 : 507+9] text := []byte("directory") for i, r := range cells { @@ -118,7 +118,7 @@ func TestHelpBw(t *testing.T) { b, _, _ := simScreen.GetContents() - cells := b[456 : 456+9] + cells := b[507 : 507+9] text := []byte("directory") for i, r := range cells {