diff options
| author | Paul Buetow <paul@buetow.org> | 2026-03-06 14:44:34 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-03-06 14:44:34 +0200 |
| commit | 479f399aae8d3b28d9714214ea624d4a8cc0e886 (patch) | |
| tree | 0609eee6378d170b3e6a4601560f58e98bf09cc8 /internal/tui/flamegraph/model.go | |
| parent | 3e08a3d199fdf603b7c0a4002ca9822b6ecf2575 (diff) | |
flamegraph: keep non-matches visible in filter and add pgup/pgdn selection jumps
Diffstat (limited to 'internal/tui/flamegraph/model.go')
| -rw-r--r-- | internal/tui/flamegraph/model.go | 105 |
1 files changed, 104 insertions, 1 deletions
diff --git a/internal/tui/flamegraph/model.go b/internal/tui/flamegraph/model.go index 07bae5d..2f40a30 100644 --- a/internal/tui/flamegraph/model.go +++ b/internal/tui/flamegraph/model.go @@ -38,6 +38,8 @@ type flameKeyMap struct { MoveDeeper key.Binding PrevSibling key.Binding NextSibling key.Binding + JumpTop key.Binding + JumpRoot key.Binding ZoomIn key.Binding ZoomUndo key.Binding ZoomReset key.Binding @@ -49,6 +51,8 @@ func defaultFlameKeyMap() flameKeyMap { MoveDeeper: key.NewBinding(key.WithKeys("k", "up")), PrevSibling: key.NewBinding(key.WithKeys("h", "left")), NextSibling: key.NewBinding(key.WithKeys("l", "right")), + JumpTop: key.NewBinding(key.WithKeys("pgup", "pageup")), + JumpRoot: key.NewBinding(key.WithKeys("pgdown", "pgdn", "pagedown")), ZoomIn: key.NewBinding(key.WithKeys("enter")), ZoomUndo: key.NewBinding(key.WithKeys("backspace", "u", "esc")), ZoomReset: key.NewBinding(), @@ -233,6 +237,12 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case isNextSiblingKey(msg, m.keys): handled = true m.moveSibling(1) + case isJumpTopKey(msg, m.keys): + handled = true + m.jumpToTop() + case isJumpRootKey(msg, m.keys): + handled = true + m.jumpToRoot() } if m.selectedIdx != prev { m.subtreeSet = computeSubtreeSetInto(m.frames, m.selectedIdx, m.subtreeSet) @@ -263,7 +273,9 @@ func (m Model) ConsumesKey(msg tea.KeyPressMsg) bool { isMoveShallowerKey(msg, m.keys), isMoveDeeperKey(msg, m.keys), isPrevSiblingKey(msg, m.keys), - isNextSiblingKey(msg, m.keys): + isNextSiblingKey(msg, m.keys), + isJumpTopKey(msg, m.keys), + isJumpRootKey(msg, m.keys): return true default: return false @@ -582,6 +594,87 @@ func (m *Model) moveSibling(delta int) { } } +func (m *Model) jumpToTop() { + if len(m.frames) == 0 { + return + } + m.clampSelection() + m.ensureSelectionNavigable() + + include := m.navigableFrameSet() + currentCol := m.frames[m.selectedIdx].Col + bestIdx := -1 + bestDepth := -1 + bestDist := int(^uint(0) >> 1) + + for idx, frame := range m.frames { + if include != nil && !include[idx] { + continue + } + dist := abs(frame.Col - currentCol) + if frame.Depth > bestDepth { + bestDepth = frame.Depth + bestIdx = idx + bestDist = dist + continue + } + if frame.Depth == bestDepth { + if dist < bestDist || (dist == bestDist && frame.Col < m.frames[bestIdx].Col) { + bestIdx = idx + bestDist = dist + } + } + } + if bestIdx >= 0 { + m.selectedIdx = bestIdx + } +} + +func (m *Model) jumpToRoot() { + if len(m.frames) == 0 { + return + } + m.clampSelection() + m.ensureSelectionNavigable() + + rootPath := m.currentRootPath() + if rootPath != "" { + if idx := m.frameIndexByPath(rootPath); idx >= 0 { + if !m.filterActive() || m.frameNavigable(idx) { + m.selectedIdx = idx + return + } + } + } + + include := m.navigableFrameSet() + currentCol := m.frames[m.selectedIdx].Col + bestIdx := -1 + bestDepth := int(^uint(0) >> 1) + bestDist := int(^uint(0) >> 1) + for idx, frame := range m.frames { + if include != nil && !include[idx] { + continue + } + dist := abs(frame.Col - currentCol) + if frame.Depth < bestDepth { + bestDepth = frame.Depth + bestDist = dist + bestIdx = idx + continue + } + if frame.Depth == bestDepth { + if dist < bestDist || (dist == bestDist && frame.Col < m.frames[bestIdx].Col) { + bestDist = dist + bestIdx = idx + } + } + } + if bestIdx >= 0 { + m.selectedIdx = bestIdx + } +} + func framesAtDepth(frames []tuiFrame, depth int) []int { return framesAtDepthFiltered(frames, depth, nil) } @@ -813,6 +906,16 @@ func isNextSiblingKey(msg tea.KeyPressMsg, keys flameKeyMap) bool { return key.Matches(msg, keys.NextSibling) || msg.Code == tea.KeyRight || keyMatchesDirection(k, "right", 'C') } +func isJumpTopKey(msg tea.KeyPressMsg, keys flameKeyMap) bool { + k := strings.ToLower(keyString(msg)) + return key.Matches(msg, keys.JumpTop) || msg.Code == tea.KeyPgUp || k == "pgup" || k == "pageup" +} + +func isJumpRootKey(msg tea.KeyPressMsg, keys flameKeyMap) bool { + k := strings.ToLower(keyString(msg)) + return key.Matches(msg, keys.JumpRoot) || msg.Code == tea.KeyPgDown || k == "pgdown" || k == "pgdn" || k == "pagedown" +} + func keyMatchesDirection(keyName, plain string, ansiFinal byte) bool { if keyName == plain || strings.HasSuffix(keyName, "+"+plain) { return true |
