Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -310,10 +310,9 @@ eza’s options are almost, but not quite, entirely unlike `ls`’s.
- **-T**, **--tree**: recurse into directories as a tree
- **-x**, **--across**: sort the grid across, rather than downwards
- **-F**, **--classify**: display type indicator by file names
- **--colo[u]r**: when to use terminal colours
- **--colo[u]r=(when)**: when to use terminal colours (always, auto, never)
- **--colo[u]r-scale**: highlight levels of file sizes distinctly
- **--icons**: display icons
- **--no-icons**: don't display icons (always overrides --icons)
- **--icons=(when)**: when to display icons (always, auto, never)
- **--hyperlink**: display entries as hyperlinks
- **-w**, **--width=(columns)**: set screen width in columns

Expand Down
5 changes: 5 additions & 0 deletions completions/bash/eza
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ _eza() {
return
;;

--icons)
mapfile -t COMPREPLY < <(compgen -W 'always automatic auto never' -- "$cur")
return
;;

-L|--level)
mapfile -t COMPREPLY < <(compgen -W '{0..9}' -- "$cur")
return
Expand Down
8 changes: 6 additions & 2 deletions completions/fish/eza.fish
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@ complete -c eza -l color \
"
complete -c eza -l color-scale \
-l colour-scale -d "Highlight levels of file sizes distinctly"
complete -c eza -l icons -d "Display icons"
complete -c eza -l no-icons -d "Don't display icons"
complete -c eza -l icons -d "When to display icons" -x -a "
always\t'Always display icons'
auto\t'Display icons if standard output is a terminal'
automatic\t'Display icons if standard output is a terminal'
never\t'Never display icons'
"
complete -c eza -l no-quotes -d "Don't quote file names with spaces"
complete -c eza -l hyperlink -d "Display entries as hyperlinks"
complete -c eza -l smart-group -d "Only show group if it has a different name from owner"
Expand Down
3 changes: 1 addition & 2 deletions completions/nush/eza.nu
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ export extern "eza" [
--colour # When to use terminal colours
--color-scale # Highlight levels of file sizes distinctly
--colour-scale # Highlight levels of file sizes distinctly
--icons # Display icons
--no-icons # Don't display icons
--icons # When to display icons
--no-quotes # Don't quote file names with spaces
--hyperlink # Display entries as hyperlinks
--group-directories-first # Sort directories before other files
Expand Down
3 changes: 1 addition & 2 deletions completions/zsh/_eza
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ __eza() {
{-F,--classify}"[Display type indicator by file names]" \
--colo{,u}r="[When to use terminal colours]:(when):(always auto automatic never)" \
--colo{,u}r-scale"[Highlight levels of file sizes distinctly]" \
--icons"[Display icons]" \
--no-icons"[Hide icons]" \
--icons="[When to display icons]:(when):(always auto automatic never)" \
--no-quotes"[Don't quote filenames with spaces]" \
--hyperlink"[Display entries as hyperlinks]" \
--group-directories-first"[Sort directories before other files]" \
Expand Down
8 changes: 5 additions & 3 deletions man/eza.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,13 @@ Manually setting this option overrides `NO_COLOR` environment.
`--color-scale`, `--colour-scale`
: Colour file sizes on a scale.

`--icons`
`--icons=WHEN`
: Display icons next to file names.

`--no-icons`
: Don't display icons. (Always overrides --icons)
Valid settings are ‘`always`’, ‘`automatic`’ (‘`auto`’ for short), and ‘`never`’.
The default value is ‘`automatic`’.

`automatic` or `auto` will display icons only when the standard output is connected to a real terminal. If `eza` is ran while in a `tty`, or the output of `eza` is either redirected to a file or piped into another program, icons will not be used. Setting this option to ‘`always`’ causes `eza` to always display icons, while ‘`never`’ disables the use of icons.

`--no-quotes`
: Don't quote file names with spaces.
Expand Down
47 changes: 38 additions & 9 deletions src/options/file_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ use crate::options::{flags, NumberSource, OptionsError};
use crate::output::file_name::{Classify, EmbedHyperlinks, Options, QuoteStyle, ShowIcons};

impl Options {
pub fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
pub fn deduce<V: Vars>(
matches: &MatchedFlags<'_>,
vars: &V,
is_a_tty: bool,
) -> Result<Self, OptionsError> {
let classify = Classify::deduce(matches)?;
let show_icons = ShowIcons::deduce(matches, vars)?;

Expand All @@ -17,6 +21,7 @@ impl Options {
show_icons,
quote_style,
embed_hyperlinks,
is_a_tty,
})
}
}
Expand All @@ -35,24 +40,48 @@ impl Classify {

impl ShowIcons {
pub fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
if matches.has(&flags::NO_ICONS)? || !matches.has(&flags::ICONS)? {
Ok(Self::Off)
} else if let Some(columns) = vars
.get_with_fallback(vars::EZA_ICON_SPACING, vars::EXA_ICON_SPACING)
enum AlwaysOrAuto {
Always,
Automatic,
}

let mode_opt = matches.get(&flags::ICONS)?;
if !matches.has(&flags::ICONS)? && mode_opt.is_none() {
return Ok(Self::Never);
}

let mode = match mode_opt {
Some(word) => match word.to_str() {
Some("always") => AlwaysOrAuto::Always,
Some("auto" | "automatic") => AlwaysOrAuto::Automatic,
Some("never") => return Ok(Self::Never),
None => AlwaysOrAuto::Automatic,
_ => return Err(OptionsError::BadArgument(&flags::ICONS, word.into())),
},
None => AlwaysOrAuto::Automatic,
};

let width = if let Some(columns) = vars
.get_with_fallback(vars::EXA_ICON_SPACING, vars::EZA_ICON_SPACING)
.and_then(|s| s.into_string().ok())
{
match columns.parse() {
Ok(width) => Ok(Self::On(width)),
Ok(width) => width,
Err(e) => {
let source = NumberSource::Env(
vars.source(vars::EZA_ICON_SPACING, vars::EXA_ICON_SPACING)
vars.source(vars::EXA_ICON_SPACING, vars::EZA_ICON_SPACING)
.unwrap(),
);
Err(OptionsError::FailedParse(columns, source, e))
return Err(OptionsError::FailedParse(columns, source, e));
}
}
} else {
Ok(Self::On(1))
1
};

match mode {
AlwaysOrAuto::Always => Ok(Self::Always(width)),
AlwaysOrAuto::Automatic => Ok(Self::Automatic(width)),
}
}
}
Expand Down
43 changes: 21 additions & 22 deletions src/options/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ pub static DEREF_LINKS: Arg = Arg { short: Some(b'X'), long: "dereference", take
pub static WIDTH: Arg = Arg { short: Some(b'w'), long: "width", takes_value: TakesValue::Necessary(None) };
pub static NO_QUOTES:Arg = Arg { short: None, long: "no-quotes",takes_value: TakesValue::Forbidden };

pub static COLOR: Arg = Arg { short: None, long: "color", takes_value: TakesValue::Necessary(Some(COLOURS)) };
pub static COLOUR: Arg = Arg { short: None, long: "colour", takes_value: TakesValue::Necessary(Some(COLOURS)) };
const COLOURS: &[&str] = &["always", "auto", "never"];
pub static COLOR: Arg = Arg { short: None, long: "color", takes_value: TakesValue::Optional(Some(WHEN)) };
pub static COLOUR: Arg = Arg { short: None, long: "colour", takes_value: TakesValue::Optional(Some(WHEN)) };
const WHEN: &[&str] = &["always", "auto", "never"];

pub static COLOR_SCALE: Arg = Arg { short: None, long: "color-scale", takes_value: TakesValue::Forbidden };
pub static COLOUR_SCALE: Arg = Arg { short: None, long: "colour-scale", takes_value: TakesValue::Forbidden };
Expand All @@ -40,23 +40,23 @@ const SORTS: Values = &[ "name", "Name", "size", "extension",
"created", "inode", "type", "none" ];

// display options
pub static BINARY: Arg = Arg { short: Some(b'b'), long: "binary", takes_value: TakesValue::Forbidden };
pub static BYTES: Arg = Arg { short: Some(b'B'), long: "bytes", takes_value: TakesValue::Forbidden };
pub static GROUP: Arg = Arg { short: Some(b'g'), long: "group", takes_value: TakesValue::Forbidden };
pub static NUMERIC: Arg = Arg { short: Some(b'n'), long: "numeric", takes_value: TakesValue::Forbidden };
pub static HEADER: Arg = Arg { short: Some(b'h'), long: "header", takes_value: TakesValue::Forbidden };
pub static ICONS: Arg = Arg { short: None, long: "icons", takes_value: TakesValue::Forbidden };
pub static INODE: Arg = Arg { short: Some(b'i'), long: "inode", takes_value: TakesValue::Forbidden };
pub static LINKS: Arg = Arg { short: Some(b'H'), long: "links", takes_value: TakesValue::Forbidden };
pub static MODIFIED: Arg = Arg { short: Some(b'm'), long: "modified", takes_value: TakesValue::Forbidden };
pub static CHANGED: Arg = Arg { short: None, long: "changed", takes_value: TakesValue::Forbidden };
pub static BLOCKSIZE: Arg = Arg { short: Some(b'S'), long: "blocksize", takes_value: TakesValue::Forbidden };
pub static TIME: Arg = Arg { short: Some(b't'), long: "time", takes_value: TakesValue::Necessary(Some(TIMES)) };
pub static ACCESSED: Arg = Arg { short: Some(b'u'), long: "accessed", takes_value: TakesValue::Forbidden };
pub static CREATED: Arg = Arg { short: Some(b'U'), long: "created", takes_value: TakesValue::Forbidden };
pub static TIME_STYLE: Arg = Arg { short: None, long: "time-style", takes_value: TakesValue::Necessary(Some(TIME_STYLES)) };
pub static HYPERLINK: Arg = Arg { short: None, long: "hyperlink", takes_value: TakesValue::Forbidden };
pub static MOUNTS: Arg = Arg { short: Some(b'M'), long: "mounts", takes_value: TakesValue::Forbidden };
pub static BINARY: Arg = Arg { short: Some(b'b'), long: "binary", takes_value: TakesValue::Forbidden };
pub static BYTES: Arg = Arg { short: Some(b'B'), long: "bytes", takes_value: TakesValue::Forbidden };
pub static GROUP: Arg = Arg { short: Some(b'g'), long: "group", takes_value: TakesValue::Forbidden };
pub static NUMERIC: Arg = Arg { short: Some(b'n'), long: "numeric", takes_value: TakesValue::Forbidden };
pub static HEADER: Arg = Arg { short: Some(b'h'), long: "header", takes_value: TakesValue::Forbidden };
pub static ICONS: Arg = Arg { short: None, long: "icons", takes_value: TakesValue::Optional(Some(WHEN))};
pub static INODE: Arg = Arg { short: Some(b'i'), long: "inode", takes_value: TakesValue::Forbidden };
pub static LINKS: Arg = Arg { short: Some(b'H'), long: "links", takes_value: TakesValue::Forbidden };
pub static MODIFIED: Arg = Arg { short: Some(b'm'), long: "modified", takes_value: TakesValue::Forbidden };
pub static CHANGED: Arg = Arg { short: None, long: "changed", takes_value: TakesValue::Forbidden };
pub static BLOCKSIZE: Arg = Arg { short: Some(b'S'), long: "blocksize", takes_value: TakesValue::Forbidden };
pub static TIME: Arg = Arg { short: Some(b't'), long: "time", takes_value: TakesValue::Necessary(Some(TIMES)) };
pub static ACCESSED: Arg = Arg { short: Some(b'u'), long: "accessed", takes_value: TakesValue::Forbidden };
pub static CREATED: Arg = Arg { short: Some(b'U'), long: "created", takes_value: TakesValue::Forbidden };
pub static TIME_STYLE: Arg = Arg { short: None, long: "time-style", takes_value: TakesValue::Necessary(Some(TIME_STYLES)) };
pub static HYPERLINK: Arg = Arg { short: None, long: "hyperlink", takes_value: TakesValue::Forbidden };
pub static MOUNTS: Arg = Arg { short: Some(b'M'), long: "mounts", takes_value: TakesValue::Forbidden };
pub static SMART_GROUP: Arg = Arg { short: None, long: "smart-group", takes_value: TakesValue::Forbidden };
const TIMES: Values = &["modified", "changed", "accessed", "created"];
const TIME_STYLES: Values = &["default", "long-iso", "full-iso", "iso", "relative"];
Expand All @@ -66,7 +66,6 @@ pub static NO_PERMISSIONS: Arg = Arg { short: None, long: "no-permissions", take
pub static NO_FILESIZE: Arg = Arg { short: None, long: "no-filesize", takes_value: TakesValue::Forbidden };
pub static NO_USER: Arg = Arg { short: None, long: "no-user", takes_value: TakesValue::Forbidden };
pub static NO_TIME: Arg = Arg { short: None, long: "no-time", takes_value: TakesValue::Forbidden };
pub static NO_ICONS: Arg = Arg { short: None, long: "no-icons", takes_value: TakesValue::Forbidden };

// optional feature options
pub static GIT: Arg = Arg { short: None, long: "git", takes_value: TakesValue::Forbidden };
Expand All @@ -88,7 +87,7 @@ pub static ALL_ARGS: Args = Args(&[

&BINARY, &BYTES, &GROUP, &NUMERIC, &HEADER, &ICONS, &INODE, &LINKS, &MODIFIED, &CHANGED,
&BLOCKSIZE, &TIME, &ACCESSED, &CREATED, &TIME_STYLE, &HYPERLINK, &MOUNTS,
&NO_PERMISSIONS, &NO_FILESIZE, &NO_USER, &NO_TIME, &NO_ICONS, &SMART_GROUP,
&NO_PERMISSIONS, &NO_FILESIZE, &NO_USER, &NO_TIME, &SMART_GROUP,

&GIT, &NO_GIT, &GIT_REPOS, &GIT_REPOS_NO_STAT,
&EXTENDED, &OCTAL, &SECURITY_CONTEXT
Expand Down
5 changes: 2 additions & 3 deletions src/options/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ DISPLAY OPTIONS
-F, --classify display type indicator by file names
--colo[u]r=WHEN when to use terminal colours (always, auto, never)
--colo[u]r-scale highlight levels of file sizes distinctly
--icons display icons
--no-icons don't display icons (always overrides --icons)
--icons=WHEN when to display icons (always, auto, never)
--no-quotes don't quote file names with spaces
--hyperlink display entries as hyperlinks
-w, --width COLS set screen width in columns
Expand Down Expand Up @@ -152,6 +151,6 @@ mod test {
fn unhelpful() {
let args = vec![];
let opts = Options::parse(args, &None);
assert!(!matches!(opts, OptionsResult::Help(_))) // no help when --help isn’t passed
assert!(!matches!(opts, OptionsResult::Help(_))); // no help when --help isn’t passed
}
}
22 changes: 15 additions & 7 deletions src/options/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ impl Args {
// Iterate over the inputs with “while let” because we need to advance
// the iterator manually whenever an argument that takes a value
// doesn’t have one in its string so it needs the next one.
let mut inputs = inputs.into_iter();
let mut inputs = inputs.into_iter().peekable();
while let Some(arg) = inputs.next() {
let bytes = os_str_to_bytes(arg);

Expand Down Expand Up @@ -198,13 +198,12 @@ impl Args {
return Err(ParseError::NeedsValue { flag, values });
}
}
TakesValue::Optional(_) => {
if let Some(next_arg) = inputs.next() {
result_flags.push((flag, Some(next_arg)));
} else {
result_flags.push((flag, None));
TakesValue::Optional(_) => match inputs.peek() {
Some(next_arg) if is_optional_arg(next_arg) => {
result_flags.push((flag, Some(inputs.next().unwrap())));
}
}
_ => result_flags.push((flag, None)),
},
}
}
}
Expand Down Expand Up @@ -332,6 +331,15 @@ impl Args {
}
}

fn is_optional_arg(arg: &OsStr) -> bool {
let bytes = os_str_to_bytes(arg);
match bytes {
// The only optional arguments allowed
b"always" | b"auto" | b"automatic" | b"never" => true,
_ => false,
}
}

/// The **matches** are the result of parsing the user’s command-line strings.
#[derive(PartialEq, Eq, Debug)]
pub struct Matches<'args> {
Expand Down
4 changes: 2 additions & 2 deletions src/options/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ use crate::output::{details, grid, Mode, TerminalWidth, View};
impl View {
pub fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
let mode = Mode::deduce(matches, vars)?;
let width = TerminalWidth::deduce(matches, vars)?;
let file_style = FileStyle::deduce(matches, vars)?;
let deref_links = matches.has(&flags::DEREF_LINKS)?;
let width = TerminalWidth::deduce(matches, vars)?;
let file_style = FileStyle::deduce(matches, vars, width.actual_terminal_width().is_some())?;
Ok(Self {
mode,
width,
Expand Down
35 changes: 22 additions & 13 deletions src/output/file_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ pub struct Options {

/// Whether to make file names hyperlinks.
pub embed_hyperlinks: EmbedHyperlinks,

/// Whether we are in a console or redirecting the output
pub is_a_tty: bool,
}

impl Options {
Expand Down Expand Up @@ -94,14 +97,17 @@ enum MountStyle {
}

/// Whether and how to show icons.
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum ShowIcons {
/// Don’t show icons at all.
Off,
/// Display icons next to file names, with the given number of spaces between
/// the icon and the file name, even when output isn’t going to a terminal.
Always(u32),

/// Same as Always, but only when output is going to a terminal, not otherwise.
Automatic(u32),

/// Show icons next to file names, with the given number of spaces between
/// the icon and the file name.
On(usize),
/// Never display them, even when output is going to a terminal.
Never,
}

/// Whether to embed hyperlinks.
Expand Down Expand Up @@ -176,13 +182,17 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
pub fn paint(&self) -> TextCellContents {
let mut bits = Vec::new();

if let ShowIcons::On(spaces_count) = self.options.show_icons {
let spaces_count_opt = match self.options.show_icons {
ShowIcons::Always(spaces_count) => Some(spaces_count),
ShowIcons::Automatic(spaces_count) if self.options.is_a_tty => Some(spaces_count),
_ => None,
};

if let Some(spaces_count) = spaces_count_opt {
let style = iconify_style(self.style());
let file_icon = icon_for_file(self.file).to_string();

bits.push(style.paint(file_icon));

bits.push(style.paint(" ".repeat(spaces_count)));
bits.push(style.paint(" ".repeat(spaces_count as usize)));
}

if self.file.parent_dir.is_none() {
Expand Down Expand Up @@ -217,11 +227,10 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
if !target.name.is_empty() {
let target_options = Options {
classify: Classify::JustFilenames,
show_icons: ShowIcons::Off,

quote_style: QuoteStyle::QuoteSpaces,

show_icons: ShowIcons::Never,
embed_hyperlinks: EmbedHyperlinks::Off,
is_a_tty: self.options.is_a_tty,
};

let target_name = FileName {
Expand Down
12 changes: 8 additions & 4 deletions src/output/grid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,16 @@ impl<'a> Render<'a> {
filename.options.embed_hyperlinks,
filename.options.show_icons,
) {
(EmbedHyperlinks::On, ShowIcons::On(spacing)) => {
filename.bare_width() + classification_width + 1 + spacing
}
(EmbedHyperlinks::On, ShowIcons::Off) => {
#[rustfmt::skip]
(EmbedHyperlinks::On, ShowIcons::Always(spacing)
| ShowIcons::Automatic(spacing)) => filename.bare_width() + classification_width + 1 + (spacing as usize),
(EmbedHyperlinks::On, ShowIcons::Never) => {
filename.bare_width() + classification_width
}
(
EmbedHyperlinks::Off,
ShowIcons::Always(spacing) | ShowIcons::Automatic(spacing),
) => filename.bare_width() + 1 + (spacing as usize),
(EmbedHyperlinks::Off, _) => *contents.width(),
};

Expand Down
Loading