diff --git a/src/params.rs b/src/params.rs index 9e3e5a3..9b7fe5e 100644 --- a/src/params.rs +++ b/src/params.rs @@ -301,9 +301,7 @@ pub fn parse_params>( }) => { if is_match { format = Some(Format::Context); - if context_count.is_some() { - context = context_count; - } + context = Some(largest_context(context, context_count)); if next_param_consumed { opts.next(); } @@ -320,9 +318,7 @@ pub fn parse_params>( }) => { if is_match { format = Some(Format::Unified); - if context_count.is_some() { - context = context_count; - } + context = Some(largest_context(context, context_count)); if next_param_consumed { opts.next(); } @@ -474,6 +470,13 @@ fn push_function_line_pattern(params: &mut Params, pattern: &str) -> Result<(), Ok(()) } +/// GNU diff uses the largest context length given, and a bare -c or -u +/// counts as a request for 3 lines. +fn largest_context(current: Option, given: Option) -> usize { + let given = given.unwrap_or(3); + current.map_or(given, |current| current.max(given)) +} + struct DiffStyleMatch { is_match: bool, context_count: Option, diff --git a/tests/by-util/test_diff.rs b/tests/by-util/test_diff.rs index 10fc0a1..1557438 100644 --- a/tests/by-util/test_diff.rs +++ b/tests/by-util/test_diff.rs @@ -1114,6 +1114,47 @@ fn ed_format_dot_line_ends_text_once() -> Result<(), Box> Ok(()) } +#[test] +fn largest_context_length_wins() -> Result<(), Box> { + let tmp_dir = tempdir()?; + let old = tmp_dir.path().join("old"); + let new = tmp_dir.path().join("new"); + + // The case from issue #340, GNU diff merges both changes into one hunk. + std::fs::write(&old, "a\nb\nc\nd\ne\nf\ng\nh\n")?; + std::fs::write(&new, "a\nB\nc\nd\ne\nf\nG\nh\n")?; + let mut cmd = cargo_bin_cmd!("diffutils"); + cmd.arg("diff").arg("-U1").arg("-u").arg(&old).arg(&new); + cmd.assert() + .code(predicate::eq(1)) + .stdout(predicate::str::contains("\n@@ -1,8 +1,8 @@\n")); + + // With one changed line in the middle, the hunk header shows how many + // lines of context were used. + let lines: String = (1..=20).map(|i| format!("{i}\n")).collect(); + std::fs::write(&old, &lines)?; + std::fs::write(&new, lines.replace("\n10\n", "\nX\n"))?; + for (args, header) in [ + (vec!["-U1", "-u"], "\n@@ -7,7 +7,7 @@\n"), + (vec!["-u", "-U1"], "\n@@ -7,7 +7,7 @@\n"), + (vec!["-U5", "-u"], "\n@@ -5,11 +5,11 @@\n"), + (vec!["-U2", "-U1"], "\n@@ -8,5 +8,5 @@\n"), + (vec!["--unified=1", "--unified"], "\n@@ -7,7 +7,7 @@\n"), + (vec!["-5u"], "\n@@ -5,11 +5,11 @@\n"), + (vec!["-C1", "-c"], "\n*** 7,13 ****\n"), + (vec!["--context=1", "--context"], "\n*** 7,13 ****\n"), + (vec!["--context", "--context=1"], "\n*** 7,13 ****\n"), + (vec!["-5c"], "\n*** 5,15 ****\n"), + ] { + let mut cmd = cargo_bin_cmd!("diffutils"); + cmd.arg("diff").args(&args).arg(&old).arg(&new); + cmd.assert() + .code(predicate::eq(1)) + .stdout(predicate::str::contains(header)); + } + Ok(()) +} + #[test] fn function_lines() -> Result<(), Box> { let tmp_dir = tempdir()?;