summaryrefslogtreecommitdiffstats
path: root/src/handlers/blame.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/handlers/blame.rs')
-rw-r--r--src/handlers/blame.rs111
1 files changed, 109 insertions, 2 deletions
diff --git a/src/handlers/blame.rs b/src/handlers/blame.rs
index f48e8841..2c22d854 100644
--- a/src/handlers/blame.rs
+++ b/src/handlers/blame.rs
@@ -8,11 +8,22 @@ use crate::color;
use crate::config;
use crate::config::delta_unreachable;
use crate::delta::{self, State, StateMachine};
+use crate::fatal;
+use crate::format::FormatStringSimple;
use crate::format::{self, Placeholder};
+use crate::format::{make_placeholder_regex, parse_line_number_format};
use crate::paint::{self, BgShouldFill, StyleSectionSpecifier};
use crate::style::Style;
use crate::utils;
+#[derive(Clone, Debug)]
+pub enum BlameLineNumbers {
+ // "none" equals a fixed string with just a separator
+ On(FormatStringSimple),
+ PerBlock(FormatStringSimple),
+ Every(usize, FormatStringSimple),
+}
+
impl<'a> StateMachine<'a> {
/// If this is a line of git blame output then render it accordingly. If
/// this is the first blame line, then set the syntax-highlighter language
@@ -49,11 +60,19 @@ impl<'a> StateMachine<'a> {
let code_style = self.config.blame_code_style.unwrap_or(metadata_style);
let separator_style = self.config.blame_separator_style.unwrap_or(code_style);
+ let (nr_prefix, line_number, nr_suffix) = format_blame_line_number(
+ &self.config.blame_separator_format,
+ blame.line_number,
+ is_repeat,
+ );
+
write!(
self.painter.writer,
- "{}{}",
+ "{}{}{}{}",
metadata_style.paint(&formatted_blame_metadata),
- separator_style.paint(&self.config.blame_separator)
+ separator_style.paint(nr_prefix),
+ metadata_style.paint(&line_number),
+ separator_style.paint(nr_suffix),
)?;
// Emit syntax-highlighted code
@@ -232,6 +251,7 @@ pub fn parse_git_blame_line<'a>(line: &'a str, timestamp_format: &str) -> Option
}
lazy_static! {
+ // line numbers are set via `blame_line_number_suffix`
pub static ref BLAME_PLACEHOLDER_REGEX: Regex =
format::make_placeholder_regex(&["timestamp", "author", "commit"]);
}
@@ -272,6 +292,93 @@ pub fn format_blame_metadata(
s
}
+pub fn format_blame_line_number(
+ format: &BlameLineNumbers,
+ line_number: usize,
+ is_repeat: bool,
+) -> (&str, String, &str) {
+ let (format, empty) = match &format {
+ BlameLineNumbers::PerBlock(format) => (format, is_repeat),
+ BlameLineNumbers::Every(n, format) => (format, is_repeat && line_number % n != 0),
+ BlameLineNumbers::On(format) => (format, false),
+ };
+ let mut result = String::new();
+
+ // depends on defaults being set when parsing arguments
+ let line_number = if format.width.is_some() {
+ format::pad(
+ line_number,
+ format.width.unwrap(),
+ format.alignment_spec.unwrap(),
+ None,
+ )
+ } else {
+ String::new()
+ };
+
+ if empty {
+ for _ in 0..measure_text_width(&line_number) {
+ result.push(' ');
+ }
+ } else {
+ result.push_str(&line_number);
+ }
+
+ (format.prefix.as_str(), result, format.suffix.as_str())
+}
+
+pub fn parse_blame_line_numbers(arg: &str) -> BlameLineNumbers {
+ if arg == "none" {
+ return BlameLineNumbers::On(crate::format::FormatStringSimple::only_string("│"));
+ }
+
+ let regex = make_placeholder_regex(&["n"]);
+ let f = match parse_line_number_format(arg, &regex, false) {
+ v if v.len() > 1 => {
+ fatal("Too many format arguments numbers for blame-line-numbers".to_string())
+ }
+ mut v => v.pop().unwrap(),
+ };
+
+ let set_defaults = |mut format: crate::format::FormatStringSimple| {
+ format.width = format.width.or(Some(4));
+ format.alignment_spec = format.alignment_spec.or(Some(crate::format::Align::Center));
+
+ format
+ };
+
+ if f.placeholder.is_none() {
+ return BlameLineNumbers::On(crate::format::FormatStringSimple::only_string(
+ f.suffix.as_str(),
+ ));
+ }
+
+ match f.fmt_type.as_str() {
+ t if t.is_empty() || t == "every" => BlameLineNumbers::On(set_defaults(f.into_simple())),
+ t if t == "block" => BlameLineNumbers::PerBlock(set_defaults(f.into_simple())),
+ every_n if every_n.starts_with("every-") => {
+ let n = every_n["every-".len()..]
+ .parse::<usize>()
+ .unwrap_or_else(|err| {
+ fatal(format!(
+ "Invalid number for blame-line-numbers in every-N argument: {}",
+ err
+ ))
+ });
+
+ if n > 1 {
+ BlameLineNumbers::Every(n, set_defaults(f.into_simple()))
+ } else {
+ BlameLineNumbers::On(set_defaults(f.into_simple()))
+ }
+ }
+ t => fatal(format!(
+ "Invalid format type \"{}\" for blame-line-numbers",
+ t
+ )),
+ }
+}
+
#[cfg(test)]
mod tests {
use itertools::Itertools;