summaryrefslogtreecommitdiff
path: root/internal/tui
diff options
context:
space:
mode:
Diffstat (limited to 'internal/tui')
-rw-r--r--internal/tui/filterstack.go13
-rw-r--r--internal/tui/tui_test.go21
2 files changed, 34 insertions, 0 deletions
diff --git a/internal/tui/filterstack.go b/internal/tui/filterstack.go
index 016d845..28667d9 100644
--- a/internal/tui/filterstack.go
+++ b/internal/tui/filterstack.go
@@ -7,6 +7,11 @@ import (
"ior/internal/globalfilter/presenter"
)
+// maxFilterHistory is the maximum number of undo levels retained by filterStack.
+// Once this cap is reached, the oldest entry is evicted so the slices never
+// grow without bound across long-running sessions or automated filter changes.
+const maxFilterHistory = 50
+
// filterStack manages the trace filter chain: the active filter, the undo
// history, and the human-readable label stack displayed in the status bar.
// It owns all filter mutation and label-generation logic so the top-level
@@ -29,6 +34,8 @@ func (f *filterStack) current() globalfilter.Filter {
// push records oldFilter in the history stack, sets global to newFilter, and
// appends an action label. Returns true when the filter actually changed.
+// When the history depth would exceed maxFilterHistory the oldest entry is
+// evicted from both slices to keep memory usage bounded.
func (f *filterStack) push(newFilter globalfilter.Filter, action string) bool {
next := newFilter.Clone()
if f.global.Equal(next) {
@@ -36,6 +43,12 @@ func (f *filterStack) push(newFilter globalfilter.Filter, action string) bool {
}
f.history = append(f.history, f.global.Clone())
f.stack = append(f.stack, globalFilterActionLabel(f.global, next, action))
+ // Evict the oldest undo level once the cap is exceeded so the slices
+ // never grow without bound across long-running sessions.
+ if len(f.history) > maxFilterHistory {
+ f.history = f.history[1:]
+ f.stack = f.stack[1:]
+ }
f.global = next
return true
}
diff --git a/internal/tui/tui_test.go b/internal/tui/tui_test.go
index ba0f8ed..a685719 100644
--- a/internal/tui/tui_test.go
+++ b/internal/tui/tui_test.go
@@ -1725,6 +1725,27 @@ func TestDashboardFooterShowsGlobalFilterStack(t *testing.T) {
}
}
+func TestFilterStackHistoryCapEvictsOldestEntries(t *testing.T) {
+ // Push maxFilterHistory+10 distinct filters and verify the slices never
+ // exceed the cap. The oldest entries must be evicted, and the most-recent
+ // maxFilterHistory entries must be retained.
+ fs := newFilterStack(globalfilter.Filter{})
+ for i := 0; i < maxFilterHistory+10; i++ {
+ f := globalfilter.Filter{}
+ f.FD = globalfilter.NewEqFilter(int64(i + 1)) // unique per iteration
+ fs.push(f, "")
+ }
+ if len(fs.history) > maxFilterHistory {
+ t.Fatalf("history exceeds cap: got %d, want <= %d", len(fs.history), maxFilterHistory)
+ }
+ if len(fs.stack) > maxFilterHistory {
+ t.Fatalf("stack exceeds cap: got %d, want <= %d", len(fs.stack), maxFilterHistory)
+ }
+ if len(fs.history) != len(fs.stack) {
+ t.Fatalf("history and stack lengths must match: history=%d stack=%d", len(fs.history), len(fs.stack))
+ }
+}
+
func TestProcessesTabEnterAppliesSelectedProcessAsGlobalFilter(t *testing.T) {
m := NewModel(-1, func(context.Context) error { return nil })
m.screen = ScreenDashboard