From d4814aa84094d381728e32ec2d86cd9bd72ec0d2 Mon Sep 17 00:00:00 2001 From: Anton Geraschenko Date: Sun, 26 Apr 2026 14:13:38 -0700 Subject: [PATCH] renderdag: emit link_line for same-column named parents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With `min_row_height(1)`, connected and disconnected same-column adjacent nodes render identically, with no `│` between connected nodes. See #1292. Before / After (box_drawing, min_row_height=1) for new `BASIC_DISCONNECTED` fixture (`dag: "A B-C"`, heads `["A", "C"]`): ``` Before: o C o B o A After: o C │ o B o A ``` --- eden/scm/lib/renderdag/src/ascii.rs | 29 +++++++++++++++++++++ eden/scm/lib/renderdag/src/ascii_large.rs | 15 +++++++++++ eden/scm/lib/renderdag/src/box_drawing.rs | 29 +++++++++++++++++++++ eden/scm/lib/renderdag/src/render.rs | 3 +++ eden/scm/lib/renderdag/src/test_fixtures.rs | 12 +++++++++ 5 files changed, 88 insertions(+) diff --git a/eden/scm/lib/renderdag/src/ascii.rs b/eden/scm/lib/renderdag/src/ascii.rs index b67f374644537..c32f894ed3124 100644 --- a/eden/scm/lib/renderdag/src/ascii.rs +++ b/eden/scm/lib/renderdag/src/ascii.rs @@ -240,6 +240,35 @@ mod tests { ); } + #[test] + fn basic_disconnected() { + assert_eq!( + render(&test_fixtures::BASIC_DISCONNECTED), + r#" + o C + | + o B + + o A"# + ); + } + + #[test] + fn basic_disconnected_min_row_height_1() { + let mut renderer = GraphRowRenderer::new() + .output() + .with_min_row_height(1) + .build_ascii(); + assert_eq!( + render_string(&test_fixtures::BASIC_DISCONNECTED, &mut renderer), + r#" + o C + | + o B + o A"# + ); + } + #[test] fn branches_and_merges() { assert_eq!( diff --git a/eden/scm/lib/renderdag/src/ascii_large.rs b/eden/scm/lib/renderdag/src/ascii_large.rs index 3baa80ff961e9..5848b710ea445 100644 --- a/eden/scm/lib/renderdag/src/ascii_large.rs +++ b/eden/scm/lib/renderdag/src/ascii_large.rs @@ -296,6 +296,21 @@ mod tests { ); } + #[test] + fn basic_disconnected() { + assert_eq!( + render(&test_fixtures::BASIC_DISCONNECTED), + r#" + o C + | + | + o B + + + o A"# + ); + } + #[test] fn branches_and_merges() { assert_eq!( diff --git a/eden/scm/lib/renderdag/src/box_drawing.rs b/eden/scm/lib/renderdag/src/box_drawing.rs index 2401b9a19d5c8..ad12db952cf14 100644 --- a/eden/scm/lib/renderdag/src/box_drawing.rs +++ b/eden/scm/lib/renderdag/src/box_drawing.rs @@ -319,6 +319,35 @@ mod tests { ); } + #[test] + fn basic_disconnected() { + assert_eq!( + render(&test_fixtures::BASIC_DISCONNECTED), + r#" + o C + │ + o B + + o A"# + ); + } + + #[test] + fn basic_disconnected_min_row_height_1() { + let mut renderer = GraphRowRenderer::new() + .output() + .with_min_row_height(1) + .build_box_drawing(); + assert_eq!( + render_string(&test_fixtures::BASIC_DISCONNECTED, &mut renderer), + r#" + o C + │ + o B + o A"# + ); + } + #[test] fn branches_and_merges() { assert_eq!( diff --git a/eden/scm/lib/renderdag/src/render.rs b/eden/scm/lib/renderdag/src/render.rs index 78081c652233e..599b3142f7fa7 100644 --- a/eden/scm/lib/renderdag/src/render.rs +++ b/eden/scm/lib/renderdag/src/render.rs @@ -518,6 +518,9 @@ where } else if i == column { link_line[i] |= LinkLine::CHILD | p.to_link_line(LinkLine::VERT_PARENT, LinkLine::VERT_ANCESTOR); + if p.id().is_some() { + need_link_line = true; + } } else { link_line[i] |= p.to_link_line(LinkLine::LEFT_FORK_PARENT, LinkLine::LEFT_FORK_ANCESTOR); diff --git a/eden/scm/lib/renderdag/src/test_fixtures.rs b/eden/scm/lib/renderdag/src/test_fixtures.rs index 4678151108ffd..46e24f52029d5 100644 --- a/eden/scm/lib/renderdag/src/test_fixtures.rs +++ b/eden/scm/lib/renderdag/src/test_fixtures.rs @@ -23,6 +23,18 @@ pub(crate) const BASIC: TestFixture = TestFixture { missing: &[], }; +// A-B is a connected pair; C is an isolated node that ends up in the same +// column after A's column is freed. Used to verify that the renderer +// visually distinguishes connected from unconnected adjacent nodes. +pub(crate) const BASIC_DISCONNECTED: TestFixture = TestFixture { + dag: "A B-C", + messages: &[], + heads: &["A", "C"], + reserve: &[], + ancestors: &[], + missing: &[], +}; + pub(crate) const BRANCHES_AND_MERGES: TestFixture = TestFixture { dag: r#" T /---------------N--O---\ T