Improve blame timestamp handling

If the timestamp is understood by the (now expanded) regex, but
does not conform to the timestamp format then just print it as-is.
This commit is contained in:
Thomas Otto 2022-01-19 23:58:48 +01:00
parent 2d7015c774
commit beb5c87ea2
2 changed files with 57 additions and 15 deletions

View File

@ -78,9 +78,12 @@ pub struct Opt {
#[arg(
long = "blame-timestamp-format",
default_value = "%Y-%m-%d %H:%M:%S %z",
value_name = "FMT"
value_name = "FMT_IN"
)]
/// Format of `git blame` timestamp in raw git output received by delta.
/// Expected formatting of the timestamp in raw `git blame` output read by delta.
///
/// Must follow the format given to the git `--date=format:...` option, i.e. the
/// `strftime` conventions. Spelled out days or months are not supported.
pub blame_timestamp_format: String,
#[arg(long = "blame-timestamp-output-format", value_name = "FMT")]

View File

@ -181,11 +181,30 @@ impl<'a> StateMachine<'a> {
}
}
#[derive(Debug)]
// The parsed time stamp, or if that fails the raw input string.
pub(crate) enum BlameTime<'a> {
Time(DateTime<FixedOffset>),
RawString(&'a str),
}
impl<'a> BlameTime<'a> {
fn format(&self, format_str: &Option<String>) -> String {
match self {
Self::Time(datetime) => match format_str {
Some(time_format) => datetime.format(time_format).to_string(),
None => chrono_humanize::HumanTime::from(*datetime).to_string(),
},
Self::RawString(s) => s.to_string(),
}
}
}
#[derive(Debug)]
pub struct BlameLine<'a> {
pub commit: &'a str,
pub author: &'a str,
pub time: DateTime<FixedOffset>,
pub time: BlameTime<'a>,
pub line_number: usize,
pub code: &'a str,
}
@ -204,11 +223,11 @@ lazy_static! {
[\ ]
\( # open ( which the previous file name may not contain in case a name does (which is more likely)
(
[^\ ].*[^\ ] # author name
[^\ ].*?[^\ ] # author name (non-greedy via '?' so the following timestamp is matched)
)
[\ ]+
( # timestamp
[0-9]{4}-[0-9]{2}-[0-9]{2}\ [0-9]{2}:[0-9]{2}:[0-9]{2}\ [-+][0-9]{4}
( # timestamp, a general regex to capture input which will be parsed by strftime
(?:[0-9]+[\ :\+-T]+)*[0-9]+ # numbers and possible separatores, then a final number
)
[\ ]+
(
@ -231,7 +250,10 @@ pub fn parse_git_blame_line<'a>(line: &'a str, timestamp_format: &str) -> Option
let author = caps.get(2).unwrap().as_str();
let timestamp = caps.get(3).unwrap().as_str();
let time = DateTime::parse_from_str(timestamp, timestamp_format).ok()?;
let time = match DateTime::parse_from_str(timestamp, timestamp_format) {
Ok(datetime) => BlameTime::Time(datetime),
Err(_) => BlameTime::RawString(timestamp),
};
let line_number = caps.get(4).unwrap().as_str().parse::<usize>().ok()?;
@ -266,12 +288,9 @@ pub fn format_blame_metadata(
let width = placeholder.width.unwrap_or(15);
let field = match placeholder.placeholder {
Some(Placeholder::Str("timestamp")) => {
Some(Cow::from(match &config.blame_timestamp_output_format {
Some(time_format) => blame.time.format(time_format).to_string(),
None => chrono_humanize::HumanTime::from(blame.time).to_string(),
}))
}
Some(Placeholder::Str("timestamp")) => Some(Cow::from(
blame.time.format(&config.blame_timestamp_output_format),
)),
Some(Placeholder::Str("author")) => Some(Cow::from(blame.author)),
Some(Placeholder::Str("commit")) => Some(delta::format_raw_line(blame.commit, config)),
None => None,
@ -399,6 +418,9 @@ mod tests {
assert!(caps.is_some());
assert!(parse_git_blame_line(line, "%Y-%m-%d %H:%M:%S %z").is_some());
}
let strange_date = "1234 (Author Name-Here 19-01-0500 18-08+2022T41 1) --date=format:'%d-%m%z %M-%H+%YT%S'";
assert!(parse_git_blame_line(strange_date, "%d-%m%z %M-%H+%YT%S").is_some());
}
#[test]
@ -450,6 +472,23 @@ mod tests {
);
}
#[test]
fn test_blame_timestamp_regex() {
// Not supported: rfc2822, the weekday start (plus possible localisation) can't be separated from the author
for timestamp_fmt in &[
"1234 (Author Name-Here 2021-08-22 18:20:19 -0700 1) default blame (iso)",
"1234 (Author Name-Here 2022-01-19T23:49:50+01:00 1) iso strict",
"1234 (Author Name-Here 2022-01-19 1) short",
"1234 (Author Name-Here 123456789 1) unix",
"1234 (Author Name-Here 123456789 +0100 1) raw",
"1234 (Author Name-Here 19-01-0500 18-08+2022T41 1) --date=format:'%d-%m%z %M-%H+%YT%S'"
] {
let caps = BLAME_LINE_REGEX.captures(timestamp_fmt);
assert!(caps.is_some());
assert!(caps.unwrap().get(3).is_some())
}
}
#[test]
fn test_color_assignment() {
let mut writer = Cursor::new(vec![0; 512]);
@ -545,7 +584,7 @@ mod tests {
BlameLine {
commit: "",
author: "",
time,
time: BlameTime::Time(time),
line_number: 0,
code: "",
}
@ -562,7 +601,7 @@ mod tests {
BlameLine {
commit: "",
author,
time: chrono::DateTime::default(),
time: BlameTime::Time(chrono::DateTime::default()),
line_number: 0,
code: "",
}