From db04d5e4ec02ea30e208c64f6f77e1cbfccb2688 Mon Sep 17 00:00:00 2001 From: Thomas Otto Date: Sat, 30 Jan 2021 12:05:41 +0100 Subject: General PlusMinus data structure Can be indexed with Minus/Plus or in a side-by-side context with Left/Right to represent the left/right Panels. --- src/config.rs | 2 +- src/features/line_numbers.rs | 57 +++++++++++++---------------- src/features/side_by_side.rs | 85 +++++++++++++++++++++++++++----------------- src/main.rs | 1 + src/paint.rs | 21 ++++++++--- src/plusminus.rs | 51 ++++++++++++++++++++++++++ 6 files changed, 147 insertions(+), 70 deletions(-) create mode 100644 src/plusminus.rs (limited to 'src') diff --git a/src/config.rs b/src/config.rs index 326153b2..1b035f2b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -166,7 +166,7 @@ impl From for Config { process::exit(1); }); - let side_by_side_data = side_by_side::SideBySideData::new( + let side_by_side_data = side_by_side::SideBySideData::new_sbs( &opt.computed.decorations_width, &opt.computed.available_terminal_width, ); diff --git a/src/features/line_numbers.rs b/src/features/line_numbers.rs index c464f8c7..3032bc00 100644 --- a/src/features/line_numbers.rs +++ b/src/features/line_numbers.rs @@ -6,9 +6,10 @@ use regex::Regex; use crate::config; use crate::delta::State; use crate::features::hyperlinks; -use crate::features::side_by_side; +use crate::features::side_by_side::PanelSide; use crate::features::OptionValueFunction; use crate::format; +use crate::plusminus::*; use crate::style::Style; pub fn make_feature() -> Vec<(String, OptionValueFunction)> { @@ -65,11 +66,11 @@ pub fn make_feature() -> Vec<(String, OptionValueFunction)> { pub fn format_and_paint_line_numbers<'a>( line_numbers_data: &'a mut LineNumbersData, state: &State, - side_by_side_panel: Option, + side_by_side_panel: Option, config: &'a config::Config, ) -> Vec> { - let m_ref = &mut line_numbers_data.hunk_minus_line_number; - let p_ref = &mut line_numbers_data.hunk_plus_line_number; + let nr_left = line_numbers_data.line_number[Minus]; + let nr_right = line_numbers_data.line_number[Plus]; let (minus_style, zero_style, plus_style) = ( config.line_numbers_minus_style, config.line_numbers_zero_style, @@ -77,20 +78,17 @@ pub fn format_and_paint_line_numbers<'a>( ); let ((minus_number, plus_number), (minus_style, plus_style)) = match state { State::HunkMinus(_) => { - let m = *m_ref; - *m_ref += 1; - ((Some(m), None), (minus_style, plus_style)) + line_numbers_data.line_number[Minus] += 1; + ((Some(nr_left), None), (minus_style, plus_style)) } State::HunkZero => { - let (m, p) = (*m_ref, *p_ref); - *m_ref += 1; - *p_ref += 1; - ((Some(m), Some(p)), (zero_style, zero_style)) + line_numbers_data.line_number[Minus] += 1; + line_numbers_data.line_number[Plus] += 1; + ((Some(nr_left), Some(nr_right)), (zero_style, zero_style)) } State::HunkPlus(_) => { - let p = *p_ref; - *p_ref += 1; - ((None, Some(p)), (minus_style, plus_style)) + line_numbers_data.line_number[Plus] += 1; + ((None, Some(nr_right)), (minus_style, plus_style)) } _ => return Vec::new(), }; @@ -99,14 +97,14 @@ pub fn format_and_paint_line_numbers<'a>( let (emit_left, emit_right) = match (config.side_by_side, side_by_side_panel) { (false, _) => (true, true), - (true, Some(side_by_side::PanelSide::Left)) => (true, false), - (true, Some(side_by_side::PanelSide::Right)) => (false, true), + (true, Some(PanelSide::Left)) => (true, false), + (true, Some(PanelSide::Right)) => (false, true), (true, None) => unreachable!(), }; if emit_left { formatted_numbers.extend(format_and_paint_line_number_field( - &line_numbers_data.left_format_data, + &line_numbers_data.format_data[PanelSide::Left], &config.line_numbers_left_style, minus_number, plus_number, @@ -120,7 +118,7 @@ pub fn format_and_paint_line_numbers<'a>( if emit_right { formatted_numbers.extend(format_and_paint_line_number_field( - &line_numbers_data.right_format_data, + &line_numbers_data.format_data[PanelSide::Right], &config.line_numbers_right_style, minus_number, plus_number, @@ -140,10 +138,8 @@ lazy_static! { #[derive(Default)] pub struct LineNumbersData<'a> { - pub left_format_data: format::FormatStringData<'a>, - pub right_format_data: format::FormatStringData<'a>, - pub hunk_minus_line_number: usize, - pub hunk_plus_line_number: usize, + pub format_data: PlusMinus>, + pub line_number: PlusMinus, pub hunk_max_line_number_width: usize, pub plus_file: String, } @@ -153,16 +149,11 @@ pub struct LineNumbersData<'a> { impl<'a> LineNumbersData<'a> { pub fn from_format_strings(left_format: &'a str, right_format: &'a str) -> LineNumbersData<'a> { Self { - left_format_data: format::parse_line_number_format( - left_format, - &*LINE_NUMBERS_PLACEHOLDER_REGEX, + format_data: PlusMinus::new( + format::parse_line_number_format(left_format, &*LINE_NUMBERS_PLACEHOLDER_REGEX), + format::parse_line_number_format(right_format, &*LINE_NUMBERS_PLACEHOLDER_REGEX), ), - right_format_data: format::parse_line_number_format( - right_format, - &*LINE_NUMBERS_PLACEHOLDER_REGEX, - ), - hunk_minus_line_number: 0, - hunk_plus_line_number: 0, + line_number: PlusMinus::new(0, 0), hunk_max_line_number_width: 0, plus_file: "".to_string(), } @@ -172,8 +163,8 @@ impl<'a> LineNumbersData<'a> { pub fn initialize_hunk(&mut self, line_numbers: &[(usize, usize)], plus_file: String) { // Typically, line_numbers has length 2: an entry for the minus file, and one for the plus // file. In the case of merge commits, it may be longer. - self.hunk_minus_line_number = line_numbers[0].0; - self.hunk_plus_line_number = line_numbers[line_numbers.len() - 1].0; + self.line_number = + PlusMinus::new(line_numbers[0].0, line_numbers[line_numbers.len() - 1].0); let hunk_max_line_number = line_numbers.iter().map(|(n, d)| n + d).max().unwrap(); self.hunk_max_line_number_width = 1 + (hunk_max_line_number as f64).log10().floor() as usize; diff --git a/src/features/side_by_side.rs b/src/features/side_by_side.rs index 77f48933..ae5911ef 100644 --- a/src/features/side_by_side.rs +++ b/src/features/side_by_side.rs @@ -1,3 +1,5 @@ +use std::ops::{Index, IndexMut}; + use itertools::Itertools; use syntect::highlighting::Style as SyntectStyle; @@ -8,6 +10,7 @@ use crate::delta::State; use crate::features::line_numbers; use crate::features::OptionValueFunction; use crate::paint::Painter; +use crate::plusminus::*; use crate::style::Style; pub fn make_feature() -> Vec<(String, OptionValueFunction)> { @@ -24,49 +27,67 @@ pub fn make_feature() -> Vec<(String, OptionValueFunction)> { ]) } +pub struct Panel { + pub width: usize, + pub offset: usize, +} + +// Same as plusminus::PlusMinusIndex but with Left/Right instead +// of Minus/Plus enum names. Only used in a side-by-side context. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum PanelSide { Left, Right, } -pub struct SideBySideData { - pub left_panel: Panel, - pub right_panel: Panel, +use PanelSide::*; + +impl Index for PlusMinus { + type Output = T; + fn index(&self, side: PanelSide) -> &Self::Output { + match side { + PanelSide::Left => &self.minus, + PanelSide::Right => &self.plus, + } + } } -pub struct Panel { - pub width: usize, - pub offset: usize, +impl IndexMut for PlusMinus { + fn index_mut(&mut self, side: PanelSide) -> &mut Self::Output { + match side { + PanelSide::Left => &mut self.minus, + PanelSide::Right => &mut self.plus, + } + } } +pub type SideBySideData = PlusMinus; + impl SideBySideData { - pub fn new(decorations_width: &cli::Width, available_terminal_width: &usize) -> Self { + 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, }; - Self { - left_panel: Panel { + SideBySideData::new( + Panel { width: panel_width, offset: 0, }, - right_panel: Panel { + Panel { width: panel_width, offset: 0, }, - } + ) } } /// 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>( - minus_syntax_style_sections: Vec>, - minus_diff_style_sections: Vec>, - minus_states: Vec<&'a State>, - plus_syntax_style_sections: Vec>, - plus_diff_style_sections: Vec>, - plus_states: Vec<&'a State>, + syntax_left_right: PlusMinus>>, + diff_left_right: PlusMinus>>, + states_left_right: PlusMinus>, line_alignment: Vec<(Option, Option)>, output_buffer: &mut String, config: &Config, @@ -76,10 +97,10 @@ pub fn paint_minus_and_plus_lines_side_by_side<'a>( for (minus_line_index, plus_line_index) in line_alignment { output_buffer.push_str(&paint_left_panel_minus_line( minus_line_index, - &minus_syntax_style_sections, - &minus_diff_style_sections, + &syntax_left_right[Left], + &diff_left_right[Left], match minus_line_index { - Some(i) => minus_states[i], + Some(i) => states_left_right[Left][i], None => &State::HunkMinus(None), }, line_numbers_data, @@ -93,10 +114,10 @@ pub fn paint_minus_and_plus_lines_side_by_side<'a>( )); output_buffer.push_str(&paint_right_panel_plus_line( plus_line_index, - &plus_syntax_style_sections, - &plus_diff_style_sections, + &syntax_left_right[Right], + &diff_left_right[Right], match plus_line_index { - Some(i) => plus_states[i], + Some(i) => states_left_right[Right][i], None => &State::HunkPlus(None), }, line_numbers_data, @@ -140,8 +161,8 @@ pub fn paint_zero_lines_side_by_side( // 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() { - d.hunk_minus_line_number -= 1; - d.hunk_plus_line_number -= 1; + d.line_number[Left] -= 1; + d.line_number[Right] -= 1; } right_pad_left_panel_line( &mut left_panel_line, @@ -194,7 +215,7 @@ fn paint_left_panel_minus_line<'a>( diff_style_sections, state, line_numbers_data, - PanelSide::Left, + Left, painted_prefix, config, ); @@ -228,7 +249,7 @@ fn paint_right_panel_plus_line<'a>( diff_style_sections, state, line_numbers_data, - PanelSide::Right, + Right, painted_prefix, config, ); @@ -341,12 +362,12 @@ fn paint_minus_or_plus_panel_line( (s, t) if s == t => {} (State::HunkPlus(_), State::HunkMinus(_)) => { if let Some(d) = line_numbers_data.as_mut() { - d.hunk_minus_line_number -= 1; + d.line_number[Left] -= 1; } } (State::HunkMinus(_), State::HunkPlus(_)) => { if let Some(d) = line_numbers_data.as_mut() { - d.hunk_plus_line_number -= 1; + d.line_number[Right] -= 1; } } _ => unreachable!(), @@ -385,7 +406,7 @@ fn right_pad_left_panel_line( }; // Pad with (maybe painted) spaces to the panel width. let text_width = ansi::measure_text_width(panel_line); - let panel_width = config.side_by_side_data.left_panel.width; + let panel_width = config.side_by_side_data[Left].width; if text_width < panel_width { let fill_style = get_right_fill_style_for_left_panel( panel_line_is_empty, @@ -420,8 +441,8 @@ fn right_fill_right_panel_line( config: &Config, ) { *panel_line = ansi::truncate_str( - panel_line, - config.side_by_side_data.right_panel.width, + &panel_line, + config.side_by_side_data[Right].width, &config.truncation_symbol, ) .to_string(); diff --git a/src/main.rs b/src/main.rs index 08d31fd8..1fdf9365 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,6 +20,7 @@ mod handlers; mod options; mod paint; mod parse_style; +mod plusminus; mod style; mod subcommands; mod syntect_color; diff --git a/src/paint.rs b/src/paint.rs index a575d3bf..7b31047d 100644 --- a/src/paint.rs +++ b/src/paint.rs @@ -12,7 +12,9 @@ use crate::delta::State; use crate::edits; use crate::features::line_numbers; use crate::features::side_by_side; +use crate::features::side_by_side::PanelSide; use crate::paint::superimpose_style_sections::superimpose_style_sections; +use crate::plusminus::*; use crate::style::Style; pub struct Painter<'a> { @@ -138,13 +140,24 @@ impl<'a> Painter<'a> { Self::get_diff_style_sections(&self.minus_lines, &self.plus_lines, self.config); if self.config.side_by_side { - side_by_side::paint_minus_and_plus_lines_side_by_side( + let syntax_left_right = PlusMinus::new( minus_line_syntax_style_sections, - minus_line_diff_style_sections, - self.minus_lines.iter().map(|(_, state)| state).collect(), plus_line_syntax_style_sections, + ); + let diff_left_right = PlusMinus::new( + minus_line_diff_style_sections, plus_line_diff_style_sections, + ); + + let states_left_right = PlusMinus::new( + self.minus_lines.iter().map(|(_, state)| state).collect(), self.plus_lines.iter().map(|(_, state)| state).collect(), + ); + + side_by_side::paint_minus_and_plus_lines_side_by_side( + syntax_left_right, + diff_left_right, + states_left_right, line_alignment, &mut self.output_buffer, self.config, @@ -405,7 +418,7 @@ impl<'a> Painter<'a> { diff_sections: &[(Style, &str)], state: &State, line_numbers_data: &mut Option<&mut line_numbers::LineNumbersData>, - side_by_side_panel: Option, + side_by_side_panel: Option, painted_prefix: Option, config: &config::Config, ) -> (String, bool) { diff --git a/src/plusminus.rs b/src/plusminus.rs new file mode 100644 index 00000000..0cdeaea9 --- /dev/null +++ b/src/plusminus.rs @@ -0,0 +1,51 @@ +use std::ops::{Index, IndexMut}; + +// Struct to represent data related to removed/minus and added/plus lines +// which can be indexed with PlusMinusIndex::{Minus, Plus}. +#[derive(Debug, PartialEq, Eq)] +pub struct PlusMinus { + pub minus: T, + pub plus: T, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PlusMinusIndex { + Minus, + Plus, +} + +pub use PlusMinusIndex::*; + +impl Index for PlusMinus { + type Output = T; + fn index(&self, side: PlusMinusIndex) -> &Self::Output { + match side { + Minus => &self.minus, + Plus => &self.plus, + } + } +} + +impl IndexMut for PlusMinus { + fn index_mut(&mut self, side: PlusMinusIndex) -> &mut Self::Output { + match side { + Minus => &mut self.minus, + Plus => &mut self.plus, + } + } +} + +impl PlusMinus { + pub fn new(minus: T, plus: T) -> Self { + PlusMinus { minus, plus } + } +} + +impl Default for PlusMinus { + fn default() -> Self { + Self { + minus: T::default(), + plus: T::default(), + } + } +} -- cgit v1.2.3