Merge branch 'master' into chesterliu/dev/win-support

This commit is contained in:
Chester Liu 2022-05-06 13:22:24 +08:00
commit d6732aea10
15 changed files with 139 additions and 56 deletions

View File

@ -28,7 +28,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
rust: [1.48.0, stable, beta, nightly]
rust: [1.56.1, stable, beta, nightly]
steps:
- name: Checkout repository

2
.gitignore vendored
View File

@ -3,7 +3,7 @@ target
# Vagrant stuff
.vagrant
ubuntu-xenial-16.04-cloudimg-console.log
*.log
# Compiled artifacts
# (see devtools/*-package-for-*.sh)

4
Cargo.lock generated
View File

@ -279,9 +279,9 @@ checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
[[package]]
name = "term_grid"
version = "0.1.7"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "230d3e804faaed5a39b08319efb797783df2fd9671b39b7596490cb486d702cf"
checksum = "a7c9eb7705cb3f0fd71d3955b23db6d372142ac139e8c473952c93bf3c3dc4b7"
dependencies = [
"unicode-width",
]

View File

@ -27,7 +27,7 @@ natord = "1.0"
num_cpus = "1.10"
number_prefix = "0.4"
scoped_threadpool = "0.1"
term_grid = "0.1"
term_grid = "0.2.0"
terminal_size = "0.1.16"
unicode-width = "0.1"
zoneinfo_compiled = "0.5.1"

View File

