2017-07-02 00:02:17 +00:00
|
|
|
|
use std::cmp::max;
|
2020-08-26 23:19:49 +00:00
|
|
|
|
use std::env;
|
2017-07-03 16:40:05 +00:00
|
|
|
|
use std::ops::Deref;
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(unix)]
|
2017-07-02 00:02:17 +00:00
|
|
|
|
use std::sync::{Mutex, MutexGuard};
|
|
|
|
|
|
|
|
|
|
use datetime::TimeZone;
|
|
|
|
|
use zoneinfo_compiled::{CompiledData, Result as TZResult};
|
|
|
|
|
|
Replace Misfire with a testable OptionsResult
This was meant to be a small change, but it spiralled into a big one.
The original intention was to separate OptionsResult and OptionsError. With these types separated, the Help and Version variants can only be returned from the Options::parse function, and the later option-parsing functions can only return success or errors.
Also, Misfire was a silly name.
As a side-effect of Options::parse returning OptionsResult instead of Result<Options, Misfire>, we could no longer use unwrap() or unwrap_err() to get the contents out. This commit makes OptionsResult into a value type, and Options::parse a pure function. It feels like it should be one, having its return value entirely dependent on its arguments, but it also loaded locales and time zones. These parts have been moved into lazy_static references, and the code still passes tests without much change.
OptionsResult isn't PartialEq yet, because the file colouring uses a Box internally.
2020-10-12 22:47:36 +00:00
|
|
|
|
use lazy_static::lazy_static;
|
2020-10-10 01:01:12 +00:00
|
|
|
|
use log::*;
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(unix)]
|
2017-07-02 00:02:17 +00:00
|
|
|
|
use users::UsersCache;
|
|
|
|
|
|
2020-10-10 18:49:46 +00:00
|
|
|
|
use crate::fs::{File, fields as f};
|
|
|
|
|
use crate::fs::feature::git::GitCache;
|
2018-12-07 23:43:31 +00:00
|
|
|
|
use crate::output::cell::TextCell;
|
|
|
|
|
use crate::output::render::TimeRender;
|
|
|
|
|
use crate::output::time::TimeFormat;
|
Massive theming and view options refactor
This commit significantly refactors the way that options are parsed. It introduces the Theme type which contains both styling and extension configuration, converts the option-parsing process into a being a pure function, and removes some rather gnarly old code.
The main purpose of the refactoring is to fix GH-318, "Tests fail when not connected to a terminal". Even though exa was compiling fine on my machine and on Travis, it was failing for automated build scripts. This was because of what the option-parsing code was trying to accomplish: it wasn't just providing a struct of the user's settings, it was also checking the terminal, providing a View directly.
This has been changed so that the options module now _only_ looks at the command-line arguments and environment variables. Instead of returning a View, it returns the user's _preference_, and it's then up to the 'main' module to examine the terminal width and figure out if the view is doable, downgrading it if necessary.
The code that used to determine the view was horrible and I'm pleased it can be cut out. Also, the terminal width used to be in a lazy_static because it was queried multiple times, and now it's not in one because it's only queried once, which is a good sign for things going in the right direction.
There are also some naming and organisational changes around themes. The blanket terms "Colours" and "Styles" have been yeeted in favour of "Theme", which handles both extensions and UI colours. The FileStyle struct has been replaced with file_name::Options, making it similar to the views in how it has an Options struct and a Render struct.
Finally, eight unit tests have been removed because they turned out to be redundant (testing --colour and --color) after examining the tangled code, and the default theme has been put in its own file in preparation for more themes.
2020-10-22 21:34:00 +00:00
|
|
|
|
use crate::theme::Theme;
|
2017-07-05 19:16:04 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Options for displaying a table.
|
Replace Misfire with a testable OptionsResult
This was meant to be a small change, but it spiralled into a big one.
The original intention was to separate OptionsResult and OptionsError. With these types separated, the Help and Version variants can only be returned from the Options::parse function, and the later option-parsing functions can only return success or errors.
Also, Misfire was a silly name.
As a side-effect of Options::parse returning OptionsResult instead of Result<Options, Misfire>, we could no longer use unwrap() or unwrap_err() to get the contents out. This commit makes OptionsResult into a value type, and Options::parse a pure function. It feels like it should be one, having its return value entirely dependent on its arguments, but it also loaded locales and time zones. These parts have been moved into lazy_static references, and the code still passes tests without much change.
OptionsResult isn't PartialEq yet, because the file colouring uses a Box internally.
2020-10-12 22:47:36 +00:00
|
|
|
|
#[derive(PartialEq, Debug)]
|
2017-07-05 19:16:04 +00:00
|
|
|
|
pub struct Options {
|
|
|
|
|
pub size_format: SizeFormat,
|
2017-07-05 20:54:43 +00:00
|
|
|
|
pub time_format: TimeFormat,
|
2021-03-19 00:46:03 +00:00
|
|
|
|
pub user_format: UserFormat,
|
2019-08-29 12:34:30 +00:00
|
|
|
|
pub columns: Columns,
|
2017-08-09 21:25:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Extra columns to display in the table.
|
Cleanup clippy warnings
warning: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> src/output/escape.rs:4:1
|
4 | pub fn escape<'a>(string: String, bits: &mut Vec<ANSIString<'a>>, good: Style, bad: Style) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
warning: this lifetime isn't used in the function definition
--> src/output/escape.rs:4:15
|
4 | pub fn escape<'a>(string: String, bits: &mut Vec<ANSIString<'_>>, good: Style, bad: Style) {
| ^^
|
warning: single-character string constant used as pattern
--> src/output/table.rs:310:41
|
310 | if file.starts_with(":") {
| ^^^ help: try using a `char` instead: `':'`
|
warning: single-character string constant used as pattern
--> src/output/table.rs:310:41
|
310 | if file.starts_with(":") {
| ^^^ help: try using a `char` instead: `':'`
|
warning: methods called `new` usually return `Self`
--> src/output/render/git.rs:38:5
|
38 | fn new(&self) -> Style;
| ^^^^^^^^^^^^^^^^^^^^^^^
|
warning: this lifetime isn't used in the function definition
--> src/output/icons.rs:40:22
|
40 | pub fn iconify_style<'a>(style: Style) -> Style {
| ^^
|
warning: lint `clippy::find_map` has been removed: this lint has been replaced by `manual_find_map`, a more specific lint
--> src/main.rs:11:10
|
11 | #![allow(clippy::find_map)]
| ^^^^^^^^^^^^^^^^
|
warning: redundant else block
--> src/fs/dir.rs:124:18
|
124 | else {
| __________________^
125 | | return None
126 | | }
| |_____________^
|
warning: redundant else block
--> src/options/view.rs:60:18
|
60 | else {
| __________________^
61 | | // the --tree case is handled by the DirAction parser later
62 | | return Ok(Self::Details(details));
63 | | }
| |_____________^
|
warning: all variants have the same postfix: `Bytes`
--> src/output/table.rs:170:1
|
170 | / pub enum SizeFormat {
171 | |
172 | | /// Format the file size using **decimal** prefixes, such as “kilo”,
173 | | /// “mega”, or “giga”.
... |
181 | | JustBytes,
182 | | }
| |_^
|
warning: all variants have the same postfix: `Bytes`
--> src/output/table.rs:171:1
|
171 | / pub enum SizeFormat {
172 | |
173 | | /// Format the file size using **decimal** prefixes, such as “kilo”,
174 | | /// “mega”, or “giga”.
... |
182 | | JustBytes,
183 | | }
| |_^
|
warning: useless use of `format!`
--> src/options/mod.rs:181:50
|
181 | return Err(OptionsError::Unsupported(format!(
| __________________________________________________^
182 | | "Options --git and --git-ignore can't be used because `git` feature was disabled in this build of exa"
183 | | )));
| |_____________^ help: consider using `.to_string()`: `"Options --git and --git-ignore can't be used because `git` feature was disabled in this build of exa".to_string()`
|
warning: stripping a prefix manually
--> src/fs/filter.rs:287:33
|
287 | if n.starts_with('.') { &n[1..] }
| ^^^^^^^
|
warning: case-sensitive file extension comparison
--> src/info/filetype.rs:24:19
|
24 | file.name.ends_with(".ninja") ||
| ^^^^^^^^^^^^^^^^^^^
|
2021-04-30 13:37:31 +00:00
|
|
|
|
#[allow(clippy::struct_excessive_bools)]
|
2020-10-10 14:30:19 +00:00
|
|
|
|
#[derive(PartialEq, Debug, Copy, Clone)]
|
2017-08-09 21:25:16 +00:00
|
|
|
|
pub struct Columns {
|
2021-03-26 08:53:31 +00:00
|
|
|
|
|
2017-08-09 21:25:16 +00:00
|
|
|
|
/// At least one of these timestamps will be shown.
|
2017-07-05 19:16:04 +00:00
|
|
|
|
pub time_types: TimeTypes,
|
2017-08-09 21:25:16 +00:00
|
|
|
|
|
|
|
|
|
// The rest are just on/off
|
2017-07-05 19:16:04 +00:00
|
|
|
|
pub inode: bool,
|
|
|
|
|
pub links: bool,
|
|
|
|
|
pub blocks: bool,
|
|
|
|
|
pub group: bool,
|
2017-09-01 18:13:47 +00:00
|
|
|
|
pub git: bool,
|
2020-07-24 11:47:34 +00:00
|
|
|
|
pub octal: bool,
|
2019-08-29 12:34:30 +00:00
|
|
|
|
|
|
|
|
|
// Defaults to true:
|
|
|
|
|
pub permissions: bool,
|
|
|
|
|
pub filesize: bool,
|
|
|
|
|
pub user: bool,
|
2017-07-05 20:01:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-08-09 21:25:16 +00:00
|
|
|
|
impl Columns {
|
2017-09-01 18:13:47 +00:00
|
|
|
|
pub fn collect(&self, actually_enable_git: bool) -> Vec<Column> {
|
|
|
|
|
let mut columns = Vec::with_capacity(4);
|
2017-07-05 19:16:04 +00:00
|
|
|
|
|
|
|
|
|
if self.inode {
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(unix)]
|
2017-07-05 19:16:04 +00:00
|
|
|
|
columns.push(Column::Inode);
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-24 11:47:34 +00:00
|
|
|
|
if self.octal {
|
2021-03-26 09:47:18 +00:00
|
|
|
|
#[cfg(unix)]
|
2020-07-24 11:47:34 +00:00
|
|
|
|
columns.push(Column::Octal);
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-29 12:34:30 +00:00
|
|
|
|
if self.permissions {
|
|
|
|
|
columns.push(Column::Permissions);
|
|
|
|
|
}
|
2017-07-05 19:16:04 +00:00
|
|
|
|
|
|
|
|
|
if self.links {
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(unix)]
|
2017-07-05 19:16:04 +00:00
|
|
|
|
columns.push(Column::HardLinks);
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-29 12:34:30 +00:00
|
|
|
|
if self.filesize {
|
|
|
|
|
columns.push(Column::FileSize);
|
|
|
|
|
}
|
2017-07-05 19:16:04 +00:00
|
|
|
|
|
|
|
|
|
if self.blocks {
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(unix)]
|
2017-07-05 19:16:04 +00:00
|
|
|
|
columns.push(Column::Blocks);
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-29 12:34:30 +00:00
|
|
|
|
if self.user {
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(unix)]
|
2019-08-29 12:34:30 +00:00
|
|
|
|
columns.push(Column::User);
|
|
|
|
|
}
|
2017-07-05 19:16:04 +00:00
|
|
|
|
|
|
|
|
|
if self.group {
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(unix)]
|
2017-07-05 19:16:04 +00:00
|
|
|
|
columns.push(Column::Group);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if self.time_types.modified {
|
|
|
|
|
columns.push(Column::Timestamp(TimeType::Modified));
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-17 04:46:50 +00:00
|
|
|
|
if self.time_types.changed {
|
|
|
|
|
columns.push(Column::Timestamp(TimeType::Changed));
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-05 19:16:04 +00:00
|
|
|
|
if self.time_types.created {
|
|
|
|
|
columns.push(Column::Timestamp(TimeType::Created));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if self.time_types.accessed {
|
|
|
|
|
columns.push(Column::Timestamp(TimeType::Accessed));
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-03 01:06:29 +00:00
|
|
|
|
if self.git && actually_enable_git {
|
2017-09-01 18:13:47 +00:00
|
|
|
|
columns.push(Column::GitStatus);
|
2017-07-05 19:16:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
columns
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-26 08:50:34 +00:00
|
|
|
|
|
2017-07-05 19:16:04 +00:00
|
|
|
|
/// A table contains these.
|
2020-10-10 14:30:19 +00:00
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
2017-07-05 19:16:04 +00:00
|
|
|
|
pub enum Column {
|
|
|
|
|
Permissions,
|
2017-08-09 20:47:51 +00:00
|
|
|
|
FileSize,
|
2017-07-05 19:16:04 +00:00
|
|
|
|
Timestamp(TimeType),
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(unix)]
|
2017-07-05 19:16:04 +00:00
|
|
|
|
Blocks,
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(unix)]
|
2017-07-05 19:16:04 +00:00
|
|
|
|
User,
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(unix)]
|
2017-07-05 19:16:04 +00:00
|
|
|
|
Group,
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(unix)]
|
2017-07-05 19:16:04 +00:00
|
|
|
|
HardLinks,
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(unix)]
|
2017-07-05 19:16:04 +00:00
|
|
|
|
Inode,
|
|
|
|
|
GitStatus,
|
2021-03-26 09:47:18 +00:00
|
|
|
|
#[cfg(unix)]
|
2020-07-24 11:47:34 +00:00
|
|
|
|
Octal,
|
2017-07-05 19:16:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Each column can pick its own **Alignment**. Usually, numbers are
|
|
|
|
|
/// right-aligned, and text is left-aligned.
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
|
pub enum Alignment {
|
2020-10-10 18:49:46 +00:00
|
|
|
|
Left,
|
|
|
|
|
Right,
|
2017-07-05 19:16:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Column {
|
2021-03-26 08:50:34 +00:00
|
|
|
|
|
2017-07-05 19:16:04 +00:00
|
|
|
|
/// Get the alignment this column should use.
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(unix)]
|
2020-10-10 14:30:19 +00:00
|
|
|
|
pub fn alignment(self) -> Alignment {
|
|
|
|
|
match self {
|
2020-10-10 18:49:46 +00:00
|
|
|
|
Self::FileSize |
|
|
|
|
|
Self::HardLinks |
|
|
|
|
|
Self::Inode |
|
|
|
|
|
Self::Blocks |
|
|
|
|
|
Self::GitStatus => Alignment::Right,
|
|
|
|
|
_ => Alignment::Left,
|
2017-07-05 19:16:04 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-31 03:04:11 +00:00
|
|
|
|
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(windows)]
|
|
|
|
|
pub fn alignment(&self) -> Alignment {
|
2021-03-31 03:04:11 +00:00
|
|
|
|
match self {
|
|
|
|
|
Self::FileSize |
|
|
|
|
|
Self::GitStatus => Alignment::Right,
|
|
|
|
|
_ => Alignment::Left,
|
2020-05-02 04:15:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-07-05 19:16:04 +00:00
|
|
|
|
|
|
|
|
|
/// Get the text that should be printed at the top, when the user elects
|
|
|
|
|
/// to have a header row printed.
|
2020-10-10 14:30:19 +00:00
|
|
|
|
pub fn header(self) -> &'static str {
|
|
|
|
|
match self {
|
2021-03-30 09:13:00 +00:00
|
|
|
|
#[cfg(unix)]
|
2020-10-10 12:55:26 +00:00
|
|
|
|
Self::Permissions => "Permissions",
|
2021-03-30 09:13:00 +00:00
|
|
|
|
#[cfg(windows)]
|
|
|
|
|
Self::Permissions => "Mode",
|
2020-10-10 12:55:26 +00:00
|
|
|
|
Self::FileSize => "Size",
|
|
|
|
|
Self::Timestamp(t) => t.header(),
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(unix)]
|
2020-10-10 12:55:26 +00:00
|
|
|
|
Self::Blocks => "Blocks",
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(unix)]
|
2020-10-10 12:55:26 +00:00
|
|
|
|
Self::User => "User",
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(unix)]
|
2020-10-10 12:55:26 +00:00
|
|
|
|
Self::Group => "Group",
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(unix)]
|
2020-10-10 12:55:26 +00:00
|
|
|
|
Self::HardLinks => "Links",
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(unix)]
|
2020-10-10 12:55:26 +00:00
|
|
|
|
Self::Inode => "inode",
|
|
|
|
|
Self::GitStatus => "Git",
|
2021-03-26 09:47:18 +00:00
|
|
|
|
#[cfg(unix)]
|
2020-10-10 12:55:26 +00:00
|
|
|
|
Self::Octal => "Octal",
|
2017-07-05 19:16:04 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-26 08:50:34 +00:00
|
|
|
|
|
2017-07-05 19:16:04 +00:00
|
|
|
|
/// Formatting options for file sizes.
|
Cleanup clippy warnings
warning: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> src/output/escape.rs:4:1
|
4 | pub fn escape<'a>(string: String, bits: &mut Vec<ANSIString<'a>>, good: Style, bad: Style) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
warning: this lifetime isn't used in the function definition
--> src/output/escape.rs:4:15
|
4 | pub fn escape<'a>(string: String, bits: &mut Vec<ANSIString<'_>>, good: Style, bad: Style) {
| ^^
|
warning: single-character string constant used as pattern
--> src/output/table.rs:310:41
|
310 | if file.starts_with(":") {
| ^^^ help: try using a `char` instead: `':'`
|
warning: single-character string constant used as pattern
--> src/output/table.rs:310:41
|
310 | if file.starts_with(":") {
| ^^^ help: try using a `char` instead: `':'`
|
warning: methods called `new` usually return `Self`
--> src/output/render/git.rs:38:5
|
38 | fn new(&self) -> Style;
| ^^^^^^^^^^^^^^^^^^^^^^^
|
warning: this lifetime isn't used in the function definition
--> src/output/icons.rs:40:22
|
40 | pub fn iconify_style<'a>(style: Style) -> Style {
| ^^
|
warning: lint `clippy::find_map` has been removed: this lint has been replaced by `manual_find_map`, a more specific lint
--> src/main.rs:11:10
|
11 | #![allow(clippy::find_map)]
| ^^^^^^^^^^^^^^^^
|
warning: redundant else block
--> src/fs/dir.rs:124:18
|
124 | else {
| __________________^
125 | | return None
126 | | }
| |_____________^
|
warning: redundant else block
--> src/options/view.rs:60:18
|
60 | else {
| __________________^
61 | | // the --tree case is handled by the DirAction parser later
62 | | return Ok(Self::Details(details));
63 | | }
| |_____________^
|
warning: all variants have the same postfix: `Bytes`
--> src/output/table.rs:170:1
|
170 | / pub enum SizeFormat {
171 | |
172 | | /// Format the file size using **decimal** prefixes, such as “kilo”,
173 | | /// “mega”, or “giga”.
... |
181 | | JustBytes,
182 | | }
| |_^
|
warning: all variants have the same postfix: `Bytes`
--> src/output/table.rs:171:1
|
171 | / pub enum SizeFormat {
172 | |
173 | | /// Format the file size using **decimal** prefixes, such as “kilo”,
174 | | /// “mega”, or “giga”.
... |
182 | | JustBytes,
183 | | }
| |_^
|
warning: useless use of `format!`
--> src/options/mod.rs:181:50
|
181 | return Err(OptionsError::Unsupported(format!(
| __________________________________________________^
182 | | "Options --git and --git-ignore can't be used because `git` feature was disabled in this build of exa"
183 | | )));
| |_____________^ help: consider using `.to_string()`: `"Options --git and --git-ignore can't be used because `git` feature was disabled in this build of exa".to_string()`
|
warning: stripping a prefix manually
--> src/fs/filter.rs:287:33
|
287 | if n.starts_with('.') { &n[1..] }
| ^^^^^^^
|
warning: case-sensitive file extension comparison
--> src/info/filetype.rs:24:19
|
24 | file.name.ends_with(".ninja") ||
| ^^^^^^^^^^^^^^^^^^^
|
2021-04-30 13:37:31 +00:00
|
|
|
|
#[allow(clippy::pub_enum_variant_names)]
|
2017-07-05 19:16:04 +00:00
|
|
|
|
#[derive(PartialEq, Debug, Copy, Clone)]
|
|
|
|
|
pub enum SizeFormat {
|
2021-03-26 08:50:34 +00:00
|
|
|
|
|
2017-07-05 19:16:04 +00:00
|
|
|
|
/// Format the file size using **decimal** prefixes, such as “kilo”,
|
|
|
|
|
/// “mega”, or “giga”.
|
|
|
|
|
DecimalBytes,
|
|
|
|
|
|
|
|
|
|
/// Format the file size using **binary** prefixes, such as “kibi”,
|
|
|
|
|
/// “mebi”, or “gibi”.
|
|
|
|
|
BinaryBytes,
|
|
|
|
|
|
|
|
|
|
/// Do no formatting and just display the size as a number of bytes.
|
|
|
|
|
JustBytes,
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-19 00:46:03 +00:00
|
|
|
|
/// Formatting options for user and group.
|
|
|
|
|
#[derive(PartialEq, Debug, Copy, Clone)]
|
|
|
|
|
pub enum UserFormat {
|
|
|
|
|
/// The UID / GID
|
|
|
|
|
Numeric,
|
|
|
|
|
/// Show the name
|
|
|
|
|
Name,
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-05 19:16:04 +00:00
|
|
|
|
impl Default for SizeFormat {
|
2020-10-10 12:55:26 +00:00
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self::DecimalBytes
|
2017-07-05 19:16:04 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-26 08:50:34 +00:00
|
|
|
|
|
2017-07-05 19:16:04 +00:00
|
|
|
|
/// The types of a file’s time fields. These three fields are standard
|
|
|
|
|
/// across most (all?) operating systems.
|
|
|
|
|
#[derive(PartialEq, Debug, Copy, Clone)]
|
|
|
|
|
pub enum TimeType {
|
2020-10-10 18:49:46 +00:00
|
|
|
|
|
2018-12-17 04:46:50 +00:00
|
|
|
|
/// The file’s modified time (`st_mtime`).
|
|
|
|
|
Modified,
|
|
|
|
|
|
|
|
|
|
/// The file’s changed time (`st_ctime`)
|
|
|
|
|
Changed,
|
2017-07-05 19:16:04 +00:00
|
|
|
|
|
|
|
|
|
/// The file’s accessed time (`st_atime`).
|
|
|
|
|
Accessed,
|
|
|
|
|
|
2018-12-17 04:46:50 +00:00
|
|
|
|
/// The file’s creation time (`btime` or `birthtime`).
|
2017-07-05 19:16:04 +00:00
|
|
|
|
Created,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TimeType {
|
2021-03-26 08:50:34 +00:00
|
|
|
|
|
2017-07-05 19:16:04 +00:00
|
|
|
|
/// Returns the text to use for a column’s heading in the columns output.
|
2018-06-19 12:58:03 +00:00
|
|
|
|
pub fn header(self) -> &'static str {
|
|
|
|
|
match self {
|
2020-10-10 12:55:26 +00:00
|
|
|
|
Self::Modified => "Date Modified",
|
|
|
|
|
Self::Changed => "Date Changed",
|
|
|
|
|
Self::Accessed => "Date Accessed",
|
|
|
|
|
Self::Created => "Date Created",
|
2017-07-05 19:16:04 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-26 08:53:31 +00:00
|
|
|
|
|
2017-07-05 19:16:04 +00:00
|
|
|
|
/// Fields for which of a file’s time fields should be displayed in the
|
|
|
|
|
/// columns output.
|
|
|
|
|
///
|
2020-10-10 18:49:46 +00:00
|
|
|
|
/// There should always be at least one of these — there’s no way to disable
|
2017-07-05 19:16:04 +00:00
|
|
|
|
/// the time columns entirely (yet).
|
|
|
|
|
#[derive(PartialEq, Debug, Copy, Clone)]
|
2020-10-13 00:46:17 +00:00
|
|
|
|
#[allow(clippy::struct_excessive_bools)]
|
2017-07-05 19:16:04 +00:00
|
|
|
|
pub struct TimeTypes {
|
|
|
|
|
pub modified: bool,
|
2021-03-26 08:50:34 +00:00
|
|
|
|
pub changed: bool,
|
2018-12-17 04:46:50 +00:00
|
|
|
|
pub accessed: bool,
|
2021-03-26 08:50:34 +00:00
|
|
|
|
pub created: bool,
|
2017-07-05 19:16:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for TimeTypes {
|
2021-03-26 08:50:34 +00:00
|
|
|
|
|
2017-07-05 19:16:04 +00:00
|
|
|
|
/// By default, display just the ‘modified’ time. This is the most
|
|
|
|
|
/// common option, which is why it has this shorthand.
|
2020-10-10 12:55:26 +00:00
|
|
|
|
fn default() -> Self {
|
2020-10-10 18:49:46 +00:00
|
|
|
|
Self {
|
|
|
|
|
modified: true,
|
|
|
|
|
changed: false,
|
|
|
|
|
accessed: false,
|
|
|
|
|
created: false,
|
|
|
|
|
}
|
2017-07-05 19:16:04 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-26 08:50:34 +00:00
|
|
|
|
|
2017-07-02 00:02:17 +00:00
|
|
|
|
/// The **environment** struct contains any data that could change between
|
2020-10-10 18:49:46 +00:00
|
|
|
|
/// running instances of exa, depending on the user’s computer’s configuration.
|
2017-07-02 00:02:17 +00:00
|
|
|
|
///
|
|
|
|
|
/// Any environment field should be able to be mocked up for test runs.
|
|
|
|
|
pub struct Environment {
|
2021-03-26 08:50:34 +00:00
|
|
|
|
|
2017-07-02 00:02:17 +00:00
|
|
|
|
/// Localisation rules for formatting numbers.
|
|
|
|
|
numeric: locale::Numeric,
|
|
|
|
|
|
2020-10-10 18:49:46 +00:00
|
|
|
|
/// The computer’s current time zone. This gets used to determine how to
|
|
|
|
|
/// offset files’ timestamps.
|
2017-07-02 00:02:17 +00:00
|
|
|
|
tz: Option<TimeZone>,
|
|
|
|
|
|
|
|
|
|
/// Mapping cache of user IDs to usernames.
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(unix)]
|
2017-07-02 00:02:17 +00:00
|
|
|
|
users: Mutex<UsersCache>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Environment {
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(unix)]
|
2020-10-13 00:36:41 +00:00
|
|
|
|
pub fn lock_users(&self) -> MutexGuard<'_, UsersCache> {
|
2017-07-02 00:02:17 +00:00
|
|
|
|
self.users.lock().unwrap()
|
|
|
|
|
}
|
|
|
|
|
|
Replace Misfire with a testable OptionsResult
This was meant to be a small change, but it spiralled into a big one.
The original intention was to separate OptionsResult and OptionsError. With these types separated, the Help and Version variants can only be returned from the Options::parse function, and the later option-parsing functions can only return success or errors.
Also, Misfire was a silly name.
As a side-effect of Options::parse returning OptionsResult instead of Result<Options, Misfire>, we could no longer use unwrap() or unwrap_err() to get the contents out. This commit makes OptionsResult into a value type, and Options::parse a pure function. It feels like it should be one, having its return value entirely dependent on its arguments, but it also loaded locales and time zones. These parts have been moved into lazy_static references, and the code still passes tests without much change.
OptionsResult isn't PartialEq yet, because the file colouring uses a Box internally.
2020-10-12 22:47:36 +00:00
|
|
|
|
fn load_all() -> Self {
|
2017-07-03 07:45:14 +00:00
|
|
|
|
let tz = match determine_time_zone() {
|
2020-10-10 18:49:46 +00:00
|
|
|
|
Ok(t) => {
|
|
|
|
|
Some(t)
|
|
|
|
|
}
|
2017-07-03 07:45:14 +00:00
|
|
|
|
Err(ref e) => {
|
|
|
|
|
println!("Unable to determine time zone: {}", e);
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
};
|
2017-07-02 00:02:17 +00:00
|
|
|
|
|
|
|
|
|
let numeric = locale::Numeric::load_user_locale()
|
2020-10-10 18:49:46 +00:00
|
|
|
|
.unwrap_or_else(|_| locale::Numeric::english());
|
2017-07-02 00:02:17 +00:00
|
|
|
|
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(unix)]
|
2017-07-03 07:45:14 +00:00
|
|
|
|
let users = Mutex::new(UsersCache::new());
|
2017-07-02 00:02:17 +00:00
|
|
|
|
|
2021-05-15 14:03:21 +00:00
|
|
|
|
Self { numeric, tz, #[cfg(unix)] users }
|
2017-07-02 00:02:17 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-26 10:40:22 +00:00
|
|
|
|
#[cfg(unix)]
|
2017-07-02 00:02:17 +00:00
|
|
|
|
fn determine_time_zone() -> TZResult<TimeZone> {
|
2020-08-31 21:20:53 +00:00
|
|
|
|
if let Ok(file) = env::var("TZ") {
|
2021-03-24 12:36:13 +00:00
|
|
|
|
TimeZone::from_file({
|
Cleanup clippy warnings
warning: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> src/output/escape.rs:4:1
|
4 | pub fn escape<'a>(string: String, bits: &mut Vec<ANSIString<'a>>, good: Style, bad: Style) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
warning: this lifetime isn't used in the function definition
--> src/output/escape.rs:4:15
|
4 | pub fn escape<'a>(string: String, bits: &mut Vec<ANSIString<'_>>, good: Style, bad: Style) {
| ^^
|
warning: single-character string constant used as pattern
--> src/output/table.rs:310:41
|
310 | if file.starts_with(":") {
| ^^^ help: try using a `char` instead: `':'`
|
warning: single-character string constant used as pattern
--> src/output/table.rs:310:41
|
310 | if file.starts_with(":") {
| ^^^ help: try using a `char` instead: `':'`
|
warning: methods called `new` usually return `Self`
--> src/output/render/git.rs:38:5
|
38 | fn new(&self) -> Style;
| ^^^^^^^^^^^^^^^^^^^^^^^
|
warning: this lifetime isn't used in the function definition
--> src/output/icons.rs:40:22
|
40 | pub fn iconify_style<'a>(style: Style) -> Style {
| ^^
|
warning: lint `clippy::find_map` has been removed: this lint has been replaced by `manual_find_map`, a more specific lint
--> src/main.rs:11:10
|
11 | #![allow(clippy::find_map)]
| ^^^^^^^^^^^^^^^^
|
warning: redundant else block
--> src/fs/dir.rs:124:18
|
124 | else {
| __________________^
125 | | return None
126 | | }
| |_____________^
|
warning: redundant else block
--> src/options/view.rs:60:18
|
60 | else {
| __________________^
61 | | // the --tree case is handled by the DirAction parser later
62 | | return Ok(Self::Details(details));
63 | | }
| |_____________^
|
warning: all variants have the same postfix: `Bytes`
--> src/output/table.rs:170:1
|
170 | / pub enum SizeFormat {
171 | |
172 | | /// Format the file size using **decimal** prefixes, such as “kilo”,
173 | | /// “mega”, or “giga”.
... |
181 | | JustBytes,
182 | | }
| |_^
|
warning: all variants have the same postfix: `Bytes`
--> src/output/table.rs:171:1
|
171 | / pub enum SizeFormat {
172 | |
173 | | /// Format the file size using **decimal** prefixes, such as “kilo”,
174 | | /// “mega”, or “giga”.
... |
182 | | JustBytes,
183 | | }
| |_^
|
warning: useless use of `format!`
--> src/options/mod.rs:181:50
|
181 | return Err(OptionsError::Unsupported(format!(
| __________________________________________________^
182 | | "Options --git and --git-ignore can't be used because `git` feature was disabled in this build of exa"
183 | | )));
| |_____________^ help: consider using `.to_string()`: `"Options --git and --git-ignore can't be used because `git` feature was disabled in this build of exa".to_string()`
|
warning: stripping a prefix manually
--> src/fs/filter.rs:287:33
|
287 | if n.starts_with('.') { &n[1..] }
| ^^^^^^^
|
warning: case-sensitive file extension comparison
--> src/info/filetype.rs:24:19
|
24 | file.name.ends_with(".ninja") ||
| ^^^^^^^^^^^^^^^^^^^
|
2021-04-30 13:37:31 +00:00
|
|
|
|
if file.starts_with('/') {
|
2021-03-24 12:36:13 +00:00
|
|
|
|
file
|
|
|
|
|
} else {
|
|
|
|
|
format!("/usr/share/zoneinfo/{}", {
|
Cleanup clippy warnings
warning: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> src/output/escape.rs:4:1
|
4 | pub fn escape<'a>(string: String, bits: &mut Vec<ANSIString<'a>>, good: Style, bad: Style) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
warning: this lifetime isn't used in the function definition
--> src/output/escape.rs:4:15
|
4 | pub fn escape<'a>(string: String, bits: &mut Vec<ANSIString<'_>>, good: Style, bad: Style) {
| ^^
|
warning: single-character string constant used as pattern
--> src/output/table.rs:310:41
|
310 | if file.starts_with(":") {
| ^^^ help: try using a `char` instead: `':'`
|
warning: single-character string constant used as pattern
--> src/output/table.rs:310:41
|
310 | if file.starts_with(":") {
| ^^^ help: try using a `char` instead: `':'`
|
warning: methods called `new` usually return `Self`
--> src/output/render/git.rs:38:5
|
38 | fn new(&self) -> Style;
| ^^^^^^^^^^^^^^^^^^^^^^^
|
warning: this lifetime isn't used in the function definition
--> src/output/icons.rs:40:22
|
40 | pub fn iconify_style<'a>(style: Style) -> Style {
| ^^
|
warning: lint `clippy::find_map` has been removed: this lint has been replaced by `manual_find_map`, a more specific lint
--> src/main.rs:11:10
|
11 | #![allow(clippy::find_map)]
| ^^^^^^^^^^^^^^^^
|
warning: redundant else block
--> src/fs/dir.rs:124:18
|
124 | else {
| __________________^
125 | | return None
126 | | }
| |_____________^
|
warning: redundant else block
--> src/options/view.rs:60:18
|
60 | else {
| __________________^
61 | | // the --tree case is handled by the DirAction parser later
62 | | return Ok(Self::Details(details));
63 | | }
| |_____________^
|
warning: all variants have the same postfix: `Bytes`
--> src/output/table.rs:170:1
|
170 | / pub enum SizeFormat {
171 | |
172 | | /// Format the file size using **decimal** prefixes, such as “kilo”,
173 | | /// “mega”, or “giga”.
... |
181 | | JustBytes,
182 | | }
| |_^
|
warning: all variants have the same postfix: `Bytes`
--> src/output/table.rs:171:1
|
171 | / pub enum SizeFormat {
172 | |
173 | | /// Format the file size using **decimal** prefixes, such as “kilo”,
174 | | /// “mega”, or “giga”.
... |
182 | | JustBytes,
183 | | }
| |_^
|
warning: useless use of `format!`
--> src/options/mod.rs:181:50
|
181 | return Err(OptionsError::Unsupported(format!(
| __________________________________________________^
182 | | "Options --git and --git-ignore can't be used because `git` feature was disabled in this build of exa"
183 | | )));
| |_____________^ help: consider using `.to_string()`: `"Options --git and --git-ignore can't be used because `git` feature was disabled in this build of exa".to_string()`
|
warning: stripping a prefix manually
--> src/fs/filter.rs:287:33
|
287 | if n.starts_with('.') { &n[1..] }
| ^^^^^^^
|
warning: case-sensitive file extension comparison
--> src/info/filetype.rs:24:19
|
24 | file.name.ends_with(".ninja") ||
| ^^^^^^^^^^^^^^^^^^^
|
2021-04-30 13:37:31 +00:00
|
|
|
|
if file.starts_with(':') {
|
2021-03-25 06:20:11 +00:00
|
|
|
|
file.replacen(":", "", 1)
|
2021-03-24 12:36:13 +00:00
|
|
|
|
} else {
|
2021-03-24 14:05:38 +00:00
|
|
|
|
file
|
2021-03-24 12:36:13 +00:00
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
} else {
|
2020-08-31 21:20:53 +00:00
|
|
|
|
TimeZone::from_file("/etc/localtime")
|
2020-08-26 23:19:49 +00:00
|
|
|
|
}
|
2017-07-02 00:02:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-26 10:40:22 +00:00
|
|
|
|
#[cfg(windows)]
|
|
|
|
|
fn determine_time_zone() -> TZResult<TimeZone> {
|
|
|
|
|
use datetime::zone::{FixedTimespan, FixedTimespanSet, StaticTimeZone, TimeZoneSource};
|
|
|
|
|
use std::borrow::Cow;
|
|
|
|
|
|
|
|
|
|
Ok(TimeZone(TimeZoneSource::Static(&StaticTimeZone {
|
|
|
|
|
name: "Unsupported",
|
|
|
|
|
fixed_timespans: FixedTimespanSet {
|
|
|
|
|
first: FixedTimespan {
|
|
|
|
|
offset: 0,
|
|
|
|
|
is_dst: false,
|
|
|
|
|
name: Cow::Borrowed("ZONE_A"),
|
|
|
|
|
},
|
|
|
|
|
rest: &[(
|
|
|
|
|
1206838800,
|
|
|
|
|
FixedTimespan {
|
|
|
|
|
offset: 3600,
|
|
|
|
|
is_dst: false,
|
|
|
|
|
name: Cow::Borrowed("ZONE_B"),
|
|
|
|
|
},
|
|
|
|
|
)],
|
|
|
|
|
},
|
|
|
|
|
})))
|
|
|
|
|
}
|
|
|
|
|
|
Replace Misfire with a testable OptionsResult
This was meant to be a small change, but it spiralled into a big one.
The original intention was to separate OptionsResult and OptionsError. With these types separated, the Help and Version variants can only be returned from the Options::parse function, and the later option-parsing functions can only return success or errors.
Also, Misfire was a silly name.
As a side-effect of Options::parse returning OptionsResult instead of Result<Options, Misfire>, we could no longer use unwrap() or unwrap_err() to get the contents out. This commit makes OptionsResult into a value type, and Options::parse a pure function. It feels like it should be one, having its return value entirely dependent on its arguments, but it also loaded locales and time zones. These parts have been moved into lazy_static references, and the code still passes tests without much change.
OptionsResult isn't PartialEq yet, because the file colouring uses a Box internally.
2020-10-12 22:47:36 +00:00
|
|
|
|
lazy_static! {
|
|
|
|
|
static ref ENVIRONMENT: Environment = Environment::load_all();
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-26 08:50:34 +00:00
|
|
|
|
|
2017-07-02 00:02:17 +00:00
|
|
|
|
pub struct Table<'a> {
|
2017-07-05 20:01:01 +00:00
|
|
|
|
columns: Vec<Column>,
|
Massive theming and view options refactor
This commit significantly refactors the way that options are parsed. It introduces the Theme type which contains both styling and extension configuration, converts the option-parsing process into a being a pure function, and removes some rather gnarly old code.
The main purpose of the refactoring is to fix GH-318, "Tests fail when not connected to a terminal". Even though exa was compiling fine on my machine and on Travis, it was failing for automated build scripts. This was because of what the option-parsing code was trying to accomplish: it wasn't just providing a struct of the user's settings, it was also checking the terminal, providing a View directly.
This has been changed so that the options module now _only_ looks at the command-line arguments and environment variables. Instead of returning a View, it returns the user's _preference_, and it's then up to the 'main' module to examine the terminal width and figure out if the view is doable, downgrading it if necessary.
The code that used to determine the view was horrible and I'm pleased it can be cut out. Also, the terminal width used to be in a lazy_static because it was queried multiple times, and now it's not in one because it's only queried once, which is a good sign for things going in the right direction.
There are also some naming and organisational changes around themes. The blanket terms "Colours" and "Styles" have been yeeted in favour of "Theme", which handles both extensions and UI colours. The FileStyle struct has been replaced with file_name::Options, making it similar to the views in how it has an Options struct and a Render struct.
Finally, eight unit tests have been removed because they turned out to be redundant (testing --colour and --color) after examining the tangled code, and the default theme has been put in its own file in preparation for more themes.
2020-10-22 21:34:00 +00:00
|
|
|
|
theme: &'a Theme,
|
2017-07-02 00:02:17 +00:00
|
|
|
|
env: &'a Environment,
|
2017-07-03 16:40:05 +00:00
|
|
|
|
widths: TableWidths,
|
2020-10-13 00:46:17 +00:00
|
|
|
|
time_format: TimeFormat,
|
2017-08-09 20:47:51 +00:00
|
|
|
|
size_format: SizeFormat,
|
2021-03-19 00:46:03 +00:00
|
|
|
|
user_format: UserFormat,
|
2017-09-01 18:13:47 +00:00
|
|
|
|
git: Option<&'a GitCache>,
|
2017-07-02 00:02:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct Row {
|
|
|
|
|
cells: Vec<TextCell>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a, 'f> Table<'a> {
|
Massive theming and view options refactor
This commit significantly refactors the way that options are parsed. It introduces the Theme type which contains both styling and extension configuration, converts the option-parsing process into a being a pure function, and removes some rather gnarly old code.
The main purpose of the refactoring is to fix GH-318, "Tests fail when not connected to a terminal". Even though exa was compiling fine on my machine and on Travis, it was failing for automated build scripts. This was because of what the option-parsing code was trying to accomplish: it wasn't just providing a struct of the user's settings, it was also checking the terminal, providing a View directly.
This has been changed so that the options module now _only_ looks at the command-line arguments and environment variables. Instead of returning a View, it returns the user's _preference_, and it's then up to the 'main' module to examine the terminal width and figure out if the view is doable, downgrading it if necessary.
The code that used to determine the view was horrible and I'm pleased it can be cut out. Also, the terminal width used to be in a lazy_static because it was queried multiple times, and now it's not in one because it's only queried once, which is a good sign for things going in the right direction.
There are also some naming and organisational changes around themes. The blanket terms "Colours" and "Styles" have been yeeted in favour of "Theme", which handles both extensions and UI colours. The FileStyle struct has been replaced with file_name::Options, making it similar to the views in how it has an Options struct and a Render struct.
Finally, eight unit tests have been removed because they turned out to be redundant (testing --colour and --color) after examining the tangled code, and the default theme has been put in its own file in preparation for more themes.
2020-10-22 21:34:00 +00:00
|
|
|
|
pub fn new(options: &'a Options, git: Option<&'a GitCache>, theme: &'a Theme) -> Table<'a> {
|
2019-08-29 12:34:30 +00:00
|
|
|
|
let columns = options.columns.collect(git.is_some());
|
2017-09-01 18:13:47 +00:00
|
|
|
|
let widths = TableWidths::zero(columns.len());
|
Replace Misfire with a testable OptionsResult
This was meant to be a small change, but it spiralled into a big one.
The original intention was to separate OptionsResult and OptionsError. With these types separated, the Help and Version variants can only be returned from the Options::parse function, and the later option-parsing functions can only return success or errors.
Also, Misfire was a silly name.
As a side-effect of Options::parse returning OptionsResult instead of Result<Options, Misfire>, we could no longer use unwrap() or unwrap_err() to get the contents out. This commit makes OptionsResult into a value type, and Options::parse a pure function. It feels like it should be one, having its return value entirely dependent on its arguments, but it also loaded locales and time zones. These parts have been moved into lazy_static references, and the code still passes tests without much change.
OptionsResult isn't PartialEq yet, because the file colouring uses a Box internally.
2020-10-12 22:47:36 +00:00
|
|
|
|
let env = &*ENVIRONMENT;
|
2017-09-01 18:13:47 +00:00
|
|
|
|
|
|
|
|
|
Table {
|
Massive theming and view options refactor
This commit significantly refactors the way that options are parsed. It introduces the Theme type which contains both styling and extension configuration, converts the option-parsing process into a being a pure function, and removes some rather gnarly old code.
The main purpose of the refactoring is to fix GH-318, "Tests fail when not connected to a terminal". Even though exa was compiling fine on my machine and on Travis, it was failing for automated build scripts. This was because of what the option-parsing code was trying to accomplish: it wasn't just providing a struct of the user's settings, it was also checking the terminal, providing a View directly.
This has been changed so that the options module now _only_ looks at the command-line arguments and environment variables. Instead of returning a View, it returns the user's _preference_, and it's then up to the 'main' module to examine the terminal width and figure out if the view is doable, downgrading it if necessary.
The code that used to determine the view was horrible and I'm pleased it can be cut out. Also, the terminal width used to be in a lazy_static because it was queried multiple times, and now it's not in one because it's only queried once, which is a good sign for things going in the right direction.
There are also some naming and organisational changes around themes. The blanket terms "Colours" and "Styles" have been yeeted in favour of "Theme", which handles both extensions and UI colours. The FileStyle struct has been replaced with file_name::Options, making it similar to the views in how it has an Options struct and a Render struct.
Finally, eight unit tests have been removed because they turned out to be redundant (testing --colour and --color) after examining the tangled code, and the default theme has been put in its own file in preparation for more themes.
2020-10-22 21:34:00 +00:00
|
|
|
|
theme,
|
2020-10-10 18:49:46 +00:00
|
|
|
|
widths,
|
|
|
|
|
columns,
|
|
|
|
|
git,
|
Replace Misfire with a testable OptionsResult
This was meant to be a small change, but it spiralled into a big one.
The original intention was to separate OptionsResult and OptionsError. With these types separated, the Help and Version variants can only be returned from the Options::parse function, and the later option-parsing functions can only return success or errors.
Also, Misfire was a silly name.
As a side-effect of Options::parse returning OptionsResult instead of Result<Options, Misfire>, we could no longer use unwrap() or unwrap_err() to get the contents out. This commit makes OptionsResult into a value type, and Options::parse a pure function. It feels like it should be one, having its return value entirely dependent on its arguments, but it also loaded locales and time zones. These parts have been moved into lazy_static references, and the code still passes tests without much change.
OptionsResult isn't PartialEq yet, because the file colouring uses a Box internally.
2020-10-12 22:47:36 +00:00
|
|
|
|
env,
|
2020-10-13 00:46:17 +00:00
|
|
|
|
time_format: options.time_format,
|
|
|
|
|
size_format: options.size_format,
|
2021-03-19 00:46:03 +00:00
|
|
|
|
user_format: options.user_format,
|
2017-08-09 20:47:51 +00:00
|
|
|
|
}
|
2017-07-02 00:02:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-07-03 19:21:33 +00:00
|
|
|
|
pub fn widths(&self) -> &TableWidths {
|
|
|
|
|
&self.widths
|
2017-07-02 00:02:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-07-03 16:40:05 +00:00
|
|
|
|
pub fn header_row(&self) -> Row {
|
|
|
|
|
let cells = self.columns.iter()
|
Massive theming and view options refactor
This commit significantly refactors the way that options are parsed. It introduces the Theme type which contains both styling and extension configuration, converts the option-parsing process into a being a pure function, and removes some rather gnarly old code.
The main purpose of the refactoring is to fix GH-318, "Tests fail when not connected to a terminal". Even though exa was compiling fine on my machine and on Travis, it was failing for automated build scripts. This was because of what the option-parsing code was trying to accomplish: it wasn't just providing a struct of the user's settings, it was also checking the terminal, providing a View directly.
This has been changed so that the options module now _only_ looks at the command-line arguments and environment variables. Instead of returning a View, it returns the user's _preference_, and it's then up to the 'main' module to examine the terminal width and figure out if the view is doable, downgrading it if necessary.
The code that used to determine the view was horrible and I'm pleased it can be cut out. Also, the terminal width used to be in a lazy_static because it was queried multiple times, and now it's not in one because it's only queried once, which is a good sign for things going in the right direction.
There are also some naming and organisational changes around themes. The blanket terms "Colours" and "Styles" have been yeeted in favour of "Theme", which handles both extensions and UI colours. The FileStyle struct has been replaced with file_name::Options, making it similar to the views in how it has an Options struct and a Render struct.
Finally, eight unit tests have been removed because they turned out to be redundant (testing --colour and --color) after examining the tangled code, and the default theme has been put in its own file in preparation for more themes.
2020-10-22 21:34:00 +00:00
|
|
|
|
.map(|c| TextCell::paint_str(self.theme.ui.header, c.header()))
|
2017-07-03 16:40:05 +00:00
|
|
|
|
.collect();
|
2017-07-02 00:02:17 +00:00
|
|
|
|
|
|
|
|
|
Row { cells }
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-13 00:36:41 +00:00
|
|
|
|
pub fn row_for_file(&self, file: &File<'_>, xattrs: bool) -> Row {
|
Only get an Env if one’s being used, also mutexes
This commit ties a table’s Environment to the fact that it contains columns.
Previously, the Details view would get its Environment, and then use those fields to actually display the details in the table: except for the case where we’re only displaying a tree, when it would just be ignored, instead.
This was caused by the “no columns” case using a Vec of no Columns behind the scenes, rather than disabling the table entirely; much like how a tap isn’t a zero-length swipe, the code should have been updated to reflect this. Now, the Environment is only created if it’s going to be used.
Also, fix a double-mutex-lock: the mutable Table had to be accessed under a lock, but the table contained a UsersCache, which *also* had to be accessed under a lock. This was changed so that the table is only updated *after* the threads have all been joined, so there’s no need for any lock at all. May fix #141, but not sure.
2017-07-03 16:04:37 +00:00
|
|
|
|
let cells = self.columns.iter()
|
2020-10-10 14:30:19 +00:00
|
|
|
|
.map(|c| self.display(file, *c, xattrs))
|
Only get an Env if one’s being used, also mutexes
This commit ties a table’s Environment to the fact that it contains columns.
Previously, the Details view would get its Environment, and then use those fields to actually display the details in the table: except for the case where we’re only displaying a tree, when it would just be ignored, instead.
This was caused by the “no columns” case using a Vec of no Columns behind the scenes, rather than disabling the table entirely; much like how a tap isn’t a zero-length swipe, the code should have been updated to reflect this. Now, the Environment is only created if it’s going to be used.
Also, fix a double-mutex-lock: the mutable Table had to be accessed under a lock, but the table contained a UsersCache, which *also* had to be accessed under a lock. This was changed so that the table is only updated *after* the threads have all been joined, so there’s no need for any lock at all. May fix #141, but not sure.
2017-07-03 16:04:37 +00:00
|
|
|
|
.collect();
|
2017-07-02 00:02:17 +00:00
|
|
|
|
|
|
|
|
|
Row { cells }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn add_widths(&mut self, row: &Row) {
|
2017-07-03 16:40:05 +00:00
|
|
|
|
self.widths.add_widths(row)
|
2017-07-02 00:02:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-10-13 00:36:41 +00:00
|
|
|
|
fn permissions_plus(&self, file: &File<'_>, xattrs: bool) -> f::PermissionsPlus {
|
2017-07-02 00:02:17 +00:00
|
|
|
|
f::PermissionsPlus {
|
|
|
|
|
file_type: file.type_char(),
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(unix)]
|
2017-07-02 00:02:17 +00:00
|
|
|
|
permissions: file.permissions(),
|
2021-03-30 09:13:00 +00:00
|
|
|
|
#[cfg(windows)]
|
|
|
|
|
attributes: file.attributes(),
|
2018-06-19 12:58:03 +00:00
|
|
|
|
xattrs,
|
2017-07-02 00:02:17 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-26 09:47:18 +00:00
|
|
|
|
#[cfg(unix)]
|
2020-10-13 00:36:41 +00:00
|
|
|
|
fn octal_permissions(&self, file: &File<'_>) -> f::OctalPermissions {
|
2020-07-24 11:47:34 +00:00
|
|
|
|
f::OctalPermissions {
|
|
|
|
|
permissions: file.permissions(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-13 00:36:41 +00:00
|
|
|
|
fn display(&self, file: &File<'_>, column: Column, xattrs: bool) -> TextCell {
|
2020-10-10 14:30:19 +00:00
|
|
|
|
match column {
|
2020-10-10 18:49:46 +00:00
|
|
|
|
Column::Permissions => {
|
Massive theming and view options refactor
This commit significantly refactors the way that options are parsed. It introduces the Theme type which contains both styling and extension configuration, converts the option-parsing process into a being a pure function, and removes some rather gnarly old code.
The main purpose of the refactoring is to fix GH-318, "Tests fail when not connected to a terminal". Even though exa was compiling fine on my machine and on Travis, it was failing for automated build scripts. This was because of what the option-parsing code was trying to accomplish: it wasn't just providing a struct of the user's settings, it was also checking the terminal, providing a View directly.
This has been changed so that the options module now _only_ looks at the command-line arguments and environment variables. Instead of returning a View, it returns the user's _preference_, and it's then up to the 'main' module to examine the terminal width and figure out if the view is doable, downgrading it if necessary.
The code that used to determine the view was horrible and I'm pleased it can be cut out. Also, the terminal width used to be in a lazy_static because it was queried multiple times, and now it's not in one because it's only queried once, which is a good sign for things going in the right direction.
There are also some naming and organisational changes around themes. The blanket terms "Colours" and "Styles" have been yeeted in favour of "Theme", which handles both extensions and UI colours. The FileStyle struct has been replaced with file_name::Options, making it similar to the views in how it has an Options struct and a Render struct.
Finally, eight unit tests have been removed because they turned out to be redundant (testing --colour and --color) after examining the tangled code, and the default theme has been put in its own file in preparation for more themes.
2020-10-22 21:34:00 +00:00
|
|
|
|
self.permissions_plus(file, xattrs).render(self.theme)
|
2020-10-10 18:49:46 +00:00
|
|
|
|
}
|
|
|
|
|
Column::FileSize => {
|
Massive theming and view options refactor
This commit significantly refactors the way that options are parsed. It introduces the Theme type which contains both styling and extension configuration, converts the option-parsing process into a being a pure function, and removes some rather gnarly old code.
The main purpose of the refactoring is to fix GH-318, "Tests fail when not connected to a terminal". Even though exa was compiling fine on my machine and on Travis, it was failing for automated build scripts. This was because of what the option-parsing code was trying to accomplish: it wasn't just providing a struct of the user's settings, it was also checking the terminal, providing a View directly.
This has been changed so that the options module now _only_ looks at the command-line arguments and environment variables. Instead of returning a View, it returns the user's _preference_, and it's then up to the 'main' module to examine the terminal width and figure out if the view is doable, downgrading it if necessary.
The code that used to determine the view was horrible and I'm pleased it can be cut out. Also, the terminal width used to be in a lazy_static because it was queried multiple times, and now it's not in one because it's only queried once, which is a good sign for things going in the right direction.
There are also some naming and organisational changes around themes. The blanket terms "Colours" and "Styles" have been yeeted in favour of "Theme", which handles both extensions and UI colours. The FileStyle struct has been replaced with file_name::Options, making it similar to the views in how it has an Options struct and a Render struct.
Finally, eight unit tests have been removed because they turned out to be redundant (testing --colour and --color) after examining the tangled code, and the default theme has been put in its own file in preparation for more themes.
2020-10-22 21:34:00 +00:00
|
|
|
|
file.size().render(self.theme, self.size_format, &self.env.numeric)
|
2020-10-10 18:49:46 +00:00
|
|
|
|
}
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(unix)]
|
2020-10-10 18:49:46 +00:00
|
|
|
|
Column::HardLinks => {
|
Massive theming and view options refactor
This commit significantly refactors the way that options are parsed. It introduces the Theme type which contains both styling and extension configuration, converts the option-parsing process into a being a pure function, and removes some rather gnarly old code.
The main purpose of the refactoring is to fix GH-318, "Tests fail when not connected to a terminal". Even though exa was compiling fine on my machine and on Travis, it was failing for automated build scripts. This was because of what the option-parsing code was trying to accomplish: it wasn't just providing a struct of the user's settings, it was also checking the terminal, providing a View directly.
This has been changed so that the options module now _only_ looks at the command-line arguments and environment variables. Instead of returning a View, it returns the user's _preference_, and it's then up to the 'main' module to examine the terminal width and figure out if the view is doable, downgrading it if necessary.
The code that used to determine the view was horrible and I'm pleased it can be cut out. Also, the terminal width used to be in a lazy_static because it was queried multiple times, and now it's not in one because it's only queried once, which is a good sign for things going in the right direction.
There are also some naming and organisational changes around themes. The blanket terms "Colours" and "Styles" have been yeeted in favour of "Theme", which handles both extensions and UI colours. The FileStyle struct has been replaced with file_name::Options, making it similar to the views in how it has an Options struct and a Render struct.
Finally, eight unit tests have been removed because they turned out to be redundant (testing --colour and --color) after examining the tangled code, and the default theme has been put in its own file in preparation for more themes.
2020-10-22 21:34:00 +00:00
|
|
|
|
file.links().render(self.theme, &self.env.numeric)
|
2020-10-10 18:49:46 +00:00
|
|
|
|
}
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(unix)]
|
2020-10-10 18:49:46 +00:00
|
|
|
|
Column::Inode => {
|
Massive theming and view options refactor
This commit significantly refactors the way that options are parsed. It introduces the Theme type which contains both styling and extension configuration, converts the option-parsing process into a being a pure function, and removes some rather gnarly old code.
The main purpose of the refactoring is to fix GH-318, "Tests fail when not connected to a terminal". Even though exa was compiling fine on my machine and on Travis, it was failing for automated build scripts. This was because of what the option-parsing code was trying to accomplish: it wasn't just providing a struct of the user's settings, it was also checking the terminal, providing a View directly.
This has been changed so that the options module now _only_ looks at the command-line arguments and environment variables. Instead of returning a View, it returns the user's _preference_, and it's then up to the 'main' module to examine the terminal width and figure out if the view is doable, downgrading it if necessary.
The code that used to determine the view was horrible and I'm pleased it can be cut out. Also, the terminal width used to be in a lazy_static because it was queried multiple times, and now it's not in one because it's only queried once, which is a good sign for things going in the right direction.
There are also some naming and organisational changes around themes. The blanket terms "Colours" and "Styles" have been yeeted in favour of "Theme", which handles both extensions and UI colours. The FileStyle struct has been replaced with file_name::Options, making it similar to the views in how it has an Options struct and a Render struct.
Finally, eight unit tests have been removed because they turned out to be redundant (testing --colour and --color) after examining the tangled code, and the default theme has been put in its own file in preparation for more themes.
2020-10-22 21:34:00 +00:00
|
|
|
|
file.inode().render(self.theme.ui.inode)
|
2020-10-10 18:49:46 +00:00
|
|
|
|
}
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(unix)]
|
2020-10-10 18:49:46 +00:00
|
|
|
|
Column::Blocks => {
|
Massive theming and view options refactor
This commit significantly refactors the way that options are parsed. It introduces the Theme type which contains both styling and extension configuration, converts the option-parsing process into a being a pure function, and removes some rather gnarly old code.
The main purpose of the refactoring is to fix GH-318, "Tests fail when not connected to a terminal". Even though exa was compiling fine on my machine and on Travis, it was failing for automated build scripts. This was because of what the option-parsing code was trying to accomplish: it wasn't just providing a struct of the user's settings, it was also checking the terminal, providing a View directly.
This has been changed so that the options module now _only_ looks at the command-line arguments and environment variables. Instead of returning a View, it returns the user's _preference_, and it's then up to the 'main' module to examine the terminal width and figure out if the view is doable, downgrading it if necessary.
The code that used to determine the view was horrible and I'm pleased it can be cut out. Also, the terminal width used to be in a lazy_static because it was queried multiple times, and now it's not in one because it's only queried once, which is a good sign for things going in the right direction.
There are also some naming and organisational changes around themes. The blanket terms "Colours" and "Styles" have been yeeted in favour of "Theme", which handles both extensions and UI colours. The FileStyle struct has been replaced with file_name::Options, making it similar to the views in how it has an Options struct and a Render struct.
Finally, eight unit tests have been removed because they turned out to be redundant (testing --colour and --color) after examining the tangled code, and the default theme has been put in its own file in preparation for more themes.
2020-10-22 21:34:00 +00:00
|
|
|
|
file.blocks().render(self.theme)
|
2020-10-10 18:49:46 +00:00
|
|
|
|
}
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(unix)]
|
2020-10-10 18:49:46 +00:00
|
|
|
|
Column::User => {
|
2021-03-19 00:46:03 +00:00
|
|
|
|
file.user().render(self.theme, &*self.env.lock_users(), self.user_format)
|
2020-10-10 18:49:46 +00:00
|
|
|
|
}
|
2020-05-02 04:15:50 +00:00
|
|
|
|
#[cfg(unix)]
|
2020-10-10 18:49:46 +00:00
|
|
|
|
Column::Group => {
|
2021-03-19 00:46:03 +00:00
|
|
|
|
file.group().render(self.theme, &*self.env.lock_users(), self.user_format)
|
2020-10-10 18:49:46 +00:00
|
|
|
|
}
|
|
|
|
|
Column::GitStatus => {
|
Massive theming and view options refactor
This commit significantly refactors the way that options are parsed. It introduces the Theme type which contains both styling and extension configuration, converts the option-parsing process into a being a pure function, and removes some rather gnarly old code.
The main purpose of the refactoring is to fix GH-318, "Tests fail when not connected to a terminal". Even though exa was compiling fine on my machine and on Travis, it was failing for automated build scripts. This was because of what the option-parsing code was trying to accomplish: it wasn't just providing a struct of the user's settings, it was also checking the terminal, providing a View directly.
This has been changed so that the options module now _only_ looks at the command-line arguments and environment variables. Instead of returning a View, it returns the user's _preference_, and it's then up to the 'main' module to examine the terminal width and figure out if the view is doable, downgrading it if necessary.
The code that used to determine the view was horrible and I'm pleased it can be cut out. Also, the terminal width used to be in a lazy_static because it was queried multiple times, and now it's not in one because it's only queried once, which is a good sign for things going in the right direction.
There are also some naming and organisational changes around themes. The blanket terms "Colours" and "Styles" have been yeeted in favour of "Theme", which handles both extensions and UI colours. The FileStyle struct has been replaced with file_name::Options, making it similar to the views in how it has an Options struct and a Render struct.
Finally, eight unit tests have been removed because they turned out to be redundant (testing --colour and --color) after examining the tangled code, and the default theme has been put in its own file in preparation for more themes.
2020-10-22 21:34:00 +00:00
|
|
|
|
self.git_status(file).render(self.theme)
|
2020-10-10 18:49:46 +00:00
|
|
|
|
}
|
2021-03-26 09:47:18 +00:00
|
|
|
|
#[cfg(unix)]
|
2020-10-10 18:49:46 +00:00
|
|
|
|
Column::Octal => {
|
Massive theming and view options refactor
This commit significantly refactors the way that options are parsed. It introduces the Theme type which contains both styling and extension configuration, converts the option-parsing process into a being a pure function, and removes some rather gnarly old code.
The main purpose of the refactoring is to fix GH-318, "Tests fail when not connected to a terminal". Even though exa was compiling fine on my machine and on Travis, it was failing for automated build scripts. This was because of what the option-parsing code was trying to accomplish: it wasn't just providing a struct of the user's settings, it was also checking the terminal, providing a View directly.
This has been changed so that the options module now _only_ looks at the command-line arguments and environment variables. Instead of returning a View, it returns the user's _preference_, and it's then up to the 'main' module to examine the terminal width and figure out if the view is doable, downgrading it if necessary.
The code that used to determine the view was horrible and I'm pleased it can be cut out. Also, the terminal width used to be in a lazy_static because it was queried multiple times, and now it's not in one because it's only queried once, which is a good sign for things going in the right direction.
There are also some naming and organisational changes around themes. The blanket terms "Colours" and "Styles" have been yeeted in favour of "Theme", which handles both extensions and UI colours. The FileStyle struct has been replaced with file_name::Options, making it similar to the views in how it has an Options struct and a Render struct.
Finally, eight unit tests have been removed because they turned out to be redundant (testing --colour and --color) after examining the tangled code, and the default theme has been put in its own file in preparation for more themes.
2020-10-22 21:34:00 +00:00
|
|
|
|
self.octal_permissions(file).render(self.theme.ui.octal)
|
2020-10-10 18:49:46 +00:00
|
|
|
|
}
|
2021-03-26 12:34:16 +00:00
|
|
|
|
|
2020-10-10 18:49:46 +00:00
|
|
|
|
Column::Timestamp(TimeType::Modified) => {
|
Massive theming and view options refactor
This commit significantly refactors the way that options are parsed. It introduces the Theme type which contains both styling and extension configuration, converts the option-parsing process into a being a pure function, and removes some rather gnarly old code.
The main purpose of the refactoring is to fix GH-318, "Tests fail when not connected to a terminal". Even though exa was compiling fine on my machine and on Travis, it was failing for automated build scripts. This was because of what the option-parsing code was trying to accomplish: it wasn't just providing a struct of the user's settings, it was also checking the terminal, providing a View directly.
This has been changed so that the options module now _only_ looks at the command-line arguments and environment variables. Instead of returning a View, it returns the user's _preference_, and it's then up to the 'main' module to examine the terminal width and figure out if the view is doable, downgrading it if necessary.
The code that used to determine the view was horrible and I'm pleased it can be cut out. Also, the terminal width used to be in a lazy_static because it was queried multiple times, and now it's not in one because it's only queried once, which is a good sign for things going in the right direction.
There are also some naming and organisational changes around themes. The blanket terms "Colours" and "Styles" have been yeeted in favour of "Theme", which handles both extensions and UI colours. The FileStyle struct has been replaced with file_name::Options, making it similar to the views in how it has an Options struct and a Render struct.
Finally, eight unit tests have been removed because they turned out to be redundant (testing --colour and --color) after examining the tangled code, and the default theme has been put in its own file in preparation for more themes.
2020-10-22 21:34:00 +00:00
|
|
|
|
file.modified_time().render(self.theme.ui.date, &self.env.tz, self.time_format)
|
2020-10-10 18:49:46 +00:00
|
|
|
|
}
|
|
|
|
|
Column::Timestamp(TimeType::Changed) => {
|
Massive theming and view options refactor
This commit significantly refactors the way that options are parsed. It introduces the Theme type which contains both styling and extension configuration, converts the option-parsing process into a being a pure function, and removes some rather gnarly old code.
The main purpose of the refactoring is to fix GH-318, "Tests fail when not connected to a terminal". Even though exa was compiling fine on my machine and on Travis, it was failing for automated build scripts. This was because of what the option-parsing code was trying to accomplish: it wasn't just providing a struct of the user's settings, it was also checking the terminal, providing a View directly.
This has been changed so that the options module now _only_ looks at the command-line arguments and environment variables. Instead of returning a View, it returns the user's _preference_, and it's then up to the 'main' module to examine the terminal width and figure out if the view is doable, downgrading it if necessary.
The code that used to determine the view was horrible and I'm pleased it can be cut out. Also, the terminal width used to be in a lazy_static because it was queried multiple times, and now it's not in one because it's only queried once, which is a good sign for things going in the right direction.
There are also some naming and organisational changes around themes. The blanket terms "Colours" and "Styles" have been yeeted in favour of "Theme", which handles both extensions and UI colours. The FileStyle struct has been replaced with file_name::Options, making it similar to the views in how it has an Options struct and a Render struct.
Finally, eight unit tests have been removed because they turned out to be redundant (testing --colour and --color) after examining the tangled code, and the default theme has been put in its own file in preparation for more themes.
2020-10-22 21:34:00 +00:00
|
|
|
|
file.changed_time().render(self.theme.ui.date, &self.env.tz, self.time_format)
|
2020-10-10 18:49:46 +00:00
|
|
|
|
}
|
|
|
|
|
Column::Timestamp(TimeType::Created) => {
|
Massive theming and view options refactor
This commit significantly refactors the way that options are parsed. It introduces the Theme type which contains both styling and extension configuration, converts the option-parsing process into a being a pure function, and removes some rather gnarly old code.
The main purpose of the refactoring is to fix GH-318, "Tests fail when not connected to a terminal". Even though exa was compiling fine on my machine and on Travis, it was failing for automated build scripts. This was because of what the option-parsing code was trying to accomplish: it wasn't just providing a struct of the user's settings, it was also checking the terminal, providing a View directly.
This has been changed so that the options module now _only_ looks at the command-line arguments and environment variables. Instead of returning a View, it returns the user's _preference_, and it's then up to the 'main' module to examine the terminal width and figure out if the view is doable, downgrading it if necessary.
The code that used to determine the view was horrible and I'm pleased it can be cut out. Also, the terminal width used to be in a lazy_static because it was queried multiple times, and now it's not in one because it's only queried once, which is a good sign for things going in the right direction.
There are also some naming and organisational changes around themes. The blanket terms "Colours" and "Styles" have been yeeted in favour of "Theme", which handles both extensions and UI colours. The FileStyle struct has been replaced with file_name::Options, making it similar to the views in how it has an Options struct and a Render struct.
Finally, eight unit tests have been removed because they turned out to be redundant (testing --colour and --color) after examining the tangled code, and the default theme has been put in its own file in preparation for more themes.
2020-10-22 21:34:00 +00:00
|
|
|
|
file.created_time().render(self.theme.ui.date, &self.env.tz, self.time_format)
|
2020-10-10 18:49:46 +00:00
|
|
|
|
}
|
|
|
|
|
Column::Timestamp(TimeType::Accessed) => {
|
Massive theming and view options refactor
This commit significantly refactors the way that options are parsed. It introduces the Theme type which contains both styling and extension configuration, converts the option-parsing process into a being a pure function, and removes some rather gnarly old code.
The main purpose of the refactoring is to fix GH-318, "Tests fail when not connected to a terminal". Even though exa was compiling fine on my machine and on Travis, it was failing for automated build scripts. This was because of what the option-parsing code was trying to accomplish: it wasn't just providing a struct of the user's settings, it was also checking the terminal, providing a View directly.
This has been changed so that the options module now _only_ looks at the command-line arguments and environment variables. Instead of returning a View, it returns the user's _preference_, and it's then up to the 'main' module to examine the terminal width and figure out if the view is doable, downgrading it if necessary.
The code that used to determine the view was horrible and I'm pleased it can be cut out. Also, the terminal width used to be in a lazy_static because it was queried multiple times, and now it's not in one because it's only queried once, which is a good sign for things going in the right direction.
There are also some naming and organisational changes around themes. The blanket terms "Colours" and "Styles" have been yeeted in favour of "Theme", which handles both extensions and UI colours. The FileStyle struct has been replaced with file_name::Options, making it similar to the views in how it has an Options struct and a Render struct.
Finally, eight unit tests have been removed because they turned out to be redundant (testing --colour and --color) after examining the tangled code, and the default theme has been put in its own file in preparation for more themes.
2020-10-22 21:34:00 +00:00
|
|
|
|
file.accessed_time().render(self.theme.ui.date, &self.env.tz, self.time_format)
|
2020-10-10 18:49:46 +00:00
|
|
|
|
}
|
2017-07-02 00:02:17 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-13 00:36:41 +00:00
|
|
|
|
fn git_status(&self, file: &File<'_>) -> f::Git {
|
2017-09-01 18:13:47 +00:00
|
|
|
|
debug!("Getting Git status for file {:?}", file.path);
|
2020-10-10 18:49:46 +00:00
|
|
|
|
|
2017-09-01 18:13:47 +00:00
|
|
|
|
self.git
|
|
|
|
|
.map(|g| g.get(&file.path, file.is_directory()))
|
|
|
|
|
.unwrap_or_default()
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-02 00:02:17 +00:00
|
|
|
|
pub fn render(&self, row: Row) -> TextCell {
|
|
|
|
|
let mut cell = TextCell::default();
|
|
|
|
|
|
2020-10-10 18:49:46 +00:00
|
|
|
|
let iter = row.cells.into_iter()
|
|
|
|
|
.zip(self.widths.iter())
|
|
|
|
|
.enumerate();
|
|
|
|
|
|
|
|
|
|
for (n, (this_cell, width)) in iter {
|
2017-07-02 00:02:17 +00:00
|
|
|
|
let padding = width - *this_cell.width;
|
|
|
|
|
|
|
|
|
|
match self.columns[n].alignment() {
|
2020-10-10 18:49:46 +00:00
|
|
|
|
Alignment::Left => {
|
|
|
|
|
cell.append(this_cell);
|
|
|
|
|
cell.add_spaces(padding);
|
|
|
|
|
}
|
|
|
|
|
Alignment::Right => {
|
|
|
|
|
cell.add_spaces(padding);
|
|
|
|
|
cell.append(this_cell);
|
|
|
|
|
}
|
2017-07-02 00:02:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cell.add_spaces(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cell
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-07-03 16:40:05 +00:00
|
|
|
|
|
2021-03-26 08:50:34 +00:00
|
|
|
|
|
2017-07-03 16:40:05 +00:00
|
|
|
|
pub struct TableWidths(Vec<usize>);
|
|
|
|
|
|
|
|
|
|
impl Deref for TableWidths {
|
|
|
|
|
type Target = [usize];
|
|
|
|
|
|
2018-06-19 12:58:03 +00:00
|
|
|
|
fn deref(&self) -> &Self::Target {
|
2017-07-03 16:40:05 +00:00
|
|
|
|
&self.0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TableWidths {
|
2020-10-10 12:55:26 +00:00
|
|
|
|
pub fn zero(count: usize) -> Self {
|
2020-10-10 18:49:46 +00:00
|
|
|
|
Self(vec![0; count])
|
2017-07-03 16:40:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn add_widths(&mut self, row: &Row) {
|
|
|
|
|
for (old_width, cell) in self.0.iter_mut().zip(row.cells.iter()) {
|
|
|
|
|
*old_width = max(*old_width, *cell.width);
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-07-03 19:21:33 +00:00
|
|
|
|
|
|
|
|
|
pub fn total(&self) -> usize {
|
|
|
|
|
self.0.len() + self.0.iter().sum::<usize>()
|
|
|
|
|
}
|
2017-07-03 16:40:05 +00:00
|
|
|
|
}
|