From 38ed1fd07df482a26a7968639669d0191c7ae3e2 Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Sun, 27 Dec 2020 16:19:39 +0000 Subject: Refactor: handle_hunk_header_line --- src/delta.rs | 161 +-------------------------------- src/features/line_numbers.rs | 2 +- src/hunk_header.rs | 211 +++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 1 + 4 files changed, 214 insertions(+), 161 deletions(-) create mode 100644 src/hunk_header.rs diff --git a/src/delta.rs b/src/delta.rs index 86508a73..238ed3e3 100644 --- a/src/delta.rs +++ b/src/delta.rs @@ -1,5 +1,4 @@ use std::borrow::Cow; -use std::fmt::Write as FmtWrite; use std::io::BufRead; use std::io::Write; @@ -12,6 +11,7 @@ use crate::config::Config; use crate::draw; use crate::features; use crate::format; +use crate::hunk_header::handle_hunk_header_line; use crate::paint::Painter; use crate::parse; use crate::style::{self, DecorationStyle}; @@ -403,165 +403,6 @@ fn handle_generic_file_meta_header_line( Ok(()) } -fn handle_hunk_header_line( - painter: &mut Painter, - line: &str, - raw_line: &str, - plus_file: &str, - config: &Config, -) -> std::io::Result<()> { - let decoration_ansi_term_style; - let mut pad = false; - let draw_fn = match config.hunk_header_style.decoration_style { - DecorationStyle::Box(style) => { - pad = true; - decoration_ansi_term_style = style; - draw::write_boxed - } - DecorationStyle::BoxWithUnderline(style) => { - pad = true; - decoration_ansi_term_style = style; - draw::write_boxed_with_underline - } - DecorationStyle::BoxWithOverline(style) => { - pad = true; - decoration_ansi_term_style = style; - draw::write_boxed // TODO: not implemented - } - DecorationStyle::BoxWithUnderOverline(style) => { - pad = true; - decoration_ansi_term_style = style; - draw::write_boxed // TODO: not implemented - } - DecorationStyle::Underline(style) => { - decoration_ansi_term_style = style; - draw::write_underlined - } - DecorationStyle::Overline(style) => { - decoration_ansi_term_style = style; - draw::write_overlined - } - DecorationStyle::UnderOverline(style) => { - decoration_ansi_term_style = style; - draw::write_underoverlined - } - DecorationStyle::NoDecoration => { - decoration_ansi_term_style = ansi_term::Style::new(); - draw::write_no_decoration - } - }; - let (raw_code_fragment, line_numbers) = parse::parse_hunk_header(&line); - // Emit the hunk header, with any requested decoration - if config.hunk_header_style.is_raw { - if config.hunk_header_style.decoration_style != DecorationStyle::NoDecoration { - writeln!(painter.writer)?; - } - draw_fn( - painter.writer, - &format!("{}{}", line, if pad { " " } else { "" }), - &format!("{}{}", raw_line, if pad { " " } else { "" }), - &config.decorations_width, - config.hunk_header_style, - decoration_ansi_term_style, - )?; - } else if config.hunk_header_style.is_omitted { - writeln!(painter.writer)?; - } else { - // Adjust the hunk-header-line before paint_lines. - // However in the case of color_only mode, - // we'll just use raw_line because we can't change raw_line structure. - let line = if config.color_only { - format!(" {}", &line) - } else { - match painter.prepare(&raw_code_fragment, false) { - s if !s.is_empty() => format!("{} ", s), - s => s, - } - }; - - // Prints the new line below hunk-header-line. - // However in the case of color_only mode, - // we won't print it because we can't change raw_line structure. - if !config.color_only { - writeln!(painter.writer)?; - } - if !line.is_empty() || config.hunk_header_style_include_file_path { - if config.hunk_header_style_include_file_path { - let _ = write!( - &mut painter.output_buffer, - "{}{} ", - config.file_style.paint(plus_file), - if line.is_empty() { "" } else { ":" }, - ); - }; - if !line.is_empty() { - let lines = vec![(line, State::HunkHeader)]; - let syntax_style_sections = Painter::get_syntax_style_sections_for_lines( - &lines, - &State::HunkHeader, - &mut painter.highlighter, - &painter.config, - ); - Painter::paint_lines( - syntax_style_sections, - vec![vec![(config.hunk_header_style, &lines[0].0)]], // TODO: compute style from state - [State::HunkHeader].iter(), - &mut painter.output_buffer, - config, - &mut None, - None, - None, - Some(false), - ); - painter.output_buffer.pop(); // trim newline - } - draw_fn( - painter.writer, - &painter.output_buffer, - &painter.output_buffer, - &config.decorations_width, - config.null_style, - decoration_ansi_term_style, - )?; - painter.output_buffer.clear(); - } - }; - - // Emit a full line-numbering - if config.line_numbers { - painter - .line_numbers_data - .initialize_hunk(line_numbers, plus_file.to_string()); - // Emit a single line number. - // However with raw mode or color-only mode, - // we should prevent the output from creating new line for printing line number. - } else if config.line_numbers_show_first_line_number - && !config.hunk_header_style.is_raw - && !config.color_only - { - let plus_line_number = line_numbers[line_numbers.len() - 1].0; - let formatted_plus_line_number = if config.hyperlinks { - features::hyperlinks::format_osc8_file_hyperlink( - plus_file, - Some(plus_line_number), - &format!("{}", plus_line_number), - config, - ) - } else { - Cow::from(format!("{}", plus_line_number)) - }; - match config.hunk_header_style.decoration_ansi_term_style() { - Some(style) => writeln!( - painter.writer, - "{}", - style.paint(formatted_plus_line_number) - )?, - None => writeln!(painter.writer, "{}", formatted_plus_line_number)?, - } - } - Ok(()) -} - /// Handle a hunk line, i.e. a minus line, a plus line, or an unchanged line. // In the case of a minus or plus line, we store the line in a // buffer. When we exit the changed region we process the collected diff --git a/src/features/line_numbers.rs b/src/features/line_numbers.rs index e2edae3e..571ff3ff 100644 --- a/src/features/line_numbers.rs +++ b/src/features/line_numbers.rs @@ -188,7 +188,7 @@ impl<'a> LineNumbersData<'a> { } /// Initialize line number data for a hunk. - pub fn initialize_hunk(&mut self, line_numbers: Vec<(usize, usize)>, plus_file: String) { + pub fn initialize_hunk(&mut self, line_numbers: &Vec<(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; diff --git a/src/hunk_header.rs b/src/hunk_header.rs new file mode 100644 index 00000000..ec4db03a --- /dev/null +++ b/src/hunk_header.rs @@ -0,0 +1,211 @@ +use std::borrow::Cow; +use std::fmt::Write as FmtWrite; +use std::io::Write; + +use crate::cli; +use crate::config::Config; +use crate::delta; +use crate::draw; +use crate::features; +use crate::paint::Painter; +use crate::parse; +use crate::style::{self, DecorationStyle}; + +/// Emit the hunk header, with any requested decoration. +pub fn handle_hunk_header_line( + painter: &mut Painter, + line: &str, + raw_line: &str, + plus_file: &str, + config: &Config, +) -> std::io::Result<()> { + let (raw_code_fragment, line_numbers) = parse::parse_hunk_header(&line); + if config.line_numbers { + painter + .line_numbers_data + .initialize_hunk(&line_numbers, plus_file.to_string()); + } + + if config.hunk_header_style.is_raw { + _write_hunk_header_raw(painter, line, raw_line, config)?; + } else if config.hunk_header_style.is_omitted { + writeln!(painter.writer)?; + } else { + _write_hunk_header(&raw_code_fragment, painter, line, plus_file, config)?; + }; + + // Do not emit a line number in color-only mode, since the extra line would break the + // requirement for output lines to be in one-to-one correspondence with input lines. + if !config.line_numbers + && config.line_numbers_show_first_line_number + && !config.hunk_header_style.is_raw + && !config.color_only + { + _write_line_number(&line_numbers, painter, plus_file, config)?; + } + Ok(()) +} + +fn _write_hunk_header_raw( + painter: &mut Painter, + line: &str, + raw_line: &str, + config: &Config, +) -> std::io::Result<()> { + let (mut draw_fn, pad, decoration_ansi_term_style) = _get_draw_fn(config); + if config.hunk_header_style.decoration_style != DecorationStyle::NoDecoration { + writeln!(painter.writer)?; + } + draw_fn( + painter.writer, + &format!("{}{}", line, if pad { " " } else { "" }), + &format!("{}{}", raw_line, if pad { " " } else { "" }), + &config.decorations_width, + config.hunk_header_style, + decoration_ansi_term_style, + )?; + Ok(()) +} + +fn _write_hunk_header( + raw_code_fragment: &str, + painter: &mut Painter, + line: &str, + plus_file: &str, + config: &Config, +) -> std::io::Result<()> { + let (mut draw_fn, _, decoration_ansi_term_style) = _get_draw_fn(config); + // Adjust the hunk-header-line before paint_lines. + // However in the case of color_only mode, + // we'll just use raw_line because we can't change raw_line structure. + let line = if config.color_only { + format!(" {}", &line) + } else { + match painter.prepare(&raw_code_fragment, false) { + s if !s.is_empty() => format!("{} ", s), + s => s, + } + }; + + // Add a blank line below the hunk-header-line for readability, unless + // color_only mode is active. + if !config.color_only { + writeln!(painter.writer)?; + } + + let mut have_hunk_header = false; + if config.hunk_header_style_include_file_path { + let _ = write!( + &mut painter.output_buffer, + "{}{} ", + config.file_style.paint(plus_file), + if line.is_empty() { "" } else { ":" }, + ); + have_hunk_header = true; + }; + if !line.is_empty() { + let lines = vec![(line, delta::State::HunkHeader)]; + let syntax_style_sections = Painter::get_syntax_style_sections_for_lines( + &lines, + &delta::State::HunkHeader, + &mut painter.highlighter, + &painter.config, + ); + Painter::paint_lines( + syntax_style_sections, + vec![vec![(config.hunk_header_style, &lines[0].0)]], // TODO: compute style from state + [delta::State::HunkHeader].iter(), + &mut painter.output_buffer, + config, + &mut None, + None, + None, + Some(false), + ); + painter.output_buffer.pop(); // trim newline + have_hunk_header = true; + } + if have_hunk_header { + draw_fn( + painter.writer, + &painter.output_buffer, + &painter.output_buffer, + &config.decorations_width, + config.null_style, + decoration_ansi_term_style, + )?; + painter.output_buffer.clear(); + } + + Ok(()) +} + +fn _get_draw_fn( + config: &Config, +) -> ( + Box< + dyn FnMut( + &mut dyn Write, + &str, + &str, + &cli::Width, + style::Style, + ansi_term::Style, + ) -> std::io::Result<()>, + >, + bool, + ansi_term::Style, +) { + match config.hunk_header_style.decoration_style { + DecorationStyle::Box(style) => (Box::new(draw::write_boxed), true, style), + DecorationStyle::BoxWithUnderline(style) => { + (Box::new(draw::write_boxed_with_underline), true, style) + } + DecorationStyle::BoxWithOverline(style) => { + // TODO: not implemented + (Box::new(draw::write_boxed), true, style) + } + DecorationStyle::BoxWithUnderOverline(style) => { + // TODO: not implemented + (Box::new(draw::write_boxed), true, style) + } + DecorationStyle::Underline(style) => (Box::new(draw::write_underlined), false, style), + DecorationStyle::Overline(style) => (Box::new(draw::write_overlined), false, style), + DecorationStyle::UnderOverline(style) => { + (Box::new(draw::write_underoverlined), false, style) + } + DecorationStyle::NoDecoration => ( + Box::new(draw::write_no_decoration), + false, + ansi_term::Style::new(), + ), + } +} + +fn _write_line_number( + line_numbers: &Vec<(usize, usize)>, + painter: &mut Painter, + plus_file: &str, + config: &Config, +) -> std::io::Result<()> { + let plus_line_number = line_numbers[line_numbers.len() - 1].0; + let formatted_plus_line_number = if config.hyperlinks { + features::hyperlinks::format_osc8_file_hyperlink( + plus_file, + Some(plus_line_number), + &format!("{}", plus_line_number), + config, + ) + } else { + Cow::from(format!("{}", plus_line_number)) + }; + match config.hunk_header_style.decoration_ansi_term_style() { + Some(style) => writeln!( + painter.writer, + "{}", + style.paint(formatted_plus_line_number) + )?, + None => writeln!(painter.writer, "{}", formatted_plus_line_number)?, + } + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index 31576463..75b813af 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,6 +18,7 @@ mod features; mod format; mod git_config; mod git_config_entry; +mod hunk_header; mod options; mod paint; mod parse; -- cgit v1.2.3 From f680b51646750adb9f6a9977ffb6b34dcb31801f Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Sun, 27 Dec 2020 18:19:05 +0000 Subject: Simplify preparation of hunk header code fragment --- src/delta.rs | 4 ++-- src/hunk_header.rs | 10 +++------- src/paint.rs | 9 ++++----- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/delta.rs b/src/delta.rs index 238ed3e3..52395f07 100644 --- a/src/delta.rs +++ b/src/delta.rs @@ -442,7 +442,7 @@ fn handle_hunk_line( }; painter .minus_lines - .push((painter.prepare(&line, true), state.clone())); + .push((painter.prepare(&line), state.clone())); state } Some('+') => { @@ -459,7 +459,7 @@ fn handle_hunk_line( }; painter .plus_lines - .push((painter.prepare(&line, true), state.clone())); + .push((painter.prepare(&line), state.clone())); state } Some(' ') => { diff --git a/src/hunk_header.rs b/src/hunk_header.rs index ec4db03a..abf042ca 100644 --- a/src/hunk_header.rs +++ b/src/hunk_header.rs @@ -75,16 +75,12 @@ fn _write_hunk_header( config: &Config, ) -> std::io::Result<()> { let (mut draw_fn, _, decoration_ansi_term_style) = _get_draw_fn(config); - // Adjust the hunk-header-line before paint_lines. - // However in the case of color_only mode, - // we'll just use raw_line because we can't change raw_line structure. let line = if config.color_only { format!(" {}", &line) + } else if !raw_code_fragment.is_empty() { + format!("{} ", raw_code_fragment) } else { - match painter.prepare(&raw_code_fragment, false) { - s if !s.is_empty() => format!("{} ", s), - s => s, - } + "".to_string() }; // Add a blank line below the hunk-header-line for readability, unless diff --git a/src/paint.rs b/src/paint.rs index bbff5a8b..bf85fb13 100644 --- a/src/paint.rs +++ b/src/paint.rs @@ -78,8 +78,7 @@ impl<'a> Painter<'a> { // Terminating with newline character is necessary for many of the sublime syntax definitions to // highlight correctly. // See https://docs.rs/syntect/3.2.0/syntect/parsing/struct.SyntaxSetBuilder.html#method.add_from_folder - pub fn prepare(&self, line: &str, append_newline: bool) -> String { - let terminator = if append_newline { "\n" } else { "" }; + pub fn prepare(&self, line: &str) -> String { if !line.is_empty() { let mut line = line.graphemes(true); @@ -90,9 +89,9 @@ impl<'a> Painter<'a> { // TODO: Things should, but do not, work if this leading space is omitted at this stage. // See comment in align::Alignment::new. line.next(); - format!(" {}{}", self.expand_tabs(line), terminator) + format!(" {}\n", self.expand_tabs(line)) } else { - terminator.to_string() + "\n".to_string() } } @@ -201,7 +200,7 @@ impl<'a> Painter<'a> { } else { None }; - let lines = vec![(self.prepare(line, true), state.clone())]; + let lines = vec![(self.prepare(line), state.clone())]; let syntax_style_sections = Painter::get_syntax_style_sections_for_lines( &lines, &state, -- cgit v1.2.3