@ -105,74 +105,73 @@ More information on how to install exa is available on [the Installation page](h
On Alpine Linux, [enable community repository](https://wiki.alpinelinux.org/wiki/Enable_Community_Repository) and install the [`exa`](https://pkgs.alpinelinux.org/package/edge/community/x86_64/exa) package.
$ apk add exa
apk add exa
### Arch Linux
On Arch, install the [`exa`](https://www.archlinux.org/packages/community/x86_64/exa/) package.
$ pacman -S exa
pacman -S exa
### Android / Termux
On Android / Termux, install the [`exa`](https://github.com/termux/termux-packages/tree/master/packages/exa) package.
$ pkg install exa
pkg install exa
### Debian
On Debian, install the [`exa`](https://packages.debian.org/unstable/exa) package.
For now, exa is in the _unstable_ repository.
On Debian, install the [`exa`](https://packages.debian.org/stable/exa) package.
$ apt install exa
apt install exa
### Fedora
On Fedora, install the [`exa`](https://src.fedoraproject.org/modules/exa) package.
$ dnf install exa
dnf install exa
### Gentoo
On Gentoo, install the [`sys-apps/exa`](https://packages.gentoo.org/packages/sys-apps/exa) package.
$ emerge sys-apps/exa
emerge sys-apps/exa
### Homebrew
If youre using [Homebrew](https://brew.sh/) on macOS, install the [`exa`](http://formulae.brew.sh/formula/exa) formula.
$ brew install exa
brew install exa
### MacPorts
If you're using [MacPorts](https://www.macports.org/) on macOS, install the [`exa`](https://ports.macports.org/port/exa/summary) port.
$ port install exa
port install exa
### Nix
On nixOS, install the [`exa`](https://github.com/NixOS/nixpkgs/blob/master/pkgs/tools/misc/exa/default.nix) package.
$ nix-env -i exa
nix-env -i exa
### openSUSE
On openSUSE, install the [`exa`](https://software.opensuse.org/package/exa) package.
$ zypper install exa
zypper install exa
### Ubuntu
On Ubuntu 20.10 (Groovy Gorilla) and later, install the [`exa`](https://packages.ubuntu.com/groovy/exa) package.
$ sudo apt install exa
sudo apt install exa
### Void Linux
On Void Linux, install the [`exa`](https://github.com/void-linux/void-packages/blob/master/srcpkgs/exa/template) package.
$ xbps-install -S exa
xbps-install -S exa
### Manual installation from GitHub
@ -185,7 +184,7 @@ For more information, see the [Manual Installation page](https://the.exa.website
If you already have a Rust environment set up, you can use the `cargo install` command:
$ cargo install exa
cargo install exa
Cargo will build the `exa` binary and place it in `$HOME/.cargo`.
@ -197,8 +196,8 @@ To build without Git support, run `cargo install --no-default-features exa` is a
<a id="development">
<h1>Development
<a href="https://blog.rust-lang.org/2020/08/03/Rust-1.45.2.html">
<img src="https://img.shields.io/badge/rustc-1.45.2+-lightgray.svg" alt="Rust 1.45.2+" />
<a href="https://blog.rust-lang.org/2021/11/01/Rust-1.56.1.html">
<img src="https://img.shields.io/badge/rustc-1.56.1+-lightgray.svg" alt="Rust 1.56.1+" />
</a>
<a href="https://github.com/ogham/exa/blob/master/LICENCE">
@ -207,16 +206,16 @@ To build without Git support, run `cargo install --no-default-features exa` is a
</h1></a>
exa is written in [Rust](https://www.rust-lang.org/).
You will need rustc version 1.45.2 or higher.
You will need rustc version 1.56.1 or higher.
The recommended way to install Rust for development is from the [official download page](https://www.rust-lang.org/tools/install), using rustup.
Once Rust is installed, you can compile exa with Cargo:
$ cargo build
$ cargo test
cargo build
cargo test
- The [just](https://github.com/casey/just) command runner can be used to run some helpful development commands, in a manner similar to `make`.
Run `just --tasks` to get an overview of whats available.
Run `just --list` to get an overview of whats available.
- If you are compiling a copy for yourself, be sure to run `cargo build --release` or `just build-release` to benefit from release-mode optimisations.
Copy the resulting binary, which will be in the `target/release` directory, into a folder in your `$PATH`.

View File

@ -224,6 +224,12 @@ Specifies the number of spaces to print between an icon (see the `--icons`
Different terminals display icons differently, as they usually take up more than one character width on screen, so theres no “standard” number of spaces that exa can use to separate an icon from text. One space may place the icon too close to the text, and two spaces may place it too far away. So the choice is left up to the user to configure depending on their terminal emulator.
## `NO_COLOR`
Disables colours in the output (regardless of its value). Can be overridden by `--color` option.
See `https://no-color.org/` for details.
## `LS_COLORS`, `EXA_COLORS`
Specifies the colour scheme used to highlight files based on their name and kind, as well as highlighting metadata and parts of the UI.

2
rust-toolchain.toml Normal file
View File

@ -0,0 +1,2 @@
[toolchain]
channel = "1.56.1"

View File

@ -38,7 +38,8 @@ impl FileExtensions {
"png", "jfi", "jfif", "jif", "jpe", "jpeg", "jpg", "gif", "bmp",
"tiff", "tif", "ppm", "pgm", "pbm", "pnm", "webp", "raw", "arw",
"svg", "stl", "eps", "dvi", "ps", "cbr", "jpf", "cbz", "xpm",
"ico", "cr2", "orf", "nef", "heif", "avif", "jxl",
"ico", "cr2", "orf", "nef", "heif", "avif", "jxl", "j2k", "jp2",
"j2c", "jpx",
])
}
@ -80,7 +81,7 @@ impl FileExtensions {
file.extension_is_one_of( &[
"zip", "tar", "Z", "z", "gz", "bz2", "a", "ar", "7z",
"iso", "dmg", "tc", "rar", "par", "tgz", "xz", "txz",
"lz", "tlz", "lzma", "deb", "rpm", "zst", "lz4",
"lz", "tlz", "lzma", "deb", "rpm", "zst", "lz4", "cpio",
])
}

View File

@ -18,7 +18,6 @@
#![allow(clippy::non_ascii_literal)]
#![allow(clippy::option_if_let_else)]
#![allow(clippy::too_many_lines)]
#![allow(clippy::unnested_or_patterns)] // TODO: remove this when we support Rust 1.53.0
#![allow(clippy::unused_self)]
#![allow(clippy::upper_case_acronyms)]
#![allow(clippy::wildcard_imports)]
@ -50,6 +49,10 @@ mod theme;
fn main() {
use std::process::exit;
unsafe {
libc::signal(libc::SIGPIPE, libc::SIG_DFL);
}
logger::configure(env::var_os(vars::EXA_DEBUG));
#[cfg(windows)]

View File

@ -5,7 +5,7 @@ use crate::theme::{Options, UseColours, ColourScale, Definitions};
impl Options {
pub fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
let use_colours = UseColours::deduce(matches)?;
let use_colours = UseColours::deduce(matches, vars)?;
let colour_scale = ColourScale::deduce(matches)?;
let definitions = if use_colours == UseColours::Never {
@ -21,10 +21,15 @@ impl Options {
impl UseColours {
fn deduce(matches: &MatchedFlags<'_>) -> Result<Self, OptionsError> {
fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
let default_value = match vars.get(vars::NO_COLOR) {
Some(_) => Self::Never,
None => Self::Automatic,
};
let word = match matches.get_where(|f| f.matches(&flags::COLOR) || f.matches(&flags::COLOUR))? {
Some(w) => w,
None => return Ok(Self::Automatic),
None => return Ok(default_value),
};
if word == "always" {
@ -87,6 +92,16 @@ mod terminal_test {
}
};
($name:ident: $type:ident <- $inputs:expr, $env:expr; $stricts:expr => $result:expr) => {
#[test]
fn $name() {
let env = $env;
for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| $type::deduce(mf, &env)) {
assert_eq!(result, $result);
}
}
};
($name:ident: $type:ident <- $inputs:expr; $stricts:expr => err $result:expr) => {
#[test]
fn $name() {
@ -95,11 +110,39 @@ mod terminal_test {
}
}
};
($name:ident: $type:ident <- $inputs:expr, $env:expr; $stricts:expr => err $result:expr) => {
#[test]
fn $name() {
let env = $env;
for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| $type::deduce(mf, &env)) {
assert_eq!(result.unwrap_err(), $result);
}
}
};
}
struct MockVars {
ls: &'static str,
exa: &'static str,
no_color: &'static str,
}
impl MockVars {
fn empty() -> MockVars {
return MockVars {
ls: "",
exa: "",
no_color: "",
};
}
fn with_no_color() -> MockVars {
return MockVars {
ls: "",
exa: "",
no_color: "true",
};
}
}
// Test impl that just returns the value it has.
@ -111,6 +154,9 @@ mod terminal_test {
else if name == vars::EXA_COLORS && ! self.exa.is_empty() {
Some(OsString::from(self.exa.clone()))
}
else if name == vars::NO_COLOR && ! self.no_color.is_empty() {
Some(OsString::from(self.no_color.clone()))
}
else {
None
}
@ -120,32 +166,33 @@ mod terminal_test {
// Default
test!(empty: UseColours <- []; Both => Ok(UseColours::Automatic));
test!(empty: UseColours <- [], MockVars::empty(); Both => Ok(UseColours::Automatic));
test!(empty_with_no_color: UseColours <- [], MockVars::with_no_color(); Both => Ok(UseColours::Never));
// --colour
test!(u_always: UseColours <- ["--colour=always"]; Both => Ok(UseColours::Always));
test!(u_auto: UseColours <- ["--colour", "auto"]; Both => Ok(UseColours::Automatic));
test!(u_never: UseColours <- ["--colour=never"]; Both => Ok(UseColours::Never));
test!(u_always: UseColours <- ["--colour=always"], MockVars::empty(); Both => Ok(UseColours::Always));
test!(u_auto: UseColours <- ["--colour", "auto"], MockVars::empty(); Both => Ok(UseColours::Automatic));
test!(u_never: UseColours <- ["--colour=never"], MockVars::empty(); Both => Ok(UseColours::Never));
// --color
test!(no_u_always: UseColours <- ["--color", "always"]; Both => Ok(UseColours::Always));
test!(no_u_auto: UseColours <- ["--color=auto"]; Both => Ok(UseColours::Automatic));
test!(no_u_never: UseColours <- ["--color", "never"]; Both => Ok(UseColours::Never));
test!(no_u_always: UseColours <- ["--color", "always"], MockVars::empty(); Both => Ok(UseColours::Always));
test!(no_u_auto: UseColours <- ["--color=auto"], MockVars::empty(); Both => Ok(UseColours::Automatic));
test!(no_u_never: UseColours <- ["--color", "never"], MockVars::empty(); Both => Ok(UseColours::Never));
// Errors
test!(no_u_error: UseColours <- ["--color=upstream"]; Both => err OptionsError::BadArgument(&flags::COLOR, OsString::from("upstream"))); // the error is for --color
test!(u_error: UseColours <- ["--colour=lovers"]; Both => err OptionsError::BadArgument(&flags::COLOR, OsString::from("lovers"))); // and so is this one!
test!(no_u_error: UseColours <- ["--color=upstream"], MockVars::empty(); Both => err OptionsError::BadArgument(&flags::COLOR, OsString::from("upstream"))); // the error is for --color
test!(u_error: UseColours <- ["--colour=lovers"], MockVars::empty(); Both => err OptionsError::BadArgument(&flags::COLOR, OsString::from("lovers"))); // and so is this one!
// Overriding
test!(overridden_1: UseColours <- ["--colour=auto", "--colour=never"]; Last => Ok(UseColours::Never));
test!(overridden_2: UseColours <- ["--color=auto", "--colour=never"]; Last => Ok(UseColours::Never));
test!(overridden_3: UseColours <- ["--colour=auto", "--color=never"]; Last => Ok(UseColours::Never));
test!(overridden_4: UseColours <- ["--color=auto", "--color=never"]; Last => Ok(UseColours::Never));
test!(overridden_1: UseColours <- ["--colour=auto", "--colour=never"], MockVars::empty(); Last => Ok(UseColours::Never));
test!(overridden_2: UseColours <- ["--color=auto", "--colour=never"], MockVars::empty(); Last => Ok(UseColours::Never));
test!(overridden_3: UseColours <- ["--colour=auto", "--color=never"], MockVars::empty(); Last => Ok(UseColours::Never));
test!(overridden_4: UseColours <- ["--color=auto", "--color=never"], MockVars::empty(); Last => Ok(UseColours::Never));
test!(overridden_5: UseColours <- ["--colour=auto", "--colour=never"]; Complain => err OptionsError::Duplicate(Flag::Long("colour"), Flag::Long("colour")));
test!(overridden_6: UseColours <- ["--color=auto", "--colour=never"]; Complain => err OptionsError::Duplicate(Flag::Long("color"), Flag::Long("colour")));
test!(overridden_7: UseColours <- ["--colour=auto", "--color=never"]; Complain => err OptionsError::Duplicate(Flag::Long("colour"), Flag::Long("color")));
test!(overridden_8: UseColours <- ["--color=auto", "--color=never"]; Complain => err OptionsError::Duplicate(Flag::Long("color"), Flag::Long("color")));
test!(overridden_5: UseColours <- ["--colour=auto", "--colour=never"], MockVars::empty(); Complain => err OptionsError::Duplicate(Flag::Long("colour"), Flag::Long("colour")));
test!(overridden_6: UseColours <- ["--color=auto", "--colour=never"], MockVars::empty(); Complain => err OptionsError::Duplicate(Flag::Long("color"), Flag::Long("colour")));
test!(overridden_7: UseColours <- ["--colour=auto", "--color=never"], MockVars::empty(); Complain => err OptionsError::Duplicate(Flag::Long("colour"), Flag::Long("color")));
test!(overridden_8: UseColours <- ["--color=auto", "--color=never"], MockVars::empty(); Complain => err OptionsError::Duplicate(Flag::Long("color"), Flag::Long("color")));
test!(scale_1: ColourScale <- ["--color-scale", "--colour-scale"]; Last => Ok(ColourScale::Gradient));
test!(scale_2: ColourScale <- ["--color-scale", ]; Last => Ok(ColourScale::Gradient));

View File

@ -15,6 +15,9 @@ pub static COLUMNS: &str = "COLUMNS";
/// Environment variable used to datetime format.
pub static TIME_STYLE: &str = "TIME_STYLE";
/// Environment variable used to disable colors.
/// See: https://no-color.org/
pub static NO_COLOR: &str = "NO_COLOR";
// exa-specific variables

View File

@ -147,7 +147,11 @@ impl<'a> AsRef<File<'a>> for Egg<'a> {
impl<'a> Render<'a> {
pub fn render<W: Write>(mut self, w: &mut W) -> io::Result<()> {
let mut pool = Pool::new(num_cpus::get() as u32);
let n_cpus = match num_cpus::get() as u32 {
0 => 1,
n => n,
};
let mut pool = Pool::new(n_cpus);
let mut rows = Vec::new();
if let Some(ref table) = self.opts.table {

View File

@ -46,6 +46,7 @@ impl<'a> Render<'a> {
grid.add(tg::Cell {
contents: filename.strings().to_string(),
width: *filename.width(),
alignment: tg::Alignment::Left,
});
}

View File

@ -263,6 +263,7 @@ impl<'a> Render<'a> {
let cell = grid::Cell {
contents: ANSIStrings(&column[row].contents).to_string(),
width: *column[row].width,
alignment: grid::Alignment::Left,
};
grid.add(cell);
@ -276,6 +277,7 @@ impl<'a> Render<'a> {
let cell = grid::Cell {
contents: ANSIStrings(&cell.contents).to_string(),
width: *cell.width,
alignment: grid::Alignment::Left,
};
grid.add(cell);

View File

@ -69,7 +69,9 @@ lazy_static! {
m.insert("Dockerfile", '\u{f308}'); // 
m.insert("ds_store", '\u{f179}'); // 
m.insert("gitignore_global", '\u{f1d3}'); // 
m.insert("gradle", '\u{e70e}'); // 
m.insert("go.mod", '\u{e626}'); // 
m.insert("go.sum", '\u{e626}'); // 
m.insert("gradle", '\u{e256}'); // 
m.insert("gruntfile.coffee", '\u{e611}'); // 
m.insert("gruntfile.js", '\u{e611}'); // 
m.insert("gruntfile.ls", '\u{e611}'); // 
@ -83,6 +85,7 @@ lazy_static! {
m.insert("Makefile", '\u{f489}'); // 
m.insert("node_modules", '\u{e718}'); // 
m.insert("npmignore", '\u{e71e}'); // 
m.insert("PKGBUILD", '\u{f303}'); // 
m.insert("rubydoc", '\u{e73b}'); // 
m.insert("yarn.lock", '\u{e718}'); // 
@ -118,6 +121,7 @@ pub fn icon_for_file(file: &File<'_>) -> char {
"bash_profile" => '\u{f489}', // 
"bashrc" => '\u{f489}', // 
"bat" => '\u{f17a}', // 
"bats" => '\u{f489}', // 
"bmp" => '\u{f1c5}', // 
"bz" => '\u{f410}', // 
"bz2" => '\u{f410}', // 
@ -134,6 +138,7 @@ pub fn icon_for_file(file: &File<'_>) -> char {
"coffee" => '\u{f0f4}', // 
"conf" => '\u{e615}', // 
"cp" => '\u{e61d}', // 
"cpio" => '\u{f410}', // 
"cpp" => '\u{e61d}', // 
"cs" => '\u{f81a}', // 
"csh" => '\u{f489}', // 
@ -156,6 +161,7 @@ pub fn icon_for_file(file: &File<'_>) -> char {
"DS_store" => '\u{f179}', // 
"dump" => '\u{f1c0}', // 
"ebook" => '\u{e28b}', // 
"ebuild" => '\u{f30d}', // 
"editorconfig" => '\u{e615}', // 
"ejs" => '\u{e618}', // 
"elm" => '\u{e62c}', // 
@ -185,7 +191,7 @@ pub fn icon_for_file(file: &File<'_>) -> char {
"gitignore" => '\u{f1d3}', // 
"gitmodules" => '\u{f1d3}', // 
"go" => '\u{e626}', // 
"gradle" => '\u{e70e}', // 
"gradle" => '\u{e256}', // 
"groovy" => '\u{e775}', // 
"gsheet" => '\u{f1c3}', // 
"gslides" => '\u{f1c4}', // 
@ -204,15 +210,21 @@ pub fn icon_for_file(file: &File<'_>) -> char {
"ini" => '\u{f17a}', // 
"ipynb" => '\u{e606}', // 
"iso" => '\u{e271}', // 
"j2c" => '\u{f1c5}', // 
"j2k" => '\u{f1c5}', // 
"jad" => '\u{e256}', // 
"jar" => '\u{e204}', // 
"java" => '\u{e204}', // 
"jar" => '\u{e256}', // 
"java" => '\u{e256}', // 
"jfi" => '\u{f1c5}', // 
"jfif" => '\u{f1c5}', // 
"jif" => '\u{f1c5}', // 
"jl" => '\u{e624}', // 
"jmd" => '\u{f48a}', // 
"jp2" => '\u{f1c5}', // 
"jpe" => '\u{f1c5}', // 
"jpeg" => '\u{f1c5}', // 
"jpg" => '\u{f1c5}', // 
"jpx" => '\u{f1c5}', // 
"js" => '\u{e74e}', // 
"json" => '\u{e60b}', // 
"jsx" => '\u{e7ba}', // 
@ -255,6 +267,7 @@ pub fn icon_for_file(file: &File<'_>) -> char {
"ogg" => '\u{f001}', // 
"ogv" => '\u{f03d}', // 
"otf" => '\u{f031}', // 
"part" => '\u{f43a}', // 
"patch" => '\u{f440}', // 
"pdf" => '\u{f1c1}', // 
"php" => '\u{e73d}', // 
@ -314,6 +327,7 @@ pub fn icon_for_file(file: &File<'_>) -> char {
"tiff" => '\u{f1c5}', // 
"tlz" => '\u{f410}', // 
"toml" => '\u{e615}', // 
"torrent" => '\u{e275}', // 
"ts" => '\u{e628}', // 
"tsv" => '\u{f1c3}', // 
"tsx" => '\u{e7ba}', // 
@ -336,8 +350,8 @@ pub fn icon_for_file(file: &File<'_>) -> char {
"xhtml" => '\u{f13b}', // 
"xls" => '\u{f1c3}', // 
"xlsx" => '\u{f1c3}', // 
"xml" => '\u{fabf}', // 謹
"xul" => '\u{fabf}', // 謹
"xml" => '\u{f121}', // 
"xul" => '\u{f121}', // 
"xz" => '\u{f410}', // 
"yaml" => '\u{f481}', // 
"yml" => '\u{f481}', // 
@ -345,6 +359,7 @@ pub fn icon_for_file(file: &File<'_>) -> char {
"zsh" => '\u{f489}', // 
"zsh-theme" => '\u{f489}', // 
"zshrc" => '\u{f489}', // 
"zst" => '\u{f410}', // 
_ => '\u{f15b}' // 
}
}