use itertools::Itertools;
use syntect::highlighting::Style as SyntectStyle;
use unicode_width::UnicodeWidthStr;
use crate::ansi;
use crate::cli;
use crate::config::{self, delta_unreachable, Config};
use crate::delta::DiffType;
use crate::delta::State;
use crate::edits;
use crate::features::{line_numbers, OptionValueFunction};
use crate::minusplus::*;
use crate::paint::{BgFillMethod, BgShouldFill, LineSections, Painter};
use crate::style::Style;
use crate::wrapping::{wrap_minusplus_block, wrap_zero_block};
pub fn make_feature() -> Vec<(String, OptionValueFunction)> {
builtin_feature!([
(
"side-by-side",
bool,
None,
_opt => true
),
("features", bool, None, _opt => "line-numbers"),
("line-numbers-left-format", String, None, _opt => "│{nm:^4}│".to_string()),
("line-numbers-right-format", String, None, _opt => "│{np:^4}│".to_string())
])
}
// Aliases for Minus/Plus because Left/Right and PanelSide makes
// more sense in a side-by-side context.
pub use crate::minusplus::MinusPlusIndex as PanelSide;
pub use MinusPlusIndex::Minus as Left;
pub use MinusPlusIndex::Plus as Right;
use super::line_numbers::LineNumbersData;
#[derive(Debug, Clone)]
pub struct Panel {
pub width: usize,
}
pub type LeftRight<T> = MinusPlus<T>;
pub type SideBySideData = LeftRight<Panel>;
impl SideBySideData {
/// Create a [`LeftRight<Panel>`](LeftRight<Panel>) named [`SideBySideData`].
pub fn new_sbs(decorations_width: &cli::Width, available_terminal_width: &usize) -> Self {
let panel_width = match decorations_width {
cli::Width::Fixed(w) => w / 2,
_ => available_terminal_width / 2,
};
SideBySideData::new(Panel { width: panel_width }, Panel { width: panel_width })
}
}
pub fn available_line_width(
config: &Config,
data: &line_numbers::LineNumbersData,
) -> line_numbers::SideBySideLineWidth {
let line_numbers_width = data.formatted_width();
// The width can be reduced by the line numbers and/or
// a possibly added/restored 1-wide "+/-/ " prefix.
let line_width = |side: PanelSide| {
config.side_by_side_data[side]
.width
.saturating_sub(line_numbers_width[side])
.saturating_sub(config.keep_plus_minus_markers as usize)
};
LeftRight::new(line_width(Left), line_width(Right))
}
pub fn line_is_too_long(line: &str, line_width: usize) -> bool {
debug_assert!(line.ends_with('\n'));
// graphemes will take care of newlines
line.width() > line_width
}
/// Return whether any of the input lines is too long, and a data
/// structure indicating which of the input lines are too long. This avoids
/// recalculating the length 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: &[(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)
}
#[allow(clippy::too_many_arguments)]
pub fn paint_minus_and_plus_lines_side_by_side(
lines: LeftRight<&Vec<(String, State)>>,
syntax_sections: LeftRight<Vec<LineSections<SyntectStyle>>>,
diff_sections: LeftRight<Vec<LineSections<Style>>>,
lines_have_homolog: LeftRight<Vec<bool>>,
line_alignment: Vec<(Option<usize>, Option<usize>)>,
line_numbers_data: &mut Option<LineNumbersData>,
output_buffer: &mut String,
config: &config::Config,
) {
let line_states = LeftRight::new(
lines[Left].iter().map(|(_, state)