diff options
Diffstat (limited to 'internal/tui')
| -rw-r--r-- | internal/tui/flamegraph/frame_animator.go | 35 | ||||
| -rw-r--r-- | internal/tui/flamegraph/frame_animator_test.go | 48 | ||||
| -rw-r--r-- | internal/tui/flamegraph/model.go | 4 |
3 files changed, 80 insertions, 7 deletions
diff --git a/internal/tui/flamegraph/frame_animator.go b/internal/tui/flamegraph/frame_animator.go index 26755e8..86e52ec 100644 --- a/internal/tui/flamegraph/frame_animator.go +++ b/internal/tui/flamegraph/frame_animator.go @@ -82,7 +82,7 @@ func (fa *FrameAnimator) reset() { // frameIndexAt returns the index of the frame rendered at terminal coordinates // (x, y), or -1 if no frame occupies that cell. showHelp adds one extra line // to the UI chrome so the frame area row calculations account for it. -func frameIndexAt(frames []tuiFrame, x, y, width, height int, showHelp bool) int { +func frameIndexAt(frames []tuiFrame, x, y, width, height int, showHelp, heightMetricActive bool) int { if len(frames) == 0 || width <= 0 || height <= 0 { return -1 } @@ -105,7 +105,7 @@ func frameIndexAt(frames []tuiFrame, x, y, width, height int, showHelp bool) int if y < 1 || y > availableRows { return -1 } - targetRow := frameCoordToTargetRow(frames, y-1, availableRows) + targetRow := frameCoordToTargetRow(frames, y-1, availableRows, heightMetricActive) if targetRow < 0 { return -1 } @@ -115,10 +115,15 @@ func frameIndexAt(frames []tuiFrame, x, y, width, height int, showHelp bool) int // frameCoordToTargetRow converts a data-area row offset (0-based, after // stripping the toolbar row) into the logical frame row index. Returns -1 when // the coordinate falls in the top padding above the first visible row. -func frameCoordToTargetRow(frames []tuiFrame, dataRow, availableRows int) int { +func frameCoordToTargetRow(frames []tuiFrame, dataRow, availableRows int, heightMetricActive bool) int { maxRow := maxFrameRowForSet(frames, nil) barHeight := computeBarHeight(availableRows, maxRow+1, maxBarVisualHeight) + leafBarHeight := barHeight visibleDepthRows := availableRows / barHeight + if heightMetricActive { + barHeight = 1 + visibleDepthRows = availableRows + } if visibleDepthRows < 1 { visibleDepthRows = 1 } @@ -126,7 +131,17 @@ func frameCoordToTargetRow(frames []tuiFrame, dataRow, availableRows int) int { if maxRow+1 > visibleDepthRows { rowOffset = maxRow + 1 - visibleDepthRows } + if heightMetricActive { + visibleNonLeafRows := max(0, maxRow-rowOffset) + leafBarHeight = availableRows - visibleNonLeafRows + if leafBarHeight < 1 { + leafBarHeight = 1 + } + } renderedRows := (maxRow - rowOffset + 1) * barHeight + if heightMetricActive { + renderedRows = leafBarHeight + max(0, maxRow-rowOffset)*barHeight + } padTop := 0 if renderedRows < availableRows { padTop = availableRows - renderedRows @@ -134,8 +149,18 @@ func frameCoordToTargetRow(frames []tuiFrame, dataRow, availableRows int) int { if dataRow < padTop { return -1 } - depthFromTop := (dataRow - padTop) / barHeight - return maxRow - depthFromTop + rowInRender := dataRow - padTop + for row := maxRow; row >= rowOffset; row-- { + rowHeight := barHeight + if heightMetricActive && row == maxRow { + rowHeight = leafBarHeight + } + if rowInRender < rowHeight { + return row + } + rowInRender -= rowHeight + } + return -1 } // findFrameAtRow scans frames for the narrowest one that occupies logical row diff --git a/internal/tui/flamegraph/frame_animator_test.go b/internal/tui/flamegraph/frame_animator_test.go new file mode 100644 index 0000000..6b4d4f7 --- /dev/null +++ b/internal/tui/flamegraph/frame_animator_test.go @@ -0,0 +1,48 @@ +package flamegraph + +import "testing" + +func TestFrameCoordToTargetRowKeepsUniformBarMapping(t *testing.T) { + frames := []tuiFrame{ + {Name: "root", Row: 0, Col: 0, Width: 20, Path: "root"}, + {Name: "a", Row: 1, Col: 0, Width: 20, Path: "root" + pathSeparator + "a"}, + {Name: "b", Row: 2, Col: 0, Width: 20, Path: "root" + pathSeparator + "a" + pathSeparator + "b"}, + {Name: "leaf", Row: 3, Col: 0, Width: 20, Path: "root" + pathSeparator + "a" + pathSeparator + "b" + pathSeparator + "leaf"}, + } + availableRows := 8 + want := []int{3, 3, 2, 2, 1, 1, 0, 0} + for dataRow, expected := range want { + if got := frameCoordToTargetRow(frames, dataRow, availableRows, false); got != expected { + t.Fatalf("dataRow=%d: got row=%d want=%d", dataRow, got, expected) + } + } +} + +func TestFrameCoordToTargetRowHeightMetricMapsExpandedLeafBand(t *testing.T) { + frames := []tuiFrame{ + {Name: "root", Row: 0, Col: 0, Width: 20, Path: "root"}, + {Name: "leaf", Row: 1, Col: 0, Width: 20, Path: "root" + pathSeparator + "leaf", HeightTotal: 100}, + } + availableRows := 6 + want := []int{1, 1, 1, 1, 1, 0} + for dataRow, expected := range want { + if got := frameCoordToTargetRow(frames, dataRow, availableRows, true); got != expected { + t.Fatalf("dataRow=%d: got row=%d want=%d", dataRow, got, expected) + } + } +} + +func TestFrameIndexAtHeightMetricMapsClicksInExpandedLeafBand(t *testing.T) { + frames := []tuiFrame{ + {Name: "root", Row: 0, Col: 0, Width: 20, Path: "root"}, + {Name: "leaf", Row: 1, Col: 0, Width: 20, Path: "root" + pathSeparator + "leaf", HeightTotal: 100}, + } + for y := 1; y <= 5; y++ { + if got := frameIndexAt(frames, 10, y, 20, 9, false, true); got != 1 { + t.Fatalf("y=%d: expected leaf frame index 1, got %d", y, got) + } + } + if got := frameIndexAt(frames, 10, 6, 20, 9, false, true); got != 0 { + t.Fatalf("y=6: expected root frame index 0, got %d", got) + } +} diff --git a/internal/tui/flamegraph/model.go b/internal/tui/flamegraph/model.go index fe9b73b..81fcb7b 100644 --- a/internal/tui/flamegraph/model.go +++ b/internal/tui/flamegraph/model.go @@ -1088,12 +1088,12 @@ func (m Model) rootSnapshotPath() string { // frameIndexAt delegates to the FrameAnimator package-level helper to convert // terminal coordinates (x, y) to a frame index, accounting for UI chrome. func (m Model) frameIndexAt(x, y int) int { - return frameIndexAt(m.frames, x, y, m.width, m.height, m.showHelp) + return frameIndexAt(m.frames, x, y, m.width, m.height, m.showHelp, strings.TrimSpace(m.heightField) != "") } // frameCoordToTargetRow delegates to the FrameAnimator package-level helper. func (m Model) frameCoordToTargetRow(dataRow, availableRows int) int { - return frameCoordToTargetRow(m.frames, dataRow, availableRows) + return frameCoordToTargetRow(m.frames, dataRow, availableRows, strings.TrimSpace(m.heightField) != "") } func (m Model) withZoomLineage(frames []tuiFrame) []tuiFrame { |
