diff --git a/src/cli.rs b/src/cli.rs index 544b25ceb..e192a3851 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -860,6 +860,16 @@ pub struct Opt { /// Display diffs in side-by-side layout. pub side_by_side: bool, + #[arg(long = "side-by-side-min-width", default_value = "0", value_name = "N")] + /// Minimum terminal width required to enable side-by-side mode. Defaults to 0. + /// + /// If the terminal width is less than this value, side-by-side mode will be disabled + /// regardless of the --side-by-side option. This allows automatic fallback to unified + /// diff when the terminal is too narrow to display side-by-side diffs comfortably. + /// Set to 0 to always use side-by-side when enabled. If not specified, side-by-side + /// will be used when enabled regardless of terminal width. + pub side_by_side_min_width: usize, + #[arg(long = "syntax-theme", value_name = "SYNTAX_THEME")] /// The syntax-highlighting theme to use. /// diff --git a/src/options/set.rs b/src/options/set.rs index 6d26d15df..5c1565c8b 100644 --- a/src/options/set.rs +++ b/src/options/set.rs @@ -214,6 +214,7 @@ pub fn set_options( show_colors, show_themes, side_by_side, + side_by_side_min_width, wrap_max_lines, wrap_right_prefix_symbol, wrap_right_percent, @@ -242,6 +243,13 @@ pub fn set_options( cli::InspectRawLines::from_str(&opt.inspect_raw_lines).unwrap(); opt.computed.paging_mode = parse_paging_mode(&opt.paging_mode); + // Apply side-by-side-min-width: disable side-by-side if terminal is too narrow. + if opt.side_by_side_min_width > 0 + && opt.computed.available_terminal_width < opt.side_by_side_min_width + { + opt.side_by_side = false; + } + // --color-only is used for interactive.diffFilter (git add -p). side-by-side, and // **-decoration-style cannot be used there (does not emit lines in 1-1 correspondence with raw git output). // See #274. @@ -608,8 +616,15 @@ fn set_widths_and_isatty(opt: &mut cli::Opt) { // If one extra character for e.g. `less --status-column` is required use "-1" // as an argument, also see #41, #10, #115 and #727. - opt.computed.available_terminal_width = - crate::utils::workarounds::windows_msys2_width_fix(term_stdout.size(), &term_stdout); + #[cfg(test)] + { + opt.computed.available_terminal_width = tests::TERMINAL_WIDTH_IN_TESTS; + } + #[cfg(not(test))] + { + opt.computed.available_terminal_width = + crate::utils::workarounds::windows_msys2_width_fix(term_stdout.size(), &term_stdout); + } let (decorations_width, background_color_extends_to_terminal_width) = match opt.width.as_deref() { @@ -863,4 +878,58 @@ pub mod tests { assert_eq!(parse_width_specifier(" - 12 ", term_width).unwrap(), 0); assert_eq!(parse_width_specifier(" 2 - 2 ", term_width).unwrap(), 0); } + + #[test] + fn test_side_by_side_min_width_disables_sbs_when_terminal_narrow() { + // Tests use TERMINAL_WIDTH_IN_TESTS = 43 + // When min-width is greater than terminal width, side-by-side should be disabled + let opt = integration_test_utils::make_options_from_args(&[ + "--side-by-side", + "--side-by-side-min-width", + "100", + ]); + assert!(!opt.side_by_side); + } + + #[test] + fn test_side_by_side_min_width_allows_sbs_when_terminal_wide_enough() { + // Tests use TERMINAL_WIDTH_IN_TESTS = 43 + // When min-width is less than or equal to terminal width, side-by-side should remain enabled + let opt = integration_test_utils::make_options_from_args(&[ + "--side-by-side", + "--side-by-side-min-width", + "40", + ]); + assert!(opt.side_by_side); + } + + #[test] + fn test_side_by_side_min_width_zero_always_allows_sbs() { + // When min-width is 0, side-by-side should always be allowed + let opt = integration_test_utils::make_options_from_args(&[ + "--side-by-side", + "--side-by-side-min-width", + "0", + ]); + assert!(opt.side_by_side); + } + + #[test] + fn test_side_by_side_min_width_from_git_config() { + use std::fs::remove_file; + let git_config_contents = b" +[delta] + side-by-side = true + side-by-side-min-width = 100 +"; + let git_config_path = "delta__test_side_by_side_min_width_from_git_config.gitconfig"; + let opt = integration_test_utils::make_options_from_args_and_git_config( + &[], + Some(git_config_contents), + Some(git_config_path), + ); + // Terminal width in tests is 43, min-width is 100, so side-by-side should be disabled + assert!(!opt.side_by_side); + remove_file(git_config_path).unwrap(); + } }