From cbebc789f9c7ee74a352949de0ebc06a78d69b4a Mon Sep 17 00:00:00 2001 From: Igor Mikushkin Date: Sun, 17 May 2026 00:11:11 +0100 Subject: [PATCH 1/2] Fix wrap symbol background on homolog pairs in side-by-side In wrap_minusplus_block the continuation symbol and padding used minus-style / plus-style even when the rest of the line was painted with non-emph, leaving a visibly mismatched chip. Pick fill_style per alignment pair: non-emph for homologs (Some, Some), minus-style / plus-style for standalone lines. --- src/wrapping.rs | 74 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 67 insertions(+), 7 deletions(-) diff --git a/src/wrapping.rs b/src/wrapping.rs index 261094a3b..cbf94925f 100644 --- a/src/wrapping.rs +++ b/src/wrapping.rs @@ -402,7 +402,10 @@ pub fn wrap_minusplus_block<'c: 'a, 'a>( let mut diff = MinusPlus::new(diff.minus.into_iter(), diff.plus.into_iter()); let mut wrapinfo = MinusPlus::new(wrapinfo[Left].iter(), wrapinfo[Right].iter()); - let fill_style = MinusPlus::new(&config.minus_style, &config.plus_style); + // Fill style for wrap symbols and right-aligned padding. + let standalone_fill_style = MinusPlus::new(&config.minus_style, &config.plus_style); + let homolog_fill_style = + MinusPlus::new(&config.minus_non_emph_style, &config.plus_non_emph_style); // Internal helper function to perform wrapping for both the syntax and the // diff highlighting (SyntectStyle and Style). @@ -477,7 +480,7 @@ pub fn wrap_minusplus_block<'c: 'a, 'a>( // This macro avoids having the same code block 4x in the alignment processing macro_rules! wrap_and_assert { - ($side:tt, $errhint:tt, $have:tt, $expected:tt) => {{ + ($side:tt, $fill_style:expr, $errhint:tt, $have:tt, $expected:tt) => {{ assert_eq!(*$have, $expected, "bad alignment index {}", $errhint); $expected += 1; @@ -489,7 +492,7 @@ pub fn wrap_minusplus_block<'c: 'a, 'a>( &mut diff[$side], &mut wrapinfo[$side], line_width[$side], - &fill_style[$side], + &$fill_style[$side], $errhint, ) }}; @@ -503,7 +506,8 @@ pub fn wrap_minusplus_block<'c: 'a, 'a>( for (minus, plus) in alignment { let (minus_extended, plus_extended) = match (minus, plus) { (Some(m), None) => { - let (minus_start, extended_to) = wrap_and_assert!(Left, "[*l*] (-)", m, m_expected); + let (minus_start, extended_to) = + wrap_and_assert!(Left, standalone_fill_style, "[*l*] (-)", m, m_expected); for i in minus_start..extended_to { new_alignment.push((Some(i), None)); @@ -512,7 +516,8 @@ pub fn wrap_minusplus_block<'c: 'a, 'a>( (extended_to - minus_start, 0) } (None, Some(p)) => { - let (plus_start, extended_to) = wrap_and_assert!(Right, "(-) [*r*]", p, p_expected); + let (plus_start, extended_to) = + wrap_and_assert!(Right, standalone_fill_style, "(-) [*r*]", p, p_expected); for i in plus_start..extended_to { new_alignment.push((None, Some(i))); @@ -522,9 +527,9 @@ pub fn wrap_minusplus_block<'c: 'a, 'a>( } (Some(m), Some(p)) => { let (minus_start, m_extended_to) = - wrap_and_assert!(Left, "[*l*] (r)", m, m_expected); + wrap_and_assert!(Left, homolog_fill_style, "[*l*] (r)", m, m_expected); let (plus_start, p_extended_to) = - wrap_and_assert!(Right, "(l) [*r*]", p, p_expected); + wrap_and_assert!(Right, homolog_fill_style, "(l) [*r*]", p, p_expected); for (new_m, new_p) in (minus_start..m_extended_to).zip(plus_start..p_extended_to) { new_alignment.push((Some(new_m), Some(new_p))); @@ -1206,4 +1211,59 @@ index 223ca50..e69de29 100644 ); } } + + #[test] + fn test_homolog_wrap_fill_uses_non_emph_style() { + // On a homolog pair, the wrap continuation symbols and any padding + // sit inside the same unbroken `*-non-emph-style` bg span + // as the surrounding wrapped content (no intervening bg SGR between them). + // + // Width 109 makes the content wrap to exactly 2 lines with the second one right-aligned: + // row 1 ends with `<` (wrap-right-symbol); + // row 2 begins with leading padding spaces then `>` (wrap-right-prefix). + let t = DeltaTest::with_args(&default_wrap_cfg_plus(&[ + "--side-by-side", + "--width", + "109", + "--minus-style", + "normal magenta", + "--minus-emph-style", + "normal brightred", + "--minus-non-emph-style", + "normal red", + "--plus-style", + "normal cyan", + "--plus-emph-style", + "normal brightgreen", + "--plus-non-emph-style", + "normal green", + ])) + .with_input(HUNK_MP_DIFF); + let raw = &t.raw_output; + + // Wrap symbol on minus line 1 shares the line's non-emph (dark red) bg. + assert!( + raw.contains("\x1b[41m = 0123456789 0123456789 0123456789 0123456789<"), + "minus-side line 1: non-emph bg span is broken before `<`:\n{}", + raw + ); + // Leading padding + `>` on minus line 2 share the non-emph (dark red) bg. + assert!( + raw.contains("\x1b[41m > 0123456789"), + "minus-side line 2: non-emph bg span doesn't cover pad+`>`:\n{}", + raw + ); + // Wrap symbol on plus line 1 shares the line's non-emph (dark green) bg. + assert!( + raw.contains("\x1b[42m = 0123456789 0123456789 0123456789 0123456789<"), + "plus-side line 1: non-emph bg span is broken before `<`:\n{}", + raw + ); + // Leading padding + `>` on plus line 2 share the non-emph (dark green) bg. + assert!( + raw.contains("\x1b[42m > 0123456789"), + "plus-side line 2: non-emph bg span doesn't cover pad+`>`:\n{}", + raw + ); + } } From 149b72bb92845d72eed855161c03b52e2a30d3d9 Mon Sep 17 00:00:00 2001 From: Igor Mikushkin Date: Tue, 26 May 2026 00:16:25 +0100 Subject: [PATCH 2/2] Fix wrap symbol background on standalone map-styled lines Companion to the homolog-pair fix. A standalone wrapped line drew its wrap symbol and padding from minus/plus-style, ignoring a map-styles repaint of the content, so a remapped moved line kept the old color on the wrap chrome. Derive the fill from the line's last painted style. --- src/wrapping.rs | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/src/wrapping.rs b/src/wrapping.rs index cbf94925f..51e64d274 100644 --- a/src/wrapping.rs +++ b/src/wrapping.rs @@ -419,6 +419,7 @@ pub fn wrap_minusplus_block<'c: 'a, 'a>( wrapinfo_iter: &mut ItWrap, line_width: usize, fill_style: &Style, + fill_from_content: bool, errhint: &'a str, ) -> (usize, usize) where @@ -455,15 +456,32 @@ pub fn wrap_minusplus_block<'c: 'a, 'a>( None }; + let diff_sections = diff_iter + .next() + .unwrap_or_else(|| panic!("bad diff alignment {}", errhint)); + + // A standalone line has one background; if map-styles repainted it, the + // wrap symbol and padding must follow that paint, not minus/plus-style. + // Homolog pairs keep the passed non-emph fill. + let diff_fill_style = if fill_from_content { + diff_sections + .iter() + .rev() + .filter(|(_, s)| s != &"\n") + .map(|(style, _)| *style) + .next() + .unwrap_or(*fill_style) + } else { + *fill_style + }; + let (start2, extended_to2) = wrap_if_too_long( config, wrapped_diff, - diff_iter - .next() - .unwrap_or_else(|| panic!("bad diff alignment {}", errhint)), + diff_sections, must_wrap, line_width, - fill_style, + &diff_fill_style, &inline_hint_style, ); @@ -480,7 +498,7 @@ pub fn wrap_minusplus_block<'c: 'a, 'a>( // This macro avoids having the same code block 4x in the alignment processing macro_rules! wrap_and_assert { - ($side:tt, $fill_style:expr, $errhint:tt, $have:tt, $expected:tt) => {{ + ($side:tt, $fill_style:expr, $fill_from_content:expr, $errhint:tt, $have:tt, $expected:tt) => {{ assert_eq!(*$have, $expected, "bad alignment index {}", $errhint); $expected += 1; @@ -493,6 +511,7 @@ pub fn wrap_minusplus_block<'c: 'a, 'a>( &mut wrapinfo[$side], line_width[$side], &$fill_style[$side], + $fill_from_content, $errhint, ) }}; @@ -507,7 +526,7 @@ pub fn wrap_minusplus_block<'c: 'a, 'a>( let (minus_extended, plus_extended) = match (minus, plus) { (Some(m), None) => { let (minus_start, extended_to) = - wrap_and_assert!(Left, standalone_fill_style, "[*l*] (-)", m, m_expected); + wrap_and_assert!(Left, standalone_fill_style, true, "[*l*] (-)", m, m_expected); for i in minus_start..extended_to { new_alignment.push((Some(i), None)); @@ -517,7 +536,7 @@ pub fn wrap_minusplus_block<'c: 'a, 'a>( } (None, Some(p)) => { let (plus_start, extended_to) = - wrap_and_assert!(Right, standalone_fill_style, "(-) [*r*]", p, p_expected); + wrap_and_assert!(Right, standalone_fill_style, true, "(-) [*r*]", p, p_expected); for i in plus_start..extended_to { new_alignment.push((None, Some(i))); @@ -527,9 +546,9 @@ pub fn wrap_minusplus_block<'c: 'a, 'a>( } (Some(m), Some(p)) => { let (minus_start, m_extended_to) = - wrap_and_assert!(Left, homolog_fill_style, "[*l*] (r)", m, m_expected); + wrap_and_assert!(Left, homolog_fill_style, false, "[*l*] (r)", m, m_expected); let (plus_start, p_extended_to) = - wrap_and_assert!(Right, homolog_fill_style, "(l) [*r*]", p, p_expected); + wrap_and_assert!(Right, homolog_fill_style, false, "(l) [*r*]", p, p_expected); for (new_m, new_p) in (minus_start..m_extended_to).zip(plus_start..p_extended_to) { new_alignment.push((Some(new_m), Some(new_p)));