mirror of
https://github.com/Llewellynvdm/exa.git
synced 2024-11-05 20:37:52 +00:00
Merge branch 'timestamps'
Conflicts: Cargo.lock
This commit is contained in:
commit
809af4e133
64
Cargo.lock
generated
64
Cargo.lock
generated
@ -3,10 +3,13 @@ name = "exa"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ansi_term 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"datetime 0.1.2 (git+https://github.com/ogham/rust-datetime.git)",
|
||||
"datetime_macros 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"getopts 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"git2 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"natord 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"number_prefix 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pad 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"users 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@ -21,7 +24,41 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "datetime"
|
||||
version = "0.1.2"
|
||||
source = "git+https://github.com/ogham/rust-datetime.git#c108628eb3519535821bc7907f2305643465acf5"
|
||||
dependencies = [
|
||||
"pad 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex_macros 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "datetime"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"pad 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex_macros 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "datetime_macros"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"datetime 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pad 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gcc"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@ -29,7 +66,7 @@ name = "getopts"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"log 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -37,7 +74,7 @@ name = "git2"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libgit2-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@ -54,7 +91,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libssh2-sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libz-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pkg-config 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@ -72,7 +109,7 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libz-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pkg-config 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@ -86,7 +123,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@ -106,18 +143,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.3.1"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"gcc 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libressl-pnacl-sys 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pkg-config 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pkg-config 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pad"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "pnacl-build-helper"
|
||||
version = "1.3.2"
|
||||
|
@ -8,9 +8,11 @@ name = "exa"
|
||||
|
||||
[dependencies]
|
||||
ansi_term = "0.4.5"
|
||||
datetime_macros = "0.1.2"
|
||||
getopts = "0.2.1"
|
||||
natord = "1.0.7"
|
||||
number_prefix = "0.2.3"
|
||||
pad = "0.1.1"
|
||||
users = "0.2.3"
|
||||
|
||||
[features]
|
||||
@ -19,4 +21,7 @@ git = [ "git2" ]
|
||||
|
||||
[dependencies.git2]
|
||||
version = "0.1.13"
|
||||
optional = true
|
||||
optional = true
|
||||
|
||||
[dependencies.datetime]
|
||||
git = "https://github.com/ogham/rust-datetime.git"
|
||||
|
@ -24,6 +24,8 @@ exa is a replacement for `ls` written in Rust.
|
||||
- **-R**, **--recurse**: recurse into subdirectories
|
||||
- **-s**, **--sort=(field)**: field to sort by
|
||||
- **-S**, **--blocks**: show number of file system blocks
|
||||
- **-t**, **--time**: which timestamp to show for a file
|
||||
- **-T**, **--tree**: recurse into subdirectories in a tree view
|
||||
- **-x**, **--across**: sort multi-column view entries across
|
||||
|
||||
You can sort by **name**, **size**, **ext**, **inode**, or **none**.
|
||||
|
@ -2,12 +2,13 @@ use std::iter::repeat;
|
||||
|
||||
use ansi_term::Style;
|
||||
|
||||
use options::SizeFormat;
|
||||
use options::{SizeFormat, TimeType};
|
||||
|
||||
#[derive(PartialEq, Debug, Copy)]
|
||||
pub enum Column {
|
||||
Permissions,
|
||||
FileSize(SizeFormat),
|
||||
Timestamp(TimeType, i64),
|
||||
Blocks,
|
||||
User,
|
||||
Group,
|
||||
@ -42,14 +43,15 @@ impl Column {
|
||||
/// to have a header row printed.
|
||||
pub fn header(&self) -> &'static str {
|
||||
match *self {
|
||||
Column::Permissions => "Permissions",
|
||||
Column::FileSize(_) => "Size",
|
||||
Column::Blocks => "Blocks",
|
||||
Column::User => "User",
|
||||
Column::Group => "Group",
|
||||
Column::HardLinks => "Links",
|
||||
Column::Inode => "inode",
|
||||
Column::GitStatus => "Git",
|
||||
Column::Permissions => "Permissions",
|
||||
Column::FileSize(_) => "Size",
|
||||
Column::Timestamp(t, _) => t.header(),
|
||||
Column::Blocks => "Blocks",
|
||||
Column::User => "User",
|
||||
Column::Group => "Group",
|
||||
Column::HardLinks => "Links",
|
||||
Column::Inode => "inode",
|
||||
Column::GitStatus => "Git",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
45
src/file.rs
45
src/file.rs
@ -8,13 +8,18 @@ use ansi_term::Colour::{Red, Green, Yellow, Blue, Purple, Cyan, Fixed};
|
||||
|
||||
use users::Users;
|
||||
|
||||
use pad::Alignment;
|
||||
|
||||
use number_prefix::{binary_prefix, decimal_prefix, Prefixed, Standalone, PrefixNames};
|
||||
|
||||
use datetime;
|
||||
use datetime::local::{LocalDateTime, DatePiece};
|
||||
|
||||
use column::{Column, Cell};
|
||||
use column::Column::*;
|
||||
use dir::Dir;
|
||||
use filetype::HasType;
|
||||
use options::SizeFormat;
|
||||
use options::{SizeFormat, TimeType};
|
||||
|
||||
/// This grey value is directly in between white and black, so it's guaranteed
|
||||
/// to show up on either backgrounded terminal.
|
||||
@ -94,14 +99,15 @@ impl<'a> File<'a> {
|
||||
/// Get the data for a column, formatted as a coloured string.
|
||||
pub fn display<U: Users>(&self, column: &Column, users_cache: &mut U) -> Cell {
|
||||
match *column {
|
||||
Permissions => self.permissions_string(),
|
||||
FileSize(f) => self.file_size(f),
|
||||
HardLinks => self.hard_links(),
|
||||
Inode => self.inode(),
|
||||
Blocks => self.blocks(),
|
||||
User => self.user(users_cache),
|
||||
Group => self.group(users_cache),
|
||||
GitStatus => self.git_status(),
|
||||
Permissions => self.permissions_string(),
|
||||
FileSize(f) => self.file_size(f),
|
||||
Timestamp(t, y) => self.timestamp(t, y),
|
||||
HardLinks => self.hard_links(),
|
||||
Inode => self.inode(),
|
||||
Blocks => self.blocks(),
|
||||
User => self.user(users_cache),
|
||||
Group => self.group(users_cache),
|
||||
GitStatus => self.git_status(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -297,6 +303,27 @@ impl<'a> File<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn timestamp(&self, time_type: TimeType, current_year: i64) -> Cell {
|
||||
|
||||
// Need to convert these values from milliseconds into seconds.
|
||||
let time_in_seconds = match time_type {
|
||||
TimeType::FileAccessed => self.stat.accessed,
|
||||
TimeType::FileModified => self.stat.modified,
|
||||
TimeType::FileCreated => self.stat.created,
|
||||
} as i64 / 1000;
|
||||
|
||||
let date = LocalDateTime::at(time_in_seconds);
|
||||
|
||||
let format = if date.year() == current_year {
|
||||
date_format!("{2>:D} {:M} {2>:h}:{02>:m}")
|
||||
}
|
||||
else {
|
||||
date_format!("{2>:D} {:M} {4>:Y}")
|
||||
};
|
||||
|
||||
Cell::paint(Blue.normal(), format.format(date).as_slice())
|
||||
}
|
||||
|
||||
/// This file's type, represented by a coloured character.
|
||||
///
|
||||
/// Although the file type can usually be guessed from the colour of the
|
||||
|
@ -1,9 +1,14 @@
|
||||
#![feature(collections, core, env, io, libc, os, path, std_misc)]
|
||||
#![feature(collections, core, env, io, libc, os, path, plugin, std_misc)]
|
||||
|
||||
#[plugin] #[no_link]
|
||||
extern crate datetime_macros;
|
||||
|
||||
extern crate ansi_term;
|
||||
extern crate datetime;
|
||||
extern crate getopts;
|
||||
extern crate natord;
|
||||
extern crate number_prefix;
|
||||
extern crate pad;
|
||||
extern crate users;
|
||||
|
||||
#[cfg(feature="git")]
|
||||
|
@ -12,6 +12,8 @@ use std::fmt;
|
||||
use getopts;
|
||||
use natord;
|
||||
|
||||
use datetime::local::{LocalDateTime, DatePiece};
|
||||
|
||||
use self::Misfire::*;
|
||||
|
||||
/// The *Options* struct represents a parsed version of the user's
|
||||
@ -50,13 +52,17 @@ impl Options {
|
||||
opts.optflag("g", "group", "show group as well as user");
|
||||
opts.optflag("h", "header", "show a header row at the top");
|
||||
opts.optflag("H", "links", "show number of hard links");
|
||||
opts.optflag("l", "long", "display extended details and attributes");
|
||||
opts.optflag("i", "inode", "show each file's inode number");
|
||||
opts.optflag("l", "long", "display extended details and attributes");
|
||||
opts.optflag("m", "modified", "display timestamp of most recent modification");
|
||||
opts.optflag("r", "reverse", "reverse order of files");
|
||||
opts.optflag("R", "recurse", "recurse into directories");
|
||||
opts.optopt ("s", "sort", "field to sort by", "WORD");
|
||||
opts.optflag("S", "blocks", "show number of file system blocks");
|
||||
opts.optopt ("t", "time", "which timestamp to show for a file", "WORD");
|
||||
opts.optflag("T", "tree", "recurse into subdirectories in a tree view");
|
||||
opts.optflag("u", "accessed", "display timestamp of last access for a file");
|
||||
opts.optflag("U", "created", "display timestamp of creation for a file");
|
||||
opts.optflag("x", "across", "sort multi-column view entries across");
|
||||
opts.optflag("?", "help", "show list of command-line options");
|
||||
|
||||
@ -231,6 +237,9 @@ impl View {
|
||||
else if matches.opt_present("blocks") {
|
||||
Err(Misfire::Useless("blocks", false, "long"))
|
||||
}
|
||||
else if matches.opt_present("time") {
|
||||
Err(Misfire::Useless("time", false, "long"))
|
||||
}
|
||||
else if matches.opt_present("oneline") {
|
||||
if matches.opt_present("across") {
|
||||
Err(Misfire::Useless("across", true, "oneline"))
|
||||
@ -279,6 +288,73 @@ impl SizeFormat {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Copy)]
|
||||
pub enum TimeType {
|
||||
FileAccessed,
|
||||
FileModified,
|
||||
FileCreated,
|
||||
}
|
||||
|
||||
impl TimeType {
|
||||
pub fn header(&self) -> &'static str {
|
||||
match *self {
|
||||
TimeType::FileAccessed => "Date Accessed",
|
||||
TimeType::FileModified => "Date Modified",
|
||||
TimeType::FileCreated => "Date Created",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Copy)]
|
||||
pub struct TimeTypes {
|
||||
accessed: bool,
|
||||
modified: bool,
|
||||
created: bool,
|
||||
}
|
||||
|
||||
impl TimeTypes {
|
||||
|
||||
/// Find which field to use based on a user-supplied word.
|
||||
fn deduce(matches: &getopts::Matches) -> Result<TimeTypes, Misfire> {
|
||||
let possible_word = matches.opt_str("time");
|
||||
let modified = matches.opt_present("modified");
|
||||
let created = matches.opt_present("created");
|
||||
let accessed = matches.opt_present("accessed");
|
||||
|
||||
if let Some(word) = possible_word {
|
||||
if modified {
|
||||
return Err(Misfire::Useless("modified", true, "time"));
|
||||
}
|
||||
else if created {
|
||||
return Err(Misfire::Useless("created", true, "time"));
|
||||
}
|
||||
else if accessed {
|
||||
return Err(Misfire::Useless("accessed", true, "time"));
|
||||
}
|
||||
|
||||
match word.as_slice() {
|
||||
"mod" | "modified" => Ok(TimeTypes { accessed: false, modified: true, created: false }),
|
||||
"acc" | "accessed" => Ok(TimeTypes { accessed: true, modified: false, created: false }),
|
||||
"cr" | "created" => Ok(TimeTypes { accessed: false, modified: false, created: true }),
|
||||
field => Err(TimeTypes::none(field)),
|
||||
}
|
||||
}
|
||||
else {
|
||||
if modified || created || accessed {
|
||||
Ok(TimeTypes { accessed: accessed, modified: modified, created: created })
|
||||
}
|
||||
else {
|
||||
Ok(TimeTypes { accessed: false, modified: true, created: false })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// How to display an error when the word didn't match with anything.
|
||||
fn none(field: &str) -> Misfire {
|
||||
Misfire::InvalidOptions(getopts::Fail::UnrecognizedOption(format!("--time {}", field)))
|
||||
}
|
||||
}
|
||||
|
||||
/// What to do when encountering a directory?
|
||||
#[derive(PartialEq, Debug, Copy)]
|
||||
pub enum DirAction {
|
||||
@ -304,6 +380,7 @@ impl DirAction {
|
||||
#[derive(PartialEq, Copy, Debug)]
|
||||
pub struct Columns {
|
||||
size_format: SizeFormat,
|
||||
time_types: TimeTypes,
|
||||
inode: bool,
|
||||
links: bool,
|
||||
blocks: bool,
|
||||
@ -314,6 +391,7 @@ impl Columns {
|
||||
pub fn deduce(matches: &getopts::Matches) -> Result<Columns, Misfire> {
|
||||
Ok(Columns {
|
||||
size_format: try!(SizeFormat::deduce(matches)),
|
||||
time_types: try!(TimeTypes::deduce(matches)),
|
||||
inode: matches.opt_present("inode"),
|
||||
links: matches.opt_present("links"),
|
||||
blocks: matches.opt_present("blocks"),
|
||||
@ -346,6 +424,20 @@ impl Columns {
|
||||
columns.push(Group);
|
||||
}
|
||||
|
||||
let current_year = LocalDateTime::now().year();
|
||||
|
||||
if self.time_types.modified {
|
||||
columns.push(Timestamp(TimeType::FileModified, current_year));
|
||||
}
|
||||
|
||||
if self.time_types.created {
|
||||
columns.push(Timestamp(TimeType::FileCreated, current_year));
|
||||
}
|
||||
|
||||
if self.time_types.accessed {
|
||||
columns.push(Timestamp(TimeType::FileAccessed, current_year));
|
||||
}
|
||||
|
||||
if cfg!(feature="git") {
|
||||
if let Some(d) = dir {
|
||||
if d.has_git_repo() {
|
||||
|
Loading…
Reference in New Issue
Block a user