exa/src/output/table.rs

581 lines
15 KiB
Rust
Raw Normal View History

use std::cmp::max;
use std::env;
use std::ops::Deref;
2020-05-02 04:15:50 +00:00
#[cfg(unix)]
use std::sync::{Mutex, MutexGuard};
use datetime::TimeZone;
use zoneinfo_compiled::{CompiledData, Result as TZResult};
use lazy_static::lazy_static;
use log::*;
2020-05-02 04:15:50 +00:00
#[cfg(unix)]
use users::UsersCache;
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;
/// Options for displaying a table.
#[derive(PartialEq, Debug)]
pub struct Options {
pub size_format: SizeFormat,
pub time_format: TimeFormat,
pub user_format: UserFormat,
pub columns: Columns,
}
/// 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)]
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct Columns {
2021-03-26 08:53:31 +00:00
/// At least one of these timestamps will be shown.
pub time_types: TimeTypes,
// The rest are just on/off
pub inode: bool,
pub links: bool,
pub blocks: bool,
pub group: bool,
pub git: bool,
pub octal: bool,
// Defaults to true:
pub permissions: bool,
pub filesize: bool,
pub user: bool,
}
impl Columns {
pub fn collect(&self, actually_enable_git: bool) -> Vec<Column> {
let mut columns = Vec::with_capacity(4);
if self.inode {
2020-05-02 04:15:50 +00:00
#[cfg(unix)]
columns.push(Column::Inode);
}
if self.octal {
2021-03-26 09:47:18 +00:00
#[cfg(unix)]
columns.push(Column::Octal);
}
if self.permissions {
columns.push(Column::Permissions);
}
if self.links {
2020-05-02 04:15:50 +00:00
#[cfg(unix)]
columns.push(Column::HardLinks);
}
if self.filesize {
columns.push(Column::FileSize);
}
if self.blocks {
2020-05-02 04:15:50 +00:00
#[cfg(unix)]
columns.push(Column::Blocks);
}
if self.user {
2020-05-02 04:15:50 +00:00
#[cfg(unix)]
columns.push(Column::User);
}
if self.group {
2020-05-02 04:15:50 +00:00
#[cfg(unix)]
columns.push(Column::Group);
}
if self.time_types.modified {
columns.push(Column::Timestamp(TimeType::Modified));
}
if self.time_types.changed {
columns.push(Column::Timestamp(TimeType::Changed));
}
if self.time_types.created {
columns.push(Column::Timestamp(TimeType::Created));
}
if self.time_types.accessed {
columns.push(Column::Timestamp(TimeType::Accessed));
}
if self.git && actually_enable_git {
columns.push(Column::GitStatus);
}
columns
}
}
2021-03-26 08:50:34 +00:00
/// A table contains these.
#[derive(Debug, Copy, Clone)]
pub enum Column {
Permissions,
FileSize,
Timestamp(TimeType),
2020-05-02 04:15:50 +00:00
#[cfg(unix)]
Blocks,
2020-05-02 04:15:50 +00:00
#[cfg(unix)]
User,
2020-05-02 04:15:50 +00:00
#[cfg(unix)]
Group,
2020-05-02 04:15:50 +00:00
#[cfg(unix)]
HardLinks,
2020-05-02 04:15:50 +00:00
#[cfg(unix)]
Inode,
GitStatus,
2021-03-26 09:47:18 +00:00
#[cfg(unix)]
Octal,
}
/// Each column can pick its own **Alignment**. Usually, numbers are
/// right-aligned, and text is left-aligned.
#[derive(Copy, Clone)]
pub enum Alignment {
Left,
Right,
}
impl Column {
2021-03-26 08:50:34 +00:00
/// Get the alignment this column should use.
2020-05-02 04:15:50 +00:00
#[cfg(unix)]
pub fn alignment(self) -> Alignment {
match self {
Self::FileSize |
Self::HardLinks |
Self::Inode |
Self::Blocks |
Self::GitStatus => Alignment::Right,
_ => Alignment::Left,
}
}
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
}
}
/// Get the text that should be printed at the top, when the user elects
/// to have a header row printed.
pub fn header(self) -> &'static str {
match self {
2021-03-30 09:13:00 +00:00
#[cfg(unix)]
Self::Permissions => "Permissions",
2021-03-30 09:13:00 +00:00
#[cfg(windows)]
Self::Permissions => "Mode",
Self::FileSize => "Size",
Self::Timestamp(t) => t.header(),
2020-05-02 04:15:50 +00:00
#[cfg(unix)]
Self::Blocks => "Blocks",
2020-05-02 04:15:50 +00:00
#[cfg(unix)]
Self::User => "User",
2020-05-02 04:15:50 +00:00
#[cfg(unix)]
Self::Group => "Group",
2020-05-02 04:15:50 +00:00
#[cfg(unix)]
Self::HardLinks => "Links",
2020-05-02 04:15:50 +00:00
#[cfg(unix)]
Self::Inode => "inode",
Self::GitStatus => "Git",
2021-03-26 09:47:18 +00:00
#[cfg(unix)]
Self::Octal => "Octal",
}
}
}
2021-03-26 08:50:34 +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)]
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum SizeFormat {
2021-03-26 08:50:34 +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,
}
/// Formatting options for user and group.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum UserFormat {
/// The UID / GID
Numeric,
/// Show the name
Name,
}
impl Default for SizeFormat {
fn default() -> Self {
Self::DecimalBytes
}
}
2021-03-26 08:50:34 +00:00
/// The types of a files time fields. These three fields are standard
/// across most (all?) operating systems.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum TimeType {
/// The files modified time (`st_mtime`).
Modified,
/// The files changed time (`st_ctime`)
Changed,
/// The files accessed time (`st_atime`).
Accessed,
/// The files creation time (`btime` or `birthtime`).
Created,
}
impl TimeType {
2021-03-26 08:50:34 +00:00
/// Returns the text to use for a columns heading in the columns output.
2018-06-19 12:58:03 +00:00
pub fn header(self) -> &'static str {
match self {
Self::Modified => "Date Modified",
Self::Changed => "Date Changed",
Self::Accessed => "Date Accessed",
Self::Created => "Date Created",
}
}
}
2021-03-26 08:53:31 +00:00
/// Fields for which of a files time fields should be displayed in the
/// columns output.
///
/// There should always be at least one of these — theres no way to disable
/// the time columns entirely (yet).
#[derive(PartialEq, Debug, Copy, Clone)]
#[allow(clippy::struct_excessive_bools)]
pub struct TimeTypes {
pub modified: bool,
2021-03-26 08:50:34 +00:00
pub changed: bool,
pub accessed: bool,
2021-03-26 08:50:34 +00:00
pub created: bool,
}
impl Default for TimeTypes {
2021-03-26 08:50:34 +00:00
/// By default, display just the modified time. This is the most
/// common option, which is why it has this shorthand.
fn default() -> Self {
Self {
modified: true,
changed: false,
accessed: false,
created: false,
}
}
}
2021-03-26 08:50:34 +00:00
/// The **environment** struct contains any data that could change between
/// running instances of exa, depending on the users computers configuration.
///
/// Any environment field should be able to be mocked up for test runs.
pub struct Environment {
2021-03-26 08:50:34 +00:00
/// Localisation rules for formatting numbers.
numeric: locale::Numeric,
/// The computers current time zone. This gets used to determine how to
/// offset files timestamps.
tz: Option<TimeZone>,
/// Mapping cache of user IDs to usernames.
2020-05-02 04:15:50 +00:00
#[cfg(unix)]
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> {
self.users.lock().unwrap()
}
fn load_all() -> Self {
let tz = match determine_time_zone() {
Ok(t) => {
Some(t)
}
Err(ref e) => {
println!("Unable to determine time zone: {}", e);
None
}
};
let numeric = locale::Numeric::load_user_locale()
.unwrap_or_else(|_| locale::Numeric::english());
2020-05-02 04:15:50 +00:00
#[cfg(unix)]
let users = Mutex::new(UsersCache::new());
Self { numeric, tz, #[cfg(unix)] users }
}
}
2021-03-26 10:40:22 +00:00
#[cfg(unix)]
fn determine_time_zone() -> TZResult<TimeZone> {
if let Ok(file) = env::var("TZ") {
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('/') {
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)
} else {
2021-03-24 14:05:38 +00:00
file
}
})
}
})
} else {
TimeZone::from_file("/etc/localtime")
}
}
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"),
},
)],
},
})))
}
lazy_static! {
static ref ENVIRONMENT: Environment = Environment::load_all();
}
2021-03-26 08:50:34 +00:00
pub struct Table<'a> {
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,
env: &'a Environment,
widths: TableWidths,
time_format: TimeFormat,
size_format: SizeFormat,
user_format: UserFormat,
git: Option<&'a GitCache>,
}
#[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> {
let columns = options.columns.collect(git.is_some());
let widths = TableWidths::zero(columns.len());
let env = &*ENVIRONMENT;
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,
widths,
columns,
git,
env,
time_format: options.time_format,
size_format: options.size_format,
user_format: options.user_format,
}
}
2017-07-03 19:21:33 +00:00
pub fn widths(&self) -> &TableWidths {
&self.widths
}
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()))
.collect();
Row { cells }
}
2020-10-13 00:36:41 +00:00
pub fn row_for_file(&self, file: &File<'_>, xattrs: bool) -> Row {
let cells = self.columns.iter()
.map(|c| self.display(file, *c, xattrs))
.collect();
Row { cells }
}
pub fn add_widths(&mut self, row: &Row) {
self.widths.add_widths(row)
}
2020-10-13 00:36:41 +00:00
fn permissions_plus(&self, file: &File<'_>, xattrs: bool) -> f::PermissionsPlus {
f::PermissionsPlus {
file_type: file.type_char(),
2020-05-02 04:15:50 +00:00
#[cfg(unix)]
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,
}
}
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 {
f::OctalPermissions {
permissions: file.permissions(),
}
}
2020-10-13 00:36:41 +00:00
fn display(&self, file: &File<'_>, column: Column, xattrs: bool) -> TextCell {
match column {
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)
}
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-05-02 04:15:50 +00:00
#[cfg(unix)]
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-05-02 04:15:50 +00:00
#[cfg(unix)]
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-05-02 04:15:50 +00:00
#[cfg(unix)]
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-05-02 04:15:50 +00:00
#[cfg(unix)]
Column::User => {
file.user().render(self.theme, &*self.env.lock_users(), self.user_format)
}
2020-05-02 04:15:50 +00:00
#[cfg(unix)]
Column::Group => {
file.group().render(self.theme, &*self.env.lock_users(), self.user_format)
}
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)
}
2021-03-26 09:47:18 +00:00
#[cfg(unix)]
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)
}
2021-03-26 12:34:16 +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)
}
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)
}
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)
}
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-13 00:36:41 +00:00
fn git_status(&self, file: &File<'_>) -> f::Git {
debug!("Getting Git status for file {:?}", file.path);
self.git
.map(|g| g.get(&file.path, file.is_directory()))
.unwrap_or_default()
}
pub fn render(&self, row: Row) -> TextCell {
let mut cell = TextCell::default();
let iter = row.cells.into_iter()
.zip(self.widths.iter())
.enumerate();
for (n, (this_cell, width)) in iter {
let padding = width - *this_cell.width;
match self.columns[n].alignment() {
Alignment::Left => {
cell.append(this_cell);
cell.add_spaces(padding);
}
Alignment::Right => {
cell.add_spaces(padding);
cell.append(this_cell);
}
}
cell.add_spaces(1);
}
cell
}
}
2021-03-26 08:50:34 +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 {
&self.0
}
}
impl TableWidths {
pub fn zero(count: usize) -> Self {
Self(vec![0; count])
}
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>()
}
}