summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Davison <dandavison7@gmail.com>2020-12-28 03:11:40 +0000
committerGitHub <noreply@github.com>2020-12-28 03:11:40 +0000
commit7990db4b94801c1a5ba437d32293626ae3878a32 (patch)
treee9db0c39a1c80fbf03b55a405e30a70485419254
parenta3c97853e0cf58b09fa1f62550db3702d77fa126 (diff)
parentf680b51646750adb9f6a9977ffb6b34dcb31801f (diff)
Merge pull request #472 from dandavison/handle-hunk-header-refactor
handle hunk header refactor
-rw-r--r--src/delta.rs165
-rw-r--r--src/features/line_numbers.rs2
-rw-r--r--src/hunk_header.rs207
-rw-r--r--src/main.rs1
-rw-r--r--src/paint.rs9
5 files changed, 216 insertions, 168 deletions
diff --git a/src/delta.rs b/src/delta.rs
index 86508a73..52395f07 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
@@ -601,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('+') => {
@@ -618,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/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..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(())
+}
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;
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,