summaryrefslogtreecommitdiffstats
path: root/src/hunk_header.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/hunk_header.rs')
-rw-r--r--src/hunk_header.rs207
1 files changed, 207 insertions, 0 deletions
diff --git a/src/hunk_header.rs b/src/hunk_header.rs
new file mode 100644
index 00000000..abf042ca
--- /dev/null
+++ b/src/hunk_header.rs
@@ -0,0 +1,207 @@
+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);
+ let line = if config.color_only {
+ format!(" {}", &line)
+ } else if !raw_code_fragment.is_empty() {
+ format!("{} ", raw_code_fragment)
+ } else {
+ "".to_string()
+ };
+
+ // 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(())
+}