diff options
| author | Paul Buetow <paul@buetow.org> | 2026-03-05 22:42:08 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-03-05 22:42:08 +0200 |
| commit | 5d6b2cff5fa13700fdfcc30d7e30f5cece2e6d38 (patch) | |
| tree | 1ba853dc4bd0505d2fbae69662bc9a9ca9ffb404 /internal/tui/flamegraph/model.go | |
| parent | 4e464d082e0c83f33f4b4659859b8a9be58987e1 (diff) | |
task 360: add flamegraph search and match navigation
Diffstat (limited to 'internal/tui/flamegraph/model.go')
| -rw-r--r-- | internal/tui/flamegraph/model.go | 38 |
1 files changed, 37 insertions, 1 deletions
diff --git a/internal/tui/flamegraph/model.go b/internal/tui/flamegraph/model.go index c4ca94a..0363b58 100644 --- a/internal/tui/flamegraph/model.go +++ b/internal/tui/flamegraph/model.go @@ -9,6 +9,7 @@ import ( "sort" "charm.land/bubbles/v2/key" + "charm.land/bubbles/v2/textinput" tea "charm.land/bubbletea/v2" ) @@ -65,6 +66,7 @@ type Model struct { zoomPath string searchActive bool + searchInput textinput.Model searchQuery string matchIndices map[int]bool subtreeSet map[int]bool @@ -93,10 +95,17 @@ type tuiFrame struct { // NewModel constructs a flamegraph tab model with default state. func NewModel(liveTrie *coreflamegraph.LiveTrie) Model { + searchInput := textinput.New() + searchInput.Prompt = "/" + searchInput.CharLimit = 0 + searchInput.SetWidth(32) + searchInput.SetStyles(textinput.DefaultStyles(true)) + return Model{ liveTrie: liveTrie, matchIndices: make(map[int]bool), subtreeSet: make(map[int]bool), + searchInput: searchInput, fieldPresets: [][]string{ {"comm", "path"}, {"tracepoint", "comm", "path"}, @@ -116,8 +125,31 @@ func (m Model) Init() tea.Cmd { func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyPressMsg: + if m.searchActive { + switch msg.String() { + case "esc": + m.clearSearch() + return m, nil + case "enter": + m.applySearchQuery(m.searchInput.Value()) + m.searchActive = false + m.searchInput.Blur() + return m, nil + } + var cmd tea.Cmd + m.searchInput, cmd = m.searchInput.Update(msg) + _ = cmd + return m, nil + } + prev := m.selectedIdx switch { + case msg.String() == "/": + m.openSearch() + case msg.String() == "n": + m.jumpMatch(1) + case msg.String() == "N": + m.jumpMatch(-1) case key.Matches(msg, m.keys.ZoomIn): m.zoomIn() case key.Matches(msg, m.keys.ZoomUndo): @@ -142,7 +174,10 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // View renders the flamegraph viewport. func (m Model) View() tea.View { - content := RenderTerminalView(m.frames, m.width, m.height, m.selectedIdx, m.subtreeSet, m.matchIndices, m.isDark) + content := RenderTerminalView(m.frames, m.width, m.height, m.selectedIdx, m.subtreeSet, m.matchIndices, m.isDark, m.searchActive, m.searchQuery) + if m.searchActive { + content = replaceFooterLine(content, m.searchFooter()) + } if m.snapshot != nil && len(m.frames) == 0 { content = common.PanelStyle.Render(fmt.Sprintf("Flame: snapshot v%d has no visible frames", m.lastVersion)) } @@ -204,6 +239,7 @@ func (m *Model) SetViewport(width, height int) { // SetDarkMode sets the active color theme mode. func (m *Model) SetDarkMode(isDark bool) { m.isDark = isDark + m.searchInput.SetStyles(textinput.DefaultStyles(isDark)) } func (m *Model) rebuildFrames() { |
