From e32342a6c45d636b52c9f52eb4731bfe294c096d Mon Sep 17 00:00:00 2001 From: Ilya Grigoriev Date: Fri, 6 Jan 2023 20:08:49 -0800 Subject: [PATCH] `jj op log`: Show durations with relative timestamps Before, the humanized versions of start and end times often were identical. --- src/commands.rs | 37 +++++++++++++++++++++---------------- src/template_parser.rs | 20 +++++++++----------- tests/test_operations.rs | 6 +++--- 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/commands.rs b/src/commands.rs index 5732d6142..fa48ad0b8 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -26,7 +26,7 @@ use clap::builder::NonEmptyStringValueParser; use clap::{ArgGroup, ArgMatches, CommandFactory, FromArgMatches, Subcommand}; use config::Source; use itertools::Itertools; -use jujutsu_lib::backend::{CommitId, ObjectId, Timestamp, TreeValue}; +use jujutsu_lib::backend::{CommitId, ObjectId, TreeValue}; use jujutsu_lib::commit::Commit; use jujutsu_lib::dag_walk::topo_order_reverse; use jujutsu_lib::git::{GitFetchError, GitRefUpdate}; @@ -61,7 +61,7 @@ use crate::formatter::{Formatter, PlainTextFormatter}; use crate::graphlog::{AsciiGraphDrawer, Edge}; use crate::progress::Progress; use crate::template_parser::{ - format_absolute_timestamp, format_timestamp_relative_to_now, TemplateParser, + format_absolute_timestamp, format_duration, format_timestamp_relative_to_now, TemplateParser, }; use crate::templater::Template; use crate::ui::Ui; @@ -3359,15 +3359,6 @@ fn cmd_op_log( struct OpTemplate { relative_timestamps: bool, } - impl OpTemplate { - fn format_timestamp(&self, timestamp: &Timestamp) -> String { - if self.relative_timestamps { - format_timestamp_relative_to_now(timestamp) - } else { - format_absolute_timestamp(timestamp) - } - } - } impl Template for OpTemplate { fn format(&self, op: &Operation, formatter: &mut dyn Formatter) -> io::Result<()> { // TODO: Make this templated @@ -3379,11 +3370,25 @@ fn cmd_op_log( })?; formatter.write_str(" ")?; formatter.with_label("time", |formatter| { - formatter.write_str(&format!( - "{} - {}", - self.format_timestamp(&metadata.start_time), - self.format_timestamp(&metadata.end_time) - )) + formatter.write_str( + &(if self.relative_timestamps { + let mut f = timeago::Formatter::new(); + f.min_unit(timeago::TimeUnit::Microseconds).ago(""); + let mut duration = + format_duration(&metadata.start_time, &metadata.end_time, &f); + if duration == "now" { + duration = "less than a microsecond".to_string() + } + let start = format_timestamp_relative_to_now(&metadata.start_time); + format!("{start}, lasted {duration}",) + } else { + format!( + "{} - {}", + format_absolute_timestamp(&metadata.start_time), + format_absolute_timestamp(&metadata.end_time) + ) + }), + ) })?; formatter.write_str("\n")?; formatter.with_label("description", |formatter| { diff --git a/src/template_parser.rs b/src/template_parser.rs index b4a6c65d9..83abfa2c3 100644 --- a/src/template_parser.rs +++ b/src/template_parser.rs @@ -123,20 +123,18 @@ pub fn format_absolute_timestamp(timestamp: &Timestamp) -> String { } } -pub fn format_timestamp_relative_to_now(timestamp: &Timestamp) -> String { - datetime_from_timestamp(timestamp) - .and_then(|datetime| { - let now = chrono::Local::now(); - - now.signed_duration_since(datetime).to_std().ok() - }) - .map(|duration| { - let f = timeago::Formatter::new(); - f.convert(duration) - }) +pub fn format_duration(from: &Timestamp, to: &Timestamp, format: &timeago::Formatter) -> String { + datetime_from_timestamp(from) + .zip(datetime_from_timestamp(to)) + .and_then(|(from, to)| to.signed_duration_since(from).to_std().ok()) + .map(|duration| format.convert(duration)) .unwrap_or_else(|| "".to_string()) } +pub fn format_timestamp_relative_to_now(timestamp: &Timestamp) -> String { + format_duration(timestamp, &Timestamp::now(), &timeago::Formatter::new()) +} + struct RelativeTimestampString; impl TemplateProperty for RelativeTimestampString { diff --git a/tests/test_operations.rs b/tests/test_operations.rs index 0a0bdf86a..e3d9118d6 100644 --- a/tests/test_operations.rs +++ b/tests/test_operations.rs @@ -44,12 +44,12 @@ fn test_op_log() { ); let regex = Regex::new(r"\d\d years").unwrap(); insta::assert_snapshot!(regex.replace_all(&stdout, "NN years"), @r###" - @ 45108169c0f8 test-username@host.example.com NN years ago - NN years ago + @ 45108169c0f8 test-username@host.example.com NN years ago, lasted less than a microsecond | describe commit 230dd059e1b059aefc0da06a2e5a7dbf22362f22 | args: jj describe -m 'description 0' - o a99a3fd5c51e test-username@host.example.com NN years ago - NN years ago + o a99a3fd5c51e test-username@host.example.com NN years ago, lasted less than a microsecond | add workspace 'default' - o 56b94dfc38e7 test-username@host.example.com NN years ago - NN years ago + o 56b94dfc38e7 test-username@host.example.com NN years ago, lasted less than a microsecond initialize repo "###); let add_workspace_id = "a99a3fd5c51e";