Ver Fonte

Resolves #270: add support for --time-style=relative

Denis Cornehl há 3 anos atrás
pai
commit
1d950086bc

+ 9 - 2
Cargo.lock

@@ -13,9 +13,9 @@ dependencies = [
 
 [[package]]
 name = "autocfg"
-version = "1.0.1"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
 [[package]]
 name = "bitflags"
@@ -75,6 +75,7 @@ dependencies = [
  "scoped_threadpool",
  "term_grid",
  "terminal_size",
+ "timeago",
  "unicode-width",
  "users",
  "zoneinfo_compiled",
@@ -296,6 +297,12 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "timeago"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ec32dde57efb15c035ac074118d7f32820451395f28cb0524a01d4e94983b26"
+
 [[package]]
 name = "tinyvec"
 version = "1.2.0"

+ 1 - 0
Cargo.toml

@@ -30,6 +30,7 @@ number_prefix = "0.4"
 scoped_threadpool = "0.1"
 term_grid = "0.2.0"
 terminal_size = "0.1.16"
+timeago = { version = "0.3.1", default-features = false }
 unicode-width = "0.1"
 zoneinfo_compiled = "0.5.1"
 

+ 1 - 1
README.md

@@ -89,7 +89,7 @@ Some of the options accept parameters:
 - Valid **--color** options are **always**, **automatic**, and **never**.
 - Valid sort fields are **accessed**, **changed**, **created**, **extension**, **Extension**, **inode**, **modified**, **name**, **Name**, **size**, **type**, and **none**. Fields starting with a capital letter sort uppercase before lowercase. The modified field has the aliases **date**, **time**, and **newest**, while its reverse has the aliases **age** and **oldest**.
 - Valid time fields are **modified**, **changed**, **accessed**, and **created**.
-- Valid time styles are **default**, **iso**, **long-iso**, and **full-iso**.
+- Valid time styles are **default**, **iso**, **long-iso**, **full-iso**, and **relative**.
 
 
 ---

+ 1 - 1
completions/bash/exa

@@ -29,7 +29,7 @@ _exa()
             ;;
 
         --time-style)
-            COMPREPLY=( $( compgen -W 'default iso long-iso full-iso --' -- "$cur" ) )
+            COMPREPLY=( $( compgen -W 'default iso long-iso full-iso relative --' -- "$cur" ) )
             return
             ;;
     esac

+ 1 - 0
completions/fish/exa.fish

@@ -79,6 +79,7 @@ complete -c exa        -l 'time-style'    -d "How to format timestamps" -x -a "
     iso\t'Display brief ISO timestamps'
     long-iso\t'Display longer ISO timestaps, up to the minute'
     full-iso\t'Display full ISO timestamps, up to the nanosecond'
+    relative\t'Display relative timestamps'
 "
 complete -c exa        -l 'no-permissions' -d "Suppress the permissions field"
 complete -c exa        -l 'octal-permissions' -d "List each file's permission in octal format"

+ 1 - 1
completions/zsh/_exa

@@ -43,7 +43,7 @@ __exa() {
         {-n,--numeric}"[List numeric user and group IDs.]" \
         {-S,--blocks}"[List each file's number of filesystem blocks]" \
         {-t,--time}="[Which time field to show]:(time field):(accessed changed created modified)" \
-        --time-style="[How to format timestamps]:(time style):(default iso long-iso full-iso)" \
+        --time-style="[How to format timestamps]:(time style):(default iso long-iso full-iso relative)" \
         --no-permissions"[Suppress the permissions field]" \
         --octal-permissions"[List each file's permission in octal format]" \
         --no-filesize"[Suppress the filesize field]" \

+ 1 - 1
man/exa.1.md

@@ -157,7 +157,7 @@ These options are available when running with `--long` (`-l`):
 `--time-style=STYLE`
 : How to format timestamps.
 
-: Valid timestamp styles are ‘`default`’, ‘`iso`’, ‘`long-iso`’, and ‘`full-iso`’.
+: Valid timestamp styles are ‘`default`’, ‘`iso`’, ‘`long-iso`’, ‘`full-iso`’, and ‘`relative`’.
 
 `-u`, `--accessed`
 : Use the accessed timestamp field.

+ 1 - 1
src/options/flags.rs

@@ -52,7 +52,7 @@ pub static ACCESSED:   Arg = Arg { short: Some(b'u'), long: "accessed",   takes_
 pub static CREATED:    Arg = Arg { short: Some(b'U'), long: "created",    takes_value: TakesValue::Forbidden };
 pub static TIME_STYLE: Arg = Arg { short: None,       long: "time-style", takes_value: TakesValue::Necessary(Some(TIME_STYLES)) };
 const TIMES: Values = &["modified", "changed", "accessed", "created"];
-const TIME_STYLES: Values = &["default", "long-iso", "full-iso", "iso"];
+const TIME_STYLES: Values = &["default", "long-iso", "full-iso", "iso", "relative"];
 
 // suppressing columns
 pub static NO_PERMISSIONS: Arg = Arg { short: None, long: "no-permissions", takes_value: TakesValue::Forbidden };

+ 1 - 1
src/options/help.rs

@@ -54,7 +54,7 @@ LONG VIEW OPTIONS
   -u, --accessed       use the accessed timestamp field
   -U, --created        use the created timestamp field
   --changed            use the changed timestamp field
-  --time-style         how to format timestamps (default, iso, long-iso, full-iso)
+  --time-style         how to format timestamps (default, iso, long-iso, full-iso, relative)
   --no-permissions     suppress the permissions field
   --octal-permissions  list each file's permission in octal format
   --no-filesize        suppress the filesize field

+ 4 - 0
src/options/view.rs

@@ -257,6 +257,9 @@ impl TimeFormat {
         if &word == "default" {
             Ok(Self::DefaultFormat)
         }
+        else if &word == "relative" {
+            Ok(Self::Relative)
+        }
         else if &word == "iso" {
             Ok(Self::ISOFormat)
         }
@@ -464,6 +467,7 @@ mod test {
         // Individual settings
         test!(default:   TimeFormat <- ["--time-style=default"], None;      Both => like Ok(TimeFormat::DefaultFormat));
         test!(iso:       TimeFormat <- ["--time-style", "iso"], None;       Both => like Ok(TimeFormat::ISOFormat));
+        test!(relative:  TimeFormat <- ["--time-style", "relative"], None;  Both => like Ok(TimeFormat::Relative));
         test!(long_iso:  TimeFormat <- ["--time-style=long-iso"], None;     Both => like Ok(TimeFormat::LongISO));
         test!(full_iso:  TimeFormat <- ["--time-style", "full-iso"], None;  Both => like Ok(TimeFormat::FullISO));
 

+ 24 - 2
src/output/time.rs

@@ -1,8 +1,10 @@
 //! Timestamp formatting.
 
-use std::time::{SystemTime, UNIX_EPOCH};
+use std::convert::TryInto;
+use std::cmp::max;
+use std::time::{SystemTime, UNIX_EPOCH, Duration};
 
-use datetime::{LocalDateTime, TimeZone, DatePiece, TimePiece};
+use datetime::{LocalDateTime, TimeZone, DatePiece, TimePiece, Instant};
 use datetime::fmt::DateFormat;
 
 use lazy_static::lazy_static;
@@ -46,6 +48,9 @@ pub enum TimeFormat {
     /// millisecond and includes its offset down to the minute. This too uses
     /// only numbers so doesn’t require any special consideration.
     FullISO,
+
+    /// Use a relative but fixed width representation.
+    Relative,
 }
 
 // There are two different formatting functions because local and zoned
@@ -58,6 +63,7 @@ impl TimeFormat {
             Self::ISOFormat      => iso_local(time),
             Self::LongISO        => long_local(time),
             Self::FullISO        => full_local(time),
+            Self::Relative       => relative(time),
         }
     }
 
@@ -67,6 +73,7 @@ impl TimeFormat {
             Self::ISOFormat      => iso_zoned(time, zone),
             Self::LongISO        => long_zoned(time, zone),
             Self::FullISO        => full_zoned(time, zone),
+            Self::Relative       => relative(time),
         }
     }
 }
@@ -113,6 +120,21 @@ fn long_zoned(time: SystemTime, zone: &TimeZone) -> String {
             date.hour(), date.minute())
 }
 
+#[allow(trivial_numeric_casts)]
+fn relative(time: SystemTime) -> String {
+    format!(
+        "{:>13}", 
+        timeago::Formatter::new().convert(
+            Duration::from_secs(
+                max(0, Instant::now().seconds() - systemtime_epoch(time))
+                // this .unwrap is safe since the call above can never result in a 
+                // value < 0
+                .try_into().unwrap()
+            )
+        )
+    )
+}
+
 #[allow(trivial_numeric_casts)]
 fn full_local(time: SystemTime) -> String {
     let date = LocalDateTime::at(systemtime_epoch(time));

+ 1 - 1
xtests/input-options.toml

@@ -42,7 +42,7 @@ tags = [ 'options' ]
 name = "exa displays an error for option that takes the wrong parameter"
 shell = "exa -l --time-style=24"
 stdout = { empty = true }
-stderr = { string = "Option --time-style has no \"24\" setting (choices: default, long-iso, full-iso, iso)" }
+stderr = { string = "Option --time-style has no \"24\" setting (choices: default, long-iso, full-iso, iso, relative)" }
 status = 3
 tags = [ 'options' ]
 

+ 1 - 1
xtests/outputs/help.ansitxt

@@ -46,7 +46,7 @@ LONG VIEW OPTIONS
   -u, --accessed       use the accessed timestamp field
   -U, --created        use the created timestamp field
   --changed            use the changed timestamp field
-  --time-style         how to format timestamps (default, iso, long-iso, full-iso)
+  --time-style         how to format timestamps (default, iso, long-iso, full-iso, relative)
   --no-permissions     suppress the permissions field
   --octal-permissions  list each file's permission in octal format
   --no-filesize        suppress the filesize field