From e5c5a62cd90b6b84ee159af13afcd4443e6efba3 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sun, 27 Sep 2026 15:13:34 +0200 Subject: [PATCH] Record how lines run through the cells of the commit graph A cell of the commit graph only records which of its edges are touched by lines. That's all the box drawing characters can show, but it doesn't say how the lines connect. A vertical line with a horizontal line passing behind it touches all four edges. So does a cell in which the line from above bends to the left, the line below comes in from the left, and a horizontal line passes through. For the lines at the top and bottom edges, record whether they run straight on or bend to the left or right, and record whether a line passes through horizontally. Nothing reads this yet. It prepares for drawing the graph with symbols that can show these differences. Co-Authored-By: Claude Opus 5.5 (1M context) --- pkg/gui/presentation/graph/cell.go | 51 ++++++++++++++++++++--------- pkg/gui/presentation/graph/graph.go | 19 +++++++++-- 2 files changed, 52 insertions(+), 18 deletions(-) diff --git a/pkg/gui/presentation/graph/cell.go b/pkg/gui/presentation/graph/cell.go index 618a2149d..5660c5745 100644 --- a/pkg/gui/presentation/graph/cell.go +++ b/pkg/gui/presentation/graph/cell.go @@ -21,17 +21,33 @@ const ( MERGE ) +// How a line that touches the top or bottom edge of a cell runs within it +type verticalLine uint8 + +const ( + noLine verticalLine = iota + // On to the opposite edge, or into the commit symbol + straightLine + // Bends towards the left edge + lineToLeft + // Bends towards the right edge + lineToRight +) + type Cell struct { - up, down, left, right bool - cellType cellType - rightStyle *style.TextStyle - style *style.TextStyle + // The lines that touch the top and bottom edges + up, down verticalLine + // Whether lines touch the left and right edges + left, right bool + // Whether a line passes through from the left edge to the right edge + horizontal bool + cellType cellType + rightStyle *style.TextStyle + style *style.TextStyle } func (cell *Cell) render(writer io.StringWriter) { - up, down, left, right := cell.up, cell.down, cell.left, cell.right - - first, second := getBoxDrawingChars(up, down, left, right) + first, second := getBoxDrawingChars(cell.up != noLine, cell.down != noLine, cell.left, cell.right) var adjustedFirst string switch cell.cellType { case CONNECTION: @@ -98,34 +114,34 @@ func cachedSprint(style style.TextStyle, str string) string { } func (cell *Cell) reset() { - cell.up = false - cell.down = false + cell.up = noLine + cell.down = noLine cell.left = false cell.right = false + cell.horizontal = false } -func (cell *Cell) setUp(style *style.TextStyle) *Cell { - cell.up = true +func (cell *Cell) setUp(style *style.TextStyle, line verticalLine) *Cell { + cell.up = line cell.style = style return cell } -func (cell *Cell) setDown(style *style.TextStyle) *Cell { - cell.down = true +func (cell *Cell) setDown(style *style.TextStyle, line verticalLine) *Cell { + cell.down = line cell.style = style return cell } func (cell *Cell) setLeft(style *style.TextStyle) *Cell { cell.left = true - if !cell.up && !cell.down { + if cell.up == noLine && cell.down == noLine { // vertical trumps left cell.style = style } return cell } -//nolint:unparam func (cell *Cell) setRight(style *style.TextStyle, override bool) *Cell { cell.right = true if cell.rightStyle == nil || override { @@ -134,6 +150,11 @@ func (cell *Cell) setRight(style *style.TextStyle, override bool) *Cell { return cell } +func (cell *Cell) setHorizontal(style *style.TextStyle, overrideRightStyle bool) *Cell { + cell.horizontal = true + return cell.setLeft(style).setRight(style, overrideRightStyle) +} + func (cell *Cell) setStyle(style *style.TextStyle) *Cell { cell.style = style return cell diff --git a/pkg/gui/presentation/graph/graph.go b/pkg/gui/presentation/graph/graph.go index c267bbbcb..996892d7b 100644 --- a/pkg/gui/presentation/graph/graph.go +++ b/pkg/gui/presentation/graph/graph.go @@ -303,17 +303,17 @@ func renderPipeSet( if left != right { for i := left + 1; i < right; i++ { - cells[i].setLeft(style).setRight(style, overrideRightStyle) + cells[i].setHorizontal(style, overrideRightStyle) } cells[left].setRight(style, overrideRightStyle) cells[right].setLeft(style) } if pipe.kind == STARTS || pipe.kind == CONTINUES { - cells[pipe.toPos].setDown(style) + cells[pipe.toPos].setDown(style, lineTowards(pipe.toPos, pipe.fromPos)) } if pipe.kind == TERMINATES || pipe.kind == CONTINUES { - cells[pipe.fromPos].setUp(style) + cells[pipe.fromPos].setUp(style, lineTowards(pipe.fromPos, pipe.toPos)) } } @@ -375,6 +375,19 @@ func renderPipeSet( return writer.String() } +// The course of a line through the top or bottom edge of the cell at pos, +// given the column of the pipe's other end +func lineTowards(pos, otherPos int16) verticalLine { + switch { + case otherPos < pos: + return lineToLeft + case otherPos > pos: + return lineToRight + default: + return straightLine + } +} + func equalHashes(a, b *string) bool { // if our selectedCommitHashPtr is nil, there is no selected commit if a == nil || b == nil {