diff options
Diffstat (limited to 'src/features/side_by_side.rs')
-rw-r--r-- | src/features/side_by_side.rs | 141 |
1 files changed, 94 insertions, 47 deletions
diff --git a/src/features/side_by_side.rs b/src/features/side_by_side.rs index 9b9a10f8..cc31249d 100644 --- a/src/features/side_by_side.rs +++ b/src/features/side_by_side.rs @@ -1,5 +1,6 @@ use itertools::Itertools; use syntect::highlighting::Style as SyntectStyle; +use unicode_segmentation::UnicodeSegmentation; use crate::ansi; use crate::cli; @@ -11,6 +12,9 @@ use crate::paint::Painter; use crate::paint::{BgFillMethod, BgShouldFill}; use crate::plusminus::*; use crate::style::Style; +use crate::wrapping::wrap_zero_block; + +pub type LineSegments<'a, S> = Vec<(S, &'a str)>; pub fn make_feature() -> Vec<(String, OptionValueFunction)> { builtin_feature!([ @@ -78,17 +82,51 @@ pub fn available_line_width( LeftRight::new(line_width(Left), line_width(Right)) } +pub fn line_is_too_long(line: &str, line_width: usize) -> bool { + let line_sum = line.graphemes(true).count(); + + // `line_sum` is too large, because both a leading "+/-/ " and a trailing + // newline are present, counted, but are never printed. So allow two more + // characters. + line_sum > line_width + 2 +} + +/// Return whether any of the input lines is too long, and a data +/// structure indicating which are too long. This avoids +/// calculating the length again later. +pub fn has_long_lines( + lines: &LeftRight<&Vec<(String, State)>>, + line_width: &line_numbers::SideBySideLineWidth, +) -> (bool, LeftRight<Vec<bool>>) { + let mut wrap_any = LeftRight::default(); + let mut wrapping_lines = LeftRight::default(); + + let mut check_if_too_long = |side| { + let lines_side: &Vec<(String, State)> = lines[side]; + wrapping_lines[side] = lines_side + .iter() + .map(|(line, _)| line_is_too_long(line, line_width[side])) + .inspect(|b| wrap_any[side] |= b) + .collect(); + }; + + check_if_too_long(Left); + check_if_too_long(Right); + + (wrap_any[Left] || wrap_any[Right], wrapping_lines) +} + /// Emit a sequence of minus and plus lines in side-by-side mode. #[allow(clippy::too_many_arguments)] pub fn paint_minus_and_plus_lines_side_by_side<'a>( - syntax_left_right: PlusMinus<Vec<Vec<(SyntectStyle, &str)>>>, - diff_left_right: PlusMinus<Vec<Vec<(Style, &str)>>>, - states_left_right: PlusMinus<Vec<&'a State>>, + syntax_left_right: LeftRight<Vec<LineSegments<'a, SyntectStyle>>>, + diff_left_right: LeftRight<Vec<LineSegments<'a, Style>>>, + states_left_right: LeftRight<Vec<State>>, line_alignment: Vec<(Option<usize>, Option<usize>)>, output_buffer: &mut String, config: &Config, line_numbers_data: &mut Option<&mut line_numbers::LineNumbersData>, - background_color_extends_to_terminal_width: PlusMinus<BgShouldFill>, + background_color_extends_to_terminal_width: LeftRight<BgShouldFill>, ) { for (minus_line_index, plus_line_index) in line_alignment { output_buffer.push_str(&paint_left_panel_minus_line( @@ -96,15 +134,10 @@ pub fn paint_minus_and_plus_lines_side_by_side<'a>( &syntax_left_right[Left], &diff_left_right[Left], match minus_line_index { - Some(i) => states_left_right[Left][i], + Some(i) => &states_left_right[Left][i], None => &State::HunkMinus(None), }, line_numbers_data, - if config.keep_plus_minus_markers { - Some(config.minus_style.paint("-")) - } else { - None - }, background_color_extends_to_terminal_width[Left], config, )); @@ -113,15 +146,10 @@ pub fn paint_minus_and_plus_lines_side_by_side<'a>( &syntax_left_right[Right], &diff_left_right[Right], match plus_line_index { - Some(i) => states_left_right[Right][i], + Some(i) => &states_left_right[Right][i], None => &State::HunkPlus(None), }, line_numbers_data, - if config.keep_plus_minus_markers { - Some(config.plus_style.paint("+")) - } else { - None - }, background_color_extends_to_terminal_width[Right], config, )); @@ -130,25 +158,36 @@ pub fn paint_minus_and_plus_lines_side_by_side<'a>( } #[allow(clippy::too_many_arguments)] -pub fn paint_zero_lines_side_by_side( - syntax_style_sections: Vec<Vec<(SyntectStyle, &str)>>, - diff_style_sections: Vec<Vec<(Style, &str)>>, +pub fn paint_zero_lines_side_by_side<'a>( + raw_line: &str, + syntax_style_sections: Vec<LineSegments<'a, SyntectStyle>>, + diff_style_sections: Vec<LineSegments<'a, Style>>, output_buffer: &mut String, config: &Config, line_numbers_data: &mut Option<&mut line_numbers::LineNumbersData>, painted_prefix: Option<ansi_term::ANSIString>, background_color_extends_to_terminal_width: BgShouldFill, ) { - let state = State::HunkZero; + let states = vec![State::HunkZero]; + + let (states, syntax_style_sections, diff_style_sections) = wrap_zero_block( + config, + raw_line, + states, + syntax_style_sections, + diff_style_sections, + line_numbers_data, + ); - for (line_index, (syntax_sections, diff_sections)) in syntax_style_sections - .iter() + for (line_index, ((syntax_sections, diff_sections), state)) in syntax_style_sections + .into_iter() .zip_eq(diff_style_sections.iter()) + .zip_eq(states.into_iter()) .enumerate() { for panel_side in &[Left, Right] { let (mut panel_line, panel_line_is_empty) = Painter::paint_line( - syntax_sections, + &syntax_sections, diff_sections, &state, line_numbers_data, @@ -168,7 +207,7 @@ pub fn paint_zero_lines_side_by_side( ); output_buffer.push_str(&panel_line); - if panel_side == &Left { + if *panel_side == Left && state != State::HunkZeroWrapped { // TODO: Avoid doing the superimpose_style_sections work twice. // HACK: These are getting incremented twice, so knock them back down once. if let Some(d) = line_numbers_data.as_mut() { @@ -184,11 +223,10 @@ pub fn paint_zero_lines_side_by_side( #[allow(clippy::too_many_arguments)] fn paint_left_panel_minus_line<'a>( line_index: Option<usize>, - syntax_style_sections: &[Vec<(SyntectStyle, &str)>], - diff_style_sections: &[Vec<(Style, &str)>], + syntax_style_sections: &[LineSegments<'a, SyntectStyle>], + diff_style_sections: &[LineSegments<'a, Style>], state: &'a State, line_numbers_data: &mut Option<&mut line_numbers::LineNumbersData>, - painted_prefix: Option<ansi_term::ANSIString>, background_color_extends_to_terminal_width: BgShouldFill, config: &Config, ) -> String { @@ -199,7 +237,6 @@ fn paint_left_panel_minus_line<'a>( state, line_numbers_data, Left, - painted_prefix, config, ); pad_panel_line_to_width( @@ -219,11 +256,10 @@ fn paint_left_panel_minus_line<'a>( #[allow(clippy::too_many_arguments)] fn paint_right_panel_plus_line<'a>( line_index: Option<usize>, - syntax_style_sections: &[Vec<(SyntectStyle, &str)>], - diff_style_sections: &[Vec<(Style, &str)>], + syntax_style_sections: &[LineSegments<'a, SyntectStyle>], + diff_style_sections: &[LineSegments<'a, Style>], state: &'a State, line_numbers_data: &mut Option<&mut line_numbers::LineNumbersData>, - painted_prefix: Option<ansi_term::ANSIString>, background_color_extends_to_terminal_width: BgShouldFill, config: &Config, ) -> String { @@ -234,7 +270,6 @@ fn paint_right_panel_plus_line<'a>( state, line_numbers_data, Right, - painted_prefix, config, ); @@ -252,10 +287,10 @@ fn paint_right_panel_plus_line<'a>( panel_line } -fn get_right_fill_style_for_panel( +fn get_right_fill_style_for_panel<'a>( line_is_empty: bool, line_index: Option<usize>, - diff_style_sections: &[Vec<(Style, &str)>], + diff_style_sections: &[LineSegments<'a, Style>], state: &State, panel_side: PanelSide, background_color_extends_to_terminal_width: BgShouldFill, @@ -311,14 +346,13 @@ fn get_right_fill_style_for_panel( // and then only emit the right field (which has a None number, i.e. blank). However, it will also // increment the minus line number, so we need to knock that back down. #[allow(clippy::too_many_arguments)] -fn paint_minus_or_plus_panel_line( +fn paint_minus_or_plus_panel_line<'a>( line_index: Option<usize>, - syntax_style_sections: &[Vec<(SyntectStyle, &str)>], - diff_style_sections: &[Vec<(Style, &str)>], + syntax_style_sections: &[LineSegments<'a, SyntectStyle>], + diff_style_sections: &[LineSegments<'a, Style>], state: &State, line_numbers_data: &mut Option<&mut line_numbers::LineNumbersData>, panel_side: PanelSide, - painted_prefix: Option<ansi_term::ANSIString>, config: &Config, ) -> (String, bool) { let (empty_line_syntax_sections, empty_line_diff_sections) = (Vec::new(), Vec::new()); @@ -343,6 +377,14 @@ fn paint_minus_or_plus_panel_line( ) }; + let painted_prefix = match (config.keep_plus_minus_markers, panel_side, state) { + (true, _, State::HunkPlusWrapped) => Some(config.plus_style.paint(" ")), + (true, _, State::HunkMinusWrapped) => Some(config.minus_style.paint(" ")), + (true, Left, _) => Some(config.minus_style.paint("-")), + (true, Right, _) => Some(config.plus_style.paint("+")), + _ => None, + }; + let (line, line_is_empty) = Painter::paint_line( line_syntax_sections, line_diff_sections, @@ -375,11 +417,11 @@ fn paint_minus_or_plus_panel_line( /// done with spaces. The right panel can be filled with spaces or using ANSI sequences /// instructing the terminal emulator to fill the background color rightwards. #[allow(clippy::too_many_arguments, clippy::comparison_chain)] -fn pad_panel_line_to_width( +fn pad_panel_line_to_width<'a>( panel_line: &mut String, panel_line_is_empty: bool, line_index: Option<usize>, - diff_style_sections: &[Vec<(Style, &str)>], + diff_style_sections: &[LineSegments<'a, Style>], state: &State, panel_side: PanelSide, background_color_extends_to_terminal_width: BgShouldFill, @@ -457,6 +499,8 @@ pub mod tests { fn test_two_minus_lines_truncated() { let mut config = make_config_from_args(&[ "--side-by-side", + "--wrap-max-lines", + "0", "--width", "28", "--line-fill-method=spaces", @@ -476,19 +520,22 @@ pub mod tests { let mut lines = output.lines().skip(7); let (line_1, line_2) = (lines.next().unwrap(), lines.next().unwrap()); let sac = strip_ansi_codes; // alias to help with `cargo fmt`-ing: - assert_eq!("│ │ │ 1 │a = 1", sac(line_1)); - assert_eq!("│ │ │ 2 │b = 234567", sac(line_2)); + assert_eq!("│ │ │ 1 │a = 1 ", sac(line_1)); + assert_eq!("│ │ │ 2 │b = 234567 ", sac(line_2)); } #[test] fn test_two_plus_lines_truncated() { let mut config = make_config_from_args(&[ "--side-by-side", + "--wrap-max-lines", + "0", "--width", "30", "--line-fill-method=spaces", ]); config.truncation_symbol = ">".into(); + let output = run_delta(TWO_PLUS_LINES_DIFF, &config); let mut lines = output.lines().skip(7); let (line_1, line_2) = (lines.next().unwrap(), lines.next().unwrap()); @@ -498,12 +545,11 @@ pub mod tests { #[test] fn test_two_plus_lines_exact_fit() { - let mut config = make_config_from_args(&["--side-by-side", "--width", "32"]); - config.truncation_symbol = ">".into(); + let config = make_config_from_args(&["--side-by-side", "--width", "32"]); let output = run_delta(TWO_PLUS_LINES_DIFF, &config); let mut lines = output.lines().skip(7); let (line_1, line_2) = (lines.next().unwrap(), lines.next().unwrap()); - assert_eq!("│ │ │ 1 │a = 1", strip_ansi_codes(line_1)); + assert_eq!("│ │ │ 1 │a = 1 ", strip_ansi_codes(line_1)); assert_eq!("│ │ │ 2 │b = 234567", strip_ansi_codes(line_2)); } @@ -513,7 +559,8 @@ pub mod tests { let output = run_delta(ONE_MINUS_ONE_PLUS_LINE_DIFF, &config); let output = strip_ansi_codes(&output); let mut lines = output.lines().skip(7); - assert_eq!("│ 1 │a = 1 │ 1 │a = 1", lines.next().unwrap()); - assert_eq!("│ 2 │b = 2 │ 2 │bb = 2", lines.next().unwrap()); + let mut lnu = move || lines.next().unwrap(); // for cargo fmt + assert_eq!("│ 1 │a = 1 │ 1 │a = 1", lnu()); + assert_eq!("│ 2 │b = 2 │ 2 │bb = 2 ", lnu()); } } |