diff options
author | Thomas Otto <th1000s@posteo.net> | 2022-01-04 21:58:39 +0100 |
---|---|---|
committer | Thomas Otto <th1000s@posteo.net> | 2022-01-16 22:13:35 +0100 |
commit | a5c256f2e48c47a47f7a5ca5f131871b14efeb7f (patch) | |
tree | ecb8d7eebcf8f87377e76884f6b9fee9434c78e5 | |
parent | b693fdd6e4bff52596e124d1d2cc299cc53ada97 (diff) |
Show blame line numbers via blame-separator-formatblame_line_numbers_before_rebase
Prefix and suffix of the format string are separator_style highlighted,
format options are none, {n}, {n:block}, {n:every-N}.
-rw-r--r-- | src/cli.rs | 12 | ||||
-rw-r--r-- | src/config.rs | 6 | ||||
-rw-r--r-- | src/handlers/blame.rs | 111 | ||||
-rw-r--r-- | src/options/set.rs | 2 |
4 files changed, 123 insertions, 8 deletions
@@ -551,9 +551,15 @@ pub struct Opt { /// Available placeholders are "{timestamp}", "{author}", and "{commit}". pub blame_format: String, - #[clap(long = "blame-separator", default_value = "│")] - /// Separator between the commit metadata and code sections of a git blame line. - pub blame_separator: String, + /// Separator between the commit metadata and code sections of a line of git blame output. Contains + /// the line number by default. Possible values are "none" to disable line numbers or a format + /// string. This may contain one "{n:}" placeholder and will display the line number on every line. + /// A type may be added after all other format specifiers and can be separated by '_': + /// If type is set to 'block' (e.g. "{n:^4_block}") the line number will only be shown when a new blame + /// block starts; or if it is set to 'every-N' the line will be show with every block and every + /// N-th (modulo) line. + #[clap(long = "blame-separator-format", default_value = "│{n:^4}│")] + pub blame_separator_format: String, #[clap(long = "blame-separator-style")] /// Style string for the separator between the commit metadata and code sections of a git blame line. diff --git a/src/config.rs b/src/config.rs index 75bc7488..baddd084 100644 --- a/src/config.rs +++ b/src/config.rs @@ -17,6 +17,8 @@ use crate::features::navigate; use crate::features::side_by_side::{self, ansifill, LeftRight}; use crate::git_config::{GitConfig, GitConfigEntry}; use crate::handlers; +use crate::handlers::blame::parse_blame_line_numbers; +use crate::handlers::blame::BlameLineNumbers; use crate::minusplus::MinusPlus; use crate::paint::BgFillMethod; use crate::parse_styles; @@ -63,8 +65,8 @@ pub struct Config { pub background_color_extends_to_terminal_width: bool, pub blame_code_style: Option<Style>, pub blame_format: String, + pub blame_separator_format: BlameLineNumbers, pub blame_palette: Vec<String>, - pub blame_separator: String, pub blame_separator_style: Option<Style>, pub blame_timestamp_format: String, pub color_only: bool, @@ -248,7 +250,7 @@ impl From<cli::Opt> for Config { blame_format: opt.blame_format, blame_code_style: styles.remove("blame-code-style"), blame_palette, - blame_separator: opt.blame_separator, + blame_separator_format: parse_blame_line_numbers(&opt.blame_separator_format), blame_separator_style: styles.remove("blame-separator-style"), blame_timestamp_format: opt.blame_timestamp_format, commit_style: styles["commit-style"], 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, ®ex, 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; diff --git a/src/options/set.rs b/src/options/set.rs index e82b9f39..0e21c226 100644 --- a/src/options/set.rs +++ b/src/options/set.rs @@ -128,8 +128,8 @@ pub fn set_options( [ blame_code_style, blame_format, + blame_separator_format, blame_palette, - blame_separator, blame_separator_style, blame_timestamp_format, color_only, |