diff options
| author | Paul Buetow <paul@buetow.org> | 2026-03-01 23:45:37 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-03-01 23:45:37 +0200 |
| commit | 5775246cb9c2ccfb3469addf6f5fe9a8fc198171 (patch) | |
| tree | 96290d1bede538c9fd352bc3954bac1ce8ab6873 /internal/tui | |
| parent | 3b4be9171b7ca13d4ff3e51d14c4e569b1a308f7 (diff) | |
Thread runtime config instead of global flags reads
Diffstat (limited to 'internal/tui')
| -rw-r--r-- | internal/tui/dashboard/model.go | 12 | ||||
| -rw-r--r-- | internal/tui/dashboard/model_test.go | 2 | ||||
| -rw-r--r-- | internal/tui/dashboard/processes.go | 7 | ||||
| -rw-r--r-- | internal/tui/dashboard/processes_test.go | 8 | ||||
| -rw-r--r-- | internal/tui/tui.go | 54 | ||||
| -rw-r--r-- | internal/tui/tui_test.go | 2 |
6 files changed, 54 insertions, 31 deletions
diff --git a/internal/tui/dashboard/model.go b/internal/tui/dashboard/model.go index c9c96c3..fc9caf6 100644 --- a/internal/tui/dashboard/model.go +++ b/internal/tui/dashboard/model.go @@ -39,6 +39,7 @@ type Model struct { refreshEvery time.Duration keys common.KeyMap + pidFilter int syscallsOffset int filesOffset int filesDirGrouped bool @@ -63,6 +64,7 @@ func NewModelWithConfig(engine SnapshotSource, streamSource *eventstream.RingBuf engine: engine, refreshEvery: time.Duration(refreshMs) * time.Millisecond, keys: keys, + pidFilter: -1, streamModel: eventstream.NewModel(streamSource), } } @@ -280,6 +282,11 @@ func (m *Model) SetStreamSource(source *eventstream.RingBuffer) { m.streamModel.SetSource(source) } +// SetPidFilter updates the active PID filter used by tab render hints. +func (m *Model) SetPidFilter(pid int) { + m.pidFilter = pid +} + // View renders the tab bar, active tab scaffold, and help bar. func (m Model) View() string { width, height := common.EffectiveViewport(m.width, m.height) @@ -299,6 +306,7 @@ func (m Model) View() string { &streamModel, width, activeHeight, + m.pidFilter, m.syscallsOffset, m.filesOffset, m.filesDirGrouped, @@ -318,7 +326,7 @@ func tickCmd(d time.Duration) tea.Cmd { return tea.Tick(d, func(time.Time) tea.Msg { return refreshTickMsg{} }) } -func renderActiveTab(tab Tab, snap *statsengine.Snapshot, streamModel *eventstream.Model, width, height, syscallsOffset, filesOffset int, filesDirGrouped bool, filesDirOffset, processesOffset int) string { +func renderActiveTab(tab Tab, snap *statsengine.Snapshot, streamModel *eventstream.Model, width, height, pidFilter, syscallsOffset, filesOffset int, filesDirGrouped bool, filesDirOffset, processesOffset int) string { if tab == TabStream { if streamModel == nil { return common.PanelStyle.Render("Stream: waiting for source...") @@ -341,7 +349,7 @@ func renderActiveTab(tab Tab, snap *statsengine.Snapshot, streamModel *eventstre } return renderFilesWithOffset(snap, width, height, filesOffset) case TabProcesses: - return renderProcessesWithOffset(snap, width, height, processesOffset) + return renderProcessesWithOffset(snap, width, height, processesOffset, pidFilter) case TabLatency: return renderLatencyGapsTab(snap, width, height) default: diff --git a/internal/tui/dashboard/model_test.go b/internal/tui/dashboard/model_test.go index 0b269b1..87b60e3 100644 --- a/internal/tui/dashboard/model_test.go +++ b/internal/tui/dashboard/model_test.go @@ -386,7 +386,7 @@ func TestRenderActiveTabUsesDirectoryFilesViewWhenGrouped(t *testing.T) { statsengine.HistogramSnapshot{}, statsengine.HistogramSnapshot{}, ) - out := renderActiveTab(TabFiles, &snap, nil, 120, 30, 0, 0, true, 0, 0) + out := renderActiveTab(TabFiles, &snap, nil, 120, 30, -1, 0, 0, true, 0, 0) if !strings.Contains(out, "Directory") { t.Fatalf("expected grouped directory files view header, got %q", out) } diff --git a/internal/tui/dashboard/processes.go b/internal/tui/dashboard/processes.go index 03a38f1..281a86a 100644 --- a/internal/tui/dashboard/processes.go +++ b/internal/tui/dashboard/processes.go @@ -2,7 +2,6 @@ package dashboard import ( "fmt" - "ior/internal/flags" "ior/internal/statsengine" "strconv" "strings" @@ -11,10 +10,10 @@ import ( ) func renderProcesses(snap *statsengine.Snapshot, width, height int) string { - return renderProcessesWithOffset(snap, width, height, 0) + return renderProcessesWithOffset(snap, width, height, 0, -1) } -func renderProcessesWithOffset(snap *statsengine.Snapshot, width, height, offset int) string { +func renderProcessesWithOffset(snap *statsengine.Snapshot, width, height, offset, pidFilter int) string { if snap == nil { return "Processes: waiting for stats..." } @@ -44,7 +43,7 @@ func renderProcessesWithOffset(snap *statsengine.Snapshot, width, height, offset tbl.SetCursor(cursor) out := tbl.View() + fmt.Sprintf("\nRow %d/%d", cursor+1, len(rows)) - if flags.Get().PidFilter > 0 { + if pidFilter > 0 { out += "\n" + "Note: this tab is most useful with All PIDs." } return out diff --git a/internal/tui/dashboard/processes_test.go b/internal/tui/dashboard/processes_test.go index 4db490d..24c1c1b 100644 --- a/internal/tui/dashboard/processes_test.go +++ b/internal/tui/dashboard/processes_test.go @@ -4,13 +4,10 @@ import ( "strings" "testing" - "ior/internal/flags" "ior/internal/statsengine" ) func TestRenderProcessesIncludesHeaders(t *testing.T) { - flags.SetPidFilter(-1) - snap := statsengine.NewSnapshot( nil, nil, nil, nil, nil, @@ -34,9 +31,6 @@ func TestRenderProcessesIncludesHeaders(t *testing.T) { } func TestRenderProcessesShowsSinglePIDNote(t *testing.T) { - flags.SetPidFilter(77) - t.Cleanup(func() { flags.SetPidFilter(-1) }) - snap := statsengine.NewSnapshot( nil, nil, nil, nil, nil, @@ -47,7 +41,7 @@ func TestRenderProcessesShowsSinglePIDNote(t *testing.T) { statsengine.HistogramSnapshot{}, ) - out := renderProcesses(&snap, 100, 20) + out := renderProcessesWithOffset(&snap, 100, 20, 0, 77) if !strings.Contains(out, "most useful with All PIDs") { t.Fatalf("expected single-pid guidance note") } diff --git a/internal/tui/tui.go b/internal/tui/tui.go index d585a0b..10e7988 100644 --- a/internal/tui/tui.go +++ b/internal/tui/tui.go @@ -127,7 +127,8 @@ func Run() error { // RunWithTraceStarter starts the TUI program with a custom trace starter. func RunWithTraceStarter(starter TraceStarter) error { - model := NewModel(flags.Get().PidFilter, starter) + cfg := flags.Get() + model := newModelWithRuntimeConfig(cfg.PidFilter, cfg.PidFilter, cfg.TUIExportEnable, starter) program := tea.NewProgram(model, tea.WithAltScreen()) _, err := program.Run() return err @@ -153,29 +154,46 @@ type Model struct { startTrace TraceStarter traceStop context.CancelFunc + + pidFilter int + exportEnabled bool } // NewModel creates the top-level TUI model. func NewModel(initialPID int, startTrace TraceStarter) Model { + cfg := flags.Get() + return newModelWithRuntimeConfig(initialPID, cfg.PidFilter, cfg.TUIExportEnable, startTrace) +} + +func newModelWithRuntimeConfig(initialPID, startupPidFilter int, exportEnabled bool, startTrace TraceStarter) Model { spin := spinner.New() spin.Spinner = spinner.MiniDot if startTrace == nil { startTrace = defaultTraceStarter } keys := Keys - if !flags.Get().TUIExportEnable { + if !exportEnabled { keys.Export = key.NewBinding() } + dashboard := dashboardui.NewModelWithConfig(lateBoundDashboardSource{}, getEventStreamSource(), 1000, keys) + pidFilter := selectedPIDFilter(startupPidFilter) + if initialPID > 0 { + pidFilter = selectedPIDFilter(initialPID) + } + dashboard.SetPidFilter(pidFilter) + model := Model{ - screen: ScreenPIDPicker, - pidPicker: pidpicker.New(), - dashboard: dashboardui.NewModelWithConfig(lateBoundDashboardSource{}, getEventStreamSource(), 1000, keys), - exporter: tuiexport.NewModel(), - probeModal: probes.NewModel(getProbeManager()), - keys: keys, - spin: spin, - startTrace: startTrace, + screen: ScreenPIDPicker, + pidPicker: pidpicker.New(), + dashboard: dashboard, + exporter: tuiexport.NewModel(), + probeModal: probes.NewModel(getProbeManager()), + keys: keys, + spin: spin, + startTrace: startTrace, + pidFilter: pidFilter, + exportEnabled: exportEnabled, } if initialPID > 0 { @@ -216,7 +234,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.stopTrace() return m, tea.Quit } - if flags.Get().TUIExportEnable && m.screen == ScreenDashboard && !m.attaching && m.lastErr == nil && key.Matches(msg, m.keys.Export) && !m.exporter.Visible() && !m.probeModal.Visible() && !m.dashboard.BlocksGlobalShortcuts() { + if m.exportEnabled && m.screen == ScreenDashboard && !m.attaching && m.lastErr == nil && key.Matches(msg, m.keys.Export) && !m.exporter.Visible() && !m.probeModal.Visible() && !m.dashboard.BlocksGlobalShortcuts() { m.exporter = m.exporter.Open() return m, nil } @@ -231,7 +249,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m.reselectTID() } case tuiexport.RequestMsg: - return m, runExportCmd(msg.Option, m.dashboard.LatestSnapshot()) + return m, runExportCmd(m.exportEnabled, msg.Option, m.dashboard.LatestSnapshot()) case tuiexport.CompletedMsg: var cmd tea.Cmd m.exporter, cmd = m.exporter.Update(msg) @@ -316,6 +334,8 @@ func (m Model) handlePidSelected(msg PidSelectedMsg) (tea.Model, tea.Cmd) { m.stopTrace() flags.SetPidFilter(pid) flags.SetTidFilter(-1) + m.pidFilter = pid + m.dashboard.SetPidFilter(pid) m.screen = ScreenDashboard m.attaching = true m.lastErr = nil @@ -324,13 +344,15 @@ func (m Model) handlePidSelected(msg PidSelectedMsg) (tea.Model, tea.Cmd) { func (m Model) handleTidSelected(msg TidSelectedMsg) (tea.Model, tea.Cmd) { tid := selectedPIDFilter(msg.Tid) - pid := flags.Get().PidFilter + pid := m.pidFilter if msg.Pid > 0 { pid = msg.Pid } m.stopTrace() flags.SetPidFilter(pid) flags.SetTidFilter(tid) + m.pidFilter = pid + m.dashboard.SetPidFilter(pid) m.screen = ScreenDashboard m.attaching = true m.lastErr = nil @@ -357,7 +379,7 @@ func (m Model) reselectPID() (tea.Model, tea.Cmd) { } func (m Model) reselectTID() (tea.Model, tea.Cmd) { - pid := flags.Get().PidFilter + pid := m.pidFilter m.stopTrace() m.screen = ScreenPIDPicker @@ -451,9 +473,9 @@ func (m Model) View() string { } } -func runExportCmd(option tuiexport.Option, snap *statsengine.Snapshot) tea.Cmd { +func runExportCmd(exportEnabled bool, option tuiexport.Option, snap *statsengine.Snapshot) tea.Cmd { return func() tea.Msg { - if !flags.Get().TUIExportEnable { + if !exportEnabled { return tuiexport.FailedMsg{Err: errors.New("tui export is disabled by -tuiExport=false")} } switch option { diff --git a/internal/tui/tui_test.go b/internal/tui/tui_test.go index b0e1861..ed361a6 100644 --- a/internal/tui/tui_test.go +++ b/internal/tui/tui_test.go @@ -467,7 +467,7 @@ func TestRunExportCmdCSVWritesFile(t *testing.T) { t.Cleanup(func() { _ = os.Chdir(prev) }) snap := &statsengine.Snapshot{TotalSyscalls: 1} - msg := runExportCmd(tuiexport.OptionCSV, snap)() + msg := runExportCmd(true, tuiexport.OptionCSV, snap)() done, ok := msg.(tuiexport.CompletedMsg) if !ok { t.Fatalf("expected CompletedMsg, got %T", msg) |
