From a7aadb7a3b41e6725ad059b6c8c8b34594b33d62 Mon Sep 17 00:00:00 2001 From: Jay Hemnani <193022578+jayhemnani9910@users.noreply.github.com> Date: Tue, 20 Oct 2026 04:05:19 +0530 Subject: [PATCH 1/2] diff: write a one-line add or delete range as a single number The normal format printed a single added or deleted line as a range, "1a2,2" and "2,2d1", where GNU diff and POSIX write "1a2" and "2d1", and -e printed "2,2d" for "2d". Changed lines already had this (see #10); do the same for added and deleted ones. --- src/ed_diff.rs | 7 +++++++ src/normal_diff.rs | 23 +++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/ed_diff.rs b/src/ed_diff.rs index 81ddfb46..f1ac5cd9 100644 --- a/src/ed_diff.rs +++ b/src/ed_diff.rs @@ -126,6 +126,7 @@ pub fn diff(expected: &[u8], actual: &[u8], params: &Params) -> Result, match (expected_count, actual_count) { (0, 0) => unreachable!(), (0, _) => writeln!(&mut output, "{}a", line_number_expected - 1).unwrap(), + (1, 0) => writeln!(&mut output, "{line_number_expected}d").unwrap(), (_, 0) => writeln!( &mut output, "{},{}d", @@ -180,6 +181,12 @@ mod tests { assert_eq!(diff, expected.as_bytes()); } + #[test] + fn test_single_line_deleted() { + let diff = diff(b"a\nb\n", b"a\n", &Params::default()).unwrap(); + assert_eq!(diff, b"2d\n"); + } + #[test] fn test_permutations() { let target = "target/ed-diff/"; diff --git a/src/normal_diff.rs b/src/normal_diff.rs index 652dc84e..c930b957 100644 --- a/src/normal_diff.rs +++ b/src/normal_diff.rs @@ -133,6 +133,13 @@ pub fn diff(expected: &[u8], actual: &[u8], params: &Params) -> Vec { let actual_count = result.actual.len(); match (expected_count, actual_count) { (0, 0) => unreachable!(), + (0, 1) => writeln!( + // exactly one line added + &mut output, + "{}a{line_number_actual}", + line_number_expected - 1 + ) + .unwrap(), (0, _) => writeln!( // 'a' stands for "Add lines" &mut output, @@ -142,6 +149,13 @@ pub fn diff(expected: &[u8], actual: &[u8], params: &Params) -> Vec { line_number_actual + actual_count - 1 ) .unwrap(), + (1, 0) => writeln!( + // exactly one line deleted + &mut output, + "{line_number_expected}d{}", + line_number_actual - 1 + ) + .unwrap(), (_, 0) => writeln!( // 'd' stands for "Delete lines" &mut output, @@ -228,6 +242,15 @@ mod tests { assert_eq!(diff, expected); } + #[test] + fn test_single_line_added_or_deleted() { + // A one-line range is written as a single number, as for c. + let added = diff(b"a\n", b"a\nb\n", &Params::default()); + assert_eq!(added, b"1a2\n> b\n".to_vec()); + let deleted = diff(b"a\nb\n", b"a\n", &Params::default()); + assert_eq!(deleted, b"2d1\n< b\n".to_vec()); + } + #[test] fn test_permutations() { let target = "target/normal-diff/"; From a7a66e74fa2edadb1f085f0841bb288292d80a9a Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sat, 24 Oct 2026 14:51:11 +0200 Subject: [PATCH 2/2] diff: test single-line ranges in normal and ed formats --- tests/by-util/test_diff.rs | 48 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/by-util/test_diff.rs b/tests/by-util/test_diff.rs index fbdd4940..bd408c99 100644 --- a/tests/by-util/test_diff.rs +++ b/tests/by-util/test_diff.rs @@ -288,3 +288,51 @@ fn sdiff_gutter_marker_column() -> Result<(), Box> { Ok(()) } + +#[test] +fn normal_format_single_line_ranges() -> Result<(), Box> { + let tmp_dir = tempdir()?; + let old = tmp_dir.path().join("old"); + let new = tmp_dir.path().join("new"); + std::fs::write(&old, "north\neast\nsouth\nwest\n")?; + std::fs::write(&new, "north\nsouth\nwest\nup\ndown\n")?; + + let mut cmd = cargo_bin_cmd!("diffutils"); + cmd.arg("diff").arg(&old).arg(&new); + cmd.assert() + .code(predicate::eq(1)) + .stdout("2d1\n< east\n4a4,5\n> up\n> down\n"); + + let mut cmd = cargo_bin_cmd!("diffutils"); + cmd.arg("diff").arg("/dev/null").arg(&old); + if cfg!(unix) { + cmd.assert() + .code(predicate::eq(1)) + .stdout("0a1,4\n> north\n> east\n> south\n> west\n"); + } + + std::fs::write(&new, "north\neast\nsouth\nwest\nup\n")?; + let mut cmd = cargo_bin_cmd!("diffutils"); + cmd.arg("diff").arg(&old).arg(&new); + cmd.assert().code(predicate::eq(1)).stdout("4a5\n> up\n"); + Ok(()) +} + +#[test] +fn ed_format_single_line_deletions() -> Result<(), Box> { + let tmp_dir = tempdir()?; + let old = tmp_dir.path().join("old"); + let new = tmp_dir.path().join("new"); + std::fs::write(&old, "red\norange\nyellow\ngreen\nblue\n")?; + std::fs::write(&new, "red\norange\nyellow\nblue\n")?; + + let mut cmd = cargo_bin_cmd!("diffutils"); + cmd.arg("diff").arg("-e").arg(&old).arg(&new); + cmd.assert().code(predicate::eq(1)).stdout("4d\n"); + + std::fs::write(&new, "red\nblue\n")?; + let mut cmd = cargo_bin_cmd!("diffutils"); + cmd.arg("diff").arg("-e").arg(&old).arg(&new); + cmd.assert().code(predicate::eq(1)).stdout("2,4d\n"); + Ok(()) +}