summaryrefslogtreecommitdiffstats
path: root/src/features/side_by_side.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/features/side_by_side.rs')
-rw-r--r--src/features/side_by_side.rs141
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());
}
}