diff --git a/src/side_diff.rs b/src/side_diff.rs index 56953d2..bcf57c9 100644 --- a/src/side_diff.rs +++ b/src/side_diff.rs @@ -285,6 +285,11 @@ fn push_output( // the diff always want to put all tabs possible in the usable are, // even in the middle space between the gutters if possible. + // `process_half_line` left the cursor one column past the half width. The gutter marker + // belongs at the middle of the gutter (`separator_pos`), which is only `half_width + 1` + // when the gutter is 3 or 4 columns wide; pad up to it for wider gutters so the marker + // doesn't drift (see #269). `format_tabs_and_spaces` is a no-op when already at/past it. + format_tabs_and_spaces(half_width + 1, separator_pos, config, output)?; output.write_all(&[symbol])?; if !right_ln.is_empty() { format_tabs_and_spaces(separator_pos + 1, column_two_offset, config, output)?; @@ -1003,6 +1008,29 @@ mod tests { assert_eq!(contains_string(&output, "equal"), 2) } + // Regression test for #269: the `-y` gutter marker must sit at the middle of the gutter + // (separator_pos) regardless of gutter width. At the default width (gutter 3) the marker + // was already correct; at other widths it drifted. Here width=40 yields a wider gutter, so + // the marker must land at column 19 (matching GNU), not the old `half_width + 1`. + #[test] + fn test_gutter_marker_column_wide_gutter() { + let params = Params { + tabsize: 8, + expand_tabs: true, + width: 40, + ..Default::default() + }; + let mut output = vec![]; + diff(b"aa\n", b"", &mut output, ¶ms); + let text = String::from_utf8(output).unwrap(); + let line = text.lines().next().unwrap(); + assert_eq!( + line.find('<'), + Some(19), + "gutter marker '<' should be at column 19 for --width=40, got: {line:?}" + ); + } + #[test] fn test_different_lines() { let params = generate_params();