mirror of
https://github.com/Llewellynvdm/exa.git
synced 2024-06-08 18:22:20 +00:00
Compare commits
197 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
753150d374 | ||
|
d7c9f4e65f | ||
|
fb05c421ae | ||
|
f039202b4f | ||
|
17893b5f57 | ||
|
08cc56d7ac | ||
|
352d32b60c | ||
|
e79f7361a7 | ||
|
a4cee84b4b | ||
|
bf41cdefa6 | ||
|
94043e1fa8 | ||
|
b8cee60acf | ||
|
c697d06670 | ||
|
ee67110333 | ||
|
39d15a317d | ||
|
8d03922e3b | ||
|
eba3646b83 | ||
|
72b2119a34 | ||
|
bbea87db91 | ||
|
1dc14eaff1 | ||
|
d24ca084a3 | ||
|
433a9a52d3 | ||
|
7c1878f0e4 | ||
|
1f409793ae | ||
|
19601267cf | ||
|
7595289698 | ||
|
2917062466 | ||
|
1b844a8dfa | ||
|
af267ba638 | ||
|
cd715a6e00 | ||
|
89bcc00e32 | ||
|
f3ca1fe6f7 | ||
|
e385cd58da | ||
|
3ca40915ae | ||
|
45b6413fd0 | ||
|
577ac91513 | ||
|
d40b7b1ff4 | ||
|
21758c81ea | ||
|
7e4944c188 | ||
|
584b53bb17 | ||
|
a4b23055a8 | ||
|
863d96150d | ||
|
a65c52d821 | ||
|
954462634a | ||
|
8ad8b33573 | ||
|
8c2956a8fd | ||
|
29422d8c93 | ||
|
6fb3740f24 | ||
|
53cb75cf2b | ||
|
d6732aea10 | ||
|
dceca33779 | ||
|
6197006d5f | ||
|
02f44c68d8 | ||
|
98a4431d6a | ||
|
fc6a6d0b38 | ||
|
ccc1f9999a | ||
|
bced9841f4 | ||
|
f5bbfa7871 | ||
|
b869bd06dc | ||
|
f6db28e25a | ||
|
0659c36897 | ||
|
a58a3313ea | ||
|
fe64690063 | ||
|
c968c388d4 | ||
|
400bb0a140 | ||
|
f0b9cceb73 | ||
|
e433b3fed0 | ||
|
42659f9345 | ||
|
a2f3ff98c2 | ||
|
4f919a6bc5 | ||
|
446903ac00 | ||
|
be6feb82d8 | ||
|
352a1e8f19 | ||
|
89d537adb4 | ||
|
4f0275395b | ||
|
c3eb8321ff | ||
|
091ab51b98 | ||
|
859666d287 | ||
|
c4b8e7af1a | ||
|
69ae5db3b6 | ||
|
3e9de0e7e1 | ||
|
af208285e8 | ||
|
ef8fd32dc6 | ||
|
2ac7024197 | ||
|
7c957f95b3 | ||
|
c6874f0b32 | ||
|
df4fb84ae1 | ||
|
a371c41711 | ||
|
aab1d3db59 | ||
|
99d653b7fa | ||
|
b32f441851 | ||
|
0332e0c7f7 | ||
|
0d645735d7 | ||
|
6af9e221a4 | ||
|
0cebf3ad1c | ||
|
4220b6f41e | ||
|
a7aca35d97 | ||
|
35aeac759e | ||
|
247d1345e7 | ||
|
1c36b71779 | ||
|
659def7138 | ||
|
ec786201c8 | ||
|
d2b6cc9185 | ||
|
75f14d23a3 | ||
|
11793973fe | ||
|
257786749f | ||
|
fe11b9d319 | ||
|
aff35a1643 | ||
|
5f49a2e840 | ||
|
439b629d90 | ||
|
26b40bf773 | ||
|
79cd5d448a | ||
|
2bef43fb1b | ||
|
91dcf52972 | ||
|
3f24f7cbcf | ||
|
4c8658ab90 | ||
|
e7a477eb15 | ||
|
69d5e1fc11 | ||
|
c24afe3a08 | ||
|
049f766d1d | ||
|
4b6cf1b5a4 | ||
|
c46329efb2 | ||
|
8de5b97804 | ||
|
dc5c42a0f2 | ||
|
78b46e219e | ||
|
f6b3975562 | ||
|
6a07b59a80 | ||
|
9881d00d00 | ||
|
a6754f3cc3 | ||
|
56c78400b8 | ||
|
42b546606e | ||
|
e3204a574e | ||
|
23a1c8a41f | ||
|
785d6ed991 | ||
|
b18e93d283 | ||
|
045172bd9e | ||
|
f8610d05ae | ||
|
86d5939abe | ||
|
90416ed3ce | ||
|
7c80070120 | ||
|
a58ad6487f | ||
|
ae62f5d18e | ||
|
d253893614 | ||
|
61ec153bcd | ||
|
4a81d2df91 | ||
|
95682f5674 | ||
|
6b8d7fcd70 | ||
|
76e336c757 | ||
|
a85c72e2a0 | ||
|
90b97753ad | ||
|
7a26b4e0f7 | ||
|
d6d35bf47e | ||
|
777cd7e815 | ||
|
8f0e4ccfdd | ||
|
293372a613 | ||
|
c0df1fb6c2 | ||
|
e3e776a1fa | ||
|
1f4e58ee52 | ||
|
dbd11d3804 | ||
|
b1c49341c0 | ||
|
550f2d29c2 | ||
|
31043462af | ||
|
311c9baf65 | ||
|
d19d66d57a | ||
|
051a46c643 | ||
|
bfd2644869 | ||
|
f6e66d982d | ||
|
bd4f144884 | ||
|
6403336028 | ||
|
e86f2b938b | ||
|
f3ae6a9bd9 | ||
|
8c10feec51 | ||
|
f673e018b5 | ||
|
3a8005c840 | ||
|
beaf561086 | ||
|
a0457f9c87 | ||
|
050931e48f | ||
|
c729e226da | ||
|
2aaead1721 | ||
|
7a4cde75eb | ||
|
0adc5c789b | ||
|
505808fd9f | ||
|
8ad46e2ee5 | ||
|
0ea8f17b22 | ||
|
00f97a9738 | ||
|
78a3bc9838 | ||
|
9d613016c0 | ||
|
e874584a55 | ||
|
33dd8fd2ca | ||
|
13b3635407 | ||
|
5503e4756e | ||
|
31583691d5 | ||
|
aeb4a679e8 | ||
|
e9d0af0343 | ||
|
0e8a4582d0 | ||
|
6a642d0f32 | ||
|
7f717c3af3 |
12
.github/ISSUE_TEMPLATE/bug_report.md
vendored
12
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -1,14 +1,8 @@
|
||||||
---
|
---
|
||||||
name: Bug report
|
name: exa is unmaintained
|
||||||
about: Report a crash, runtime error, or invalid output in exa
|
about: Please use the active fork eza instead. <https://github.com/eza-community/eza>
|
||||||
---
|
---
|
||||||
|
|
||||||
If exa does something unexpected, or its output looks wrong, or it displays an error on the screen, or if it outright crashes, then please include the following information in your report:
|
exa is unmaintained, please use the active fork eza instead. <https://github.com/eza-community/eza>
|
||||||
|
|
||||||
- The version of exa being used (`exa --version`)
|
|
||||||
- The command-line arguments you are using
|
|
||||||
- Your operating system and hardware platform
|
|
||||||
|
|
||||||
If it’s a crash, please include the full text of the crash that gets printed to the screen. If you’re seeing unexpected behaviour, a screenshot of the issue will help a lot.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
15
.github/ISSUE_TEMPLATE/compilation_error.md
vendored
15
.github/ISSUE_TEMPLATE/compilation_error.md
vendored
|
@ -1,15 +0,0 @@
|
||||||
---
|
|
||||||
name: Compilation error
|
|
||||||
about: Report a problem compiling exa
|
|
||||||
---
|
|
||||||
|
|
||||||
If exa fails to compile, or if there is a problem during the build process, then please include the following information in your report:
|
|
||||||
|
|
||||||
- The exact exa commit you are building (`git rev-parse --short HEAD`)
|
|
||||||
- The version of rustc you are compiling it with (`rustc --version`)
|
|
||||||
- Your operating system and hardware platform
|
|
||||||
- The Rust build target (the _exact_ output of `rustc --print cfg`)
|
|
||||||
|
|
||||||
If you are seeing compilation errors, please include the output of the build process.
|
|
||||||
|
|
||||||
---
|
|
1
.github/ISSUE_TEMPLATE/config.yml
vendored
1
.github/ISSUE_TEMPLATE/config.yml
vendored
|
@ -1 +0,0 @@
|
||||||
blank_issues_enabled: true
|
|
4
.github/ISSUE_TEMPLATE/feature_request.md
vendored
4
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
@ -1,4 +0,0 @@
|
||||||
---
|
|
||||||
name: Feature request
|
|
||||||
about: Request a feature or enhancement to exa
|
|
||||||
---
|
|
4
.github/ISSUE_TEMPLATE/question.md
vendored
4
.github/ISSUE_TEMPLATE/question.md
vendored
|
@ -1,4 +0,0 @@
|
||||||
---
|
|
||||||
name: Question
|
|
||||||
about: Ask a question about exa
|
|
||||||
---
|
|
46
.github/workflows/unit-tests.yml
vendored
Normal file
46
.github/workflows/unit-tests.yml
vendored
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
name: Unit tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
paths:
|
||||||
|
- '.github/workflows/*'
|
||||||
|
- 'src/**'
|
||||||
|
- 'Cargo.*'
|
||||||
|
- build.rs
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
paths:
|
||||||
|
- '.github/workflows/*'
|
||||||
|
- 'src/**'
|
||||||
|
- 'Cargo.*'
|
||||||
|
- build.rs
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
unit-tests:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
continue-on-error: ${{ matrix.rust == 'nightly' }}
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, macos-latest]
|
||||||
|
rust: [1.66.1, stable, beta, nightly]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install Rust toolchain
|
||||||
|
uses: dtolnay/rust-toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: ${{ matrix.rust }}
|
||||||
|
|
||||||
|
- name: Install cargo-hack
|
||||||
|
run: cargo install cargo-hack@0.5.27
|
||||||
|
|
||||||
|
- name: Run unit tests
|
||||||
|
run: cargo hack test --feature-powerset
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -3,7 +3,7 @@ target
|
||||||
|
|
||||||
# Vagrant stuff
|
# Vagrant stuff
|
||||||
.vagrant
|
.vagrant
|
||||||
ubuntu-xenial-16.04-cloudimg-console.log
|
*.log
|
||||||
|
|
||||||
# Compiled artifacts
|
# Compiled artifacts
|
||||||
# (see devtools/*-package-for-*.sh)
|
# (see devtools/*-package-for-*.sh)
|
||||||
|
|
19
.travis.yml
19
.travis.yml
|
@ -1,19 +0,0 @@
|
||||||
language: rust
|
|
||||||
rust:
|
|
||||||
- 1.42.0
|
|
||||||
- stable
|
|
||||||
- beta
|
|
||||||
- nightly
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
fast_finish: true
|
|
||||||
allow_failures:
|
|
||||||
- rust: nightly
|
|
||||||
|
|
||||||
include:
|
|
||||||
- name: 'Rust: test with all features'
|
|
||||||
rust: stable
|
|
||||||
install:
|
|
||||||
- cargo install cargo-hack
|
|
||||||
script:
|
|
||||||
- cargo hack test --feature-powerset
|
|
106
Cargo.lock
generated
106
Cargo.lock
generated
|
@ -1,5 +1,7 @@
|
||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ansi_term"
|
name = "ansi_term"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
|
@ -23,24 +25,24 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.3.4"
|
version = "1.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
|
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.61"
|
version = "1.0.67"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d"
|
checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jobserver",
|
"jobserver",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "0.1.10"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "datetime"
|
name = "datetime"
|
||||||
|
@ -57,7 +59,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "exa"
|
name = "exa"
|
||||||
version = "0.10.0"
|
version = "0.10.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term",
|
"ansi_term",
|
||||||
"datetime",
|
"datetime",
|
||||||
|
@ -72,17 +74,27 @@ dependencies = [
|
||||||
"number_prefix",
|
"number_prefix",
|
||||||
"scoped_threadpool",
|
"scoped_threadpool",
|
||||||
"term_grid",
|
"term_grid",
|
||||||
"term_size",
|
"terminal_size",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
"users",
|
"users",
|
||||||
"zoneinfo_compiled",
|
"zoneinfo_compiled",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "git2"
|
name = "form_urlencoded"
|
||||||
version = "0.13.11"
|
version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e094214efbc7fdbbdee952147e493b00e99a4e52817492277e98967ae918165"
|
checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
|
||||||
|
dependencies = [
|
||||||
|
"matches",
|
||||||
|
"percent-encoding",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "git2"
|
||||||
|
version = "0.13.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9831e983241f8c5591ed53f17d874833e2fa82cac2625f3888c50cbfe136cba"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -100,18 +112,18 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.1.17"
|
version = "0.1.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
|
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "0.2.0"
|
version = "0.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9"
|
checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"matches",
|
"matches",
|
||||||
"unicode-bidi",
|
"unicode-bidi",
|
||||||
|
@ -120,9 +132,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jobserver"
|
name = "jobserver"
|
||||||
version = "0.1.21"
|
version = "0.1.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2"
|
checksum = "972f5ae5d1cb9c6ae417789196c803205313edde988685da5e3aae0827b9e7fd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
@ -135,15 +147,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.79"
|
version = "0.2.93"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743"
|
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libgit2-sys"
|
name = "libgit2-sys"
|
||||||
version = "0.12.14+1.1.0"
|
version = "0.12.21+1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f25af58e6495f7caf2919d08f212de550cfa3ed2f5e744988938ea292b9f549"
|
checksum = "86271bacd72b2b9e854c3dcfb82efd538f15f870e4c11af66900effb462f6825"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -174,9 +186,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.11"
|
version = "0.4.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
|
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
@ -211,18 +223,18 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl-src"
|
name = "openssl-src"
|
||||||
version = "111.12.0+1.1.1h"
|
version = "111.15.0+1.1.1k"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "858a4132194f8570a7ee9eb8629e85b23cbc4565f2d4a162e87556e5956abf61"
|
checksum = "b1a5f6ae2ac04393b217ea9f700cd04fa9bf3d93fae2872069f3d15d908af70a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl-sys"
|
name = "openssl-sys"
|
||||||
version = "0.9.58"
|
version = "0.9.61"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de"
|
checksum = "313752393519e876837e09e1fa183ddef0be7735868dced3196f4472d536277f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"cc",
|
"cc",
|
||||||
|
@ -267,18 +279,18 @@ checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "term_grid"
|
name = "term_grid"
|
||||||
version = "0.1.7"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "230d3e804faaed5a39b08319efb797783df2fd9671b39b7596490cb486d702cf"
|
checksum = "a7c9eb7705cb3f0fd71d3955b23db6d372142ac139e8c473952c93bf3c3dc4b7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "term_size"
|
name = "terminal_size"
|
||||||
version = "0.3.2"
|
version = "0.1.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9"
|
checksum = "86ca8ced750734db02076f44132d802af0b33b09942331f4459dde8636fd2406"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"winapi",
|
"winapi",
|
||||||
|
@ -286,24 +298,33 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinyvec"
|
name = "tinyvec"
|
||||||
version = "0.3.4"
|
version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117"
|
checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342"
|
||||||
|
dependencies = [
|
||||||
|
"tinyvec_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tinyvec_macros"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-bidi"
|
name = "unicode-bidi"
|
||||||
version = "0.3.4"
|
version = "0.3.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
|
checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"matches",
|
"matches",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-normalization"
|
name = "unicode-normalization"
|
||||||
version = "0.1.13"
|
version = "0.1.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977"
|
checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
]
|
]
|
||||||
|
@ -316,10 +337,11 @@ checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.1.1"
|
version = "2.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb"
|
checksum = "9ccd964113622c8e9322cfac19eb1004a07e636c545f325da085d5cdde6f1f8b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"form_urlencoded",
|
||||||
"idna",
|
"idna",
|
||||||
"matches",
|
"matches",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
|
@ -337,9 +359,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vcpkg"
|
name = "vcpkg"
|
||||||
version = "0.2.10"
|
version = "0.2.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c"
|
checksum = "cbdbff6266a24120518560b5dc983096efb98462e51d0d68169895b237be3e5d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
|
|
24
Cargo.toml
24
Cargo.toml
|
@ -1,15 +1,16 @@
|
||||||
[package]
|
[package]
|
||||||
name = "exa"
|
name = "exa"
|
||||||
description = "A modern replacement for ls"
|
description = "A modern replacement for ls"
|
||||||
|
|
||||||
authors = ["Benjamin Sago <ogham@bsago.me>"]
|
authors = ["Benjamin Sago <ogham@bsago.me>"]
|
||||||
categories = ["command-line-utilities"]
|
categories = ["command-line-utilities"]
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
|
rust-version = "1.66.1"
|
||||||
exclude = ["/devtools/*", "/Justfile", "/Vagrantfile", "/screenshots.png"]
|
exclude = ["/devtools/*", "/Justfile", "/Vagrantfile", "/screenshots.png"]
|
||||||
|
readme = "README.md"
|
||||||
homepage = "https://the.exa.website/"
|
homepage = "https://the.exa.website/"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/ogham/exa"
|
repository = "https://github.com/ogham/exa"
|
||||||
version = "0.10.0"
|
version = "0.10.1"
|
||||||
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
@ -27,12 +28,14 @@ natord = "1.0"
|
||||||
num_cpus = "1.10"
|
num_cpus = "1.10"
|
||||||
number_prefix = "0.4"
|
number_prefix = "0.4"
|
||||||
scoped_threadpool = "0.1"
|
scoped_threadpool = "0.1"
|
||||||
term_grid = "0.1"
|
term_grid = "0.2.0"
|
||||||
term_size = "0.3"
|
terminal_size = "0.1.16"
|
||||||
unicode-width = "0.1"
|
unicode-width = "0.1"
|
||||||
users = "0.11"
|
|
||||||
zoneinfo_compiled = "0.5.1"
|
zoneinfo_compiled = "0.5.1"
|
||||||
|
|
||||||
|
[target.'cfg(unix)'.dependencies]
|
||||||
|
users = "0.11"
|
||||||
|
|
||||||
[dependencies.datetime]
|
[dependencies.datetime]
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
@ -63,7 +66,7 @@ lto = true
|
||||||
|
|
||||||
|
|
||||||
[package.metadata.deb]
|
[package.metadata.deb]
|
||||||
license-file = [ "LICENCE" ]
|
license-file = [ "LICENCE", "4" ]
|
||||||
depends = "$auto"
|
depends = "$auto"
|
||||||
extended-description = """
|
extended-description = """
|
||||||
exa is a replacement for ls written in Rust.
|
exa is a replacement for ls written in Rust.
|
||||||
|
@ -72,6 +75,9 @@ section = "utils"
|
||||||
priority = "optional"
|
priority = "optional"
|
||||||
assets = [
|
assets = [
|
||||||
[ "target/release/exa", "/usr/bin/exa", "0755" ],
|
[ "target/release/exa", "/usr/bin/exa", "0755" ],
|
||||||
[ "contrib/man/exa.1", "/usr/share/man/man1/exa.1", "0644" ],
|
[ "target/release/../man/exa.1", "/usr/share/man/man1/exa.1", "0644" ],
|
||||||
[ "contrib/completions.bash", "/etc/bash_completion.d/exa", "0644" ],
|
[ "target/release/../man/exa_colors.5", "/usr/share/man/man5/exa_colors.5", "0644" ],
|
||||||
|
[ "completions/bash/exa", "/usr/share/bash-completion/completions/exa", "0644" ],
|
||||||
|
[ "completions/zsh/_exa", "/usr/share/zsh/site-functions/_exa", "0644" ],
|
||||||
|
[ "completions/fish/exa.fish", "/usr/share/fish/vendor_completions.d/exa.fish", "0644" ],
|
||||||
]
|
]
|
||||||
|
|
61
README.md
61
README.md
|
@ -1,17 +1,19 @@
|
||||||
|
# exa is unmaintained, use the [fork eza](https://github.com/eza-community/eza) instead.
|
||||||
|
|
||||||
|
(This repository isn’t archived because the only person with the rights to do so is unreachable).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<h1>exa</h1>
|
|
||||||
|
# exa
|
||||||
|
|
||||||
[exa](https://the.exa.website/) is a modern replacement for _ls_.
|
[exa](https://the.exa.website/) is a modern replacement for _ls_.
|
||||||
|
|
||||||
**README Sections:** [Options](#options) — [Installation](#installation) — [Development](#development)
|
**README Sections:** [Options](#options) — [Installation](#installation) — [Development](#development)
|
||||||
|
|
||||||
<a href="https://travis-ci.org/github/ogham/exa">
|
[![Unit tests](https://github.com/ogham/exa/actions/workflows/unit-tests.yml/badge.svg)](https://github.com/ogham/exa/actions/workflows/unit-tests.yml)
|
||||||
<img src="https://travis-ci.org/ogham/exa.svg?branch=master" alt="Build status" />
|
[![Say thanks!](https://img.shields.io/badge/Say%20Thanks-!-1EAEDB.svg)]()
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="https://saythanks.io/to/ogham%40bsago.me">
|
|
||||||
<img src="https://img.shields.io/badge/Say%20Thanks-!-1EAEDB.svg" alt="Say thanks!" />
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
![Screenshots of exa](screenshots.png)
|
![Screenshots of exa](screenshots.png)
|
||||||
|
@ -109,74 +111,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.
|
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
|
### Arch Linux
|
||||||
|
|
||||||
On Arch, install the [`exa`](https://www.archlinux.org/packages/community/x86_64/exa/) package.
|
On Arch, install the [`exa`](https://www.archlinux.org/packages/community/x86_64/exa/) package.
|
||||||
|
|
||||||
$ pacman -S exa
|
pacman -S exa
|
||||||
|
|
||||||
### Android / Termux
|
### Android / Termux
|
||||||
|
|
||||||
On Android / Termux, install the [`exa`](https://github.com/termux/termux-packages/tree/master/packages/exa) package.
|
On Android / Termux, install the [`exa`](https://github.com/termux/termux-packages/tree/master/packages/exa) package.
|
||||||
|
|
||||||
$ pkg install exa
|
pkg install exa
|
||||||
|
|
||||||
### Debian
|
### Debian
|
||||||
|
|
||||||
On Debian, install the [`exa`](https://packages.debian.org/unstable/exa) package.
|
On Debian, install the [`exa`](https://packages.debian.org/stable/exa) package.
|
||||||
For now, exa is in the _unstable_ repository.
|
|
||||||
|
|
||||||
$ apt install exa
|
apt install exa
|
||||||
|
|
||||||
### Fedora
|
### Fedora
|
||||||
|
|
||||||
On Fedora, install the [`exa`](https://src.fedoraproject.org/modules/exa) package.
|
On Fedora, install the [`exa`](https://src.fedoraproject.org/modules/exa) package.
|
||||||
|
|
||||||
$ dnf install exa
|
dnf install exa
|
||||||
|
|
||||||
### Gentoo
|
### Gentoo
|
||||||
|
|
||||||
On Gentoo, install the [`sys-apps/exa`](https://packages.gentoo.org/packages/sys-apps/exa) package.
|
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
|
### Homebrew
|
||||||
|
|
||||||
If you’re using [Homebrew](https://brew.sh/) on macOS, install the [`exa`](http://formulae.brew.sh/formula/exa) formula.
|
If you’re using [Homebrew](https://brew.sh/) on macOS, install the [`exa`](http://formulae.brew.sh/formula/exa) formula.
|
||||||
|
|
||||||
$ brew install exa
|
brew install exa
|
||||||
|
|
||||||
### MacPorts
|
### MacPorts
|
||||||
|
|
||||||
If you're using [MacPorts](https://www.macports.org/) on macOS, install the [`exa`](https://ports.macports.org/port/exa/summary) port.
|
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
|
### Nix
|
||||||
|
|
||||||
On nixOS, install the [`exa`](https://github.com/NixOS/nixpkgs/blob/master/pkgs/tools/misc/exa/default.nix) package.
|
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
|
### openSUSE
|
||||||
|
|
||||||
On openSUSE, install the [`exa`](https://software.opensuse.org/package/exa) package.
|
On openSUSE, install the [`exa`](https://software.opensuse.org/package/exa) package.
|
||||||
|
|
||||||
$ zypper install exa
|
zypper install exa
|
||||||
|
|
||||||
### Ubuntu
|
### Ubuntu
|
||||||
|
|
||||||
On Ubuntu 20.10 (Groovy Gorilla) and later, install the [`exa`](https://packages.ubuntu.com/groovy/exa) package.
|
On Ubuntu 20.10 (Groovy Gorilla) and later, install the [`exa`](https://packages.ubuntu.com/jammy/exa) package.
|
||||||
|
|
||||||
$ sudo apt install exa
|
sudo apt install exa
|
||||||
|
|
||||||
### Void Linux
|
### Void Linux
|
||||||
|
|
||||||
On Void Linux, install the [`exa`](https://github.com/void-linux/void-packages/blob/master/srcpkgs/exa/template) package.
|
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
|
### Manual installation from GitHub
|
||||||
|
|
||||||
|
@ -189,7 +190,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:
|
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`.
|
Cargo will build the `exa` binary and place it in `$HOME/.cargo`.
|
||||||
|
|
||||||
|
@ -201,8 +202,8 @@ To build without Git support, run `cargo install --no-default-features exa` is a
|
||||||
<a id="development">
|
<a id="development">
|
||||||
<h1>Development
|
<h1>Development
|
||||||
|
|
||||||
<a href="https://blog.rust-lang.org/2020/03/12/Rust-1.42.html">
|
<a href="https://blog.rust-lang.org/2023/01/10/Rust-1.66.1.html">
|
||||||
<img src="https://img.shields.io/badge/rustc-1.42+-lightgray.svg" alt="Rust 1.42+" />
|
<img src="https://img.shields.io/badge/rustc-1.66.1+-lightgray.svg" alt="Rust 1.66.1+" />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a href="https://github.com/ogham/exa/blob/master/LICENCE">
|
<a href="https://github.com/ogham/exa/blob/master/LICENCE">
|
||||||
|
@ -211,16 +212,16 @@ To build without Git support, run `cargo install --no-default-features exa` is a
|
||||||
</h1></a>
|
</h1></a>
|
||||||
|
|
||||||
exa is written in [Rust](https://www.rust-lang.org/).
|
exa is written in [Rust](https://www.rust-lang.org/).
|
||||||
You will need rustc version 1.42.0 or higher.
|
You will need rustc version 1.66.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.
|
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:
|
Once Rust is installed, you can compile exa with Cargo:
|
||||||
|
|
||||||
$ cargo build
|
cargo build
|
||||||
$ cargo test
|
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`.
|
- 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 what’s available.
|
Run `just --list` to get an overview of what’s 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.
|
- 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`.
|
Copy the resulting binary, which will be in the `target/release` directory, into a folder in your `$PATH`.
|
||||||
|
|
3
build.rs
3
build.rs
|
@ -38,9 +38,10 @@ fn main() -> io::Result<()> {
|
||||||
|
|
||||||
// We need to create these files in the Cargo output directory.
|
// We need to create these files in the Cargo output directory.
|
||||||
let out = PathBuf::from(env::var("OUT_DIR").unwrap());
|
let out = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||||
|
let path = &out.join("version_string.txt");
|
||||||
|
|
||||||
// Bland version text
|
// Bland version text
|
||||||
let mut f = File::create(&out.join("version_string.txt"))?;
|
let mut f = File::create(path).unwrap_or_else(|_| { panic!("{}", path.to_string_lossy().to_string()) });
|
||||||
writeln!(f, "{}", strip_codes(&ver))?;
|
writeln!(f, "{}", strip_codes(&ver))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -8,6 +8,11 @@ _exa()
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
--colour)
|
||||||
|
COMPREPLY=( $( compgen -W 'always auto never' -- "$cur" ) )
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
|
||||||
-L|--level)
|
-L|--level)
|
||||||
COMPREPLY=( $( compgen -W '{0..9}' -- "$cur" ) )
|
COMPREPLY=( $( compgen -W '{0..9}' -- "$cur" ) )
|
||||||
return
|
return
|
||||||
|
@ -19,19 +24,28 @@ _exa()
|
||||||
;;
|
;;
|
||||||
|
|
||||||
-t|--time)
|
-t|--time)
|
||||||
COMPREPLY=( $( compgen -W 'modified changed accessed created --' -- $cur ) )
|
COMPREPLY=( $( compgen -W 'modified changed accessed created --' -- "$cur" ) )
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
|
|
||||||
--time-style)
|
--time-style)
|
||||||
COMPREPLY=( $( compgen -W 'default iso long-iso full-iso --' -- $cur ) )
|
COMPREPLY=( $( compgen -W 'default iso long-iso full-iso --' -- "$cur" ) )
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
case "$cur" in
|
case "$cur" in
|
||||||
|
# _parse_help doesn’t pick up short options when they are on the same line than long options
|
||||||
|
--*)
|
||||||
|
# colo[u]r isn’t parsed correctly so we filter these options out and add them by hand
|
||||||
|
parse_help=$( exa --help | grep -oE ' (\-\-[[:alnum:]@-]+)' | tr -d ' ' | grep -v '\-\-colo' )
|
||||||
|
completions=$( echo '--color --colour --color-scale --colour-scale' $parse_help )
|
||||||
|
COMPREPLY=( $( compgen -W "$completions" -- "$cur" ) )
|
||||||
|
;;
|
||||||
|
|
||||||
-*)
|
-*)
|
||||||
COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "$cur" ) )
|
completions=$( exa --help | grep -oE ' (\-[[:alnum:]@])' | tr -d ' ' )
|
||||||
|
COMPREPLY=( $( compgen -W "$completions" -- "$cur" ) )
|
||||||
;;
|
;;
|
||||||
|
|
||||||
*)
|
*)
|
|
@ -10,10 +10,14 @@ complete -c exa -s 'x' -l 'across' -d "Sort the grid across, rather than d
|
||||||
complete -c exa -s 'R' -l 'recurse' -d "Recurse into directories"
|
complete -c exa -s 'R' -l 'recurse' -d "Recurse into directories"
|
||||||
complete -c exa -s 'T' -l 'tree' -d "Recurse into directories as a tree"
|
complete -c exa -s 'T' -l 'tree' -d "Recurse into directories as a tree"
|
||||||
complete -c exa -s 'F' -l 'classify' -d "Display type indicator by file names"
|
complete -c exa -s 'F' -l 'classify' -d "Display type indicator by file names"
|
||||||
complete -c exa -l 'color' -d "When to use terminal colours"
|
complete -c exa -l 'color' \
|
||||||
complete -c exa -l 'colour' -d "When to use terminal colours"
|
-l 'colour' -d "When to use terminal colours" -x -a "
|
||||||
complete -c exa -l 'color-scale' -d "Highlight levels of file sizes distinctly"
|
always\t'Always use colour'
|
||||||
complete -c exa -l 'colour-scale' -d "Highlight levels of file sizes distinctly"
|
auto\t'Use colour if standard output is a terminal'
|
||||||
|
never\t'Never use colour'
|
||||||
|
"
|
||||||
|
complete -c exa -l 'color-scale' \
|
||||||
|
-l 'colour-scale' -d "Highlight levels of file sizes distinctly"
|
||||||
complete -c exa -l 'icons' -d "Display icons"
|
complete -c exa -l 'icons' -d "Display icons"
|
||||||
complete -c exa -l 'no-icons' -d "Don't display icons"
|
complete -c exa -l 'no-icons' -d "Don't display icons"
|
||||||
|
|
||||||
|
@ -22,9 +26,9 @@ complete -c exa -l 'group-directories-first' -d "Sort directories before other f
|
||||||
complete -c exa -l 'git-ignore' -d "Ignore files mentioned in '.gitignore'"
|
complete -c exa -l 'git-ignore' -d "Ignore files mentioned in '.gitignore'"
|
||||||
complete -c exa -s 'a' -l 'all' -d "Show hidden and 'dot' files"
|
complete -c exa -s 'a' -l 'all' -d "Show hidden and 'dot' files"
|
||||||
complete -c exa -s 'd' -l 'list-dirs' -d "List directories like regular files"
|
complete -c exa -s 'd' -l 'list-dirs' -d "List directories like regular files"
|
||||||
complete -c exa -s 'L' -l 'level' -d "Limit the depth of recursion" -a "1 2 3 4 5 6 7 8 9"
|
complete -c exa -s 'L' -l 'level' -d "Limit the depth of recursion" -x -a "1 2 3 4 5 6 7 8 9"
|
||||||
complete -c exa -s 'r' -l 'reverse' -d "Reverse the sort order"
|
complete -c exa -s 'r' -l 'reverse' -d "Reverse the sort order"
|
||||||
complete -c exa -s 's' -l 'sort' -x -d "Which field to sort by" -a "
|
complete -c exa -s 's' -l 'sort' -d "Which field to sort by" -x -a "
|
||||||
accessed\t'Sort by file accessed time'
|
accessed\t'Sort by file accessed time'
|
||||||
age\t'Sort by file modified time (newest first)'
|
age\t'Sort by file modified time (newest first)'
|
||||||
changed\t'Sort by changed time'
|
changed\t'Sort by changed time'
|
||||||
|
@ -56,10 +60,10 @@ complete -c exa -s 'b' -l 'binary' -d "List file sizes with binary prefixes"
|
||||||
complete -c exa -s 'B' -l 'bytes' -d "List file sizes in bytes, without any prefixes"
|
complete -c exa -s 'B' -l 'bytes' -d "List file sizes in bytes, without any prefixes"
|
||||||
complete -c exa -s 'g' -l 'group' -d "List each file's group"
|
complete -c exa -s 'g' -l 'group' -d "List each file's group"
|
||||||
complete -c exa -s 'h' -l 'header' -d "Add a header row to each column"
|
complete -c exa -s 'h' -l 'header' -d "Add a header row to each column"
|
||||||
complete -c exa -s 'h' -l 'links' -d "List each file's number of hard links"
|
complete -c exa -s 'H' -l 'links' -d "List each file's number of hard links"
|
||||||
complete -c exa -s 'g' -l 'group' -d "List each file's inode number"
|
complete -c exa -s 'i' -l 'inode' -d "List each file's inode number"
|
||||||
complete -c exa -s 'S' -l 'blocks' -d "List each file's number of filesystem blocks"
|
complete -c exa -s 'S' -l 'blocks' -d "List each file's number of filesystem blocks"
|
||||||
complete -c exa -s 't' -l 'time' -x -d "Which timestamp field to list" -a "
|
complete -c exa -s 't' -l 'time' -d "Which timestamp field to list" -x -a "
|
||||||
modified\t'Display modified time'
|
modified\t'Display modified time'
|
||||||
changed\t'Display changed time'
|
changed\t'Display changed time'
|
||||||
accessed\t'Display accessed time'
|
accessed\t'Display accessed time'
|
||||||
|
@ -70,7 +74,7 @@ complete -c exa -s 'n' -l 'numeric' -d "List numeric user and group IDs."
|
||||||
complete -c exa -l 'changed' -d "Use the changed timestamp field"
|
complete -c exa -l 'changed' -d "Use the changed timestamp field"
|
||||||
complete -c exa -s 'u' -l 'accessed' -d "Use the accessed timestamp field"
|
complete -c exa -s 'u' -l 'accessed' -d "Use the accessed timestamp field"
|
||||||
complete -c exa -s 'U' -l 'created' -d "Use the created timestamp field"
|
complete -c exa -s 'U' -l 'created' -d "Use the created timestamp field"
|
||||||
complete -c exa -l 'time-style' -x -d "How to format timestamps" -a "
|
complete -c exa -l 'time-style' -d "How to format timestamps" -x -a "
|
||||||
default\t'Use the default time style'
|
default\t'Use the default time style'
|
||||||
iso\t'Display brief ISO timestamps'
|
iso\t'Display brief ISO timestamps'
|
||||||
long-iso\t'Display longer ISO timestaps, up to the minute'
|
long-iso\t'Display longer ISO timestaps, up to the minute'
|
|
@ -1,7 +1,7 @@
|
||||||
#compdef exa
|
#compdef exa
|
||||||
|
|
||||||
# Save this file as _exa in /usr/local/share/zsh/site-functions or in any
|
# Save this file as _exa in /usr/local/share/zsh/site-functions or in any
|
||||||
# other folder in $fpath. E. g. save it in a folder called ~/.zfunc and add a
|
# other folder in $fpath. E.g. save it in a folder called ~/.zfunc and add a
|
||||||
# line containing `fpath=(~/.zfunc $fpath)` somewhere before `compinit` in your
|
# line containing `fpath=(~/.zfunc $fpath)` somewhere before `compinit` in your
|
||||||
# ~/.zshrc.
|
# ~/.zshrc.
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ __exa() {
|
||||||
{-R,--recurse}"[Recurse into directories]" \
|
{-R,--recurse}"[Recurse into directories]" \
|
||||||
{-T,--tree}"[Recurse into directories as a tree]" \
|
{-T,--tree}"[Recurse into directories as a tree]" \
|
||||||
{-F,--classify}"[Display type indicator by file names]" \
|
{-F,--classify}"[Display type indicator by file names]" \
|
||||||
--colo{,u}r"[When to use terminal colours]" \
|
--colo{,u}r="[When to use terminal colours]:(when):(always auto never)" \
|
||||||
--colo{,u}r-scale"[Highlight levels of file sizes distinctly]" \
|
--colo{,u}r-scale"[Highlight levels of file sizes distinctly]" \
|
||||||
--icons"[Display icons]" \
|
--icons"[Display icons]" \
|
||||||
--no-icons"[Hide icons]" \
|
--no-icons"[Hide icons]" \
|
|
@ -11,17 +11,17 @@ bash /vagrant/devtools/dev-versions.sh
|
||||||
# Configure the Cool Prompt™ (not actually trademarked).
|
# Configure the Cool Prompt™ (not actually trademarked).
|
||||||
# The Cool Prompt tells you whether you’re in debug or strict mode, whether
|
# The Cool Prompt tells you whether you’re in debug or strict mode, whether
|
||||||
# you have colours configured, and whether your last command failed.
|
# you have colours configured, and whether your last command failed.
|
||||||
function nonzero_return() { RETVAL=$?; [ $RETVAL -ne 0 ] && echo "$RETVAL "; }
|
nonzero_return() { RETVAL=$?; [ "$RETVAL" -ne 0 ] && echo "$RETVAL "; }
|
||||||
function debug_mode() { [ "$EXA_DEBUG" == "trace" ] && echo -n "trace-"; [ -n "$EXA_DEBUG" ] && echo "debug "; }
|
debug_mode() { [ "$EXA_DEBUG" == "trace" ] && echo -n "trace-"; [ -n "$EXA_DEBUG" ] && echo "debug "; }
|
||||||
function strict_mode() { [ -n "$EXA_STRICT" ] && echo "strict "; }
|
strict_mode() { [ -n "$EXA_STRICT" ] && echo "strict "; }
|
||||||
function lsc_mode() { [ -n "$LS_COLORS" ] && echo "lsc "; }
|
lsc_mode() { [ -n "$LS_COLORS" ] && echo "lsc "; }
|
||||||
function exac_mode() { [ -n "$EXA_COLORS" ] && echo "exac "; }
|
exac_mode() { [ -n "$EXA_COLORS" ] && echo "exac "; }
|
||||||
export PS1="\[\e[1;36m\]\h \[\e[32m\]\w \[\e[31m\]\`nonzero_return\`\[\e[35m\]\`debug_mode\`\[\e[32m\]\`lsc_mode\`\[\e[1;32m\]\`exac_mode\`\[\e[33m\]\`strict_mode\`\[\e[36m\]\\$\[\e[0m\] "
|
export PS1="\[\e[1;36m\]\h \[\e[32m\]\w \[\e[31m\]\`nonzero_return\`\[\e[35m\]\`debug_mode\`\[\e[32m\]\`lsc_mode\`\[\e[1;32m\]\`exac_mode\`\[\e[33m\]\`strict_mode\`\[\e[36m\]\\$\[\e[0m\] "
|
||||||
|
|
||||||
|
|
||||||
# The ‘debug’ function lets you switch debug mode on and off.
|
# The ‘debug’ function lets you switch debug mode on and off.
|
||||||
# Turn it on if you need to see exa’s debugging logs.
|
# Turn it on if you need to see exa’s debugging logs.
|
||||||
function debug () {
|
debug() {
|
||||||
case "$1" in
|
case "$1" in
|
||||||
""|"on") export EXA_DEBUG=1 ;;
|
""|"on") export EXA_DEBUG=1 ;;
|
||||||
"off") export EXA_DEBUG= ;;
|
"off") export EXA_DEBUG= ;;
|
||||||
|
@ -33,11 +33,12 @@ function debug () {
|
||||||
|
|
||||||
# The ‘strict’ function lets you switch strict mode on and off.
|
# The ‘strict’ function lets you switch strict mode on and off.
|
||||||
# Turn it on if you’d like exa’s command-line arguments checked.
|
# Turn it on if you’d like exa’s command-line arguments checked.
|
||||||
function strict () {
|
strict() {
|
||||||
case "$1" in "on") export EXA_STRICT=1 ;;
|
case "$1" in
|
||||||
|
"on") export EXA_STRICT=1 ;;
|
||||||
"off") export EXA_STRICT= ;;
|
"off") export EXA_STRICT= ;;
|
||||||
"") [ -n "$EXA_STRICT" ] && echo "strict on" || echo "strict off" ;;
|
"") [ -n "$EXA_STRICT" ] && echo "strict on" || echo "strict off" ;;
|
||||||
*) echo "Usage: strict on|off"; return 1 ;;
|
*) echo "Usage: strict on|off"; return 1 ;;
|
||||||
esac;
|
esac;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +46,7 @@ function strict () {
|
||||||
# environment variables. There’s also a ‘hacker’ theme which turns everything
|
# environment variables. There’s also a ‘hacker’ theme which turns everything
|
||||||
# green, which is usually used for checking that all colour codes work, and
|
# green, which is usually used for checking that all colour codes work, and
|
||||||
# for looking cool while you phreak some mainframes or whatever.
|
# for looking cool while you phreak some mainframes or whatever.
|
||||||
function colors () {
|
colors() {
|
||||||
case "$1" in
|
case "$1" in
|
||||||
"ls")
|
"ls")
|
||||||
export LS_COLORS="di=34:ln=35:so=32:pi=33:ex=31:bd=34;46:cd=34;43:su=30;41:sg=30;46:tw=30;42:ow=30;43"
|
export LS_COLORS="di=34:ln=35:so=32:pi=33:ex=31:bd=34;46:cd=34;43:su=30;41:sg=30;46:tw=30;42:ow=30;43"
|
||||||
|
|
|
@ -252,7 +252,7 @@ sudo chown $FIXED_USER:$FIXED_USER -R "$TEST_ROOT/attributes"
|
||||||
|
|
||||||
# A sample Git repository
|
# A sample Git repository
|
||||||
# This uses cd because it's easier than telling Git where to go each time
|
# This uses cd because it's easier than telling Git where to go each time
|
||||||
echo -e "\033[1m[10/13]\033[0m Creating Git testcases (1/3)"
|
echo -e "\033[1m[10/13]\033[0m Creating Git testcases (1/4)"
|
||||||
mkdir "$TEST_ROOT/git"
|
mkdir "$TEST_ROOT/git"
|
||||||
cd "$TEST_ROOT/git"
|
cd "$TEST_ROOT/git"
|
||||||
git init >/dev/null
|
git init >/dev/null
|
||||||
|
@ -281,7 +281,7 @@ sudo chown $FIXED_USER:$FIXED_USER -R "$TEST_ROOT/git"
|
||||||
|
|
||||||
# A second Git repository
|
# A second Git repository
|
||||||
# for testing two at once
|
# for testing two at once
|
||||||
echo -e "\033[1m[11/13]\033[0m Creating Git testcases (2/3)"
|
echo -e "\033[1m[11/13]\033[0m Creating Git testcases (2/4)"
|
||||||
mkdir -p "$TEST_ROOT/git2/deeply/nested/directory"
|
mkdir -p "$TEST_ROOT/git2/deeply/nested/directory"
|
||||||
cd "$TEST_ROOT/git2"
|
cd "$TEST_ROOT/git2"
|
||||||
git init >/dev/null
|
git init >/dev/null
|
||||||
|
@ -300,6 +300,8 @@ touch "ignoreds/music.m4a"
|
||||||
mkdir "ignoreds/nested"
|
mkdir "ignoreds/nested"
|
||||||
touch "ignoreds/nested/70s grove.mp3"
|
touch "ignoreds/nested/70s grove.mp3"
|
||||||
touch "ignoreds/nested/funky chicken.m4a"
|
touch "ignoreds/nested/funky chicken.m4a"
|
||||||
|
mkdir "ignoreds/nested2"
|
||||||
|
touch "ignoreds/nested2/ievan polkka.mp3"
|
||||||
|
|
||||||
mkdir "target"
|
mkdir "target"
|
||||||
touch "target/another ignored file"
|
touch "target/another ignored file"
|
||||||
|
@ -319,7 +321,7 @@ sudo chown $FIXED_USER:$FIXED_USER -R "$TEST_ROOT/git2"
|
||||||
|
|
||||||
# A third Git repository
|
# A third Git repository
|
||||||
# Regression test for https://github.com/ogham/exa/issues/526
|
# Regression test for https://github.com/ogham/exa/issues/526
|
||||||
echo -e "\033[1m[12/13]\033[0m Creating Git testcases (3/3)"
|
echo -e "\033[1m[12/13]\033[0m Creating Git testcases (3/4)"
|
||||||
mkdir -p "$TEST_ROOT/git3"
|
mkdir -p "$TEST_ROOT/git3"
|
||||||
cd "$TEST_ROOT/git3"
|
cd "$TEST_ROOT/git3"
|
||||||
git init >/dev/null
|
git init >/dev/null
|
||||||
|
@ -332,6 +334,20 @@ find "$TEST_ROOT/git3" -exec touch {} -h -t $FIXED_DATE \;
|
||||||
sudo chown $FIXED_USER:$FIXED_USER -R "$TEST_ROOT/git3"
|
sudo chown $FIXED_USER:$FIXED_USER -R "$TEST_ROOT/git3"
|
||||||
|
|
||||||
|
|
||||||
|
# A fourth Git repository
|
||||||
|
# Regression test for https://github.com/ogham/exa/issues/698
|
||||||
|
echo -e "\033[1m[12/13]\033[0m Creating Git testcases (4/4)"
|
||||||
|
mkdir -p "$TEST_ROOT/git4"
|
||||||
|
cd "$TEST_ROOT/git4"
|
||||||
|
git init >/dev/null
|
||||||
|
|
||||||
|
# Create a non UTF-8 file
|
||||||
|
touch 'P'$'\b\211''UUU'
|
||||||
|
|
||||||
|
find "$TEST_ROOT/git4" -exec touch {} -h -t $FIXED_DATE \;
|
||||||
|
sudo chown $FIXED_USER:$FIXED_USER -R "$TEST_ROOT/git4"
|
||||||
|
|
||||||
|
|
||||||
# Hidden and dot file testcases.
|
# Hidden and dot file testcases.
|
||||||
# We need to set the permissions of `.` and `..` because they actually
|
# We need to set the permissions of `.` and `..` because they actually
|
||||||
# get displayed in the output here, so this has to come last.
|
# get displayed in the output here, so this has to come last.
|
||||||
|
|
|
@ -9,7 +9,7 @@ set -e
|
||||||
|
|
||||||
|
|
||||||
# Linux check!
|
# Linux check!
|
||||||
uname=`uname -s`
|
uname=$(uname -s)
|
||||||
if [[ "$uname" != "Linux" ]]; then
|
if [[ "$uname" != "Linux" ]]; then
|
||||||
echo "Gotta be on Linux to run this (detected '$uname')!"
|
echo "Gotta be on Linux to run this (detected '$uname')!"
|
||||||
exit 1
|
exit 1
|
||||||
|
@ -29,8 +29,8 @@ fi
|
||||||
|
|
||||||
# Weekly builds have a bit more information in their version number (see build.rs).
|
# Weekly builds have a bit more information in their version number (see build.rs).
|
||||||
if [[ "$1" == "--weekly" ]]; then
|
if [[ "$1" == "--weekly" ]]; then
|
||||||
git_hash=`GIT_DIR=/vagrant/.git git rev-parse --short --verify HEAD`
|
git_hash=$(GIT_DIR=/vagrant/.git git rev-parse --short --verify HEAD)
|
||||||
date=`date +"%Y-%m-%d"`
|
date=$(date +"%Y-%m-%d")
|
||||||
echo "Building exa weekly v$exa_version, date $date, Git hash $git_hash"
|
echo "Building exa weekly v$exa_version, date $date, Git hash $git_hash"
|
||||||
else
|
else
|
||||||
echo "Building exa v$exa_version"
|
echo "Building exa v$exa_version"
|
||||||
|
@ -57,9 +57,10 @@ strip -v "$exa_linux_binary"
|
||||||
# the binaries can have consistent names, and it’s still possible to tell
|
# the binaries can have consistent names, and it’s still possible to tell
|
||||||
# different *downloads* apart.
|
# different *downloads* apart.
|
||||||
echo -e "\n\033[4mZipping binary...\033[0m"
|
echo -e "\n\033[4mZipping binary...\033[0m"
|
||||||
if [[ "$1" == "--weekly" ]]
|
if [[ "$1" == "--weekly" ]]; then
|
||||||
then exa_linux_zip="/vagrant/exa-linux-x86_64-${exa_version}-${date}-${git_hash}.zip"
|
exa_linux_zip="/vagrant/exa-linux-x86_64-${exa_version}-${date}-${git_hash}.zip"
|
||||||
else exa_linux_zip="/vagrant/exa-linux-x86_64.zip"
|
else
|
||||||
|
exa_linux_zip="/vagrant/exa-linux-x86_64.zip"
|
||||||
fi
|
fi
|
||||||
rm -vf "$exa_linux_zip"
|
rm -vf "$exa_linux_zip"
|
||||||
zip -j "$exa_linux_zip" "$exa_linux_binary"
|
zip -j "$exa_linux_zip" "$exa_linux_binary"
|
||||||
|
|
|
@ -11,7 +11,7 @@ set -e
|
||||||
|
|
||||||
# Virtualising macOS is a legal minefield, so this script is ‘local’ instead
|
# Virtualising macOS is a legal minefield, so this script is ‘local’ instead
|
||||||
# of ‘dev’: I run it from my actual machine, rather than from a VM.
|
# of ‘dev’: I run it from my actual machine, rather than from a VM.
|
||||||
uname=`uname -s`
|
uname=$(uname -s)
|
||||||
if [[ "$uname" != "Darwin" ]]; then
|
if [[ "$uname" != "Darwin" ]]; then
|
||||||
echo "Gotta be on Darwin to run this (detected '$uname')!"
|
echo "Gotta be on Darwin to run this (detected '$uname')!"
|
||||||
exit 1
|
exit 1
|
||||||
|
@ -36,8 +36,8 @@ fi
|
||||||
|
|
||||||
# Weekly builds have a bit more information in their version number (see build.rs).
|
# Weekly builds have a bit more information in their version number (see build.rs).
|
||||||
if [[ "$1" == "--weekly" ]]; then
|
if [[ "$1" == "--weekly" ]]; then
|
||||||
git_hash=`GIT_DIR=$exa_root/.git git rev-parse --short --verify HEAD`
|
git_hash=$(GIT_DIR=$exa_root/.git git rev-parse --short --verify HEAD)
|
||||||
date=`date +"%Y-%m-%d"`
|
date=$(date +"%Y-%m-%d")
|
||||||
echo "Building exa weekly v$exa_version, date $date, Git hash $git_hash"
|
echo "Building exa weekly v$exa_version, date $date, Git hash $git_hash"
|
||||||
else
|
else
|
||||||
echo "Building exa v$exa_version"
|
echo "Building exa v$exa_version"
|
||||||
|
@ -65,9 +65,10 @@ echo "strip $exa_macos_binary"
|
||||||
# the binaries can have consistent names, and it’s still possible to tell
|
# the binaries can have consistent names, and it’s still possible to tell
|
||||||
# different *downloads* apart.
|
# different *downloads* apart.
|
||||||
echo -e "\n\033[4mZipping binary...\033[0m"
|
echo -e "\n\033[4mZipping binary...\033[0m"
|
||||||
if [[ "$1" == "--weekly" ]]
|
if [[ "$1" == "--weekly" ]]; then
|
||||||
then exa_macos_zip="$exa_root/exa-macos-x86_64-${exa_version}-${date}-${git_hash}.zip"
|
exa_macos_zip="$exa_root/exa-macos-x86_64-${exa_version}-${date}-${git_hash}.zip"
|
||||||
else exa_macos_zip="$exa_root/exa-macos-x86_64-${exa_version}.zip"
|
else
|
||||||
|
exa_macos_zip="$exa_root/exa-macos-x86_64-${exa_version}.zip"
|
||||||
fi
|
fi
|
||||||
rm -vf "$exa_macos_zip" | sed 's/^/removing /'
|
rm -vf "$exa_macos_zip" | sed 's/^/removing /'
|
||||||
zip -j "$exa_macos_zip" "$exa_macos_binary"
|
zip -j "$exa_macos_zip" "$exa_macos_binary"
|
||||||
|
|
12
man/exa.1.md
12
man/exa.1.md
|
@ -84,7 +84,7 @@ FILTERING AND SORTING OPTIONS
|
||||||
Use this twice to also show the ‘`.`’ and ‘`..`’ directories.
|
Use this twice to also show the ‘`.`’ and ‘`..`’ directories.
|
||||||
|
|
||||||
`-d`, `--list-dirs`
|
`-d`, `--list-dirs`
|
||||||
: List directories like regular files.
|
: List directories as regular files, rather than recursing and listing their contents.
|
||||||
|
|
||||||
`-L`, `--level=DEPTH`
|
`-L`, `--level=DEPTH`
|
||||||
: Limit the depth of recursion.
|
: Limit the depth of recursion.
|
||||||
|
@ -183,6 +183,10 @@ These options are available when running with `--long` (`-l`):
|
||||||
`--git` [if exa was built with git support]
|
`--git` [if exa was built with git support]
|
||||||
: List each file’s Git status, if tracked.
|
: List each file’s Git status, if tracked.
|
||||||
|
|
||||||
|
This adds a two-character column indicating the staged and unstaged statuses respectively. The status character can be ‘`-`’ for not modified, ‘`M`’ for a modified file, ‘`N`’ for a new file, ‘`D`’ for deleted, ‘`R`’ for renamed, ‘`T`’ for type-change, ‘`I`’ for ignored, and ‘`U`’ for conflicted.
|
||||||
|
|
||||||
|
Directories will be shown to have the status of their contents, which is how ‘deleted’ is possible: if a directory contains a file that has a certain status, it will be shown to have that status.
|
||||||
|
|
||||||
|
|
||||||
ENVIRONMENT VARIABLES
|
ENVIRONMENT VARIABLES
|
||||||
=====================
|
=====================
|
||||||
|
@ -220,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 there’s 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.
|
Different terminals display icons differently, as they usually take up more than one character width on screen, so there’s 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`
|
## `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.
|
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
2
rust-toolchain.toml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[toolchain]
|
||||||
|
channel = "1.66.1"
|
|
@ -111,6 +111,13 @@ impl<'dir, 'ig> Files<'dir, 'ig> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Also hide _prefix files on Windows because it's used by old applications
|
||||||
|
// as an alternative to dot-prefix files.
|
||||||
|
#[cfg(windows)]
|
||||||
|
if ! self.dotfiles && filename.starts_with('_') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if self.git_ignoring {
|
if self.git_ignoring {
|
||||||
let git_status = self.git.map(|g| g.get(path, false)).unwrap_or_default();
|
let git_status = self.git.map(|g| g.get(path, false)).unwrap_or_default();
|
||||||
if git_status.unstaged == GitStatus::Ignored {
|
if git_status.unstaged == GitStatus::Ignored {
|
||||||
|
@ -121,9 +128,8 @@ impl<'dir, 'ig> Files<'dir, 'ig> {
|
||||||
return Some(File::from_args(path.clone(), self.dir, filename)
|
return Some(File::from_args(path.clone(), self.dir, filename)
|
||||||
.map_err(|e| (path.clone(), e)))
|
.map_err(|e| (path.clone(), e)))
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
return None
|
return None
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -170,7 +176,7 @@ impl<'dir, 'ig> Iterator for Files<'dir, 'ig> {
|
||||||
/// Usually files in Unix use a leading dot to be hidden or visible, but two
|
/// Usually files in Unix use a leading dot to be hidden or visible, but two
|
||||||
/// entries in particular are “extra-hidden”: `.` and `..`, which only become
|
/// entries in particular are “extra-hidden”: `.` and `..`, which only become
|
||||||
/// visible after an extra `-a` option.
|
/// visible after an extra `-a` option.
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
pub enum DotFilter {
|
pub enum DotFilter {
|
||||||
|
|
||||||
/// Shows files, dotfiles, and `.` and `..`.
|
/// Shows files, dotfiles, and `.` and `..`.
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
/// into them and print out their contents. The recurse mode does this by
|
/// into them and print out their contents. The recurse mode does this by
|
||||||
/// having extra output blocks at the end, while the tree mode will show
|
/// having extra output blocks at the end, while the tree mode will show
|
||||||
/// directories inline, with their contents immediately underneath.
|
/// directories inline, with their contents immediately underneath.
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
pub enum DirAction {
|
pub enum DirAction {
|
||||||
|
|
||||||
/// This directory should be listed along with the regular files, instead
|
/// This directory should be listed along with the regular files, instead
|
||||||
|
@ -51,14 +51,14 @@ impl DirAction {
|
||||||
match self {
|
match self {
|
||||||
Self::AsFile => true,
|
Self::AsFile => true,
|
||||||
Self::Recurse(o) => o.tree,
|
Self::Recurse(o) => o.tree,
|
||||||
_ => false,
|
Self::List => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// The options that determine how to recurse into a directory.
|
/// The options that determine how to recurse into a directory.
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
pub struct RecurseOptions {
|
pub struct RecurseOptions {
|
||||||
|
|
||||||
/// Whether recursion should be done as a tree or as multiple individual
|
/// Whether recursion should be done as a tree or as multiple individual
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
//! Getting the Git status of files and directories.
|
//! Getting the Git status of files and directories.
|
||||||
|
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
use std::os::unix::ffi::OsStrExt;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
@ -205,6 +208,11 @@ fn repo_to_statuses(repo: &git2::Repository, workdir: &Path) -> Git {
|
||||||
match repo.statuses(None) {
|
match repo.statuses(None) {
|
||||||
Ok(es) => {
|
Ok(es) => {
|
||||||
for e in es.iter() {
|
for e in es.iter() {
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
let path = workdir.join(Path::new(OsStr::from_bytes(e.path_bytes())));
|
||||||
|
// TODO: handle non Unix systems better:
|
||||||
|
// https://github.com/ogham/exa/issues/698
|
||||||
|
#[cfg(not(target_family = "unix"))]
|
||||||
let path = workdir.join(Path::new(e.path().unwrap()));
|
let path = workdir.join(Path::new(e.path().unwrap()));
|
||||||
let elem = (path, e.status());
|
let elem = (path, e.status());
|
||||||
statuses.push(elem);
|
statuses.push(elem);
|
||||||
|
@ -242,25 +250,40 @@ impl Git {
|
||||||
else { self.file_status(index) }
|
else { self.file_status(index) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the status for the file at the given path.
|
/// Get the user-facing status of a file.
|
||||||
|
/// We check the statuses directly applying to a file, and for the ignored
|
||||||
|
/// status we check if any of its parents directories is ignored by git.
|
||||||
fn file_status(&self, file: &Path) -> f::Git {
|
fn file_status(&self, file: &Path) -> f::Git {
|
||||||
let path = reorient(file);
|
let path = reorient(file);
|
||||||
|
|
||||||
self.statuses.iter()
|
let s = self.statuses.iter()
|
||||||
.find(|p| p.0.as_path() == path)
|
.filter(|p| if p.1 == git2::Status::IGNORED {
|
||||||
.map(|&(_, s)| f::Git { staged: index_status(s), unstaged: working_tree_status(s) })
|
path.starts_with(&p.0)
|
||||||
.unwrap_or_default()
|
} else {
|
||||||
|
p.0 == path
|
||||||
|
})
|
||||||
|
.fold(git2::Status::empty(), |a, b| a | b.1);
|
||||||
|
|
||||||
|
let staged = index_status(s);
|
||||||
|
let unstaged = working_tree_status(s);
|
||||||
|
f::Git { staged, unstaged }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the combined status for all the files whose paths begin with the
|
/// Get the combined, user-facing status of a directory.
|
||||||
/// path that gets passed in. This is used for getting the status of
|
/// Statuses are aggregating (for example, a directory is considered
|
||||||
/// directories, which don’t really have an ‘official’ status.
|
/// modified if any file under it has the status modified), except for
|
||||||
|
/// ignored status which applies to files under (for example, a directory
|
||||||
|
/// is considered ignored if one of its parent directories is ignored).
|
||||||
fn dir_status(&self, dir: &Path) -> f::Git {
|
fn dir_status(&self, dir: &Path) -> f::Git {
|
||||||
let path = reorient(dir);
|
let path = reorient(dir);
|
||||||
|
|
||||||
let s = self.statuses.iter()
|
let s = self.statuses.iter()
|
||||||
.filter(|p| p.0.starts_with(&path))
|
.filter(|p| if p.1 == git2::Status::IGNORED {
|
||||||
.fold(git2::Status::empty(), |a, b| a | b.1);
|
path.starts_with(&p.0)
|
||||||
|
} else {
|
||||||
|
p.0.starts_with(&path)
|
||||||
|
})
|
||||||
|
.fold(git2::Status::empty(), |a, b| a | b.1);
|
||||||
|
|
||||||
let staged = index_status(s);
|
let staged = index_status(s);
|
||||||
let unstaged = working_tree_status(s);
|
let unstaged = working_tree_status(s);
|
||||||
|
@ -273,6 +296,7 @@ impl Git {
|
||||||
/// Paths need to be absolute for them to be compared properly, otherwise
|
/// Paths need to be absolute for them to be compared properly, otherwise
|
||||||
/// you’d ask a repo about “./README.md” but it only knows about
|
/// you’d ask a repo about “./README.md” but it only knows about
|
||||||
/// “/vagrant/README.md”, prefixed by the workdir.
|
/// “/vagrant/README.md”, prefixed by the workdir.
|
||||||
|
#[cfg(unix)]
|
||||||
fn reorient(path: &Path) -> PathBuf {
|
fn reorient(path: &Path) -> PathBuf {
|
||||||
use std::env::current_dir;
|
use std::env::current_dir;
|
||||||
|
|
||||||
|
@ -285,6 +309,14 @@ fn reorient(path: &Path) -> PathBuf {
|
||||||
path.canonicalize().unwrap_or(path)
|
path.canonicalize().unwrap_or(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn reorient(path: &Path) -> PathBuf {
|
||||||
|
let unc_path = path.canonicalize().unwrap();
|
||||||
|
// On Windows UNC path is returned. We need to strip the prefix for it to work.
|
||||||
|
let normal_path = unc_path.as_os_str().to_str().unwrap().trim_left_matches("\\\\?\\");
|
||||||
|
return PathBuf::from(normal_path);
|
||||||
|
}
|
||||||
|
|
||||||
/// The character to display if the file has been modified, but not staged.
|
/// The character to display if the file has been modified, but not staged.
|
||||||
fn working_tree_status(status: git2::Status) -> f::GitStatus {
|
fn working_tree_status(status: git2::Status) -> f::GitStatus {
|
||||||
match status {
|
match status {
|
||||||
|
|
|
@ -167,7 +167,7 @@ mod lister {
|
||||||
unsafe {
|
unsafe {
|
||||||
listxattr(
|
listxattr(
|
||||||
c_path.as_ptr(),
|
c_path.as_ptr(),
|
||||||
buf.as_mut_ptr() as *mut c_char,
|
buf.as_mut_ptr().cast::<c_char>(),
|
||||||
bufsize as size_t,
|
bufsize as size_t,
|
||||||
self.c_flags,
|
self.c_flags,
|
||||||
)
|
)
|
||||||
|
@ -178,7 +178,7 @@ mod lister {
|
||||||
unsafe {
|
unsafe {
|
||||||
getxattr(
|
getxattr(
|
||||||
c_path.as_ptr(),
|
c_path.as_ptr(),
|
||||||
buf.as_ptr() as *const c_char,
|
buf.as_ptr().cast::<c_char>(),
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
@ -246,7 +246,7 @@ mod lister {
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
listxattr(
|
listxattr(
|
||||||
c_path.as_ptr() as *const _,
|
c_path.as_ptr().cast(),
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
|
@ -261,8 +261,8 @@ mod lister {
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
listxattr(
|
listxattr(
|
||||||
c_path.as_ptr() as *const _,
|
c_path.as_ptr().cast(),
|
||||||
buf.as_mut_ptr() as *mut c_char,
|
buf.as_mut_ptr().cast(),
|
||||||
bufsize as size_t,
|
bufsize as size_t,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -276,8 +276,8 @@ mod lister {
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
getxattr(
|
getxattr(
|
||||||
c_path.as_ptr() as *const _,
|
c_path.as_ptr().cast(),
|
||||||
buf.as_ptr() as *const c_char,
|
buf.as_ptr().cast(),
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
|
|
|
@ -82,13 +82,27 @@ pub struct Permissions {
|
||||||
pub setuid: bool,
|
pub setuid: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The file's FileAttributes field, available only on Windows.
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct Attributes {
|
||||||
|
pub archive: bool,
|
||||||
|
pub directory: bool,
|
||||||
|
pub readonly: bool,
|
||||||
|
pub hidden: bool,
|
||||||
|
pub system: bool,
|
||||||
|
pub reparse_point: bool,
|
||||||
|
}
|
||||||
|
|
||||||
/// The three pieces of information that are displayed as a single column in
|
/// The three pieces of information that are displayed as a single column in
|
||||||
/// the details view. These values are fused together to make the output a
|
/// the details view. These values are fused together to make the output a
|
||||||
/// little more compressed.
|
/// little more compressed.
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct PermissionsPlus {
|
pub struct PermissionsPlus {
|
||||||
pub file_type: Type,
|
pub file_type: Type,
|
||||||
|
#[cfg(unix)]
|
||||||
pub permissions: Permissions,
|
pub permissions: Permissions,
|
||||||
|
#[cfg(windows)]
|
||||||
|
pub attributes: Attributes,
|
||||||
pub xattrs: bool,
|
pub xattrs: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,7 +176,7 @@ pub enum Size {
|
||||||
/// data is rarely useful — I can’t think of a time when I’ve seen it and
|
/// data is rarely useful — I can’t think of a time when I’ve seen it and
|
||||||
/// learnt something. So we discard it and just output “-” instead.
|
/// learnt something. So we discard it and just output “-” instead.
|
||||||
///
|
///
|
||||||
/// See this answer for more: http://unix.stackexchange.com/a/68266
|
/// See this answer for more: <https://unix.stackexchange.com/a/68266>
|
||||||
None,
|
None,
|
||||||
|
|
||||||
/// This file is a block or character device, so instead of a size, print
|
/// This file is a block or character device, so instead of a size, print
|
||||||
|
@ -196,7 +210,7 @@ pub struct Time {
|
||||||
/// A file’s status in a Git repository. Whether a file is in a repository or
|
/// A file’s status in a Git repository. Whether a file is in a repository or
|
||||||
/// not is handled by the Git module, rather than having a “null” variant in
|
/// not is handled by the Git module, rather than having a “null” variant in
|
||||||
/// this enum.
|
/// this enum.
|
||||||
#[derive(PartialEq, Copy, Clone)]
|
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||||
pub enum GitStatus {
|
pub enum GitStatus {
|
||||||
|
|
||||||
/// This file hasn’t changed since the last commit.
|
/// This file hasn’t changed since the last commit.
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
//! Files, and methods and fields to access their metadata.
|
//! Files, and methods and fields to access their metadata.
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
#[cfg(unix)]
|
||||||
use std::os::unix::fs::{FileTypeExt, MetadataExt, PermissionsExt};
|
use std::os::unix::fs::{FileTypeExt, MetadataExt, PermissionsExt};
|
||||||
|
#[cfg(windows)]
|
||||||
|
use std::os::windows::fs::MetadataExt;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
|
@ -78,11 +81,11 @@ impl<'dir> File<'dir> {
|
||||||
let metadata = std::fs::symlink_metadata(&path)?;
|
let metadata = std::fs::symlink_metadata(&path)?;
|
||||||
let is_all_all = false;
|
let is_all_all = false;
|
||||||
|
|
||||||
Ok(File { path, parent_dir, metadata, ext, name, is_all_all })
|
Ok(File { name, ext, path, metadata, parent_dir, is_all_all })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_aa_current(parent_dir: &'dir Dir) -> io::Result<File<'dir>> {
|
pub fn new_aa_current(parent_dir: &'dir Dir) -> io::Result<File<'dir>> {
|
||||||
let path = parent_dir.path.to_path_buf();
|
let path = parent_dir.path.clone();
|
||||||
let ext = File::ext(&path);
|
let ext = File::ext(&path);
|
||||||
|
|
||||||
debug!("Statting file {:?}", &path);
|
debug!("Statting file {:?}", &path);
|
||||||
|
@ -174,6 +177,7 @@ impl<'dir> File<'dir> {
|
||||||
/// Whether this file is both a regular file *and* executable for the
|
/// Whether this file is both a regular file *and* executable for the
|
||||||
/// current user. An executable file has a different purpose from an
|
/// current user. An executable file has a different purpose from an
|
||||||
/// executable directory, so they should be highlighted differently.
|
/// executable directory, so they should be highlighted differently.
|
||||||
|
#[cfg(unix)]
|
||||||
pub fn is_executable_file(&self) -> bool {
|
pub fn is_executable_file(&self) -> bool {
|
||||||
let bit = modes::USER_EXECUTE;
|
let bit = modes::USER_EXECUTE;
|
||||||
self.is_file() && (self.metadata.permissions().mode() & bit) == bit
|
self.is_file() && (self.metadata.permissions().mode() & bit) == bit
|
||||||
|
@ -185,21 +189,25 @@ impl<'dir> File<'dir> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether this file is a named pipe on the filesystem.
|
/// Whether this file is a named pipe on the filesystem.
|
||||||
|
#[cfg(unix)]
|
||||||
pub fn is_pipe(&self) -> bool {
|
pub fn is_pipe(&self) -> bool {
|
||||||
self.metadata.file_type().is_fifo()
|
self.metadata.file_type().is_fifo()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether this file is a char device on the filesystem.
|
/// Whether this file is a char device on the filesystem.
|
||||||
|
#[cfg(unix)]
|
||||||
pub fn is_char_device(&self) -> bool {
|
pub fn is_char_device(&self) -> bool {
|
||||||
self.metadata.file_type().is_char_device()
|
self.metadata.file_type().is_char_device()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether this file is a block device on the filesystem.
|
/// Whether this file is a block device on the filesystem.
|
||||||
|
#[cfg(unix)]
|
||||||
pub fn is_block_device(&self) -> bool {
|
pub fn is_block_device(&self) -> bool {
|
||||||
self.metadata.file_type().is_block_device()
|
self.metadata.file_type().is_block_device()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether this file is a socket on the filesystem.
|
/// Whether this file is a socket on the filesystem.
|
||||||
|
#[cfg(unix)]
|
||||||
pub fn is_socket(&self) -> bool {
|
pub fn is_socket(&self) -> bool {
|
||||||
self.metadata.file_type().is_socket()
|
self.metadata.file_type().is_socket()
|
||||||
}
|
}
|
||||||
|
@ -213,13 +221,13 @@ impl<'dir> File<'dir> {
|
||||||
path.to_path_buf()
|
path.to_path_buf()
|
||||||
}
|
}
|
||||||
else if let Some(dir) = self.parent_dir {
|
else if let Some(dir) = self.parent_dir {
|
||||||
dir.join(&*path)
|
dir.join(path)
|
||||||
}
|
}
|
||||||
else if let Some(parent) = self.path.parent() {
|
else if let Some(parent) = self.path.parent() {
|
||||||
parent.join(&*path)
|
parent.join(path)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
self.path.join(&*path)
|
self.path.join(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,6 +278,7 @@ impl<'dir> File<'dir> {
|
||||||
/// is uncommon, while you come across directories and other types
|
/// is uncommon, while you come across directories and other types
|
||||||
/// with multiple links much more often. Thus, it should get highlighted
|
/// with multiple links much more often. Thus, it should get highlighted
|
||||||
/// more attentively.
|
/// more attentively.
|
||||||
|
#[cfg(unix)]
|
||||||
pub fn links(&self) -> f::Links {
|
pub fn links(&self) -> f::Links {
|
||||||
let count = self.metadata.nlink();
|
let count = self.metadata.nlink();
|
||||||
|
|
||||||
|
@ -280,6 +289,7 @@ impl<'dir> File<'dir> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This file’s inode.
|
/// This file’s inode.
|
||||||
|
#[cfg(unix)]
|
||||||
pub fn inode(&self) -> f::Inode {
|
pub fn inode(&self) -> f::Inode {
|
||||||
f::Inode(self.metadata.ino())
|
f::Inode(self.metadata.ino())
|
||||||
}
|
}
|
||||||
|
@ -287,6 +297,7 @@ impl<'dir> File<'dir> {
|
||||||
/// This file’s number of filesystem blocks.
|
/// This file’s number of filesystem blocks.
|
||||||
///
|
///
|
||||||
/// (Not the size of each block, which we don’t actually report on)
|
/// (Not the size of each block, which we don’t actually report on)
|
||||||
|
#[cfg(unix)]
|
||||||
pub fn blocks(&self) -> f::Blocks {
|
pub fn blocks(&self) -> f::Blocks {
|
||||||
if self.is_file() || self.is_link() {
|
if self.is_file() || self.is_link() {
|
||||||
f::Blocks::Some(self.metadata.blocks())
|
f::Blocks::Some(self.metadata.blocks())
|
||||||
|
@ -297,11 +308,13 @@ impl<'dir> File<'dir> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The ID of the user that own this file.
|
/// The ID of the user that own this file.
|
||||||
|
#[cfg(unix)]
|
||||||
pub fn user(&self) -> f::User {
|
pub fn user(&self) -> f::User {
|
||||||
f::User(self.metadata.uid())
|
f::User(self.metadata.uid())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The ID of the group that owns this file.
|
/// The ID of the group that owns this file.
|
||||||
|
#[cfg(unix)]
|
||||||
pub fn group(&self) -> f::Group {
|
pub fn group(&self) -> f::Group {
|
||||||
f::Group(self.metadata.gid())
|
f::Group(self.metadata.gid())
|
||||||
}
|
}
|
||||||
|
@ -314,15 +327,21 @@ impl<'dir> File<'dir> {
|
||||||
///
|
///
|
||||||
/// Block and character devices return their device IDs, because they
|
/// Block and character devices return their device IDs, because they
|
||||||
/// usually just have a file size of zero.
|
/// usually just have a file size of zero.
|
||||||
|
#[cfg(unix)]
|
||||||
pub fn size(&self) -> f::Size {
|
pub fn size(&self) -> f::Size {
|
||||||
if self.is_directory() {
|
if self.is_directory() {
|
||||||
f::Size::None
|
f::Size::None
|
||||||
}
|
}
|
||||||
else if self.is_char_device() || self.is_block_device() {
|
else if self.is_char_device() || self.is_block_device() {
|
||||||
let dev = self.metadata.rdev();
|
let device_ids = self.metadata.rdev().to_be_bytes();
|
||||||
|
|
||||||
|
// In C-land, getting the major and minor device IDs is done with
|
||||||
|
// preprocessor macros called `major` and `minor` that depend on
|
||||||
|
// the size of `dev_t`, but we just take the second-to-last and
|
||||||
|
// last bytes.
|
||||||
f::Size::DeviceIDs(f::DeviceIDs {
|
f::Size::DeviceIDs(f::DeviceIDs {
|
||||||
major: (dev / 256) as u8,
|
major: device_ids[6],
|
||||||
minor: (dev % 256) as u8,
|
minor: device_ids[7],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -330,12 +349,23 @@ impl<'dir> File<'dir> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
pub fn size(&self) -> f::Size {
|
||||||
|
if self.is_directory() {
|
||||||
|
f::Size::None
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
f::Size::Some(self.metadata.len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// This file’s last modified timestamp, if available on this platform.
|
/// This file’s last modified timestamp, if available on this platform.
|
||||||
pub fn modified_time(&self) -> Option<SystemTime> {
|
pub fn modified_time(&self) -> Option<SystemTime> {
|
||||||
self.metadata.modified().ok()
|
self.metadata.modified().ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This file’s last changed timestamp, if available on this platform.
|
/// This file’s last changed timestamp, if available on this platform.
|
||||||
|
#[cfg(unix)]
|
||||||
pub fn changed_time(&self) -> Option<SystemTime> {
|
pub fn changed_time(&self) -> Option<SystemTime> {
|
||||||
let (mut sec, mut nanosec) = (self.metadata.ctime(), self.metadata.ctime_nsec());
|
let (mut sec, mut nanosec) = (self.metadata.ctime(), self.metadata.ctime_nsec());
|
||||||
|
|
||||||
|
@ -345,7 +375,7 @@ impl<'dir> File<'dir> {
|
||||||
nanosec -= 1_000_000_000;
|
nanosec -= 1_000_000_000;
|
||||||
}
|
}
|
||||||
|
|
||||||
let duration = Duration::new(sec.abs() as u64, nanosec.abs() as u32);
|
let duration = Duration::new(sec.unsigned_abs(), nanosec.unsigned_abs() as u32);
|
||||||
Some(UNIX_EPOCH - duration)
|
Some(UNIX_EPOCH - duration)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -354,6 +384,11 @@ impl<'dir> File<'dir> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
pub fn changed_time(&self) -> Option<SystemTime> {
|
||||||
|
return self.modified_time()
|
||||||
|
}
|
||||||
|
|
||||||
/// This file’s last accessed timestamp, if available on this platform.
|
/// This file’s last accessed timestamp, if available on this platform.
|
||||||
pub fn accessed_time(&self) -> Option<SystemTime> {
|
pub fn accessed_time(&self) -> Option<SystemTime> {
|
||||||
self.metadata.accessed().ok()
|
self.metadata.accessed().ok()
|
||||||
|
@ -369,6 +404,7 @@ impl<'dir> File<'dir> {
|
||||||
/// This is used a the leftmost character of the permissions column.
|
/// This is used a the leftmost character of the permissions column.
|
||||||
/// The file type can usually be guessed from the colour of the file, but
|
/// The file type can usually be guessed from the colour of the file, but
|
||||||
/// ls puts this character there.
|
/// ls puts this character there.
|
||||||
|
#[cfg(unix)]
|
||||||
pub fn type_char(&self) -> f::Type {
|
pub fn type_char(&self) -> f::Type {
|
||||||
if self.is_file() {
|
if self.is_file() {
|
||||||
f::Type::File
|
f::Type::File
|
||||||
|
@ -396,7 +432,21 @@ impl<'dir> File<'dir> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
pub fn type_char(&self) -> f::Type {
|
||||||
|
if self.is_file() {
|
||||||
|
f::Type::File
|
||||||
|
}
|
||||||
|
else if self.is_directory() {
|
||||||
|
f::Type::Directory
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
f::Type::Special
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// This file’s permissions, with flags for each bit.
|
/// This file’s permissions, with flags for each bit.
|
||||||
|
#[cfg(unix)]
|
||||||
pub fn permissions(&self) -> f::Permissions {
|
pub fn permissions(&self) -> f::Permissions {
|
||||||
let bits = self.metadata.mode();
|
let bits = self.metadata.mode();
|
||||||
let has_bit = |bit| bits & bit == bit;
|
let has_bit = |bit| bits & bit == bit;
|
||||||
|
@ -420,6 +470,22 @@ impl<'dir> File<'dir> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
pub fn attributes(&self) -> f::Attributes {
|
||||||
|
let bits = self.metadata.file_attributes();
|
||||||
|
let has_bit = |bit| bits & bit == bit;
|
||||||
|
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
|
||||||
|
f::Attributes {
|
||||||
|
directory: has_bit(0x10),
|
||||||
|
archive: has_bit(0x20),
|
||||||
|
readonly: has_bit(0x1),
|
||||||
|
hidden: has_bit(0x2),
|
||||||
|
system: has_bit(0x4),
|
||||||
|
reparse_point: has_bit(0x400),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Whether this file’s extension is any of the strings that get passed in.
|
/// Whether this file’s extension is any of the strings that get passed in.
|
||||||
///
|
///
|
||||||
/// This will always return `false` if the file has no extension.
|
/// This will always return `false` if the file has no extension.
|
||||||
|
@ -477,6 +543,7 @@ impl<'dir> FileTarget<'dir> {
|
||||||
|
|
||||||
/// More readable aliases for the permission bits exposed by libc.
|
/// More readable aliases for the permission bits exposed by libc.
|
||||||
#[allow(trivial_numeric_casts)]
|
#[allow(trivial_numeric_casts)]
|
||||||
|
#[cfg(unix)]
|
||||||
mod modes {
|
mod modes {
|
||||||
|
|
||||||
// The `libc::mode_t` type’s actual type varies, but the value returned
|
// The `libc::mode_t` type’s actual type varies, but the value returned
|
||||||
|
@ -554,6 +621,7 @@ mod filename_test {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(unix)]
|
||||||
fn topmost() {
|
fn topmost() {
|
||||||
assert_eq!("/", File::filename(Path::new("/")))
|
assert_eq!("/", File::filename(Path::new("/")))
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
|
#[cfg(unix)]
|
||||||
use std::os::unix::fs::MetadataExt;
|
use std::os::unix::fs::MetadataExt;
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::fs::DotFilter;
|
use crate::fs::DotFilter;
|
||||||
use crate::fs::File;
|
use crate::fs::File;
|
||||||
|
@ -23,7 +23,7 @@ use crate::fs::File;
|
||||||
/// The filter also governs sorting the list. After being filtered, pairs of
|
/// The filter also governs sorting the list. After being filtered, pairs of
|
||||||
/// files are compared and sorted based on the result, with the sort field
|
/// files are compared and sorted based on the result, with the sort field
|
||||||
/// performing the comparison.
|
/// performing the comparison.
|
||||||
#[derive(PartialEq, Debug, Clone)]
|
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||||
pub struct FileFilter {
|
pub struct FileFilter {
|
||||||
|
|
||||||
/// Whether directories should be listed first, and other types of file
|
/// Whether directories should be listed first, and other types of file
|
||||||
|
@ -50,31 +50,8 @@ pub struct FileFilter {
|
||||||
///
|
///
|
||||||
/// This came about more or less by a complete historical accident,
|
/// This came about more or less by a complete historical accident,
|
||||||
/// when the original `ls` tried to hide `.` and `..`:
|
/// when the original `ls` tried to hide `.` and `..`:
|
||||||
/// https://plus.google.com/+RobPikeTheHuman/posts/R58WgWwN9jp
|
|
||||||
///
|
///
|
||||||
/// When one typed ls, however, these files appeared, so either Ken or
|
/// [Linux History: How Dot Files Became Hidden Files](https://linux-audit.com/linux-history-how-dot-files-became-hidden-files/)
|
||||||
/// Dennis added a simple test to the program. It was in assembler then,
|
|
||||||
/// but the code in question was equivalent to something like this:
|
|
||||||
/// if (name[0] == '.') continue;
|
|
||||||
/// This statement was a little shorter than what it should have been,
|
|
||||||
/// which is:
|
|
||||||
/// if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue;
|
|
||||||
/// but hey, it was easy.
|
|
||||||
///
|
|
||||||
/// Two things resulted.
|
|
||||||
///
|
|
||||||
/// First, a bad precedent was set. A lot of other lazy programmers
|
|
||||||
/// introduced bugs by making the same simplification. Actual files
|
|
||||||
/// beginning with periods are often skipped when they should be counted.
|
|
||||||
///
|
|
||||||
/// Second, and much worse, the idea of a "hidden" or "dot" file was
|
|
||||||
/// created. As a consequence, more lazy programmers started dropping
|
|
||||||
/// files into everyone's home directory. I don't have all that much
|
|
||||||
/// stuff installed on the machine I'm using to type this, but my home
|
|
||||||
/// directory has about a hundred dot files and I don't even know what
|
|
||||||
/// most of them are or whether they're still needed. Every file name
|
|
||||||
/// evaluation that goes through my home directory is slowed down by
|
|
||||||
/// this accumulated sludge.
|
|
||||||
pub dot_filter: DotFilter,
|
pub dot_filter: DotFilter,
|
||||||
|
|
||||||
/// Glob patterns to ignore. Any file name that matches *any* of these
|
/// Glob patterns to ignore. Any file name that matches *any* of these
|
||||||
|
@ -82,9 +59,6 @@ pub struct FileFilter {
|
||||||
pub ignore_patterns: IgnorePatterns,
|
pub ignore_patterns: IgnorePatterns,
|
||||||
|
|
||||||
/// Whether to ignore Git-ignored patterns.
|
/// Whether to ignore Git-ignored patterns.
|
||||||
/// This is implemented completely separately from the actual Git
|
|
||||||
/// repository scanning — a `.gitignore` file will still be scanned even
|
|
||||||
/// if there’s no `.git` folder present.
|
|
||||||
pub git_ignore: GitIgnore,
|
pub git_ignore: GitIgnore,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,7 +89,7 @@ impl FileFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sort the files in the given vector based on the sort field option.
|
/// Sort the files in the given vector based on the sort field option.
|
||||||
pub fn sort_files<'a, F>(&self, files: &mut Vec<F>)
|
pub fn sort_files<'a, F>(&self, files: &mut [F])
|
||||||
where F: AsRef<File<'a>>
|
where F: AsRef<File<'a>>
|
||||||
{
|
{
|
||||||
files.sort_by(|a, b| {
|
files.sort_by(|a, b| {
|
||||||
|
@ -139,7 +113,7 @@ impl FileFilter {
|
||||||
|
|
||||||
|
|
||||||
/// User-supplied field to sort by.
|
/// User-supplied field to sort by.
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
pub enum SortField {
|
pub enum SortField {
|
||||||
|
|
||||||
/// Don’t apply any sorting. This is usually used as an optimisation in
|
/// Don’t apply any sorting. This is usually used as an optimisation in
|
||||||
|
@ -157,6 +131,7 @@ pub enum SortField {
|
||||||
|
|
||||||
/// The file’s inode, which usually corresponds to the order in which
|
/// The file’s inode, which usually corresponds to the order in which
|
||||||
/// files were created on the filesystem, more or less.
|
/// files were created on the filesystem, more or less.
|
||||||
|
#[cfg(unix)]
|
||||||
FileInode,
|
FileInode,
|
||||||
|
|
||||||
/// The time the file was modified (the “mtime”).
|
/// The time the file was modified (the “mtime”).
|
||||||
|
@ -173,7 +148,7 @@ pub enum SortField {
|
||||||
/// slows the whole operation down, so many systems will only update the
|
/// slows the whole operation down, so many systems will only update the
|
||||||
/// timestamp in certain circumstances. This has become common enough that
|
/// timestamp in certain circumstances. This has become common enough that
|
||||||
/// it’s now expected behaviour!
|
/// it’s now expected behaviour!
|
||||||
/// http://unix.stackexchange.com/a/8842
|
/// <https://unix.stackexchange.com/a/8842>
|
||||||
AccessedDate,
|
AccessedDate,
|
||||||
|
|
||||||
/// The time the file was changed (the “ctime”).
|
/// The time the file was changed (the “ctime”).
|
||||||
|
@ -182,7 +157,7 @@ pub enum SortField {
|
||||||
/// changed — its permissions, owners, or link count.
|
/// changed — its permissions, owners, or link count.
|
||||||
///
|
///
|
||||||
/// In original Unix, this was, however, meant as creation time.
|
/// In original Unix, this was, however, meant as creation time.
|
||||||
/// https://www.bell-labs.com/usr/dmr/www/cacm.html
|
/// <https://www.bell-labs.com/usr/dmr/www/cacm.html>
|
||||||
ChangedDate,
|
ChangedDate,
|
||||||
|
|
||||||
/// The time the file was created (the “btime” or “birthtime”).
|
/// The time the file was created (the “btime” or “birthtime”).
|
||||||
|
@ -219,7 +194,7 @@ pub enum SortField {
|
||||||
/// lowercase letters because it takes the difference between the two cases
|
/// lowercase letters because it takes the difference between the two cases
|
||||||
/// into account? I gave up and just named these two variants after the
|
/// into account? I gave up and just named these two variants after the
|
||||||
/// effects they have.
|
/// effects they have.
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
pub enum SortCase {
|
pub enum SortCase {
|
||||||
|
|
||||||
/// Sort files case-sensitively with uppercase first, with ‘A’ coming
|
/// Sort files case-sensitively with uppercase first, with ‘A’ coming
|
||||||
|
@ -250,6 +225,7 @@ impl SortField {
|
||||||
Self::Name(AaBbCc) => natord::compare_ignore_case(&a.name, &b.name),
|
Self::Name(AaBbCc) => natord::compare_ignore_case(&a.name, &b.name),
|
||||||
|
|
||||||
Self::Size => a.metadata.len().cmp(&b.metadata.len()),
|
Self::Size => a.metadata.len().cmp(&b.metadata.len()),
|
||||||
|
#[cfg(unix)]
|
||||||
Self::FileInode => a.metadata.ino().cmp(&b.metadata.ino()),
|
Self::FileInode => a.metadata.ino().cmp(&b.metadata.ino()),
|
||||||
Self::ModifiedDate => a.modified_time().cmp(&b.modified_time()),
|
Self::ModifiedDate => a.modified_time().cmp(&b.modified_time()),
|
||||||
Self::AccessedDate => a.accessed_time().cmp(&b.accessed_time()),
|
Self::AccessedDate => a.accessed_time().cmp(&b.accessed_time()),
|
||||||
|
@ -284,8 +260,10 @@ impl SortField {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn strip_dot(n: &str) -> &str {
|
fn strip_dot(n: &str) -> &str {
|
||||||
if n.starts_with('.') { &n[1..] }
|
match n.strip_prefix('.') {
|
||||||
else { n }
|
Some(s) => s,
|
||||||
|
None => n,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,7 +271,7 @@ impl SortField {
|
||||||
/// The **ignore patterns** are a list of globs that are tested against
|
/// The **ignore patterns** are a list of globs that are tested against
|
||||||
/// each filename, and if any of them match, that file isn’t displayed.
|
/// each filename, and if any of them match, that file isn’t displayed.
|
||||||
/// This lets a user hide, say, text files by ignoring `*.txt`.
|
/// This lets a user hide, say, text files by ignoring `*.txt`.
|
||||||
#[derive(PartialEq, Default, Debug, Clone)]
|
#[derive(PartialEq, Eq, Default, Debug, Clone)]
|
||||||
pub struct IgnorePatterns {
|
pub struct IgnorePatterns {
|
||||||
patterns: Vec<glob::Pattern>,
|
patterns: Vec<glob::Pattern>,
|
||||||
}
|
}
|
||||||
|
@ -345,35 +323,20 @@ impl IgnorePatterns {
|
||||||
fn is_ignored(&self, file: &str) -> bool {
|
fn is_ignored(&self, file: &str) -> bool {
|
||||||
self.patterns.iter().any(|p| p.matches(file))
|
self.patterns.iter().any(|p| p.matches(file))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test whether the given file should be hidden from the results.
|
|
||||||
pub fn is_ignored_path(&self, file: &Path) -> bool {
|
|
||||||
self.patterns.iter().any(|p| p.matches_path(file))
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(ogham): The fact that `is_ignored_path` is pub while `is_ignored`
|
|
||||||
// isn’t probably means it’s in the wrong place
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Whether to ignore or display files that are mentioned in `.gitignore` files.
|
/// Whether to ignore or display files that Git would ignore.
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
pub enum GitIgnore {
|
pub enum GitIgnore {
|
||||||
|
|
||||||
/// Ignore files that Git would ignore. This means doing a check for a
|
/// Ignore files that Git would ignore.
|
||||||
/// `.gitignore` file, possibly recursively up the filesystem tree.
|
|
||||||
CheckAndIgnore,
|
CheckAndIgnore,
|
||||||
|
|
||||||
/// Display files, even if Git would ignore them.
|
/// Display files, even if Git would ignore them.
|
||||||
Off,
|
Off,
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is not fully baked yet. The `ignore` crate lists a lot more files that
|
|
||||||
// we aren’t checking:
|
|
||||||
//
|
|
||||||
// > By default, all ignore files found are respected. This includes .ignore,
|
|
||||||
// > .gitignore, .git/info/exclude and even your global gitignore globs,
|
|
||||||
// > usually found in $XDG_CONFIG_HOME/git/ignore.
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -383,31 +346,31 @@ mod test_ignores {
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_matches_nothing() {
|
fn empty_matches_nothing() {
|
||||||
let pats = IgnorePatterns::empty();
|
let pats = IgnorePatterns::empty();
|
||||||
assert_eq!(false, pats.is_ignored("nothing"));
|
assert!(!pats.is_ignored("nothing"));
|
||||||
assert_eq!(false, pats.is_ignored("test.mp3"));
|
assert!(!pats.is_ignored("test.mp3"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ignores_a_glob() {
|
fn ignores_a_glob() {
|
||||||
let (pats, fails) = IgnorePatterns::parse_from_iter(vec![ "*.mp3" ]);
|
let (pats, fails) = IgnorePatterns::parse_from_iter(vec![ "*.mp3" ]);
|
||||||
assert!(fails.is_empty());
|
assert!(fails.is_empty());
|
||||||
assert_eq!(false, pats.is_ignored("nothing"));
|
assert!(!pats.is_ignored("nothing"));
|
||||||
assert_eq!(true, pats.is_ignored("test.mp3"));
|
assert!(pats.is_ignored("test.mp3"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ignores_an_exact_filename() {
|
fn ignores_an_exact_filename() {
|
||||||
let (pats, fails) = IgnorePatterns::parse_from_iter(vec![ "nothing" ]);
|
let (pats, fails) = IgnorePatterns::parse_from_iter(vec![ "nothing" ]);
|
||||||
assert!(fails.is_empty());
|
assert!(fails.is_empty());
|
||||||
assert_eq!(true, pats.is_ignored("nothing"));
|
assert!(pats.is_ignored("nothing"));
|
||||||
assert_eq!(false, pats.is_ignored("test.mp3"));
|
assert!(!pats.is_ignored("test.mp3"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ignores_both() {
|
fn ignores_both() {
|
||||||
let (pats, fails) = IgnorePatterns::parse_from_iter(vec![ "nothing", "*.mp3" ]);
|
let (pats, fails) = IgnorePatterns::parse_from_iter(vec![ "nothing", "*.mp3" ]);
|
||||||
assert!(fails.is_empty());
|
assert!(fails.is_empty());
|
||||||
assert_eq!(true, pats.is_ignored("nothing"));
|
assert!(pats.is_ignored("nothing"));
|
||||||
assert_eq!(true, pats.is_ignored("test.mp3"));
|
assert!(pats.is_ignored("test.mp3"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ use crate::output::icons::FileIcon;
|
||||||
use crate::theme::FileColours;
|
use crate::theme::FileColours;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq)]
|
#[derive(Debug, Default, PartialEq, Eq)]
|
||||||
pub struct FileExtensions;
|
pub struct FileExtensions;
|
||||||
|
|
||||||
impl FileExtensions {
|
impl FileExtensions {
|
||||||
|
@ -19,13 +19,14 @@ impl FileExtensions {
|
||||||
/// An “immediate” file is something that can be run or activated somehow
|
/// An “immediate” file is something that can be run or activated somehow
|
||||||
/// in order to kick off the build of a project. It’s usually only present
|
/// in order to kick off the build of a project. It’s usually only present
|
||||||
/// in directories full of source code.
|
/// in directories full of source code.
|
||||||
|
#[allow(clippy::case_sensitive_file_extension_comparisons)]
|
||||||
fn is_immediate(&self, file: &File<'_>) -> bool {
|
fn is_immediate(&self, file: &File<'_>) -> bool {
|
||||||
file.name.to_lowercase().starts_with("readme") ||
|
file.name.to_lowercase().starts_with("readme") ||
|
||||||
file.name.ends_with(".ninja") ||
|
file.name.ends_with(".ninja") ||
|
||||||
file.name_is_one_of( &[
|
file.name_is_one_of( &[
|
||||||
"Makefile", "Cargo.toml", "SConstruct", "CMakeLists.txt",
|
"Makefile", "Cargo.toml", "SConstruct", "CMakeLists.txt",
|
||||||
"build.gradle", "pom.xml", "Rakefile", "package.json", "Gruntfile.js",
|
"build.gradle", "pom.xml", "Rakefile", "package.json", "Gruntfile.js",
|
||||||
"Gruntfile.coffee", "BUILD", "BUILD.bazel", "WORKSPACE", "build.xml",
|
"Gruntfile.coffee", "BUILD", "BUILD.bazel", "WORKSPACE", "build.xml", "Podfile",
|
||||||
"webpack.config.js", "meson.build", "composer.json", "RoboFile.php", "PKGBUILD",
|
"webpack.config.js", "meson.build", "composer.json", "RoboFile.php", "PKGBUILD",
|
||||||
"Justfile", "Procfile", "Dockerfile", "Containerfile", "Vagrantfile", "Brewfile",
|
"Justfile", "Procfile", "Dockerfile", "Containerfile", "Vagrantfile", "Brewfile",
|
||||||
"Gemfile", "Pipfile", "build.sbt", "mix.exs", "bsconfig.json", "tsconfig.json",
|
"Gemfile", "Pipfile", "build.sbt", "mix.exs", "bsconfig.json", "tsconfig.json",
|
||||||
|
@ -34,10 +35,11 @@ impl FileExtensions {
|
||||||
|
|
||||||
fn is_image(&self, file: &File<'_>) -> bool {
|
fn is_image(&self, file: &File<'_>) -> bool {
|
||||||
file.extension_is_one_of( &[
|
file.extension_is_one_of( &[
|
||||||
"png", "jpeg", "jpg", "gif", "bmp", "tiff", "tif",
|
"png", "jfi", "jfif", "jif", "jpe", "jpeg", "jpg", "gif", "bmp",
|
||||||
"ppm", "pgm", "pbm", "pnm", "webp", "raw", "arw",
|
"tiff", "tif", "ppm", "pgm", "pbm", "pnm", "webp", "raw", "arw",
|
||||||
"svg", "stl", "eps", "dvi", "ps", "cbr", "jpf",
|
"svg", "stl", "eps", "dvi", "ps", "cbr", "jpf", "cbz", "xpm",
|
||||||
"cbz", "xpm", "ico", "cr2", "orf", "nef", "heif",
|
"ico", "cr2", "orf", "nef", "heif", "avif", "jxl", "j2k", "jp2",
|
||||||
|
"j2c", "jpx",
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,14 +81,14 @@ impl FileExtensions {
|
||||||
file.extension_is_one_of( &[
|
file.extension_is_one_of( &[
|
||||||
"zip", "tar", "Z", "z", "gz", "bz2", "a", "ar", "7z",
|
"zip", "tar", "Z", "z", "gz", "bz2", "a", "ar", "7z",
|
||||||
"iso", "dmg", "tc", "rar", "par", "tgz", "xz", "txz",
|
"iso", "dmg", "tc", "rar", "par", "tgz", "xz", "txz",
|
||||||
"lz", "tlz", "lzma", "deb", "rpm", "zst",
|
"lz", "tlz", "lzma", "deb", "rpm", "zst", "lz4", "cpio",
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_temp(&self, file: &File<'_>) -> bool {
|
fn is_temp(&self, file: &File<'_>) -> bool {
|
||||||
file.name.ends_with('~')
|
file.name.ends_with('~')
|
||||||
|| (file.name.starts_with('#') && file.name.ends_with('#'))
|
|| (file.name.starts_with('#') && file.name.ends_with('#'))
|
||||||
|| file.extension_is_one_of( &[ "tmp", "swp", "swo", "swn", "bak", "bk" ])
|
|| file.extension_is_one_of( &[ "tmp", "swp", "swo", "swn", "bak", "bkp", "bk" ])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_compiled(&self, file: &File<'_>) -> bool {
|
fn is_compiled(&self, file: &File<'_>) -> bool {
|
||||||
|
|
28
src/main.rs
28
src/main.rs
|
@ -7,18 +7,19 @@
|
||||||
#![warn(unused)]
|
#![warn(unused)]
|
||||||
|
|
||||||
#![warn(clippy::all, clippy::pedantic)]
|
#![warn(clippy::all, clippy::pedantic)]
|
||||||
|
#![allow(clippy::cast_precision_loss)]
|
||||||
|
#![allow(clippy::cast_possible_truncation)]
|
||||||
|
#![allow(clippy::cast_possible_wrap)]
|
||||||
|
#![allow(clippy::cast_sign_loss)]
|
||||||
#![allow(clippy::enum_glob_use)]
|
#![allow(clippy::enum_glob_use)]
|
||||||
#![allow(clippy::find_map)]
|
|
||||||
#![allow(clippy::map_unwrap_or)]
|
#![allow(clippy::map_unwrap_or)]
|
||||||
#![allow(clippy::match_same_arms)]
|
#![allow(clippy::match_same_arms)]
|
||||||
#![allow(clippy::missing_const_for_fn)]
|
|
||||||
#![allow(clippy::missing_errors_doc)]
|
|
||||||
#![allow(clippy::module_name_repetitions)]
|
#![allow(clippy::module_name_repetitions)]
|
||||||
#![allow(clippy::must_use_candidate)]
|
|
||||||
#![allow(clippy::non_ascii_literal)]
|
#![allow(clippy::non_ascii_literal)]
|
||||||
#![allow(clippy::option_if_let_else)]
|
#![allow(clippy::option_if_let_else)]
|
||||||
#![allow(clippy::too_many_lines)]
|
#![allow(clippy::too_many_lines)]
|
||||||
#![allow(clippy::unused_self)]
|
#![allow(clippy::unused_self)]
|
||||||
|
#![allow(clippy::upper_case_acronyms)]
|
||||||
#![allow(clippy::wildcard_imports)]
|
#![allow(clippy::wildcard_imports)]
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
|
@ -48,10 +49,20 @@ mod theme;
|
||||||
fn main() {
|
fn main() {
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
unsafe {
|
||||||
|
libc::signal(libc::SIGPIPE, libc::SIG_DFL);
|
||||||
|
}
|
||||||
|
|
||||||
logger::configure(env::var_os(vars::EXA_DEBUG));
|
logger::configure(env::var_os(vars::EXA_DEBUG));
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
if let Err(e) = ansi_term::enable_ansi_support() {
|
||||||
|
warn!("Failed to enable ANSI support: {}", e);
|
||||||
|
}
|
||||||
|
|
||||||
let args: Vec<_> = env::args_os().skip(1).collect();
|
let args: Vec<_> = env::args_os().skip(1).collect();
|
||||||
match Options::parse(args.iter().map(|e| e.as_ref()), &LiveVars) {
|
match Options::parse(args.iter().map(std::convert::AsRef::as_ref), &LiveVars) {
|
||||||
OptionsResult::Ok(options, mut input_paths) => {
|
OptionsResult::Ok(options, mut input_paths) => {
|
||||||
|
|
||||||
// List the current directory by default.
|
// List the current directory by default.
|
||||||
|
@ -155,6 +166,9 @@ fn git_options(options: &Options, args: &[&OsStr]) -> Option<GitCache> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'args> Exa<'args> {
|
impl<'args> Exa<'args> {
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Will return `Err` if printing to stderr fails.
|
||||||
pub fn run(mut self) -> io::Result<i32> {
|
pub fn run(mut self) -> io::Result<i32> {
|
||||||
debug!("Running with options: {:#?}", self.options);
|
debug!("Running with options: {:#?}", self.options);
|
||||||
|
|
||||||
|
@ -282,7 +296,7 @@ impl<'args> Exa<'args> {
|
||||||
|
|
||||||
let git_ignoring = self.options.filter.git_ignore == GitIgnore::CheckAndIgnore;
|
let git_ignoring = self.options.filter.git_ignore == GitIgnore::CheckAndIgnore;
|
||||||
let git = self.git.as_ref();
|
let git = self.git.as_ref();
|
||||||
let r = details::Render { dir, files, theme, file_style, opts, filter, recurse, git_ignoring, git };
|
let r = details::Render { dir, files, theme, file_style, opts, recurse, filter, git_ignoring, git };
|
||||||
r.render(&mut self.writer)
|
r.render(&mut self.writer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,7 +320,7 @@ impl<'args> Exa<'args> {
|
||||||
let git_ignoring = self.options.filter.git_ignore == GitIgnore::CheckAndIgnore;
|
let git_ignoring = self.options.filter.git_ignore == GitIgnore::CheckAndIgnore;
|
||||||
|
|
||||||
let git = self.git.as_ref();
|
let git = self.git.as_ref();
|
||||||
let r = details::Render { dir, files, theme, file_style, opts, filter, recurse, git_ignoring, git };
|
let r = details::Render { dir, files, theme, file_style, opts, recurse, filter, git_ignoring, git };
|
||||||
r.render(&mut self.writer)
|
r.render(&mut self.writer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//! Parsing the options for `DirAction`.
|
//! Parsing the options for `DirAction`.
|
||||||
|
|
||||||
use crate::options::parser::MatchedFlags;
|
use crate::options::parser::MatchedFlags;
|
||||||
use crate::options::{flags, OptionsError};
|
use crate::options::{flags, OptionsError, NumberSource};
|
||||||
|
|
||||||
use crate::fs::dir_action::{DirAction, RecurseOptions};
|
use crate::fs::dir_action::{DirAction, RecurseOptions};
|
||||||
|
|
||||||
|
@ -55,17 +55,21 @@ impl RecurseOptions {
|
||||||
/// determined earlier. The maximum level should be a number, and this
|
/// determined earlier. The maximum level should be a number, and this
|
||||||
/// will fail with an `Err` if it isn’t.
|
/// will fail with an `Err` if it isn’t.
|
||||||
pub fn deduce(matches: &MatchedFlags<'_>, tree: bool) -> Result<Self, OptionsError> {
|
pub fn deduce(matches: &MatchedFlags<'_>, tree: bool) -> Result<Self, OptionsError> {
|
||||||
let max_depth = if let Some(level) = matches.get(&flags::LEVEL)? {
|
if let Some(level) = matches.get(&flags::LEVEL)? {
|
||||||
match level.to_string_lossy().parse() {
|
let arg_str = level.to_string_lossy();
|
||||||
Ok(l) => Some(l),
|
match arg_str.parse() {
|
||||||
Err(e) => return Err(OptionsError::FailedParse(e)),
|
Ok(l) => {
|
||||||
|
Ok(Self { tree, max_depth: Some(l) })
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
let source = NumberSource::Arg(&flags::LEVEL);
|
||||||
|
Err(OptionsError::FailedParse(arg_str.to_string(), source, e))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
None
|
Ok(Self { tree, max_depth: None })
|
||||||
};
|
}
|
||||||
|
|
||||||
Ok(Self { tree, max_depth })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ use crate::options::parser::{Arg, Flag, ParseError};
|
||||||
|
|
||||||
|
|
||||||
/// Something wrong with the combination of options the user has picked.
|
/// Something wrong with the combination of options the user has picked.
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
pub enum OptionsError {
|
pub enum OptionsError {
|
||||||
|
|
||||||
/// There was an error (from `getopts`) parsing the arguments.
|
/// There was an error (from `getopts`) parsing the arguments.
|
||||||
|
@ -37,18 +37,38 @@ pub enum OptionsError {
|
||||||
TreeAllAll,
|
TreeAllAll,
|
||||||
|
|
||||||
/// A numeric option was given that failed to be parsed as a number.
|
/// A numeric option was given that failed to be parsed as a number.
|
||||||
FailedParse(ParseIntError),
|
FailedParse(String, NumberSource, ParseIntError),
|
||||||
|
|
||||||
/// A glob ignore was given that failed to be parsed as a pattern.
|
/// A glob ignore was given that failed to be parsed as a pattern.
|
||||||
FailedGlobPattern(String),
|
FailedGlobPattern(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The source of a string that failed to be parsed as a number.
|
||||||
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
|
pub enum NumberSource {
|
||||||
|
|
||||||
|
/// It came... from a command-line argument!
|
||||||
|
Arg(&'static Arg),
|
||||||
|
|
||||||
|
/// It came... from the enviroment!
|
||||||
|
Env(&'static str),
|
||||||
|
}
|
||||||
|
|
||||||
impl From<glob::PatternError> for OptionsError {
|
impl From<glob::PatternError> for OptionsError {
|
||||||
fn from(error: glob::PatternError) -> Self {
|
fn from(error: glob::PatternError) -> Self {
|
||||||
Self::FailedGlobPattern(error.to_string())
|
Self::FailedGlobPattern(error.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for NumberSource {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Arg(arg) => write!(f, "option {}", arg),
|
||||||
|
Self::Env(env) => write!(f, "environment variable {}", env),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for OptionsError {
|
impl fmt::Display for OptionsError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
use crate::options::parser::TakesValue;
|
use crate::options::parser::TakesValue;
|
||||||
|
@ -71,7 +91,7 @@ impl fmt::Display for OptionsError {
|
||||||
Self::Useless(a, true, b) => write!(f, "Option {} is useless given option {}", a, b),
|
Self::Useless(a, true, b) => write!(f, "Option {} is useless given option {}", a, b),
|
||||||
Self::Useless2(a, b1, b2) => write!(f, "Option {} is useless without options {} or {}", a, b1, b2),
|
Self::Useless2(a, b1, b2) => write!(f, "Option {} is useless without options {} or {}", a, b1, b2),
|
||||||
Self::TreeAllAll => write!(f, "Option --tree is useless given --all --all"),
|
Self::TreeAllAll => write!(f, "Option --tree is useless given --all --all"),
|
||||||
Self::FailedParse(ref e) => write!(f, "Failed to parse number: {}", e),
|
Self::FailedParse(s, n, e) => write!(f, "Value {:?} not valid for {}: {}", s, n, e),
|
||||||
Self::FailedGlobPattern(ref e) => write!(f, "Failed to parse glob pattern: {}", e),
|
Self::FailedGlobPattern(ref e) => write!(f, "Failed to parse glob pattern: {}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,7 +119,7 @@ impl OptionsError {
|
||||||
|
|
||||||
|
|
||||||
/// A list of legal choices for an argument-taking option.
|
/// A list of legal choices for an argument-taking option.
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
pub struct Choices(pub &'static [&'static str]);
|
pub struct Choices(pub &'static [&'static str]);
|
||||||
|
|
||||||
impl fmt::Display for Choices {
|
impl fmt::Display for Choices {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::options::{flags, OptionsError};
|
use crate::options::{flags, OptionsError, NumberSource};
|
||||||
use crate::options::parser::MatchedFlags;
|
use crate::options::parser::MatchedFlags;
|
||||||
use crate::options::vars::{self, Vars};
|
use crate::options::vars::{self, Vars};
|
||||||
|
|
||||||
|
@ -30,8 +30,13 @@ impl ShowIcons {
|
||||||
}
|
}
|
||||||
else if let Some(columns) = vars.get(vars::EXA_ICON_SPACING).and_then(|s| s.into_string().ok()) {
|
else if let Some(columns) = vars.get(vars::EXA_ICON_SPACING).and_then(|s| s.into_string().ok()) {
|
||||||
match columns.parse() {
|
match columns.parse() {
|
||||||
Ok(width) => Ok(Self::On(width)),
|
Ok(width) => {
|
||||||
Err(e) => Err(OptionsError::FailedParse(e)),
|
Ok(Self::On(width))
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
let source = NumberSource::Env(vars::EXA_ICON_SPACING);
|
||||||
|
Err(OptionsError::FailedParse(columns, source, e))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -88,6 +88,7 @@ impl SortField {
|
||||||
"cr" | "created" => {
|
"cr" | "created" => {
|
||||||
Self::CreatedDate
|
Self::CreatedDate
|
||||||
}
|
}
|
||||||
|
#[cfg(unix)]
|
||||||
"inode" => {
|
"inode" => {
|
||||||
Self::FileInode
|
Self::FileInode
|
||||||
}
|
}
|
||||||
|
@ -294,7 +295,6 @@ mod test {
|
||||||
mod ignore_patterns {
|
mod ignore_patterns {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
use glob;
|
|
||||||
|
|
||||||
fn pat(string: &'static str) -> glob::Pattern {
|
fn pat(string: &'static str) -> glob::Pattern {
|
||||||
glob::Pattern::new(string).unwrap()
|
glob::Pattern::new(string).unwrap()
|
||||||
|
|
|
@ -27,7 +27,7 @@ DISPLAY OPTIONS
|
||||||
|
|
||||||
FILTERING AND SORTING OPTIONS
|
FILTERING AND SORTING OPTIONS
|
||||||
-a, --all show hidden and 'dot' files
|
-a, --all show hidden and 'dot' files
|
||||||
-d, --list-dirs list directories like regular files
|
-d, --list-dirs list directories as files; don't list their contents
|
||||||
-L, --level DEPTH limit the depth of recursion
|
-L, --level DEPTH limit the depth of recursion
|
||||||
-r, --reverse reverse the sort order
|
-r, --reverse reverse the sort order
|
||||||
-s, --sort SORT_FIELD which field to sort by
|
-s, --sort SORT_FIELD which field to sort by
|
||||||
|
@ -69,7 +69,7 @@ static EXTENDED_HELP: &str = " -@, --extended list each file's extended
|
||||||
/// All the information needed to display the help text, which depends
|
/// All the information needed to display the help text, which depends
|
||||||
/// on which features are enabled and whether the user only wants to
|
/// on which features are enabled and whether the user only wants to
|
||||||
/// see one section’s help.
|
/// see one section’s help.
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
pub struct HelpString;
|
pub struct HelpString;
|
||||||
|
|
||||||
impl HelpString {
|
impl HelpString {
|
||||||
|
|
|
@ -84,7 +84,7 @@ mod theme;
|
||||||
mod view;
|
mod view;
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
pub use self::error::OptionsError;
|
pub use self::error::{OptionsError, NumberSource};
|
||||||
|
|
||||||
mod help;
|
mod help;
|
||||||
use self::help::HelpString;
|
use self::help::HelpString;
|
||||||
|
@ -178,7 +178,7 @@ impl Options {
|
||||||
fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
|
fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
|
||||||
if cfg!(not(feature = "git")) &&
|
if cfg!(not(feature = "git")) &&
|
||||||
matches.has_where_any(|f| f.matches(&flags::GIT) || f.matches(&flags::GIT_IGNORE)).is_some() {
|
matches.has_where_any(|f| f.matches(&flags::GIT) || f.matches(&flags::GIT_IGNORE)).is_some() {
|
||||||
return Err(OptionsError::Unsupported(format!(
|
return Err(OptionsError::Unsupported(String::from(
|
||||||
"Options --git and --git-ignore can't be used because `git` feature was disabled in this build of exa"
|
"Options --git and --git-ignore can't be used because `git` feature was disabled in this build of exa"
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
@ -216,7 +216,7 @@ pub mod test {
|
||||||
use crate::options::parser::{Arg, MatchedFlags};
|
use crate::options::parser::{Arg, MatchedFlags};
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
pub enum Strictnesses {
|
pub enum Strictnesses {
|
||||||
Last,
|
Last,
|
||||||
Complain,
|
Complain,
|
||||||
|
@ -228,14 +228,14 @@ pub mod test {
|
||||||
/// both, then both should resolve to the same result.
|
/// both, then both should resolve to the same result.
|
||||||
///
|
///
|
||||||
/// It returns a vector with one or two elements in.
|
/// It returns a vector with one or two elements in.
|
||||||
/// These elements can then be tested with assert_eq or what have you.
|
/// These elements can then be tested with `assert_eq` or what have you.
|
||||||
pub fn parse_for_test<T, F>(inputs: &[&str], args: &'static [&'static Arg], strictnesses: Strictnesses, get: F) -> Vec<T>
|
pub fn parse_for_test<T, F>(inputs: &[&str], args: &'static [&'static Arg], strictnesses: Strictnesses, get: F) -> Vec<T>
|
||||||
where F: Fn(&MatchedFlags<'_>) -> T
|
where F: Fn(&MatchedFlags<'_>) -> T
|
||||||
{
|
{
|
||||||
use self::Strictnesses::*;
|
use self::Strictnesses::*;
|
||||||
use crate::options::parser::{Args, Strictness};
|
use crate::options::parser::{Args, Strictness};
|
||||||
|
|
||||||
let bits = inputs.into_iter().map(OsStr::new).collect::<Vec<_>>();
|
let bits = inputs.iter().map(OsStr::new).collect::<Vec<_>>();
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
|
|
||||||
if strictnesses == Last || strictnesses == Both {
|
if strictnesses == Last || strictnesses == Both {
|
||||||
|
|
|
@ -52,7 +52,7 @@ pub type Values = &'static [&'static str];
|
||||||
|
|
||||||
/// A **flag** is either of the two argument types, because they have to
|
/// A **flag** is either of the two argument types, because they have to
|
||||||
/// be in the same array together.
|
/// be in the same array together.
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
pub enum Flag {
|
pub enum Flag {
|
||||||
Short(ShortArg),
|
Short(ShortArg),
|
||||||
Long(LongArg),
|
Long(LongArg),
|
||||||
|
@ -77,7 +77,7 @@ impl fmt::Display for Flag {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether redundant arguments should be considered a problem.
|
/// Whether redundant arguments should be considered a problem.
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
pub enum Strictness {
|
pub enum Strictness {
|
||||||
|
|
||||||
/// Throw an error when an argument doesn’t do anything, either because
|
/// Throw an error when an argument doesn’t do anything, either because
|
||||||
|
@ -91,7 +91,7 @@ pub enum Strictness {
|
||||||
|
|
||||||
/// Whether a flag takes a value. This is applicable to both long and short
|
/// Whether a flag takes a value. This is applicable to both long and short
|
||||||
/// arguments.
|
/// arguments.
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
pub enum TakesValue {
|
pub enum TakesValue {
|
||||||
|
|
||||||
/// This flag has to be followed by a value.
|
/// This flag has to be followed by a value.
|
||||||
|
@ -108,7 +108,7 @@ pub enum TakesValue {
|
||||||
|
|
||||||
|
|
||||||
/// An **argument** can be matched by one of the user’s input strings.
|
/// An **argument** can be matched by one of the user’s input strings.
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
pub struct Arg {
|
pub struct Arg {
|
||||||
|
|
||||||
/// The short argument that matches it, if any.
|
/// The short argument that matches it, if any.
|
||||||
|
@ -136,7 +136,7 @@ impl fmt::Display for Arg {
|
||||||
|
|
||||||
|
|
||||||
/// Literally just several args.
|
/// Literally just several args.
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
pub struct Args(pub &'static [&'static Arg]);
|
pub struct Args(pub &'static [&'static Arg]);
|
||||||
|
|
||||||
impl Args {
|
impl Args {
|
||||||
|
@ -146,8 +146,6 @@ impl Args {
|
||||||
pub fn parse<'args, I>(&self, inputs: I, strictness: Strictness) -> Result<Matches<'args>, ParseError>
|
pub fn parse<'args, I>(&self, inputs: I, strictness: Strictness) -> Result<Matches<'args>, ParseError>
|
||||||
where I: IntoIterator<Item = &'args OsStr>
|
where I: IntoIterator<Item = &'args OsStr>
|
||||||
{
|
{
|
||||||
use std::os::unix::ffi::OsStrExt;
|
|
||||||
|
|
||||||
let mut parsing = true;
|
let mut parsing = true;
|
||||||
|
|
||||||
// The results that get built up.
|
// The results that get built up.
|
||||||
|
@ -159,7 +157,7 @@ impl Args {
|
||||||
// doesn’t have one in its string so it needs the next one.
|
// 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();
|
||||||
while let Some(arg) = inputs.next() {
|
while let Some(arg) = inputs.next() {
|
||||||
let bytes = arg.as_bytes();
|
let bytes = os_str_to_bytes(arg);
|
||||||
|
|
||||||
// Stop parsing if one of the arguments is the literal string “--”.
|
// Stop parsing if one of the arguments is the literal string “--”.
|
||||||
// This allows a file named “--arg” to be specified by passing in
|
// This allows a file named “--arg” to be specified by passing in
|
||||||
|
@ -174,7 +172,7 @@ impl Args {
|
||||||
|
|
||||||
// If the string starts with *two* dashes then it’s a long argument.
|
// If the string starts with *two* dashes then it’s a long argument.
|
||||||
else if bytes.starts_with(b"--") {
|
else if bytes.starts_with(b"--") {
|
||||||
let long_arg_name = OsStr::from_bytes(&bytes[2..]);
|
let long_arg_name = bytes_to_os_str(&bytes[2..]);
|
||||||
|
|
||||||
// If there’s an equals in it, then the string before the
|
// If there’s an equals in it, then the string before the
|
||||||
// equals will be the flag’s name, and the string after it
|
// equals will be the flag’s name, and the string after it
|
||||||
|
@ -221,7 +219,7 @@ impl Args {
|
||||||
// If the string starts with *one* dash then it’s one or more
|
// If the string starts with *one* dash then it’s one or more
|
||||||
// short arguments.
|
// short arguments.
|
||||||
else if bytes.starts_with(b"-") && arg != "-" {
|
else if bytes.starts_with(b"-") && arg != "-" {
|
||||||
let short_arg = OsStr::from_bytes(&bytes[1..]);
|
let short_arg = bytes_to_os_str(&bytes[1..]);
|
||||||
|
|
||||||
// If there’s an equals in it, then the argument immediately
|
// If there’s an equals in it, then the argument immediately
|
||||||
// before the equals was the one that has the value, with the
|
// before the equals was the one that has the value, with the
|
||||||
|
@ -236,7 +234,7 @@ impl Args {
|
||||||
// it’s an error if any of the first set of arguments actually
|
// it’s an error if any of the first set of arguments actually
|
||||||
// takes a value.
|
// takes a value.
|
||||||
if let Some((before, after)) = split_on_equals(short_arg) {
|
if let Some((before, after)) = split_on_equals(short_arg) {
|
||||||
let (arg_with_value, other_args) = before.as_bytes().split_last().unwrap();
|
let (arg_with_value, other_args) = os_str_to_bytes(before).split_last().unwrap();
|
||||||
|
|
||||||
// Process the characters immediately following the dash...
|
// Process the characters immediately following the dash...
|
||||||
for byte in other_args {
|
for byte in other_args {
|
||||||
|
@ -291,7 +289,7 @@ impl Args {
|
||||||
TakesValue::Optional(values) => {
|
TakesValue::Optional(values) => {
|
||||||
if index < bytes.len() - 1 {
|
if index < bytes.len() - 1 {
|
||||||
let remnants = &bytes[index+1 ..];
|
let remnants = &bytes[index+1 ..];
|
||||||
result_flags.push((flag, Some(OsStr::from_bytes(remnants))));
|
result_flags.push((flag, Some(bytes_to_os_str(remnants))));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if let Some(next_arg) = inputs.next() {
|
else if let Some(next_arg) = inputs.next() {
|
||||||
|
@ -342,7 +340,7 @@ impl Args {
|
||||||
|
|
||||||
|
|
||||||
/// The **matches** are the result of parsing the user’s command-line strings.
|
/// The **matches** are the result of parsing the user’s command-line strings.
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
pub struct Matches<'args> {
|
pub struct Matches<'args> {
|
||||||
|
|
||||||
/// The flags that were parsed from the user’s input.
|
/// The flags that were parsed from the user’s input.
|
||||||
|
@ -353,7 +351,7 @@ pub struct Matches<'args> {
|
||||||
pub frees: Vec<&'args OsStr>,
|
pub frees: Vec<&'args OsStr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
pub struct MatchedFlags<'args> {
|
pub struct MatchedFlags<'args> {
|
||||||
|
|
||||||
/// The individual flags from the user’s input, in the order they were
|
/// The individual flags from the user’s input, in the order they were
|
||||||
|
@ -432,7 +430,7 @@ impl<'a> MatchedFlags<'a> {
|
||||||
.filter(|tuple| tuple.1.is_some() && predicate(&tuple.0))
|
.filter(|tuple| tuple.1.is_some() && predicate(&tuple.0))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
if those.len() < 2 { Ok(those.first().cloned().map(|t| t.1.unwrap())) }
|
if those.len() < 2 { Ok(those.first().copied().map(|t| t.1.unwrap())) }
|
||||||
else { Err(OptionsError::Duplicate(those[0].0, those[1].0)) }
|
else { Err(OptionsError::Duplicate(those[0].0, those[1].0)) }
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -464,7 +462,7 @@ impl<'a> MatchedFlags<'a> {
|
||||||
|
|
||||||
/// A problem with the user’s input that meant it couldn’t be parsed into a
|
/// A problem with the user’s input that meant it couldn’t be parsed into a
|
||||||
/// coherent list of arguments.
|
/// coherent list of arguments.
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
pub enum ParseError {
|
pub enum ParseError {
|
||||||
|
|
||||||
/// A flag that has to take a value was not given one.
|
/// A flag that has to take a value was not given one.
|
||||||
|
@ -495,19 +493,42 @@ impl fmt::Display for ParseError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn os_str_to_bytes<'b>(s: &'b OsStr) -> &'b [u8]{
|
||||||
|
use std::os::unix::ffi::OsStrExt;
|
||||||
|
|
||||||
|
return s.as_bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn bytes_to_os_str<'b>(b: &'b [u8]) -> &'b OsStr{
|
||||||
|
use std::os::unix::ffi::OsStrExt;
|
||||||
|
|
||||||
|
return OsStr::from_bytes(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn os_str_to_bytes<'b>(s: &'b OsStr) -> &'b [u8]{
|
||||||
|
return s.to_str().unwrap().as_bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn bytes_to_os_str<'b>(b: &'b [u8]) -> &'b OsStr{
|
||||||
|
use std::str;
|
||||||
|
|
||||||
|
return OsStr::new(str::from_utf8(b).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
/// Splits a string on its `=` character, returning the two substrings on
|
/// Splits a string on its `=` character, returning the two substrings on
|
||||||
/// either side. Returns `None` if there’s no equals or a string is missing.
|
/// either side. Returns `None` if there’s no equals or a string is missing.
|
||||||
fn split_on_equals(input: &OsStr) -> Option<(&OsStr, &OsStr)> {
|
fn split_on_equals(input: &OsStr) -> Option<(&OsStr, &OsStr)> {
|
||||||
use std::os::unix::ffi::OsStrExt;
|
if let Some(index) = os_str_to_bytes(input).iter().position(|elem| *elem == b'=') {
|
||||||
|
let (before, after) = os_str_to_bytes(input).split_at(index);
|
||||||
if let Some(index) = input.as_bytes().iter().position(|elem| *elem == b'=') {
|
|
||||||
let (before, after) = input.as_bytes().split_at(index);
|
|
||||||
|
|
||||||
// The after string contains the = that we need to remove.
|
// The after string contains the = that we need to remove.
|
||||||
if ! before.is_empty() && after.len() >= 2 {
|
if ! before.is_empty() && after.len() >= 2 {
|
||||||
return Some((OsStr::from_bytes(before),
|
return Some((bytes_to_os_str(before),
|
||||||
OsStr::from_bytes(&after[1..])))
|
bytes_to_os_str(&after[1..])))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -722,6 +743,6 @@ mod matches_test {
|
||||||
fn no_count() {
|
fn no_count() {
|
||||||
let flags = MatchedFlags { flags: Vec::new(), strictness: Strictness::UseLastArguments };
|
let flags = MatchedFlags { flags: Vec::new(), strictness: Strictness::UseLastArguments };
|
||||||
|
|
||||||
assert_eq!(flags.has(&COUNT).unwrap(), false);
|
assert!(!flags.has(&COUNT).unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::theme::{Options, UseColours, ColourScale, Definitions};
|
||||||
|
|
||||||
impl Options {
|
impl Options {
|
||||||
pub fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
|
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 colour_scale = ColourScale::deduce(matches)?;
|
||||||
|
|
||||||
let definitions = if use_colours == UseColours::Never {
|
let definitions = if use_colours == UseColours::Never {
|
||||||
|
@ -21,10 +21,15 @@ impl Options {
|
||||||
|
|
||||||
|
|
||||||
impl UseColours {
|
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))? {
|
let word = match matches.get_where(|f| f.matches(&flags::COLOR) || f.matches(&flags::COLOUR))? {
|
||||||
Some(w) => w,
|
Some(w) => w,
|
||||||
None => return Ok(Self::Automatic),
|
None => return Ok(default_value),
|
||||||
};
|
};
|
||||||
|
|
||||||
if word == "always" {
|
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) => {
|
($name:ident: $type:ident <- $inputs:expr; $stricts:expr => err $result:expr) => {
|
||||||
#[test]
|
#[test]
|
||||||
fn $name() {
|
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 {
|
struct MockVars {
|
||||||
ls: &'static str,
|
ls: &'static str,
|
||||||
exa: &'static str,
|
exa: &'static str,
|
||||||
|
no_color: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MockVars {
|
||||||
|
fn empty() -> MockVars {
|
||||||
|
MockVars {
|
||||||
|
ls: "",
|
||||||
|
exa: "",
|
||||||
|
no_color: "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn with_no_color() -> MockVars {
|
||||||
|
MockVars {
|
||||||
|
ls: "",
|
||||||
|
exa: "",
|
||||||
|
no_color: "true",
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test impl that just returns the value it has.
|
// 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() {
|
else if name == vars::EXA_COLORS && ! self.exa.is_empty() {
|
||||||
Some(OsString::from(self.exa.clone()))
|
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 {
|
else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -120,32 +166,33 @@ mod terminal_test {
|
||||||
|
|
||||||
|
|
||||||
// Default
|
// 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
|
// --colour
|
||||||
test!(u_always: UseColours <- ["--colour=always"]; Both => Ok(UseColours::Always));
|
test!(u_always: UseColours <- ["--colour=always"], MockVars::empty(); Both => Ok(UseColours::Always));
|
||||||
test!(u_auto: UseColours <- ["--colour", "auto"]; Both => Ok(UseColours::Automatic));
|
test!(u_auto: UseColours <- ["--colour", "auto"], MockVars::empty(); Both => Ok(UseColours::Automatic));
|
||||||
test!(u_never: UseColours <- ["--colour=never"]; Both => Ok(UseColours::Never));
|
test!(u_never: UseColours <- ["--colour=never"], MockVars::empty(); Both => Ok(UseColours::Never));
|
||||||
|
|
||||||
// --color
|
// --color
|
||||||
test!(no_u_always: UseColours <- ["--color", "always"]; Both => Ok(UseColours::Always));
|
test!(no_u_always: UseColours <- ["--color", "always"], MockVars::empty(); Both => Ok(UseColours::Always));
|
||||||
test!(no_u_auto: UseColours <- ["--color=auto"]; Both => Ok(UseColours::Automatic));
|
test!(no_u_auto: UseColours <- ["--color=auto"], MockVars::empty(); Both => Ok(UseColours::Automatic));
|
||||||
test!(no_u_never: UseColours <- ["--color", "never"]; Both => Ok(UseColours::Never));
|
test!(no_u_never: UseColours <- ["--color", "never"], MockVars::empty(); Both => Ok(UseColours::Never));
|
||||||
|
|
||||||
// Errors
|
// Errors
|
||||||
test!(no_u_error: UseColours <- ["--color=upstream"]; Both => err OptionsError::BadArgument(&flags::COLOR, OsString::from("upstream"))); // the error is for --color
|
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"]; Both => err OptionsError::BadArgument(&flags::COLOR, OsString::from("lovers"))); // and so is this one!
|
test!(u_error: UseColours <- ["--colour=lovers"], MockVars::empty(); Both => err OptionsError::BadArgument(&flags::COLOR, OsString::from("lovers"))); // and so is this one!
|
||||||
|
|
||||||
// Overriding
|
// Overriding
|
||||||
test!(overridden_1: UseColours <- ["--colour=auto", "--colour=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"]; 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"]; 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"]; 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_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"]; Complain => err OptionsError::Duplicate(Flag::Long("color"), 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"]; Complain => err OptionsError::Duplicate(Flag::Long("colour"), Flag::Long("color")));
|
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"]; Complain => err OptionsError::Duplicate(Flag::Long("color"), 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_1: ColourScale <- ["--color-scale", "--colour-scale"]; Last => Ok(ColourScale::Gradient));
|
||||||
test!(scale_2: ColourScale <- ["--color-scale", ]; Last => Ok(ColourScale::Gradient));
|
test!(scale_2: ColourScale <- ["--color-scale", ]; Last => Ok(ColourScale::Gradient));
|
||||||
|
|
|
@ -15,6 +15,9 @@ pub static COLUMNS: &str = "COLUMNS";
|
||||||
/// Environment variable used to datetime format.
|
/// Environment variable used to datetime format.
|
||||||
pub static TIME_STYLE: &str = "TIME_STYLE";
|
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
|
// exa-specific variables
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::options::flags;
|
||||||
use crate::options::parser::MatchedFlags;
|
use crate::options::parser::MatchedFlags;
|
||||||
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
pub struct VersionString;
|
pub struct VersionString;
|
||||||
// There were options here once, but there aren’t anymore!
|
// There were options here once, but there aren’t anymore!
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::fs::feature::xattr;
|
use crate::fs::feature::xattr;
|
||||||
use crate::options::{flags, OptionsError, Vars};
|
use crate::options::{flags, OptionsError, NumberSource, Vars};
|
||||||
use crate::options::parser::MatchedFlags;
|
use crate::options::parser::MatchedFlags;
|
||||||
use crate::output::{View, Mode, TerminalWidth, grid, details};
|
use crate::output::{View, Mode, TerminalWidth, grid, details};
|
||||||
use crate::output::grid_details::{self, RowThreshold};
|
use crate::output::grid_details::{self, RowThreshold};
|
||||||
|
@ -32,13 +32,10 @@ impl Mode {
|
||||||
let flag = matches.has_where_any(|f| f.matches(&flags::LONG) || f.matches(&flags::ONE_LINE)
|
let flag = matches.has_where_any(|f| f.matches(&flags::LONG) || f.matches(&flags::ONE_LINE)
|
||||||
|| f.matches(&flags::GRID) || f.matches(&flags::TREE));
|
|| f.matches(&flags::GRID) || f.matches(&flags::TREE));
|
||||||
|
|
||||||
let flag = match flag {
|
let flag = if let Some(f) = flag { f } else {
|
||||||
Some(f) => f,
|
Self::strict_check_long_flags(matches)?;
|
||||||
None => {
|
let grid = grid::Options::deduce(matches)?;
|
||||||
Self::strict_check_long_flags(matches)?;
|
return Ok(Self::Grid(grid));
|
||||||
let grid = grid::Options::deduce(matches)?;
|
|
||||||
return Ok(Self::Grid(grid));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if flag.matches(&flags::LONG)
|
if flag.matches(&flags::LONG)
|
||||||
|
@ -57,10 +54,9 @@ impl Mode {
|
||||||
let grid_details = grid_details::Options { grid, details, row_threshold };
|
let grid_details = grid_details::Options { grid, details, row_threshold };
|
||||||
return Ok(Self::GridDetails(grid_details));
|
return Ok(Self::GridDetails(grid_details));
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
// the --tree case is handled by the DirAction parser later
|
// the --tree case is handled by the DirAction parser later
|
||||||
return Ok(Self::Details(details));
|
return Ok(Self::Details(details));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::strict_check_long_flags(matches)?;
|
Self::strict_check_long_flags(matches)?;
|
||||||
|
@ -151,8 +147,13 @@ impl TerminalWidth {
|
||||||
|
|
||||||
if let Some(columns) = vars.get(vars::COLUMNS).and_then(|s| s.into_string().ok()) {
|
if let Some(columns) = vars.get(vars::COLUMNS).and_then(|s| s.into_string().ok()) {
|
||||||
match columns.parse() {
|
match columns.parse() {
|
||||||
Ok(width) => Ok(Self::Set(width)),
|
Ok(width) => {
|
||||||
Err(e) => Err(OptionsError::FailedParse(e)),
|
Ok(Self::Set(width))
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
let source = NumberSource::Env(vars::COLUMNS);
|
||||||
|
Err(OptionsError::FailedParse(columns, source, e))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -168,8 +169,13 @@ impl RowThreshold {
|
||||||
|
|
||||||
if let Some(columns) = vars.get(vars::EXA_GRID_ROWS).and_then(|s| s.into_string().ok()) {
|
if let Some(columns) = vars.get(vars::EXA_GRID_ROWS).and_then(|s| s.into_string().ok()) {
|
||||||
match columns.parse() {
|
match columns.parse() {
|
||||||
Ok(rows) => Ok(Self::MinimumRows(rows)),
|
Ok(rows) => {
|
||||||
Err(e) => Err(OptionsError::FailedParse(e)),
|
Ok(Self::MinimumRows(rows))
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
let source = NumberSource::Env(vars::EXA_GRID_ROWS);
|
||||||
|
Err(OptionsError::FailedParse(columns, source, e))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -185,7 +191,7 @@ impl TableOptions {
|
||||||
let size_format = SizeFormat::deduce(matches)?;
|
let size_format = SizeFormat::deduce(matches)?;
|
||||||
let user_format = UserFormat::deduce(matches)?;
|
let user_format = UserFormat::deduce(matches)?;
|
||||||
let columns = Columns::deduce(matches)?;
|
let columns = Columns::deduce(matches)?;
|
||||||
Ok(Self { time_format, size_format, columns , user_format})
|
Ok(Self { size_format, time_format, user_format, columns })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,7 +211,7 @@ impl Columns {
|
||||||
let filesize = ! matches.has(&flags::NO_FILESIZE)?;
|
let filesize = ! matches.has(&flags::NO_FILESIZE)?;
|
||||||
let user = ! matches.has(&flags::NO_USER)?;
|
let user = ! matches.has(&flags::NO_USER)?;
|
||||||
|
|
||||||
Ok(Self { time_types, git, octal, blocks, group, inode, links, permissions, filesize, user })
|
Ok(Self { time_types, inode, links, blocks, group, git, octal, permissions, filesize, user })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,7 +378,7 @@ mod test {
|
||||||
|
|
||||||
($name:ident: $type:ident <- $inputs:expr; $stricts:expr => err $result:expr) => {
|
($name:ident: $type:ident <- $inputs:expr; $stricts:expr => err $result:expr) => {
|
||||||
/// Special macro for testing Err results.
|
/// Special macro for testing Err results.
|
||||||
/// This is needed because sometimes the Ok type doesn’t implement PartialEq.
|
/// This is needed because sometimes the Ok type doesn’t implement `PartialEq`.
|
||||||
#[test]
|
#[test]
|
||||||
fn $name() {
|
fn $name() {
|
||||||
for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| $type::deduce(mf)) {
|
for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| $type::deduce(mf)) {
|
||||||
|
@ -383,7 +389,7 @@ mod test {
|
||||||
|
|
||||||
($name:ident: $type:ident <- $inputs:expr; $stricts:expr => like $pat:pat) => {
|
($name:ident: $type:ident <- $inputs:expr; $stricts:expr => like $pat:pat) => {
|
||||||
/// More general macro for testing against a pattern.
|
/// More general macro for testing against a pattern.
|
||||||
/// Instead of using PartialEq, this just tests if it matches a pat.
|
/// Instead of using `PartialEq`, this just tests if it matches a pat.
|
||||||
#[test]
|
#[test]
|
||||||
fn $name() {
|
fn $name() {
|
||||||
for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| $type::deduce(mf)) {
|
for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| $type::deduce(mf)) {
|
||||||
|
|
|
@ -77,11 +77,9 @@ impl TextCell {
|
||||||
///
|
///
|
||||||
/// This method allocates a `String` to hold the spaces.
|
/// This method allocates a `String` to hold the spaces.
|
||||||
pub fn add_spaces(&mut self, count: usize) {
|
pub fn add_spaces(&mut self, count: usize) {
|
||||||
use std::iter::repeat;
|
|
||||||
|
|
||||||
(*self.width) += count;
|
(*self.width) += count;
|
||||||
|
|
||||||
let spaces: String = repeat(' ').take(count).collect();
|
let spaces: String = " ".repeat(count);
|
||||||
self.contents.0.push(Style::default().paint(spaces));
|
self.contents.0.push(Style::default().paint(spaces));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +191,7 @@ impl TextCellContents {
|
||||||
///
|
///
|
||||||
/// It has `From` impls that convert an input string or fixed with to values
|
/// It has `From` impls that convert an input string or fixed with to values
|
||||||
/// of this type, and will `Deref` to the contained `usize` value.
|
/// of this type, and will `Deref` to the contained `usize` value.
|
||||||
#[derive(PartialEq, Debug, Clone, Copy, Default)]
|
#[derive(PartialEq, Eq, Debug, Clone, Copy, Default)]
|
||||||
pub struct DisplayWidth(usize);
|
pub struct DisplayWidth(usize);
|
||||||
|
|
||||||
impl<'a> From<&'a str> for DisplayWidth {
|
impl<'a> From<&'a str> for DisplayWidth {
|
||||||
|
|
|
@ -91,7 +91,7 @@ use crate::theme::Theme;
|
||||||
///
|
///
|
||||||
/// Almost all the heavy lifting is done in a Table object, which handles the
|
/// Almost all the heavy lifting is done in a Table object, which handles the
|
||||||
/// columns for each row.
|
/// columns for each row.
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
|
|
||||||
/// Options specific to drawing a table.
|
/// Options specific to drawing a table.
|
||||||
|
@ -147,7 +147,11 @@ impl<'a> AsRef<File<'a>> for Egg<'a> {
|
||||||
|
|
||||||
impl<'a> Render<'a> {
|
impl<'a> Render<'a> {
|
||||||
pub fn render<W: Write>(mut self, w: &mut W) -> io::Result<()> {
|
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();
|
let mut rows = Vec::new();
|
||||||
|
|
||||||
if let Some(ref table) = self.opts.table {
|
if let Some(ref table) = self.opts.table {
|
||||||
|
@ -157,7 +161,7 @@ impl<'a> Render<'a> {
|
||||||
(None, _) => {/* Keep Git how it is */},
|
(None, _) => {/* Keep Git how it is */},
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut table = Table::new(table, self.git, &self.theme);
|
let mut table = Table::new(table, self.git, self.theme);
|
||||||
|
|
||||||
if self.opts.header {
|
if self.opts.header {
|
||||||
let header = table.header_row();
|
let header = table.header_row();
|
||||||
|
@ -350,9 +354,10 @@ impl<'a> Render<'a> {
|
||||||
fn render_error(&self, error: &io::Error, tree: TreeParams, path: Option<PathBuf>) -> Row {
|
fn render_error(&self, error: &io::Error, tree: TreeParams, path: Option<PathBuf>) -> Row {
|
||||||
use crate::output::file_name::Colours;
|
use crate::output::file_name::Colours;
|
||||||
|
|
||||||
let error_message = match path {
|
let error_message = if let Some(path) = path {
|
||||||
Some(path) => format!("<{}: {}>", path.display(), error),
|
format!("<{}: {}>", path.display(), error)
|
||||||
None => format!("<{}>", error),
|
} else {
|
||||||
|
format!("<{}>", error)
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: broken_symlink() doesn’t quite seem like the right name for
|
// TODO: broken_symlink() doesn’t quite seem like the right name for
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use ansi_term::{ANSIString, Style};
|
use ansi_term::{ANSIString, Style};
|
||||||
|
|
||||||
|
|
||||||
pub fn escape<'a>(string: String, bits: &mut Vec<ANSIString<'a>>, good: Style, bad: Style) {
|
pub fn escape(string: String, bits: &mut Vec<ANSIString<'_>>, good: Style, bad: Style) {
|
||||||
if string.chars().all(|c| c >= 0x20 as char && c != 0x7f as char) {
|
if string.chars().all(|c| c >= 0x20 as char && c != 0x7f as char) {
|
||||||
bits.push(good.paint(string));
|
bits.push(good.paint(string));
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -54,7 +54,7 @@ enum LinkStyle {
|
||||||
|
|
||||||
|
|
||||||
/// Whether to append file class characters to the file names.
|
/// Whether to append file class characters to the file names.
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
pub enum Classify {
|
pub enum Classify {
|
||||||
|
|
||||||
/// Just display the file names, without any characters.
|
/// Just display the file names, without any characters.
|
||||||
|
@ -73,7 +73,7 @@ impl Default for Classify {
|
||||||
|
|
||||||
|
|
||||||
/// Whether and how to show icons.
|
/// Whether and how to show icons.
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
pub enum ShowIcons {
|
pub enum ShowIcons {
|
||||||
|
|
||||||
/// Don’t show icons at all.
|
/// Don’t show icons at all.
|
||||||
|
@ -173,7 +173,7 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
|
||||||
show_icons: ShowIcons::Off,
|
show_icons: ShowIcons::Off,
|
||||||
};
|
};
|
||||||
|
|
||||||
let target = FileName {
|
let target_name = FileName {
|
||||||
file: target,
|
file: target,
|
||||||
colours: self.colours,
|
colours: self.colours,
|
||||||
target: None,
|
target: None,
|
||||||
|
@ -181,9 +181,15 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
|
||||||
options: target_options,
|
options: target_options,
|
||||||
};
|
};
|
||||||
|
|
||||||
for bit in target.coloured_file_name() {
|
for bit in target_name.coloured_file_name() {
|
||||||
bits.push(bit);
|
bits.push(bit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Classify::AddFileIndicators = self.options.classify {
|
||||||
|
if let Some(class) = self.classify_char(target) {
|
||||||
|
bits.push(Style::default().paint(class));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +212,7 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if let Classify::AddFileIndicators = self.options.classify {
|
else if let Classify::AddFileIndicators = self.options.classify {
|
||||||
if let Some(class) = self.classify_char() {
|
if let Some(class) = self.classify_char(self.file) {
|
||||||
bits.push(Style::default().paint(class));
|
bits.push(Style::default().paint(class));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -220,7 +226,7 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
|
||||||
let coconut = parent.components().count();
|
let coconut = parent.components().count();
|
||||||
|
|
||||||
if coconut == 1 && parent.has_root() {
|
if coconut == 1 && parent.has_root() {
|
||||||
bits.push(self.colours.symlink_path().paint("/"));
|
bits.push(self.colours.symlink_path().paint(std::path::MAIN_SEPARATOR.to_string()));
|
||||||
}
|
}
|
||||||
else if coconut >= 1 {
|
else if coconut >= 1 {
|
||||||
escape(
|
escape(
|
||||||
|
@ -229,26 +235,27 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
|
||||||
self.colours.symlink_path(),
|
self.colours.symlink_path(),
|
||||||
self.colours.control_char(),
|
self.colours.control_char(),
|
||||||
);
|
);
|
||||||
bits.push(self.colours.symlink_path().paint("/"));
|
bits.push(self.colours.symlink_path().paint(std::path::MAIN_SEPARATOR.to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The character to be displayed after a file when classifying is on, if
|
/// The character to be displayed after a file when classifying is on, if
|
||||||
/// the file’s type has one associated with it.
|
/// the file’s type has one associated with it.
|
||||||
fn classify_char(&self) -> Option<&'static str> {
|
#[cfg(unix)]
|
||||||
if self.file.is_executable_file() {
|
fn classify_char(&self, file: &File<'_>) -> Option<&'static str> {
|
||||||
|
if file.is_executable_file() {
|
||||||
Some("*")
|
Some("*")
|
||||||
}
|
}
|
||||||
else if self.file.is_directory() {
|
else if file.is_directory() {
|
||||||
Some("/")
|
Some("/")
|
||||||
}
|
}
|
||||||
else if self.file.is_pipe() {
|
else if file.is_pipe() {
|
||||||
Some("|")
|
Some("|")
|
||||||
}
|
}
|
||||||
else if self.file.is_link() {
|
else if file.is_link() {
|
||||||
Some("@")
|
Some("@")
|
||||||
}
|
}
|
||||||
else if self.file.is_socket() {
|
else if file.is_socket() {
|
||||||
Some("=")
|
Some("=")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -256,6 +263,19 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn classify_char(&self, file: &File<'_>) -> Option<&'static str> {
|
||||||
|
if file.is_directory() {
|
||||||
|
Some("/")
|
||||||
|
}
|
||||||
|
else if file.is_link() {
|
||||||
|
Some("@")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns at least one ANSI-highlighted string representing this file’s
|
/// Returns at least one ANSI-highlighted string representing this file’s
|
||||||
/// name using the given set of colours.
|
/// name using the given set of colours.
|
||||||
///
|
///
|
||||||
|
@ -295,11 +315,16 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
|
||||||
|
|
||||||
match self.file {
|
match self.file {
|
||||||
f if f.is_directory() => self.colours.directory(),
|
f if f.is_directory() => self.colours.directory(),
|
||||||
|
#[cfg(unix)]
|
||||||
f if f.is_executable_file() => self.colours.executable_file(),
|
f if f.is_executable_file() => self.colours.executable_file(),
|
||||||
f if f.is_link() => self.colours.symlink(),
|
f if f.is_link() => self.colours.symlink(),
|
||||||
|
#[cfg(unix)]
|
||||||
f if f.is_pipe() => self.colours.pipe(),
|
f if f.is_pipe() => self.colours.pipe(),
|
||||||
|
#[cfg(unix)]
|
||||||
f if f.is_block_device() => self.colours.block_device(),
|
f if f.is_block_device() => self.colours.block_device(),
|
||||||
|
#[cfg(unix)]
|
||||||
f if f.is_char_device() => self.colours.char_device(),
|
f if f.is_char_device() => self.colours.char_device(),
|
||||||
|
#[cfg(unix)]
|
||||||
f if f.is_socket() => self.colours.socket(),
|
f if f.is_socket() => self.colours.socket(),
|
||||||
f if ! f.is_file() => self.colours.special(),
|
f if ! f.is_file() => self.colours.special(),
|
||||||
_ => self.colours.colour_file(self.file),
|
_ => self.colours.colour_file(self.file),
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::output::file_name::Options as FileStyle;
|
||||||
use crate::theme::Theme;
|
use crate::theme::Theme;
|
||||||
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
pub across: bool,
|
pub across: bool,
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,7 @@ impl<'a> Render<'a> {
|
||||||
grid.add(tg::Cell {
|
grid.add(tg::Cell {
|
||||||
contents: filename.strings().to_string(),
|
contents: filename.strings().to_string(),
|
||||||
width: *filename.width(),
|
width: *filename.width(),
|
||||||
|
alignment: tg::Alignment::Left,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ use crate::output::tree::{TreeParams, TreeDepth};
|
||||||
use crate::theme::Theme;
|
use crate::theme::Theme;
|
||||||
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
pub grid: GridOptions,
|
pub grid: GridOptions,
|
||||||
pub details: DetailsOptions,
|
pub details: DetailsOptions,
|
||||||
|
@ -39,7 +39,7 @@ impl Options {
|
||||||
/// small directory of four files in four columns, the files just look spaced
|
/// small directory of four files in four columns, the files just look spaced
|
||||||
/// out and it’s harder to see what’s going on. So it can be enabled just for
|
/// out and it’s harder to see what’s going on. So it can be enabled just for
|
||||||
/// larger directory listings.
|
/// larger directory listings.
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
pub enum RowThreshold {
|
pub enum RowThreshold {
|
||||||
|
|
||||||
/// Only use grid-details view if it would result in at least this many
|
/// Only use grid-details view if it would result in at least this many
|
||||||
|
@ -158,7 +158,7 @@ impl<'a> Render<'a> {
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let mut last_working_grid = self.make_grid(1, options, &file_names, rows.clone(), &drender);
|
let mut last_working_grid = self.make_grid(1, options, &file_names, rows.clone(), &drender);
|
||||||
|
|
||||||
if file_names.len() == 1 {
|
if file_names.len() == 1 {
|
||||||
return Some((last_working_grid, 1));
|
return Some((last_working_grid, 1));
|
||||||
}
|
}
|
||||||
|
@ -174,22 +174,21 @@ impl<'a> Render<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if the_grid_fits {
|
if the_grid_fits {
|
||||||
if column_count == file_names.len() {
|
last_working_grid = grid;
|
||||||
return Some((grid, column_count));
|
}
|
||||||
} else {
|
|
||||||
last_working_grid = grid;
|
if !the_grid_fits || column_count == file_names.len() {
|
||||||
}
|
let last_column_count = if the_grid_fits { column_count } else { column_count - 1 };
|
||||||
} else {
|
// If we’ve figured out how many columns can fit in the user’s terminal,
|
||||||
// If we’ve figured out how many columns can fit in the user’s
|
// and it turns out there aren’t enough rows to make it worthwhile
|
||||||
// terminal, and it turns out there aren’t enough rows to
|
// (according to EXA_GRID_ROWS), then just resort to the lines view.
|
||||||
// make it worthwhile, then just resort to the lines view.
|
|
||||||
if let RowThreshold::MinimumRows(thresh) = self.row_threshold {
|
if let RowThreshold::MinimumRows(thresh) = self.row_threshold {
|
||||||
if last_working_grid.fit_into_columns(column_count - 1).row_count() < thresh {
|
if last_working_grid.fit_into_columns(last_column_count).row_count() < thresh {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Some((last_working_grid, column_count - 1));
|
return Some((last_working_grid, last_column_count));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,7 +202,7 @@ impl<'a> Render<'a> {
|
||||||
(None, _) => {/* Keep Git how it is */},
|
(None, _) => {/* Keep Git how it is */},
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut table = Table::new(options, self.git, &self.theme);
|
let mut table = Table::new(options, self.git, self.theme);
|
||||||
let mut rows = Vec::new();
|
let mut rows = Vec::new();
|
||||||
|
|
||||||
if self.details.header {
|
if self.details.header {
|
||||||
|
@ -264,6 +263,7 @@ impl<'a> Render<'a> {
|
||||||
let cell = grid::Cell {
|
let cell = grid::Cell {
|
||||||
contents: ANSIStrings(&column[row].contents).to_string(),
|
contents: ANSIStrings(&column[row].contents).to_string(),
|
||||||
width: *column[row].width,
|
width: *column[row].width,
|
||||||
|
alignment: grid::Alignment::Left,
|
||||||
};
|
};
|
||||||
|
|
||||||
grid.add(cell);
|
grid.add(cell);
|
||||||
|
@ -277,6 +277,7 @@ impl<'a> Render<'a> {
|
||||||
let cell = grid::Cell {
|
let cell = grid::Cell {
|
||||||
contents: ANSIStrings(&cell.contents).to_string(),
|
contents: ANSIStrings(&cell.contents).to_string(),
|
||||||
width: *cell.width,
|
width: *cell.width,
|
||||||
|
alignment: grid::Alignment::Left,
|
||||||
};
|
};
|
||||||
|
|
||||||
grid.add(cell);
|
grid.add(cell);
|
||||||
|
|
|
@ -37,7 +37,7 @@ impl Icons {
|
||||||
/// - If neither is set, just use the default style.
|
/// - If neither is set, just use the default style.
|
||||||
/// - Attributes such as bold or underline should not be used to paint the
|
/// - Attributes such as bold or underline should not be used to paint the
|
||||||
/// icon, as they can make it look weird.
|
/// icon, as they can make it look weird.
|
||||||
pub fn iconify_style<'a>(style: Style) -> Style {
|
pub fn iconify_style(style: Style) -> Style {
|
||||||
style.background.or(style.foreground)
|
style.background.or(style.foreground)
|
||||||
.map(Style::from)
|
.map(Style::from)
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
|
@ -69,7 +69,9 @@ lazy_static! {
|
||||||
m.insert("Dockerfile", '\u{f308}'); //
|
m.insert("Dockerfile", '\u{f308}'); //
|
||||||
m.insert("ds_store", '\u{f179}'); //
|
m.insert("ds_store", '\u{f179}'); //
|
||||||
m.insert("gitignore_global", '\u{f1d3}'); //
|
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.coffee", '\u{e611}'); //
|
||||||
m.insert("gruntfile.js", '\u{e611}'); //
|
m.insert("gruntfile.js", '\u{e611}'); //
|
||||||
m.insert("gruntfile.ls", '\u{e611}'); //
|
m.insert("gruntfile.ls", '\u{e611}'); //
|
||||||
|
@ -80,9 +82,10 @@ lazy_static! {
|
||||||
m.insert("include", '\u{e5fc}'); //
|
m.insert("include", '\u{e5fc}'); //
|
||||||
m.insert("lib", '\u{f121}'); //
|
m.insert("lib", '\u{f121}'); //
|
||||||
m.insert("localized", '\u{f179}'); //
|
m.insert("localized", '\u{f179}'); //
|
||||||
m.insert("Makefile", '\u{e779}'); //
|
m.insert("Makefile", '\u{f489}'); //
|
||||||
m.insert("node_modules", '\u{e718}'); //
|
m.insert("node_modules", '\u{e718}'); //
|
||||||
m.insert("npmignore", '\u{e71e}'); //
|
m.insert("npmignore", '\u{e71e}'); //
|
||||||
|
m.insert("PKGBUILD", '\u{f303}'); //
|
||||||
m.insert("rubydoc", '\u{e73b}'); //
|
m.insert("rubydoc", '\u{e73b}'); //
|
||||||
m.insert("yarn.lock", '\u{e718}'); //
|
m.insert("yarn.lock", '\u{e718}'); //
|
||||||
|
|
||||||
|
@ -110,6 +113,7 @@ pub fn icon_for_file(file: &File<'_>) -> char {
|
||||||
"apk" => '\u{e70e}', //
|
"apk" => '\u{e70e}', //
|
||||||
"apple" => '\u{f179}', //
|
"apple" => '\u{f179}', //
|
||||||
"avi" => '\u{f03d}', //
|
"avi" => '\u{f03d}', //
|
||||||
|
"avif" => '\u{f1c5}', //
|
||||||
"avro" => '\u{e60b}', //
|
"avro" => '\u{e60b}', //
|
||||||
"awk" => '\u{f489}', //
|
"awk" => '\u{f489}', //
|
||||||
"bash" => '\u{f489}', //
|
"bash" => '\u{f489}', //
|
||||||
|
@ -117,6 +121,7 @@ pub fn icon_for_file(file: &File<'_>) -> char {
|
||||||
"bash_profile" => '\u{f489}', //
|
"bash_profile" => '\u{f489}', //
|
||||||
"bashrc" => '\u{f489}', //
|
"bashrc" => '\u{f489}', //
|
||||||
"bat" => '\u{f17a}', //
|
"bat" => '\u{f17a}', //
|
||||||
|
"bats" => '\u{f489}', //
|
||||||
"bmp" => '\u{f1c5}', //
|
"bmp" => '\u{f1c5}', //
|
||||||
"bz" => '\u{f410}', //
|
"bz" => '\u{f410}', //
|
||||||
"bz2" => '\u{f410}', //
|
"bz2" => '\u{f410}', //
|
||||||
|
@ -128,19 +133,20 @@ pub fn icon_for_file(file: &File<'_>) -> char {
|
||||||
"class" => '\u{e256}', //
|
"class" => '\u{e256}', //
|
||||||
"clj" => '\u{e768}', //
|
"clj" => '\u{e768}', //
|
||||||
"cljs" => '\u{e76a}', //
|
"cljs" => '\u{e76a}', //
|
||||||
"cls" => '\u{e600}', //
|
"cls" => '\u{f034}', //
|
||||||
"cmd" => '\u{e70f}', //
|
"cmd" => '\u{e70f}', //
|
||||||
"coffee" => '\u{f0f4}', //
|
"coffee" => '\u{f0f4}', //
|
||||||
"conf" => '\u{e615}', //
|
"conf" => '\u{e615}', //
|
||||||
"cp" => '\u{e61d}', //
|
"cp" => '\u{e61d}', //
|
||||||
|
"cpio" => '\u{f410}', //
|
||||||
"cpp" => '\u{e61d}', //
|
"cpp" => '\u{e61d}', //
|
||||||
"cs" => '\u{f81a}', //
|
"cs" => '\u{f031b}', //
|
||||||
"csh" => '\u{f489}', //
|
"csh" => '\u{f489}', //
|
||||||
"cshtml" => '\u{f1fa}', //
|
"cshtml" => '\u{f1fa}', //
|
||||||
"csproj" => '\u{f81a}', //
|
"csproj" => '\u{f031b}', //
|
||||||
"css" => '\u{e749}', //
|
"css" => '\u{e749}', //
|
||||||
"csv" => '\u{f1c3}', //
|
"csv" => '\u{f1c3}', //
|
||||||
"csx" => '\u{f81a}', //
|
"csx" => '\u{f031b}', //
|
||||||
"cxx" => '\u{e61d}', //
|
"cxx" => '\u{e61d}', //
|
||||||
"d" => '\u{e7af}', //
|
"d" => '\u{e7af}', //
|
||||||
"dart" => '\u{e798}', //
|
"dart" => '\u{e798}', //
|
||||||
|
@ -155,6 +161,7 @@ pub fn icon_for_file(file: &File<'_>) -> char {
|
||||||
"DS_store" => '\u{f179}', //
|
"DS_store" => '\u{f179}', //
|
||||||
"dump" => '\u{f1c0}', //
|
"dump" => '\u{f1c0}', //
|
||||||
"ebook" => '\u{e28b}', //
|
"ebook" => '\u{e28b}', //
|
||||||
|
"ebuild" => '\u{f30d}', //
|
||||||
"editorconfig" => '\u{e615}', //
|
"editorconfig" => '\u{e615}', //
|
||||||
"ejs" => '\u{e618}', //
|
"ejs" => '\u{e618}', //
|
||||||
"elm" => '\u{e62c}', //
|
"elm" => '\u{e62c}', //
|
||||||
|
@ -170,6 +177,9 @@ pub fn icon_for_file(file: &File<'_>) -> char {
|
||||||
"flac" => '\u{f001}', //
|
"flac" => '\u{f001}', //
|
||||||
"flv" => '\u{f03d}', //
|
"flv" => '\u{f03d}', //
|
||||||
"font" => '\u{f031}', //
|
"font" => '\u{f031}', //
|
||||||
|
"fs" => '\u{e7a7}', //
|
||||||
|
"fsi" => '\u{e7a7}', //
|
||||||
|
"fsx" => '\u{e7a7}', //
|
||||||
"gdoc" => '\u{f1c2}', //
|
"gdoc" => '\u{f1c2}', //
|
||||||
"gem" => '\u{e21e}', //
|
"gem" => '\u{e21e}', //
|
||||||
"gemfile" => '\u{e21e}', //
|
"gemfile" => '\u{e21e}', //
|
||||||
|
@ -181,7 +191,7 @@ pub fn icon_for_file(file: &File<'_>) -> char {
|
||||||
"gitignore" => '\u{f1d3}', //
|
"gitignore" => '\u{f1d3}', //
|
||||||
"gitmodules" => '\u{f1d3}', //
|
"gitmodules" => '\u{f1d3}', //
|
||||||
"go" => '\u{e626}', //
|
"go" => '\u{e626}', //
|
||||||
"gradle" => '\u{e70e}', //
|
"gradle" => '\u{e256}', //
|
||||||
"groovy" => '\u{e775}', //
|
"groovy" => '\u{e775}', //
|
||||||
"gsheet" => '\u{f1c3}', //
|
"gsheet" => '\u{f1c3}', //
|
||||||
"gslides" => '\u{f1c4}', //
|
"gslides" => '\u{f1c4}', //
|
||||||
|
@ -196,28 +206,41 @@ pub fn icon_for_file(file: &File<'_>) -> char {
|
||||||
"hxx" => '\u{f0fd}', //
|
"hxx" => '\u{f0fd}', //
|
||||||
"ico" => '\u{f1c5}', //
|
"ico" => '\u{f1c5}', //
|
||||||
"image" => '\u{f1c5}', //
|
"image" => '\u{f1c5}', //
|
||||||
|
"img" => '\u{e271}', //
|
||||||
"iml" => '\u{e7b5}', //
|
"iml" => '\u{e7b5}', //
|
||||||
"ini" => '\u{f17a}', //
|
"ini" => '\u{f17a}', //
|
||||||
"ipynb" => '\u{e606}', //
|
"ipynb" => '\u{e678}', //
|
||||||
"iso" => '\u{e271}', //
|
"iso" => '\u{e271}', //
|
||||||
|
"j2c" => '\u{f1c5}', //
|
||||||
|
"j2k" => '\u{f1c5}', //
|
||||||
"jad" => '\u{e256}', //
|
"jad" => '\u{e256}', //
|
||||||
"jar" => '\u{e204}', //
|
"jar" => '\u{e256}', //
|
||||||
"java" => '\u{e204}', //
|
"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}', //
|
"jpeg" => '\u{f1c5}', //
|
||||||
"jpg" => '\u{f1c5}', //
|
"jpg" => '\u{f1c5}', //
|
||||||
|
"jpx" => '\u{f1c5}', //
|
||||||
"js" => '\u{e74e}', //
|
"js" => '\u{e74e}', //
|
||||||
"json" => '\u{e60b}', //
|
"json" => '\u{e60b}', //
|
||||||
"jsx" => '\u{e7ba}', //
|
"jsx" => '\u{e7ba}', //
|
||||||
|
"jxl" => '\u{f1c5}', //
|
||||||
"ksh" => '\u{f489}', //
|
"ksh" => '\u{f489}', //
|
||||||
"latex" => '\u{e600}', //
|
"latex" => '\u{f034}', //
|
||||||
"less" => '\u{e758}', //
|
"less" => '\u{e758}', //
|
||||||
"lhs" => '\u{e777}', //
|
"lhs" => '\u{e777}', //
|
||||||
"license" => '\u{f718}', //
|
"license" => '\u{f0219}', //
|
||||||
"localized" => '\u{f179}', //
|
"localized" => '\u{f179}', //
|
||||||
"lock" => '\u{f023}', //
|
"lock" => '\u{f023}', //
|
||||||
"log" => '\u{f18d}', //
|
"log" => '\u{f18d}', //
|
||||||
"lua" => '\u{e620}', //
|
"lua" => '\u{e620}', //
|
||||||
"lz" => '\u{f410}', //
|
"lz" => '\u{f410}', //
|
||||||
|
"lz4" => '\u{f410}', //
|
||||||
"lzh" => '\u{f410}', //
|
"lzh" => '\u{f410}', //
|
||||||
"lzma" => '\u{f410}', //
|
"lzma" => '\u{f410}', //
|
||||||
"lzo" => '\u{f410}', //
|
"lzo" => '\u{f410}', //
|
||||||
|
@ -227,6 +250,7 @@ pub fn icon_for_file(file: &File<'_>) -> char {
|
||||||
"markdown" => '\u{f48a}', //
|
"markdown" => '\u{f48a}', //
|
||||||
"md" => '\u{f48a}', //
|
"md" => '\u{f48a}', //
|
||||||
"mjs" => '\u{e74e}', //
|
"mjs" => '\u{e74e}', //
|
||||||
|
"mk" => '\u{f489}', //
|
||||||
"mkd" => '\u{f48a}', //
|
"mkd" => '\u{f48a}', //
|
||||||
"mkv" => '\u{f03d}', //
|
"mkv" => '\u{f03d}', //
|
||||||
"mobi" => '\u{e28b}', //
|
"mobi" => '\u{e28b}', //
|
||||||
|
@ -236,7 +260,7 @@ pub fn icon_for_file(file: &File<'_>) -> char {
|
||||||
"msi" => '\u{e70f}', //
|
"msi" => '\u{e70f}', //
|
||||||
"mustache" => '\u{e60f}', //
|
"mustache" => '\u{e60f}', //
|
||||||
"nix" => '\u{f313}', //
|
"nix" => '\u{f313}', //
|
||||||
"node" => '\u{f898}', //
|
"node" => '\u{f0399}', //
|
||||||
"npmignore" => '\u{e71e}', //
|
"npmignore" => '\u{e71e}', //
|
||||||
"odp" => '\u{f1c4}', //
|
"odp" => '\u{f1c4}', //
|
||||||
"ods" => '\u{f1c3}', //
|
"ods" => '\u{f1c3}', //
|
||||||
|
@ -244,11 +268,15 @@ pub fn icon_for_file(file: &File<'_>) -> char {
|
||||||
"ogg" => '\u{f001}', //
|
"ogg" => '\u{f001}', //
|
||||||
"ogv" => '\u{f03d}', //
|
"ogv" => '\u{f03d}', //
|
||||||
"otf" => '\u{f031}', //
|
"otf" => '\u{f031}', //
|
||||||
|
"part" => '\u{f43a}', //
|
||||||
"patch" => '\u{f440}', //
|
"patch" => '\u{f440}', //
|
||||||
"pdf" => '\u{f1c1}', //
|
"pdf" => '\u{f1c1}', //
|
||||||
"php" => '\u{e73d}', //
|
"php" => '\u{e73d}', //
|
||||||
"pl" => '\u{e769}', //
|
"pl" => '\u{e769}', //
|
||||||
|
"plx" => '\u{e769}', //
|
||||||
|
"pm" => '\u{e769}', //
|
||||||
"png" => '\u{f1c5}', //
|
"png" => '\u{f1c5}', //
|
||||||
|
"pod" => '\u{e769}', //
|
||||||
"ppt" => '\u{f1c4}', //
|
"ppt" => '\u{f1c4}', //
|
||||||
"pptx" => '\u{f1c4}', //
|
"pptx" => '\u{f1c4}', //
|
||||||
"procfile" => '\u{e21e}', //
|
"procfile" => '\u{e21e}', //
|
||||||
|
@ -276,7 +304,7 @@ pub fn icon_for_file(file: &File<'_>) -> char {
|
||||||
"rspec_parallel"=> '\u{e21e}', //
|
"rspec_parallel"=> '\u{e21e}', //
|
||||||
"rspec_status" => '\u{e21e}', //
|
"rspec_status" => '\u{e21e}', //
|
||||||
"rss" => '\u{f09e}', //
|
"rss" => '\u{f09e}', //
|
||||||
"rtf" => '\u{f718}', //
|
"rtf" => '\u{f0219}', //
|
||||||
"ru" => '\u{e21e}', //
|
"ru" => '\u{e21e}', //
|
||||||
"rubydoc" => '\u{e73b}', //
|
"rubydoc" => '\u{e73b}', //
|
||||||
"sass" => '\u{e603}', //
|
"sass" => '\u{e603}', //
|
||||||
|
@ -289,28 +317,34 @@ pub fn icon_for_file(file: &File<'_>) -> char {
|
||||||
"so" => '\u{f17c}', //
|
"so" => '\u{f17c}', //
|
||||||
"sql" => '\u{f1c0}', //
|
"sql" => '\u{f1c0}', //
|
||||||
"sqlite3" => '\u{e7c4}', //
|
"sqlite3" => '\u{e7c4}', //
|
||||||
|
"sty" => '\u{f034}', //
|
||||||
"styl" => '\u{e600}', //
|
"styl" => '\u{e600}', //
|
||||||
"stylus" => '\u{e600}', //
|
"stylus" => '\u{e600}', //
|
||||||
"svg" => '\u{f1c5}', //
|
"svg" => '\u{f1c5}', //
|
||||||
"swift" => '\u{e755}', //
|
"swift" => '\u{e755}', //
|
||||||
|
"t" => '\u{e769}', //
|
||||||
"tar" => '\u{f410}', //
|
"tar" => '\u{f410}', //
|
||||||
"taz" => '\u{f410}', //
|
"taz" => '\u{f410}', //
|
||||||
"tbz" => '\u{f410}', //
|
"tbz" => '\u{f410}', //
|
||||||
"tbz2" => '\u{f410}', //
|
"tbz2" => '\u{f410}', //
|
||||||
"tex" => '\u{e600}', //
|
"tex" => '\u{f034}', //
|
||||||
|
"tgz" => '\u{f410}', //
|
||||||
"tiff" => '\u{f1c5}', //
|
"tiff" => '\u{f1c5}', //
|
||||||
|
"tlz" => '\u{f410}', //
|
||||||
"toml" => '\u{e615}', //
|
"toml" => '\u{e615}', //
|
||||||
|
"torrent" => '\u{e275}', //
|
||||||
"ts" => '\u{e628}', //
|
"ts" => '\u{e628}', //
|
||||||
"tsv" => '\u{f1c3}', //
|
"tsv" => '\u{f1c3}', //
|
||||||
"tsx" => '\u{e7ba}', //
|
"tsx" => '\u{e7ba}', //
|
||||||
"ttf" => '\u{f031}', //
|
"ttf" => '\u{f031}', //
|
||||||
"twig" => '\u{e61c}', //
|
"twig" => '\u{e61c}', //
|
||||||
"txt" => '\u{f15c}', //
|
"txt" => '\u{f15c}', //
|
||||||
|
"txz" => '\u{f410}', //
|
||||||
"tz" => '\u{f410}', //
|
"tz" => '\u{f410}', //
|
||||||
"tzo" => '\u{f410}', //
|
"tzo" => '\u{f410}', //
|
||||||
"video" => '\u{f03d}', //
|
"video" => '\u{f03d}', //
|
||||||
"vim" => '\u{e62b}', //
|
"vim" => '\u{e62b}', //
|
||||||
"vue" => '\u{fd42}', // ﵂
|
"vue" => '\u{f0844}', //
|
||||||
"war" => '\u{e256}', //
|
"war" => '\u{e256}', //
|
||||||
"wav" => '\u{f001}', //
|
"wav" => '\u{f001}', //
|
||||||
"webm" => '\u{f03d}', //
|
"webm" => '\u{f03d}', //
|
||||||
|
@ -321,8 +355,8 @@ pub fn icon_for_file(file: &File<'_>) -> char {
|
||||||
"xhtml" => '\u{f13b}', //
|
"xhtml" => '\u{f13b}', //
|
||||||
"xls" => '\u{f1c3}', //
|
"xls" => '\u{f1c3}', //
|
||||||
"xlsx" => '\u{f1c3}', //
|
"xlsx" => '\u{f1c3}', //
|
||||||
"xml" => '\u{fabf}', // 謹
|
"xml" => '\u{f05c0}', //
|
||||||
"xul" => '\u{fabf}', // 謹
|
"xul" => '\u{f05c0}', //
|
||||||
"xz" => '\u{f410}', //
|
"xz" => '\u{f410}', //
|
||||||
"yaml" => '\u{f481}', //
|
"yaml" => '\u{f481}', //
|
||||||
"yml" => '\u{f481}', //
|
"yml" => '\u{f481}', //
|
||||||
|
@ -330,6 +364,7 @@ pub fn icon_for_file(file: &File<'_>) -> char {
|
||||||
"zsh" => '\u{f489}', //
|
"zsh" => '\u{f489}', //
|
||||||
"zsh-theme" => '\u{f489}', //
|
"zsh-theme" => '\u{f489}', //
|
||||||
"zshrc" => '\u{f489}', //
|
"zshrc" => '\u{f489}', //
|
||||||
|
"zst" => '\u{f410}', //
|
||||||
_ => '\u{f15b}' //
|
_ => '\u{f15b}' //
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ pub struct View {
|
||||||
|
|
||||||
|
|
||||||
/// The **mode** is the “type” of output.
|
/// The **mode** is the “type” of output.
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
#[allow(clippy::large_enum_variant)]
|
#[allow(clippy::large_enum_variant)]
|
||||||
pub enum Mode {
|
pub enum Mode {
|
||||||
Grid(grid::Options),
|
Grid(grid::Options),
|
||||||
|
@ -37,7 +37,7 @@ pub enum Mode {
|
||||||
|
|
||||||
|
|
||||||
/// The width of the terminal requested by the user.
|
/// The width of the terminal requested by the user.
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
pub enum TerminalWidth {
|
pub enum TerminalWidth {
|
||||||
|
|
||||||
/// The user requested this specific number of columns.
|
/// The user requested this specific number of columns.
|
||||||
|
@ -55,7 +55,7 @@ impl TerminalWidth {
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Self::Set(width) => Some(width),
|
Self::Set(width) => Some(width),
|
||||||
Self::Automatic => term_size::dimensions_stdout().map(|t| t.0),
|
Self::Automatic => terminal_size::terminal_size().map(|(w, _)| w.0.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ pub mod test {
|
||||||
let blox = f::Blocks::None;
|
let blox = f::Blocks::None;
|
||||||
let expected = TextCell::blank(Green.italic());
|
let expected = TextCell::blank(Green.italic());
|
||||||
|
|
||||||
assert_eq!(expected, blox.render(&TestColours).into());
|
assert_eq!(expected, blox.render(&TestColours));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -52,6 +52,6 @@ pub mod test {
|
||||||
let blox = f::Blocks::Some(3005);
|
let blox = f::Blocks::Some(3005);
|
||||||
let expected = TextCell::paint_str(Red.blink(), "3005");
|
let expected = TextCell::paint_str(Red.blink(), "3005");
|
||||||
|
|
||||||
assert_eq!(expected, blox.render(&TestColours).into());
|
assert_eq!(expected, blox.render(&TestColours));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ impl f::GitStatus {
|
||||||
|
|
||||||
pub trait Colours {
|
pub trait Colours {
|
||||||
fn not_modified(&self) -> Style;
|
fn not_modified(&self) -> Style;
|
||||||
|
#[allow(clippy::new_ret_no_self)]
|
||||||
fn new(&self) -> Style;
|
fn new(&self) -> Style;
|
||||||
fn modified(&self) -> Style;
|
fn modified(&self) -> Style;
|
||||||
fn deleted(&self) -> Style;
|
fn deleted(&self) -> Style;
|
||||||
|
@ -84,7 +85,7 @@ pub mod test {
|
||||||
].into(),
|
].into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(expected, stati.render(&TestColours).into())
|
assert_eq!(expected, stati.render(&TestColours))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -103,6 +104,6 @@ pub mod test {
|
||||||
].into(),
|
].into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(expected, stati.render(&TestColours).into())
|
assert_eq!(expected, stati.render(&TestColours))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,8 @@ pub mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn blocklessness() {
|
fn blocklessness() {
|
||||||
let io = f::Inode(1414213);
|
let io = f::Inode(1_414_213);
|
||||||
let expected = TextCell::paint_str(Cyan.underline(), "1414213");
|
let expected = TextCell::paint_str(Cyan.underline(), "1414213");
|
||||||
assert_eq!(expected, io.render(Cyan.underline()).into());
|
assert_eq!(expected, io.render(Cyan.underline()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ pub mod test {
|
||||||
contents: vec![ Blue.paint("1") ].into(),
|
contents: vec![ Blue.paint("1") ].into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(expected, stati.render(&TestColours, &locale::Numeric::english()).into());
|
assert_eq!(expected, stati.render(&TestColours, &locale::Numeric::english()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -67,7 +67,7 @@ pub mod test {
|
||||||
contents: vec![ Blue.paint("3,005") ].into(),
|
contents: vec![ Blue.paint("3,005") ].into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(expected, stati.render(&TestColours, &locale::Numeric::english()).into());
|
assert_eq!(expected, stati.render(&TestColours, &locale::Numeric::english()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -82,6 +82,6 @@ pub mod test {
|
||||||
contents: vec![ Blue.on(Red).paint("3,005") ].into(),
|
contents: vec![ Blue.on(Red).paint("3,005") ].into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(expected, stati.render(&TestColours, &locale::Numeric::english()).into());
|
assert_eq!(expected, stati.render(&TestColours, &locale::Numeric::english()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,9 @@ pub use self::filetype::Colours as FiletypeColours;
|
||||||
mod git;
|
mod git;
|
||||||
pub use self::git::Colours as GitColours;
|
pub use self::git::Colours as GitColours;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
mod groups;
|
mod groups;
|
||||||
|
#[cfg(unix)]
|
||||||
pub use self::groups::Colours as GroupColours;
|
pub use self::groups::Colours as GroupColours;
|
||||||
|
|
||||||
mod inode;
|
mod inode;
|
||||||
|
@ -26,7 +28,9 @@ mod times;
|
||||||
pub use self::times::Render as TimeRender;
|
pub use self::times::Render as TimeRender;
|
||||||
// times does too
|
// times does too
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
mod users;
|
mod users;
|
||||||
|
#[cfg(unix)]
|
||||||
pub use self::users::Colours as UserColours;
|
pub use self::users::Colours as UserColours;
|
||||||
|
|
||||||
mod octal;
|
mod octal;
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::output::cell::TextCell;
|
||||||
|
|
||||||
impl f::OctalPermissions {
|
impl f::OctalPermissions {
|
||||||
fn bits_to_octal(r: bool, w: bool, x: bool) -> u8 {
|
fn bits_to_octal(r: bool, w: bool, x: bool) -> u8 {
|
||||||
(r as u8) * 4 + (w as u8) * 2 + (x as u8)
|
u8::from(r) * 4 + u8::from(w) * 2 + u8::from(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(&self, style: Style) -> TextCell {
|
pub fn render(&self, style: Style) -> TextCell {
|
||||||
|
@ -40,7 +40,7 @@ pub mod test {
|
||||||
let octal = f::OctalPermissions{ permissions: bits };
|
let octal = f::OctalPermissions{ permissions: bits };
|
||||||
|
|
||||||
let expected = TextCell::paint_str(Purple.bold(), "0755");
|
let expected = TextCell::paint_str(Purple.bold(), "0755");
|
||||||
assert_eq!(expected, octal.render(Purple.bold()).into());
|
assert_eq!(expected, octal.render(Purple.bold()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -54,7 +54,7 @@ pub mod test {
|
||||||
let octal = f::OctalPermissions{ permissions: bits };
|
let octal = f::OctalPermissions{ permissions: bits };
|
||||||
|
|
||||||
let expected = TextCell::paint_str(Purple.bold(), "0644");
|
let expected = TextCell::paint_str(Purple.bold(), "0644");
|
||||||
assert_eq!(expected, octal.render(Purple.bold()).into());
|
assert_eq!(expected, octal.render(Purple.bold()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -68,7 +68,7 @@ pub mod test {
|
||||||
let octal = f::OctalPermissions{ permissions: bits };
|
let octal = f::OctalPermissions{ permissions: bits };
|
||||||
|
|
||||||
let expected = TextCell::paint_str(Purple.bold(), "0600");
|
let expected = TextCell::paint_str(Purple.bold(), "0600");
|
||||||
assert_eq!(expected, octal.render(Purple.bold()).into());
|
assert_eq!(expected, octal.render(Purple.bold()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -82,7 +82,7 @@ pub mod test {
|
||||||
let octal = f::OctalPermissions{ permissions: bits };
|
let octal = f::OctalPermissions{ permissions: bits };
|
||||||
|
|
||||||
let expected = TextCell::paint_str(Purple.bold(), "4777");
|
let expected = TextCell::paint_str(Purple.bold(), "4777");
|
||||||
assert_eq!(expected, octal.render(Purple.bold()).into());
|
assert_eq!(expected, octal.render(Purple.bold()));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ pub mod test {
|
||||||
let octal = f::OctalPermissions{ permissions: bits };
|
let octal = f::OctalPermissions{ permissions: bits };
|
||||||
|
|
||||||
let expected = TextCell::paint_str(Purple.bold(), "2777");
|
let expected = TextCell::paint_str(Purple.bold(), "2777");
|
||||||
assert_eq!(expected, octal.render(Purple.bold()).into());
|
assert_eq!(expected, octal.render(Purple.bold()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -111,6 +111,6 @@ pub mod test {
|
||||||
let octal = f::OctalPermissions{ permissions: bits };
|
let octal = f::OctalPermissions{ permissions: bits };
|
||||||
|
|
||||||
let expected = TextCell::paint_str(Purple.bold(), "1777");
|
let expected = TextCell::paint_str(Purple.bold(), "1777");
|
||||||
assert_eq!(expected, octal.render(Purple.bold()).into());
|
assert_eq!(expected, octal.render(Purple.bold()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ use crate::output::render::FiletypeColours;
|
||||||
|
|
||||||
|
|
||||||
impl f::PermissionsPlus {
|
impl f::PermissionsPlus {
|
||||||
|
#[cfg(unix)]
|
||||||
pub fn render<C: Colours+FiletypeColours>(&self, colours: &C) -> TextCell {
|
pub fn render<C: Colours+FiletypeColours>(&self, colours: &C) -> TextCell {
|
||||||
let mut chars = vec![ self.file_type.render(colours) ];
|
let mut chars = vec![ self.file_type.render(colours) ];
|
||||||
chars.extend(self.permissions.render(colours, self.file_type.is_regular_file()));
|
chars.extend(self.permissions.render(colours, self.file_type.is_regular_file()));
|
||||||
|
@ -22,6 +23,17 @@ impl f::PermissionsPlus {
|
||||||
contents: chars.into(),
|
contents: chars.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
pub fn render<C: Colours+FiletypeColours>(&self, colours: &C) -> TextCell {
|
||||||
|
let mut chars = vec![ self.attributes.render_type(colours) ];
|
||||||
|
chars.extend(self.attributes.render(colours));
|
||||||
|
|
||||||
|
TextCell {
|
||||||
|
width: DisplayWidth::from(chars.len()),
|
||||||
|
contents: chars.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -76,6 +88,33 @@ impl f::Permissions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl f::Attributes {
|
||||||
|
pub fn render<C: Colours+FiletypeColours>(&self, colours: &C) -> Vec<ANSIString<'static>> {
|
||||||
|
let bit = |bit, chr: &'static str, style: Style| {
|
||||||
|
if bit { style.paint(chr) }
|
||||||
|
else { colours.dash().paint("-") }
|
||||||
|
};
|
||||||
|
|
||||||
|
vec![
|
||||||
|
bit(self.archive, "a", colours.normal()),
|
||||||
|
bit(self.readonly, "r", colours.user_read()),
|
||||||
|
bit(self.hidden, "h", colours.special_user_file()),
|
||||||
|
bit(self.system, "s", colours.special_other()),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_type<C: Colours+FiletypeColours>(&self, colours: &C) -> ANSIString<'static> {
|
||||||
|
if self.reparse_point {
|
||||||
|
return colours.pipe().paint("l")
|
||||||
|
}
|
||||||
|
else if self.directory {
|
||||||
|
return colours.directory().paint("d")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return colours.dash().paint("-")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Colours {
|
pub trait Colours {
|
||||||
fn dash(&self) -> Style;
|
fn dash(&self) -> Style;
|
||||||
|
|
|
@ -36,20 +36,20 @@ impl f::Size {
|
||||||
};
|
};
|
||||||
|
|
||||||
let (prefix, n) = match result {
|
let (prefix, n) = match result {
|
||||||
NumberPrefix::Standalone(b) => return TextCell::paint(colours.size(None), b.to_string()),
|
NumberPrefix::Standalone(b) => return TextCell::paint(colours.size(None), numerics.format_int(b)),
|
||||||
NumberPrefix::Prefixed(p, n) => (p, n),
|
NumberPrefix::Prefixed(p, n) => (p, n),
|
||||||
};
|
};
|
||||||
|
|
||||||
let symbol = prefix.symbol();
|
let symbol = prefix.symbol();
|
||||||
let decimal_to_diplay = if n < 10_f64 { 1 } else { 0 };
|
let number = if n < 10_f64 {
|
||||||
let number = numerics.format_float(n, decimal_to_diplay);
|
numerics.format_float(n, 1)
|
||||||
|
} else {
|
||||||
// The numbers and symbols are guaranteed to be written in ASCII, so
|
numerics.format_int(n.round() as isize)
|
||||||
// we can skip the display width calculation.
|
};
|
||||||
let width = DisplayWidth::from(number.len() + symbol.len());
|
|
||||||
|
|
||||||
TextCell {
|
TextCell {
|
||||||
width,
|
// symbol is guaranteed to be ASCII since unit prefixes are hardcoded.
|
||||||
|
width: DisplayWidth::from(&*number) + symbol.len(),
|
||||||
contents: vec![
|
contents: vec![
|
||||||
colours.size(Some(prefix)).paint(number),
|
colours.size(Some(prefix)).paint(number),
|
||||||
colours.unit(Some(prefix)).paint(symbol),
|
colours.unit(Some(prefix)).paint(symbol),
|
||||||
|
@ -153,7 +153,7 @@ pub mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn file_bytes() {
|
fn file_bytes() {
|
||||||
let directory = f::Size::Some(1048576);
|
let directory = f::Size::Some(1_048_576);
|
||||||
let expected = TextCell {
|
let expected = TextCell {
|
||||||
width: DisplayWidth::from(9),
|
width: DisplayWidth::from(9),
|
||||||
contents: vec![
|
contents: vec![
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
#[cfg(unix)]
|
||||||
use std::sync::{Mutex, MutexGuard};
|
use std::sync::{Mutex, MutexGuard};
|
||||||
|
|
||||||
use datetime::TimeZone;
|
use datetime::TimeZone;
|
||||||
|
@ -8,6 +9,7 @@ use zoneinfo_compiled::{CompiledData, Result as TZResult};
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use log::*;
|
use log::*;
|
||||||
|
#[cfg(unix)]
|
||||||
use users::UsersCache;
|
use users::UsersCache;
|
||||||
|
|
||||||
use crate::fs::{File, fields as f};
|
use crate::fs::{File, fields as f};
|
||||||
|
@ -19,7 +21,7 @@ use crate::theme::Theme;
|
||||||
|
|
||||||
|
|
||||||
/// Options for displaying a table.
|
/// Options for displaying a table.
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
pub size_format: SizeFormat,
|
pub size_format: SizeFormat,
|
||||||
pub time_format: TimeFormat,
|
pub time_format: TimeFormat,
|
||||||
|
@ -28,7 +30,8 @@ pub struct Options {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extra columns to display in the table.
|
/// Extra columns to display in the table.
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
pub struct Columns {
|
pub struct Columns {
|
||||||
|
|
||||||
/// At least one of these timestamps will be shown.
|
/// At least one of these timestamps will be shown.
|
||||||
|
@ -53,10 +56,12 @@ impl Columns {
|
||||||
let mut columns = Vec::with_capacity(4);
|
let mut columns = Vec::with_capacity(4);
|
||||||
|
|
||||||
if self.inode {
|
if self.inode {
|
||||||
|
#[cfg(unix)]
|
||||||
columns.push(Column::Inode);
|
columns.push(Column::Inode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.octal {
|
if self.octal {
|
||||||
|
#[cfg(unix)]
|
||||||
columns.push(Column::Octal);
|
columns.push(Column::Octal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,6 +70,7 @@ impl Columns {
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.links {
|
if self.links {
|
||||||
|
#[cfg(unix)]
|
||||||
columns.push(Column::HardLinks);
|
columns.push(Column::HardLinks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,14 +79,17 @@ impl Columns {
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.blocks {
|
if self.blocks {
|
||||||
|
#[cfg(unix)]
|
||||||
columns.push(Column::Blocks);
|
columns.push(Column::Blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.user {
|
if self.user {
|
||||||
|
#[cfg(unix)]
|
||||||
columns.push(Column::User);
|
columns.push(Column::User);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.group {
|
if self.group {
|
||||||
|
#[cfg(unix)]
|
||||||
columns.push(Column::Group);
|
columns.push(Column::Group);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,12 +124,18 @@ pub enum Column {
|
||||||
Permissions,
|
Permissions,
|
||||||
FileSize,
|
FileSize,
|
||||||
Timestamp(TimeType),
|
Timestamp(TimeType),
|
||||||
|
#[cfg(unix)]
|
||||||
Blocks,
|
Blocks,
|
||||||
|
#[cfg(unix)]
|
||||||
User,
|
User,
|
||||||
|
#[cfg(unix)]
|
||||||
Group,
|
Group,
|
||||||
|
#[cfg(unix)]
|
||||||
HardLinks,
|
HardLinks,
|
||||||
|
#[cfg(unix)]
|
||||||
Inode,
|
Inode,
|
||||||
GitStatus,
|
GitStatus,
|
||||||
|
#[cfg(unix)]
|
||||||
Octal,
|
Octal,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,6 +150,7 @@ pub enum Alignment {
|
||||||
impl Column {
|
impl Column {
|
||||||
|
|
||||||
/// Get the alignment this column should use.
|
/// Get the alignment this column should use.
|
||||||
|
#[cfg(unix)]
|
||||||
pub fn alignment(self) -> Alignment {
|
pub fn alignment(self) -> Alignment {
|
||||||
match self {
|
match self {
|
||||||
Self::FileSize |
|
Self::FileSize |
|
||||||
|
@ -146,19 +162,37 @@ impl Column {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
pub fn alignment(&self) -> Alignment {
|
||||||
|
match self {
|
||||||
|
Self::FileSize |
|
||||||
|
Self::GitStatus => Alignment::Right,
|
||||||
|
_ => Alignment::Left,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the text that should be printed at the top, when the user elects
|
/// Get the text that should be printed at the top, when the user elects
|
||||||
/// to have a header row printed.
|
/// to have a header row printed.
|
||||||
pub fn header(self) -> &'static str {
|
pub fn header(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
|
#[cfg(unix)]
|
||||||
Self::Permissions => "Permissions",
|
Self::Permissions => "Permissions",
|
||||||
|
#[cfg(windows)]
|
||||||
|
Self::Permissions => "Mode",
|
||||||
Self::FileSize => "Size",
|
Self::FileSize => "Size",
|
||||||
Self::Timestamp(t) => t.header(),
|
Self::Timestamp(t) => t.header(),
|
||||||
|
#[cfg(unix)]
|
||||||
Self::Blocks => "Blocks",
|
Self::Blocks => "Blocks",
|
||||||
|
#[cfg(unix)]
|
||||||
Self::User => "User",
|
Self::User => "User",
|
||||||
|
#[cfg(unix)]
|
||||||
Self::Group => "Group",
|
Self::Group => "Group",
|
||||||
|
#[cfg(unix)]
|
||||||
Self::HardLinks => "Links",
|
Self::HardLinks => "Links",
|
||||||
|
#[cfg(unix)]
|
||||||
Self::Inode => "inode",
|
Self::Inode => "inode",
|
||||||
Self::GitStatus => "Git",
|
Self::GitStatus => "Git",
|
||||||
|
#[cfg(unix)]
|
||||||
Self::Octal => "Octal",
|
Self::Octal => "Octal",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,7 +200,8 @@ impl Column {
|
||||||
|
|
||||||
|
|
||||||
/// Formatting options for file sizes.
|
/// Formatting options for file sizes.
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[allow(clippy::enum_variant_names)]
|
||||||
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
pub enum SizeFormat {
|
pub enum SizeFormat {
|
||||||
|
|
||||||
/// Format the file size using **decimal** prefixes, such as “kilo”,
|
/// Format the file size using **decimal** prefixes, such as “kilo”,
|
||||||
|
@ -182,7 +217,7 @@ pub enum SizeFormat {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Formatting options for user and group.
|
/// Formatting options for user and group.
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
pub enum UserFormat {
|
pub enum UserFormat {
|
||||||
/// The UID / GID
|
/// The UID / GID
|
||||||
Numeric,
|
Numeric,
|
||||||
|
@ -199,7 +234,7 @@ impl Default for SizeFormat {
|
||||||
|
|
||||||
/// The types of a file’s time fields. These three fields are standard
|
/// The types of a file’s time fields. These three fields are standard
|
||||||
/// across most (all?) operating systems.
|
/// across most (all?) operating systems.
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
pub enum TimeType {
|
pub enum TimeType {
|
||||||
|
|
||||||
/// The file’s modified time (`st_mtime`).
|
/// The file’s modified time (`st_mtime`).
|
||||||
|
@ -234,7 +269,7 @@ impl TimeType {
|
||||||
///
|
///
|
||||||
/// There should always be at least one of these — there’s no way to disable
|
/// There should always be at least one of these — there’s no way to disable
|
||||||
/// the time columns entirely (yet).
|
/// the time columns entirely (yet).
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
#[allow(clippy::struct_excessive_bools)]
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
pub struct TimeTypes {
|
pub struct TimeTypes {
|
||||||
pub modified: bool,
|
pub modified: bool,
|
||||||
|
@ -272,10 +307,12 @@ pub struct Environment {
|
||||||
tz: Option<TimeZone>,
|
tz: Option<TimeZone>,
|
||||||
|
|
||||||
/// Mapping cache of user IDs to usernames.
|
/// Mapping cache of user IDs to usernames.
|
||||||
|
#[cfg(unix)]
|
||||||
users: Mutex<UsersCache>,
|
users: Mutex<UsersCache>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Environment {
|
impl Environment {
|
||||||
|
#[cfg(unix)]
|
||||||
pub fn lock_users(&self) -> MutexGuard<'_, UsersCache> {
|
pub fn lock_users(&self) -> MutexGuard<'_, UsersCache> {
|
||||||
self.users.lock().unwrap()
|
self.users.lock().unwrap()
|
||||||
}
|
}
|
||||||
|
@ -294,21 +331,23 @@ impl Environment {
|
||||||
let numeric = locale::Numeric::load_user_locale()
|
let numeric = locale::Numeric::load_user_locale()
|
||||||
.unwrap_or_else(|_| locale::Numeric::english());
|
.unwrap_or_else(|_| locale::Numeric::english());
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
let users = Mutex::new(UsersCache::new());
|
let users = Mutex::new(UsersCache::new());
|
||||||
|
|
||||||
Self { tz, numeric, users }
|
Self { numeric, tz, #[cfg(unix)] users }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
fn determine_time_zone() -> TZResult<TimeZone> {
|
fn determine_time_zone() -> TZResult<TimeZone> {
|
||||||
if let Ok(file) = env::var("TZ") {
|
if let Ok(file) = env::var("TZ") {
|
||||||
TimeZone::from_file({
|
TimeZone::from_file({
|
||||||
if file.starts_with("/") {
|
if file.starts_with('/') {
|
||||||
file
|
file
|
||||||
} else {
|
} else {
|
||||||
format!("/usr/share/zoneinfo/{}", {
|
format!("/usr/share/zoneinfo/{}", {
|
||||||
if file.starts_with(":") {
|
if file.starts_with(':') {
|
||||||
file.replacen(":", "", 1)
|
file.replacen(':', "", 1)
|
||||||
} else {
|
} else {
|
||||||
file
|
file
|
||||||
}
|
}
|
||||||
|
@ -320,6 +359,31 @@ fn determine_time_zone() -> TZResult<TimeZone> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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! {
|
lazy_static! {
|
||||||
static ref ENVIRONMENT: Environment = Environment::load_all();
|
static ref ENVIRONMENT: Environment = Environment::load_all();
|
||||||
}
|
}
|
||||||
|
@ -386,11 +450,15 @@ impl<'a, 'f> Table<'a> {
|
||||||
fn permissions_plus(&self, file: &File<'_>, xattrs: bool) -> f::PermissionsPlus {
|
fn permissions_plus(&self, file: &File<'_>, xattrs: bool) -> f::PermissionsPlus {
|
||||||
f::PermissionsPlus {
|
f::PermissionsPlus {
|
||||||
file_type: file.type_char(),
|
file_type: file.type_char(),
|
||||||
|
#[cfg(unix)]
|
||||||
permissions: file.permissions(),
|
permissions: file.permissions(),
|
||||||
|
#[cfg(windows)]
|
||||||
|
attributes: file.attributes(),
|
||||||
xattrs,
|
xattrs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
fn octal_permissions(&self, file: &File<'_>) -> f::OctalPermissions {
|
fn octal_permissions(&self, file: &File<'_>) -> f::OctalPermissions {
|
||||||
f::OctalPermissions {
|
f::OctalPermissions {
|
||||||
permissions: file.permissions(),
|
permissions: file.permissions(),
|
||||||
|
@ -405,24 +473,30 @@ impl<'a, 'f> Table<'a> {
|
||||||
Column::FileSize => {
|
Column::FileSize => {
|
||||||
file.size().render(self.theme, self.size_format, &self.env.numeric)
|
file.size().render(self.theme, self.size_format, &self.env.numeric)
|
||||||
}
|
}
|
||||||
|
#[cfg(unix)]
|
||||||
Column::HardLinks => {
|
Column::HardLinks => {
|
||||||
file.links().render(self.theme, &self.env.numeric)
|
file.links().render(self.theme, &self.env.numeric)
|
||||||
}
|
}
|
||||||
|
#[cfg(unix)]
|
||||||
Column::Inode => {
|
Column::Inode => {
|
||||||
file.inode().render(self.theme.ui.inode)
|
file.inode().render(self.theme.ui.inode)
|
||||||
}
|
}
|
||||||
|
#[cfg(unix)]
|
||||||
Column::Blocks => {
|
Column::Blocks => {
|
||||||
file.blocks().render(self.theme)
|
file.blocks().render(self.theme)
|
||||||
}
|
}
|
||||||
|
#[cfg(unix)]
|
||||||
Column::User => {
|
Column::User => {
|
||||||
file.user().render(self.theme, &*self.env.lock_users(), self.user_format)
|
file.user().render(self.theme, &*self.env.lock_users(), self.user_format)
|
||||||
}
|
}
|
||||||
|
#[cfg(unix)]
|
||||||
Column::Group => {
|
Column::Group => {
|
||||||
file.group().render(self.theme, &*self.env.lock_users(), self.user_format)
|
file.group().render(self.theme, &*self.env.lock_users(), self.user_format)
|
||||||
}
|
}
|
||||||
Column::GitStatus => {
|
Column::GitStatus => {
|
||||||
self.git_status(file).render(self.theme)
|
self.git_status(file).render(self.theme)
|
||||||
}
|
}
|
||||||
|
#[cfg(unix)]
|
||||||
Column::Octal => {
|
Column::Octal => {
|
||||||
self.octal_permissions(file).render(self.theme.ui.octal)
|
self.octal_permissions(file).render(self.theme.ui.octal)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
use datetime::{LocalDateTime, TimeZone, DatePiece, TimePiece, Month};
|
use datetime::{LocalDateTime, TimeZone, DatePiece, TimePiece};
|
||||||
use datetime::fmt::DateFormat;
|
use datetime::fmt::DateFormat;
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
@ -25,7 +25,7 @@ use unicode_width::UnicodeWidthStr;
|
||||||
///
|
///
|
||||||
/// Currently exa does not support *custom* styles, where the user enters a
|
/// Currently exa does not support *custom* styles, where the user enters a
|
||||||
/// format string in an environment variable or something. Just these four.
|
/// format string in an environment variable or something. Just these four.
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
pub enum TimeFormat {
|
pub enum TimeFormat {
|
||||||
|
|
||||||
/// The **default format** uses the user’s locale to print month names,
|
/// The **default format** uses the user’s locale to print month names,
|
||||||
|
@ -75,40 +75,25 @@ impl TimeFormat {
|
||||||
#[allow(trivial_numeric_casts)]
|
#[allow(trivial_numeric_casts)]
|
||||||
fn default_local(time: SystemTime) -> String {
|
fn default_local(time: SystemTime) -> String {
|
||||||
let date = LocalDateTime::at(systemtime_epoch(time));
|
let date = LocalDateTime::at(systemtime_epoch(time));
|
||||||
|
let date_format = get_dateformat(&date);
|
||||||
if date.year() == *CURRENT_YEAR {
|
date_format.format(&date, &*LOCALE)
|
||||||
format!("{:2} {} {:02}:{:02}",
|
|
||||||
date.day(), month_to_abbrev(date.month()),
|
|
||||||
date.hour(), date.minute())
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
let date_format = match *MAXIMUM_MONTH_WIDTH {
|
|
||||||
4 => &*FOUR_WIDE_DATE_TIME,
|
|
||||||
5 => &*FIVE_WIDE_DATE_TIME,
|
|
||||||
_ => &*OTHER_WIDE_DATE_TIME,
|
|
||||||
};
|
|
||||||
|
|
||||||
date_format.format(&date, &*LOCALE)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(trivial_numeric_casts)]
|
#[allow(trivial_numeric_casts)]
|
||||||
fn default_zoned(time: SystemTime, zone: &TimeZone) -> String {
|
fn default_zoned(time: SystemTime, zone: &TimeZone) -> String {
|
||||||
let date = zone.to_zoned(LocalDateTime::at(systemtime_epoch(time)));
|
let date = zone.to_zoned(LocalDateTime::at(systemtime_epoch(time)));
|
||||||
|
let date_format = get_dateformat(&date);
|
||||||
|
date_format.format(&date, &*LOCALE)
|
||||||
|
}
|
||||||
|
|
||||||
if date.year() == *CURRENT_YEAR {
|
fn get_dateformat(date: &LocalDateTime) -> &'static DateFormat<'static> {
|
||||||
format!("{:2} {} {:02}:{:02}",
|
match (is_recent(date), *MAXIMUM_MONTH_WIDTH) {
|
||||||
date.day(), month_to_abbrev(date.month()),
|
(true, 4) => &FOUR_WIDE_DATE_TIME,
|
||||||
date.hour(), date.minute())
|
(true, 5) => &FIVE_WIDE_DATE_TIME,
|
||||||
}
|
(true, _) => &OTHER_WIDE_DATE_TIME,
|
||||||
else {
|
(false, 4) => &FOUR_WIDE_DATE_YEAR,
|
||||||
let date_format = match *MAXIMUM_MONTH_WIDTH {
|
(false, 5) => &FIVE_WIDE_DATE_YEAR,
|
||||||
4 => &*FOUR_WIDE_DATE_YEAR,
|
(false, _) => &OTHER_WIDE_DATE_YEAR,
|
||||||
5 => &*FIVE_WIDE_DATE_YEAR,
|
|
||||||
_ => &*OTHER_WIDE_DATE_YEAR,
|
|
||||||
};
|
|
||||||
|
|
||||||
date_format.format(&date, &*LOCALE)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,7 +138,7 @@ fn full_zoned(time: SystemTime, zone: &TimeZone) -> String {
|
||||||
fn iso_local(time: SystemTime) -> String {
|
fn iso_local(time: SystemTime) -> String {
|
||||||
let date = LocalDateTime::at(systemtime_epoch(time));
|
let date = LocalDateTime::at(systemtime_epoch(time));
|
||||||
|
|
||||||
if is_recent(date) {
|
if is_recent(&date) {
|
||||||
format!("{:02}-{:02} {:02}:{:02}",
|
format!("{:02}-{:02} {:02}:{:02}",
|
||||||
date.month() as usize, date.day(),
|
date.month() as usize, date.day(),
|
||||||
date.hour(), date.minute())
|
date.hour(), date.minute())
|
||||||
|
@ -168,7 +153,7 @@ fn iso_local(time: SystemTime) -> String {
|
||||||
fn iso_zoned(time: SystemTime, zone: &TimeZone) -> String {
|
fn iso_zoned(time: SystemTime, zone: &TimeZone) -> String {
|
||||||
let date = zone.to_zoned(LocalDateTime::at(systemtime_epoch(time)));
|
let date = zone.to_zoned(LocalDateTime::at(systemtime_epoch(time)));
|
||||||
|
|
||||||
if is_recent(date) {
|
if is_recent(&date) {
|
||||||
format!("{:02}-{:02} {:02}:{:02}",
|
format!("{:02}-{:02} {:02}:{:02}",
|
||||||
date.month() as usize, date.day(),
|
date.month() as usize, date.day(),
|
||||||
date.hour(), date.minute())
|
date.hour(), date.minute())
|
||||||
|
@ -206,27 +191,10 @@ fn systemtime_nanos(time: SystemTime) -> u32 {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_recent(date: LocalDateTime) -> bool {
|
fn is_recent(date: &LocalDateTime) -> bool {
|
||||||
date.year() == *CURRENT_YEAR
|
date.year() == *CURRENT_YEAR
|
||||||
}
|
}
|
||||||
|
|
||||||
fn month_to_abbrev(month: Month) -> &'static str {
|
|
||||||
match month {
|
|
||||||
Month::January => "Jan",
|
|
||||||
Month::February => "Feb",
|
|
||||||
Month::March => "Mar",
|
|
||||||
Month::April => "Apr",
|
|
||||||
Month::May => "May",
|
|
||||||
Month::June => "Jun",
|
|
||||||
Month::July => "Jul",
|
|
||||||
Month::August => "Aug",
|
|
||||||
Month::September => "Sep",
|
|
||||||
Month::October => "Oct",
|
|
||||||
Month::November => "Nov",
|
|
||||||
Month::December => "Dec",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
|
||||||
|
@ -250,15 +218,15 @@ lazy_static! {
|
||||||
};
|
};
|
||||||
|
|
||||||
static ref FOUR_WIDE_DATE_TIME: DateFormat<'static> = DateFormat::parse(
|
static ref FOUR_WIDE_DATE_TIME: DateFormat<'static> = DateFormat::parse(
|
||||||
"{2>:D} {4<:M} {2>:h}:{02>:m}"
|
"{2>:D} {4<:M} {02>:h}:{02>:m}"
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
static ref FIVE_WIDE_DATE_TIME: DateFormat<'static> = DateFormat::parse(
|
static ref FIVE_WIDE_DATE_TIME: DateFormat<'static> = DateFormat::parse(
|
||||||
"{2>:D} {5<:M} {2>:h}:{02>:m}"
|
"{2>:D} {5<:M} {02>:h}:{02>:m}"
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
static ref OTHER_WIDE_DATE_TIME: DateFormat<'static> = DateFormat::parse(
|
static ref OTHER_WIDE_DATE_TIME: DateFormat<'static> = DateFormat::parse(
|
||||||
"{2>:D} {:M} {2>:h}:{02>:m}"
|
"{2>:D} {:M} {02>:h}:{02>:m}"
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
static ref FOUR_WIDE_DATE_YEAR: DateFormat<'static> = DateFormat::parse(
|
static ref FOUR_WIDE_DATE_YEAR: DateFormat<'static> = DateFormat::parse(
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
//! each directory)
|
//! each directory)
|
||||||
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
pub enum TreePart {
|
pub enum TreePart {
|
||||||
|
|
||||||
/// Rightmost column, *not* the last in the directory.
|
/// Rightmost column, *not* the last in the directory.
|
||||||
|
@ -253,19 +253,19 @@ mod iter_test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_iteration() {
|
fn test_iteration() {
|
||||||
let foos = &[ "first", "middle", "last" ];
|
let foos = &[ "first", "middle", "last" ];
|
||||||
let mut iter = TreeDepth::root().iterate_over(foos.into_iter());
|
let mut iter = TreeDepth::root().iterate_over(foos.iter());
|
||||||
|
|
||||||
let next = iter.next().unwrap();
|
let next = iter.next().unwrap();
|
||||||
assert_eq!(&"first", next.1);
|
assert_eq!(&"first", next.1);
|
||||||
assert_eq!(false, next.0.last);
|
assert!(!next.0.last);
|
||||||
|
|
||||||
let next = iter.next().unwrap();
|
let next = iter.next().unwrap();
|
||||||
assert_eq!(&"middle", next.1);
|
assert_eq!(&"middle", next.1);
|
||||||
assert_eq!(false, next.0.last);
|
assert!(!next.0.last);
|
||||||
|
|
||||||
let next = iter.next().unwrap();
|
let next = iter.next().unwrap();
|
||||||
assert_eq!(&"last", next.1);
|
assert_eq!(&"last", next.1);
|
||||||
assert_eq!(true, next.0.last);
|
assert!(next.0.last);
|
||||||
|
|
||||||
assert!(iter.next().is_none());
|
assert!(iter.next().is_none());
|
||||||
}
|
}
|
||||||
|
@ -273,7 +273,7 @@ mod iter_test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_empty() {
|
fn test_empty() {
|
||||||
let nothing: &[usize] = &[];
|
let nothing: &[usize] = &[];
|
||||||
let mut iter = TreeDepth::root().iterate_over(nothing.into_iter());
|
let mut iter = TreeDepth::root().iterate_over(nothing.iter());
|
||||||
assert!(iter.next().is_none());
|
assert!(iter.next().is_none());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ pub use self::lsc::LSColors;
|
||||||
mod default_theme;
|
mod default_theme;
|
||||||
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
|
|
||||||
pub use_colours: UseColours,
|
pub use_colours: UseColours,
|
||||||
|
@ -31,7 +31,7 @@ pub struct Options {
|
||||||
/// Turning them on when output is going to, say, a pipe, would make programs
|
/// Turning them on when output is going to, say, a pipe, would make programs
|
||||||
/// such as `grep` or `more` not work properly. So the `Automatic` mode does
|
/// such as `grep` or `more` not work properly. So the `Automatic` mode does
|
||||||
/// this check and only displays colours when they can be truly appreciated.
|
/// this check and only displays colours when they can be truly appreciated.
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
pub enum UseColours {
|
pub enum UseColours {
|
||||||
|
|
||||||
/// Display them even when output isn’t going to a terminal.
|
/// Display them even when output isn’t going to a terminal.
|
||||||
|
@ -44,13 +44,13 @@ pub enum UseColours {
|
||||||
Never,
|
Never,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
pub enum ColourScale {
|
pub enum ColourScale {
|
||||||
Fixed,
|
Fixed,
|
||||||
Gradient,
|
Gradient,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Default)]
|
#[derive(PartialEq, Eq, Debug, Default)]
|
||||||
pub struct Definitions {
|
pub struct Definitions {
|
||||||
pub ls: Option<String>,
|
pub ls: Option<String>,
|
||||||
pub exa: Option<String>,
|
pub exa: Option<String>,
|
||||||
|
@ -229,6 +229,7 @@ impl render::GitColours for Theme {
|
||||||
fn conflicted(&self) -> Style { self.ui.git.conflicted }
|
fn conflicted(&self) -> Style { self.ui.git.conflicted }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
impl render::GroupColours for Theme {
|
impl render::GroupColours for Theme {
|
||||||
fn yours(&self) -> Style { self.ui.users.group_yours }
|
fn yours(&self) -> Style { self.ui.users.group_yours }
|
||||||
fn not_yours(&self) -> Style { self.ui.users.group_not_yours }
|
fn not_yours(&self) -> Style { self.ui.users.group_not_yours }
|
||||||
|
@ -261,11 +262,11 @@ impl render::SizeColours for Theme {
|
||||||
use number_prefix::Prefix::*;
|
use number_prefix::Prefix::*;
|
||||||
|
|
||||||
match prefix {
|
match prefix {
|
||||||
None => self.ui.size.number_byte,
|
Some(Kilo | Kibi) => self.ui.size.number_kilo,
|
||||||
Some(Kilo) | Some(Kibi) => self.ui.size.number_kilo,
|
Some(Mega | Mebi) => self.ui.size.number_mega,
|
||||||
Some(Mega) | Some(Mebi) => self.ui.size.number_mega,
|
Some(Giga | Gibi) => self.ui.size.number_giga,
|
||||||
Some(Giga) | Some(Gibi) => self.ui.size.number_giga,
|
Some(_) => self.ui.size.number_huge,
|
||||||
Some(_) => self.ui.size.number_huge,
|
None => self.ui.size.number_byte,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,11 +274,11 @@ impl render::SizeColours for Theme {
|
||||||
use number_prefix::Prefix::*;
|
use number_prefix::Prefix::*;
|
||||||
|
|
||||||
match prefix {
|
match prefix {
|
||||||
None => self.ui.size.unit_byte,
|
Some(Kilo | Kibi) => self.ui.size.unit_kilo,
|
||||||
Some(Kilo) | Some(Kibi) => self.ui.size.unit_kilo,
|
Some(Mega | Mebi) => self.ui.size.unit_mega,
|
||||||
Some(Mega) | Some(Mebi) => self.ui.size.unit_mega,
|
Some(Giga | Gibi) => self.ui.size.unit_giga,
|
||||||
Some(Giga) | Some(Gibi) => self.ui.size.unit_giga,
|
Some(_) => self.ui.size.unit_huge,
|
||||||
Some(_) => self.ui.size.unit_huge,
|
None => self.ui.size.unit_byte,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,6 +288,7 @@ impl render::SizeColours for Theme {
|
||||||
fn minor(&self) -> Style { self.ui.size.minor }
|
fn minor(&self) -> Style { self.ui.size.minor }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
impl render::UserColours for Theme {
|
impl render::UserColours for Theme {
|
||||||
fn you(&self) -> Style { self.ui.users.user_you }
|
fn you(&self) -> Style { self.ui.users.user_you }
|
||||||
fn someone_else(&self) -> Style { self.ui.users.user_someone_else }
|
fn someone_else(&self) -> Style { self.ui.users.user_someone_else }
|
||||||
|
|
|
@ -68,7 +68,7 @@ tags = [ 'long', 'time-style' ]
|
||||||
[[cmd]]
|
[[cmd]]
|
||||||
name = "‘exa -l’ using a locale with 4-character-long month abbreviations (‘ja_JP’) sizes the date column correctly"
|
name = "‘exa -l’ using a locale with 4-character-long month abbreviations (‘ja_JP’) sizes the date column correctly"
|
||||||
shell = "exa -l /testcases/dates"
|
shell = "exa -l /testcases/dates"
|
||||||
environment = { LC_ALL = "ja_JP.UTF-8", LANG = "ja_JP.UTF-8" }
|
environment = { LC_TIME = "ja_JP.UTF-8", LANG = "ja_JP.UTF-8" }
|
||||||
stdout = { file = "outputs/dates_long_localejp.ansitxt" }
|
stdout = { file = "outputs/dates_long_localejp.ansitxt" }
|
||||||
stderr = { empty = true }
|
stderr = { empty = true }
|
||||||
status = 0
|
status = 0
|
||||||
|
@ -77,8 +77,17 @@ tags = [ 'long', 'locales' ]
|
||||||
[[cmd]]
|
[[cmd]]
|
||||||
name = "‘exa -l’ using a locale with 5-character-long month abbreviations (‘fr_FR’) sizes the date column correctly"
|
name = "‘exa -l’ using a locale with 5-character-long month abbreviations (‘fr_FR’) sizes the date column correctly"
|
||||||
shell = "exa -l /testcases/dates"
|
shell = "exa -l /testcases/dates"
|
||||||
environment = { LC_ALL = "fr_FR.UTF-8", LANG = "fr_FR.UTF-8" }
|
environment = { LC_TIME = "fr_FR.UTF-8", LANG = "fr_FR.UTF-8" }
|
||||||
stdout = { file = "outputs/dates_long_localefr.ansitxt" }
|
stdout = { file = "outputs/dates_long_localefr.ansitxt" }
|
||||||
stderr = { empty = true }
|
stderr = { empty = true }
|
||||||
status = 0
|
status = 0
|
||||||
tags = [ 'long', 'locales' ]
|
tags = [ 'long', 'locales' ]
|
||||||
|
|
||||||
|
[[cmd]]
|
||||||
|
name = "‘exa -l’ using a locale (‘fr_FR’) display dates of the current year with localized month name"
|
||||||
|
shell = "exa -l /testcases/files"
|
||||||
|
environment = { LC_TIME = "fr_FR.UTF-8", LANG = "fr_FR.UTF-8" }
|
||||||
|
stdout = { file = "outputs/dates_long_currentyear_localefr.ansitxt" }
|
||||||
|
stderr = { empty = true }
|
||||||
|
status = 0
|
||||||
|
tags = [ 'long', 'locales' ]
|
||||||
|
|
|
@ -61,3 +61,14 @@ stdout = { file = "outputs/files_long_colourscale_bytes.ansitxt" }
|
||||||
stderr = { empty = true }
|
stderr = { empty = true }
|
||||||
status = 0
|
status = 0
|
||||||
tags = [ 'long', 'colour-scale', 'bytes' ]
|
tags = [ 'long', 'colour-scale', 'bytes' ]
|
||||||
|
|
||||||
|
[[cmd]]
|
||||||
|
name = "‘exa -l’ produces a details table with major and minor device IDs"
|
||||||
|
shell = "cd /dev; exa -l mem null port zero full random urandom --sort=none --no-time"
|
||||||
|
stdout = { file = "outputs/dev_long.ansitxt" }
|
||||||
|
stderr = { empty = true }
|
||||||
|
status = 0
|
||||||
|
tags = [ 'long', 'dev' ]
|
||||||
|
|
||||||
|
# these particular device IDs should be fixed:
|
||||||
|
# https://raw.githubusercontent.com/torvalds/linux/master/Documentation/admin-guide/devices.txt
|
||||||
|
|
|
@ -43,3 +43,11 @@ stdout = { file = "outputs/specials_long_classify.ansitxt" }
|
||||||
stderr = { empty = true }
|
stderr = { empty = true }
|
||||||
status = 0
|
status = 0
|
||||||
tags = [ 'long', 'classify' ]
|
tags = [ 'long', 'classify' ]
|
||||||
|
|
||||||
|
[[cmd]]
|
||||||
|
name = "‘exa -lF’ handles and classifies symlink kinds"
|
||||||
|
shell = "exa -lF --no-time /testcases/links"
|
||||||
|
stdout = { file = "outputs/links_long_classify.ansitxt" }
|
||||||
|
stderr = { empty = true }
|
||||||
|
status = 0
|
||||||
|
tags = [ 'long', 'classify' ]
|
||||||
|
|
|
@ -1,3 +1,22 @@
|
||||||
|
# Command-line errors
|
||||||
|
|
||||||
|
[[cmd]]
|
||||||
|
name = "‘exa --aoeu’ displays an error"
|
||||||
|
shell = "exa --aoeu"
|
||||||
|
stdout = { empty = true }
|
||||||
|
stderr = { file = "outputs/error_invalid_option.ansitxt" }
|
||||||
|
status = 3
|
||||||
|
tags = [ 'error' ]
|
||||||
|
|
||||||
|
[[cmd]]
|
||||||
|
name = "‘exa -Taa’ displays an error"
|
||||||
|
shell = "exa -Taa"
|
||||||
|
stdout = { empty = true }
|
||||||
|
stderr = { file = "outputs/error_tree_all_all.ansitxt" }
|
||||||
|
status = 3
|
||||||
|
tags = [ 'error' ]
|
||||||
|
|
||||||
|
|
||||||
# Error suggestions
|
# Error suggestions
|
||||||
|
|
||||||
[[cmd]]
|
[[cmd]]
|
||||||
|
@ -15,3 +34,85 @@ stdout = { empty = true }
|
||||||
stderr = { string = "To sort newest files last, try \"--sort newest\", or just \"-snew\""}
|
stderr = { string = "To sort newest files last, try \"--sort newest\", or just \"-snew\""}
|
||||||
status = 3
|
status = 3
|
||||||
tags = [ 'error', 'long', 'sort' ]
|
tags = [ 'error', 'long', 'sort' ]
|
||||||
|
|
||||||
|
|
||||||
|
# Invalid values for $COLUMNS
|
||||||
|
|
||||||
|
[[cmd]]
|
||||||
|
name = "‘COLUMNS=999... exa’ shows an error about the number size"
|
||||||
|
shell = "exa"
|
||||||
|
environment = { "COLUMNS" = "99999999999999999999999" }
|
||||||
|
stdout = { empty = true }
|
||||||
|
stderr = { file = "outputs/error_columns_nines.ansitxt" }
|
||||||
|
status = 3
|
||||||
|
tags = [ 'error', 'env' ]
|
||||||
|
|
||||||
|
[[cmd]]
|
||||||
|
name = "‘COLUMNS=abcdef exa’ shows an error about invalid digits"
|
||||||
|
shell = "exa"
|
||||||
|
environment = { "COLUMNS" = "abcdef" }
|
||||||
|
stdout = { empty = true }
|
||||||
|
stderr = { file = "outputs/error_columns_invalid.ansitxt" }
|
||||||
|
status = 3
|
||||||
|
tags = [ 'error', 'env' ]
|
||||||
|
|
||||||
|
|
||||||
|
# Invalid values for $EXA_GRID_ROWS
|
||||||
|
|
||||||
|
[[cmd]]
|
||||||
|
name = "‘EXA_GRID_ROWS=999... exa -lG’ shows an error about the number size"
|
||||||
|
shell = "exa -lG"
|
||||||
|
environment = { "EXA_GRID_ROWS" = "99999999999999999999999" }
|
||||||
|
stdout = { empty = true }
|
||||||
|
stderr = { file = "outputs/error_grid_rows_nines.ansitxt" }
|
||||||
|
status = 3
|
||||||
|
tags = [ 'error', 'env' ]
|
||||||
|
|
||||||
|
[[cmd]]
|
||||||
|
name = "‘EXA_GRID_ROWS=abcdef exa -lG’ shows an error about invalid digits"
|
||||||
|
shell = "exa -lG"
|
||||||
|
environment = { "EXA_GRID_ROWS" = "abcdef" }
|
||||||
|
stdout = { empty = true }
|
||||||
|
stderr = { file = "outputs/error_grid_rows_invalid.ansitxt" }
|
||||||
|
status = 3
|
||||||
|
tags = [ 'error', 'env' ]
|
||||||
|
|
||||||
|
|
||||||
|
# Invalid values for $EXA_ICON_SPACING
|
||||||
|
|
||||||
|
[[cmd]]
|
||||||
|
name = "‘EXA_ICON_SPACING=999... exa --icons’ shows an error about the number size"
|
||||||
|
shell = "exa --icons"
|
||||||
|
environment = { "EXA_ICON_SPACING" = "99999999999999999999999" }
|
||||||
|
stdout = { empty = true }
|
||||||
|
stderr = { file = "outputs/error_icon_spacing_nines.ansitxt" }
|
||||||
|
status = 3
|
||||||
|
tags = [ 'error', 'env', 'icons' ]
|
||||||
|
|
||||||
|
[[cmd]]
|
||||||
|
name = "‘EXA_ICON_SPACING=abcdef exa --icons’ shows an error about invalid digits"
|
||||||
|
shell = "exa --icons"
|
||||||
|
environment = { "EXA_ICON_SPACING" = "abcdef" }
|
||||||
|
stdout = { empty = true }
|
||||||
|
stderr = { file = "outputs/error_icon_spacing_invalid.ansitxt" }
|
||||||
|
status = 3
|
||||||
|
tags = [ 'error', 'env', 'icons' ]
|
||||||
|
|
||||||
|
|
||||||
|
# Invalid values for --level (-L)
|
||||||
|
|
||||||
|
[[cmd]]
|
||||||
|
name = "‘exa -TL999...’ shows an error about the number size"
|
||||||
|
shell = "exa -TL99999999999999999999999"
|
||||||
|
stdout = { empty = true }
|
||||||
|
stderr = { file = "outputs/error_level_nines.ansitxt" }
|
||||||
|
status = 3
|
||||||
|
tags = [ 'error', 'tree', 'level' ]
|
||||||
|
|
||||||
|
[[cmd]]
|
||||||
|
name = "‘exa -TLabcdef’ shows an error about invalid digits"
|
||||||
|
shell = "exa -TLabcdef"
|
||||||
|
stdout = { empty = true }
|
||||||
|
stderr = { file = "outputs/error_level_invalid.ansitxt" }
|
||||||
|
status = 3
|
||||||
|
tags = [ 'error', 'tree', 'level' ]
|
||||||
|
|
|
@ -101,13 +101,21 @@ status = 0
|
||||||
tags = [ 'long', 'git' ]
|
tags = [ 'long', 'git' ]
|
||||||
|
|
||||||
[[cmd]]
|
[[cmd]]
|
||||||
name = "‘exa --git -l’ with an ignored directory argument does not flag the contents as ignored"
|
name = "‘exa --git -l’ with an ignored directory argument flags the contents as ignored"
|
||||||
shell = "exa --git -l /testcases/git2/target"
|
shell = "exa --git -l /testcases/git2/target"
|
||||||
stdout = { file = "outputs/git2_long_ignoreddir.ansitxt" }
|
stdout = { file = "outputs/git2_long_ignoreddir.ansitxt" }
|
||||||
stderr = { empty = true }
|
stderr = { empty = true }
|
||||||
status = 0
|
status = 0
|
||||||
tags = [ 'long', 'git' ]
|
tags = [ 'long', 'git' ]
|
||||||
|
|
||||||
|
[[cmd]]
|
||||||
|
name = "‘exa --git -l --list-dirs’ with a directory argument doesn’t flag it as ignored if only the content is"
|
||||||
|
shell = "exa --git -l --list-dirs /testcases/git2/ignoreds/nested2"
|
||||||
|
stdout = { file = "outputs/git2_long_ignoredcontent.ansitxt" }
|
||||||
|
stderr = { empty = true }
|
||||||
|
status = 0
|
||||||
|
tags = [ 'long', 'git' ]
|
||||||
|
|
||||||
[[cmd]]
|
[[cmd]]
|
||||||
name = "‘exa --git -l’ with a nested repository argument uses the sub-repository rules"
|
name = "‘exa --git -l’ with a nested repository argument uses the sub-repository rules"
|
||||||
shell = "exa --git -l /testcases/git2/deeply/nested/repository"
|
shell = "exa --git -l /testcases/git2/deeply/nested/repository"
|
||||||
|
@ -136,6 +144,18 @@ status = 0
|
||||||
tags = [ 'long', 'git' ]
|
tags = [ 'long', 'git' ]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# The forth Git repo: non UTF-8 file
|
||||||
|
|
||||||
|
[[cmd]]
|
||||||
|
name = "‘exa --git -l’ handles non UTF8 file in Git repositories"
|
||||||
|
shell = "exa --git -l /testcases/git4"
|
||||||
|
stdout = { file = "outputs/git4_long.ansitxt" }
|
||||||
|
stderr = { empty = true }
|
||||||
|
status = 0
|
||||||
|
tags = [ 'long', 'git' ]
|
||||||
|
|
||||||
|
|
||||||
# Both repositories 1 and 2 at once
|
# Both repositories 1 and 2 at once
|
||||||
|
|
||||||
[[cmd]]
|
[[cmd]]
|
||||||
|
@ -155,7 +175,7 @@ status = 0
|
||||||
tags = [ 'long', 'git' ]
|
tags = [ 'long', 'git' ]
|
||||||
|
|
||||||
[[cmd]]
|
[[cmd]]
|
||||||
name = "‘exa --git -l’ shows a Git status column for multiple repositories across multiple directories"
|
name = "‘exa --git -l’ shows a Git status column for multiple repositories across multiple directories 2"
|
||||||
shell = "exa --git -l /testcases/{git2/deeply/nested/directory,git/edits,git2/target,git2/deeply,git}"
|
shell = "exa --git -l /testcases/{git2/deeply/nested/directory,git/edits,git2/target,git2/deeply,git}"
|
||||||
stdout = { file = "outputs/git1+2_long_nested.ansitxt" }
|
stdout = { file = "outputs/git1+2_long_nested.ansitxt" }
|
||||||
stderr = { empty = true }
|
stderr = { empty = true }
|
||||||
|
|
|
@ -81,19 +81,49 @@ tags = [ 'env', 'long', 'grid' ]
|
||||||
# check if exa is using the minimum number of columns with headers
|
# check if exa is using the minimum number of columns with headers
|
||||||
|
|
||||||
[[cmd]]
|
[[cmd]]
|
||||||
name = "‘COLUMN=200 exa -lGh’ with one file don’t produce extra columns even if there place for more"
|
name = "‘COLUMNS=200 exa -lGh’ with one file don’t produce extra columns even if there place for more"
|
||||||
shell = "exa -lGh /testcases/files/10_bytes"
|
shell = "exa -lGh /testcases/files/10_bytes"
|
||||||
environment = { COLUMNS = "200" }
|
environment = { COLUMNS = "200" }
|
||||||
stdout = { file = "outputs/files_paths_long_grid_header_1file.ansitxt" }
|
stdout = { file = "outputs/files_long_grid_header_1file.ansitxt" }
|
||||||
stderr = { empty = true }
|
stderr = { empty = true }
|
||||||
status = 0
|
status = 0
|
||||||
tags = [ 'env', 'long', 'grid' ]
|
tags = [ 'env', 'long', 'grid' ]
|
||||||
|
|
||||||
[[cmd]]
|
[[cmd]]
|
||||||
name = "‘COLUMN=200 exa -lGh’ with several files don’t produce extra columns even if there place for more"
|
name = "‘COLUMNS=200 exa -lGh’ with several files don’t produce extra columns even if there place for more"
|
||||||
shell = "exa -lGh /testcases/files/10_{bytes,KiB}"
|
shell = "exa -lGh /testcases/files/10_{bytes,KiB}"
|
||||||
environment = { COLUMNS = "200" }
|
environment = { COLUMNS = "200" }
|
||||||
stdout = { file = "outputs/files_paths_long_grid_header_2files.ansitxt" }
|
stdout = { file = "outputs/files_long_grid_header_2files.ansitxt" }
|
||||||
|
stderr = { empty = true }
|
||||||
|
status = 0
|
||||||
|
tags = [ 'env', 'long', 'grid' ]
|
||||||
|
|
||||||
|
|
||||||
|
# check if EXA_GRID_ROWS is working
|
||||||
|
|
||||||
|
[[cmd]]
|
||||||
|
name = "‘COLUMNS=200 EXA_GRID_ROWS=2 exa -lG’ with three files produces a grid details of 1 column"
|
||||||
|
shell = "exa -lG /testcases/files/1_*"
|
||||||
|
environment = { COLUMNS = "200", EXA_GRID_ROWS = "2" }
|
||||||
|
stdout = { file = "outputs/files_long_grid_exa_grid_rows_2_3files.ansitxt" }
|
||||||
|
stderr = { empty = true }
|
||||||
|
status = 0
|
||||||
|
tags = [ 'env', 'long', 'grid' ]
|
||||||
|
|
||||||
|
[[cmd]]
|
||||||
|
name = "‘COLUMNS=200 EXA_GRID_ROWS=5 exa -lG’ with 15 files produces a grid details of 3 columns"
|
||||||
|
shell = "exa -lG /testcases/files/1*"
|
||||||
|
environment = { COLUMNS = "200", EXA_GRID_ROWS = "5" }
|
||||||
|
stdout = { file = "outputs/files_long_grid_exa_grid_rows_5_15files.ansitxt" }
|
||||||
|
stderr = { empty = true }
|
||||||
|
status = 0
|
||||||
|
tags = [ 'env', 'long', 'grid' ]
|
||||||
|
|
||||||
|
[[cmd]]
|
||||||
|
name = "‘COLUMNS=200 EXA_GRID_ROWS=6 exa -lG’ with 15 files produces a grid details of 1 column"
|
||||||
|
shell = "exa -lG /testcases/files/1*"
|
||||||
|
environment = { COLUMNS = "200", EXA_GRID_ROWS = "6" }
|
||||||
|
stdout = { file = "outputs/files_long_grid_exa_grid_rows_6_15files.ansitxt" }
|
||||||
stderr = { empty = true }
|
stderr = { empty = true }
|
||||||
status = 0
|
status = 0
|
||||||
tags = [ 'env', 'long', 'grid' ]
|
tags = [ 'env', 'long', 'grid' ]
|
||||||
|
|
39
xtests/outputs/dates_long_currentyear_localefr.ansitxt
Normal file
39
xtests/outputs/dates_long_currentyear_localefr.ansitxt
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m1[0m cassowary [34m 1 janv. 12:34[0m 1_bytes
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m1,0[0m[32mk[0m cassowary [34m 1 janv. 12:34[0m 1_KiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m1,0[0m[32mM[0m cassowary [34m 1 janv. 12:34[0m 1_MiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m2[0m cassowary [34m 1 janv. 12:34[0m 2_bytes
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m2,0[0m[32mk[0m cassowary [34m 1 janv. 12:34[0m 2_KiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m2,1[0m[32mM[0m cassowary [34m 1 janv. 12:34[0m 2_MiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m3[0m cassowary [34m 1 janv. 12:34[0m 3_bytes
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m3,1[0m[32mk[0m cassowary [34m 1 janv. 12:34[0m 3_KiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m3,1[0m[32mM[0m cassowary [34m 1 janv. 12:34[0m 3_MiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m4[0m cassowary [34m 1 janv. 12:34[0m 4_bytes
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m4,1[0m[32mk[0m cassowary [34m 1 janv. 12:34[0m 4_KiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m4,2[0m[32mM[0m cassowary [34m 1 janv. 12:34[0m 4_MiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m5[0m cassowary [34m 1 janv. 12:34[0m 5_bytes
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m5,1[0m[32mk[0m cassowary [34m 1 janv. 12:34[0m 5_KiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m5,2[0m[32mM[0m cassowary [34m 1 janv. 12:34[0m 5_MiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m6[0m cassowary [34m 1 janv. 12:34[0m 6_bytes
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m6,1[0m[32mk[0m cassowary [34m 1 janv. 12:34[0m 6_KiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m6,3[0m[32mM[0m cassowary [34m 1 janv. 12:34[0m 6_MiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m7[0m cassowary [34m 1 janv. 12:34[0m 7_bytes
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m7,2[0m[32mk[0m cassowary [34m 1 janv. 12:34[0m 7_KiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m7,3[0m[32mM[0m cassowary [34m 1 janv. 12:34[0m 7_MiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m8[0m cassowary [34m 1 janv. 12:34[0m 8_bytes
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m8,2[0m[32mk[0m cassowary [34m 1 janv. 12:34[0m 8_KiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m8,4[0m[32mM[0m cassowary [34m 1 janv. 12:34[0m 8_MiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m9[0m cassowary [34m 1 janv. 12:34[0m 9_bytes
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m9,2[0m[32mk[0m cassowary [34m 1 janv. 12:34[0m 9_KiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m9,4[0m[32mM[0m cassowary [34m 1 janv. 12:34[0m 9_MiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m10[0m cassowary [34m 1 janv. 12:34[0m 10_bytes
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m10[0m[32mk[0m cassowary [34m 1 janv. 12:34[0m 10_KiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m10[0m[32mM[0m cassowary [34m 1 janv. 12:34[0m 10_MiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m11[0m cassowary [34m 1 janv. 12:34[0m 11_bytes
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m11[0m[32mk[0m cassowary [34m 1 janv. 12:34[0m 11_KiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m12[0m[32mM[0m cassowary [34m 1 janv. 12:34[0m 11_MiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m12[0m cassowary [34m 1 janv. 12:34[0m 12_bytes
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m12[0m[32mk[0m cassowary [34m 1 janv. 12:34[0m 12_KiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m13[0m[32mM[0m cassowary [34m 1 janv. 12:34[0m 12_MiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m13[0m cassowary [34m 1 janv. 12:34[0m 13_bytes
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m13[0m[32mk[0m cassowary [34m 1 janv. 12:34[0m 13_KiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m14[0m[32mM[0m cassowary [34m 1 janv. 12:34[0m 13_MiB
|
7
xtests/outputs/dev_long.ansitxt
Normal file
7
xtests/outputs/dev_long.ansitxt
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[1;33mcr[31mw[0m[38;5;244m-[33mr[38;5;244m-----[0m [1;32m1[0m[38;5;244m,[32m1[0m root [1;33mmem[0m
|
||||||
|
[1;33mcr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[31mw[38;5;244m-[0m [1;32m1[0m[38;5;244m,[32m3[0m root [1;33mnull[0m
|
||||||
|
[1;33mcr[31mw[0m[38;5;244m-[33mr[38;5;244m-----[0m [1;32m1[0m[38;5;244m,[32m4[0m root [1;33mport[0m
|
||||||
|
[1;33mcr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[31mw[38;5;244m-[0m [1;32m1[0m[38;5;244m,[32m5[0m root [1;33mzero[0m
|
||||||
|
[1;33mcr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[31mw[38;5;244m-[0m [1;32m1[0m[38;5;244m,[32m7[0m root [1;33mfull[0m
|
||||||
|
[1;33mcr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[31mw[38;5;244m-[0m [1;32m1[0m[38;5;244m,[32m8[0m root [1;33mrandom[0m
|
||||||
|
[1;33mcr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[31mw[38;5;244m-[0m [1;32m1[0m[38;5;244m,[32m9[0m root [1;33murandom[0m
|
1
xtests/outputs/error_columns_invalid.ansitxt
Normal file
1
xtests/outputs/error_columns_invalid.ansitxt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
exa: Value "abcdef" not valid for environment variable COLUMNS: invalid digit found in string
|
1
xtests/outputs/error_columns_nines.ansitxt
Normal file
1
xtests/outputs/error_columns_nines.ansitxt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
exa: Value "99999999999999999999999" not valid for environment variable COLUMNS: number too large to fit in target type
|
1
xtests/outputs/error_grid_rows_invalid.ansitxt
Normal file
1
xtests/outputs/error_grid_rows_invalid.ansitxt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
exa: Value "abcdef" not valid for environment variable EXA_GRID_ROWS: invalid digit found in string
|
1
xtests/outputs/error_grid_rows_nines.ansitxt
Normal file
1
xtests/outputs/error_grid_rows_nines.ansitxt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
exa: Value "99999999999999999999999" not valid for environment variable EXA_GRID_ROWS: number too large to fit in target type
|
1
xtests/outputs/error_icon_spacing_invalid.ansitxt
Normal file
1
xtests/outputs/error_icon_spacing_invalid.ansitxt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
exa: Value "abcdef" not valid for environment variable EXA_ICON_SPACING: invalid digit found in string
|
1
xtests/outputs/error_icon_spacing_nines.ansitxt
Normal file
1
xtests/outputs/error_icon_spacing_nines.ansitxt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
exa: Value "99999999999999999999999" not valid for environment variable EXA_ICON_SPACING: number too large to fit in target type
|
1
xtests/outputs/error_invalid_option.ansitxt
Normal file
1
xtests/outputs/error_invalid_option.ansitxt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
exa: Unknown argument --aoeu
|
1
xtests/outputs/error_level_invalid.ansitxt
Normal file
1
xtests/outputs/error_level_invalid.ansitxt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
exa: Value "abcdef" not valid for option --level (-L): invalid digit found in string
|
1
xtests/outputs/error_level_nines.ansitxt
Normal file
1
xtests/outputs/error_level_nines.ansitxt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
exa: Value "99999999999999999999999" not valid for option --level (-L): number too large to fit in target type
|
1
xtests/outputs/error_tree_all_all.ansitxt
Normal file
1
xtests/outputs/error_tree_all_all.ansitxt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
exa: Option --tree is useless given --all --all
|
|
@ -0,0 +1,3 @@
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m1[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m1_bytes
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m1.0[0m[32mk[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m1_KiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m1.0[0m[32mM[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m1_MiB
|
|
@ -0,0 +1,5 @@
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m10[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m10_bytes .[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m12[0m[32mM[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m11_MiB .[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m13[0m[32mk[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m13_KiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m10[0m[32mk[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m10_KiB .[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m12[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m12_bytes .[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m14[0m[32mM[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m13_MiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m10[0m[32mM[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m10_MiB .[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m12[0m[32mk[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m12_KiB .[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m1[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m1_bytes
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m11[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m11_bytes .[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m13[0m[32mM[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m12_MiB .[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m1.0[0m[32mk[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m1_KiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m11[0m[32mk[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m11_KiB .[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m13[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m13_bytes .[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m1.0[0m[32mM[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m1_MiB
|
|
@ -0,0 +1,15 @@
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m1[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m1_bytes
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m1.0[0m[32mk[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m1_KiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m1.0[0m[32mM[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m1_MiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m10[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m10_bytes
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m10[0m[32mk[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m10_KiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m10[0m[32mM[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m10_MiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m11[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m11_bytes
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m11[0m[32mk[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m11_KiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m12[0m[32mM[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m11_MiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m12[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m12_bytes
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m12[0m[32mk[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m12_KiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m13[0m[32mM[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m12_MiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m13[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m13_bytes
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m13[0m[32mk[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m13_KiB
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[38;5;244m--[33mr[38;5;244m--[0m [1;32m14[0m[32mM[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/files/[0m13_MiB
|
|
@ -8,7 +8,7 @@
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m20[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[34mM[0m unstaged
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m20[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[34mM[0m unstaged
|
||||||
|
|
||||||
/testcases/git2/target:
|
/testcases/git2/target:
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m--[0m another ignored file
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[0m[2mI[0m another ignored file
|
||||||
|
|
||||||
/testcases/git2/deeply:
|
/testcases/git2/deeply:
|
||||||
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[32mN[0m [1;34mnested[0m
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[32mN[0m [1;34mnested[0m
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
[38;5;92mmusic.m4a[0m [1;34mnested[0m
|
[38;5;92mmusic.m4a[0m [1;34mnested[0m [1;34mnested2[0m
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
[38;5;92mmusic.m4a[0m
|
[38;5;92mmusic.m4a[0m
|
||||||
[1;34mnested[0m
|
[1;34mnested[0m
|
||||||
|
[1;34mnested2[0m
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;92mmusic.m4a[0m
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;92mmusic.m4a[0m
|
||||||
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [1;34mnested[0m
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [1;34mnested[0m
|
||||||
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [1;34mnested2[0m
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;92mmusic.m4a[0m [1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [1;34mnested[0m
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;92mmusic.m4a[0m [1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [1;34mnested[0m [1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [1;34mnested2[0m
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;92mmusic.m4a[0m
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;92mmusic.m4a[0m
|
||||||
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [1;34mnested[0m
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [1;34mnested[0m
|
||||||
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [1;34mnested2[0m
|
||||||
|
|
||||||
/testcases/git2/ignoreds/nested:
|
/testcases/git2/ignoreds/nested:
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;92mfunky chicken.m4a[0m
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;92mfunky chicken.m4a[0m
|
||||||
|
|
||||||
|
/testcases/git2/ignoreds/nested2:
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/git2/[1;34mignoreds[0m
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [36m/testcases/git2/[1;34mignoreds[0m
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m├──[0m [38;5;92mmusic.m4a[0m
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m├──[0m [38;5;92mmusic.m4a[0m
|
||||||
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m└──[0m [1;34mnested[0m
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m├──[0m [1;34mnested[0m
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m └──[0m [38;5;92mfunky chicken.m4a[0m
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m│ └──[0m [38;5;92mfunky chicken.m4a[0m
|
||||||
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m└──[0m [1;34mnested2[0m
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
[36m/testcases/git2/[1;34mignoreds[0m
|
[36m/testcases/git2/[1;34mignoreds[0m
|
||||||
[38;5;244m├──[0m [38;5;92mmusic.m4a[0m
|
[38;5;244m├──[0m [38;5;92mmusic.m4a[0m
|
||||||
[38;5;244m└──[0m [1;34mnested[0m
|
[38;5;244m├──[0m [1;34mnested[0m
|
||||||
[38;5;244m └──[0m [38;5;92mfunky chicken.m4a[0m
|
[38;5;244m│ └──[0m [38;5;92mfunky chicken.m4a[0m
|
||||||
|
[38;5;244m└──[0m [1;34mnested2[0m
|
||||||
|
|
1
xtests/outputs/git2_long_ignoredcontent.ansitxt
Normal file
1
xtests/outputs/git2_long_ignoredcontent.ansitxt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m--[0m [36m/testcases/git2/ignoreds/[1;34mnested2[0m
|
|
@ -1 +1 @@
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m--[0m another ignored file
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[0m[2mI[0m another ignored file
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[32mN[0m [38;5;92mmusic.m4a[0m
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[32mN[0m [38;5;92mmusic.m4a[0m
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[0m[2mI[0m [38;5;92mmusic.mp3[0m
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[0m[2mI[0m [38;5;92mmusic.mp3[0m
|
||||||
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[32mN[0m [1;34mnested[0m
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[32mN[0m [1;34mnested[0m
|
||||||
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m--[0m [1;34mnested2[0m
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[32mN[0m [38;5;92mmusic.m4a[0m
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[32mN[0m [38;5;92mmusic.m4a[0m
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[0m[2mI[0m [38;5;92mmusic.mp3[0m
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[0m[2mI[0m [38;5;92mmusic.mp3[0m
|
||||||
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[32mN[0m [1;34mnested[0m
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[32mN[0m [1;34mnested[0m
|
||||||
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m--[0m [1;34mnested2[0m
|
||||||
|
|
||||||
/testcases/git2/target:
|
/testcases/git2/target:
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m--[0m another ignored file
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[0m[2mI[0m another ignored file
|
||||||
|
|
|
@ -20,10 +20,14 @@
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[32mN[0m [38;5;92mmusic.m4a[0m
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[32mN[0m [38;5;92mmusic.m4a[0m
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[0m[2mI[0m [38;5;92mmusic.mp3[0m
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[0m[2mI[0m [38;5;92mmusic.mp3[0m
|
||||||
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[32mN[0m [1;34mnested[0m
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[32mN[0m [1;34mnested[0m
|
||||||
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m--[0m [1;34mnested2[0m
|
||||||
|
|
||||||
/testcases/git2/ignoreds/nested:
|
/testcases/git2/ignoreds/nested:
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[0m[2mI[0m [38;5;92m70s grove.mp3[0m
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[0m[2mI[0m [38;5;92m70s grove.mp3[0m
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[32mN[0m [38;5;92mfunky chicken.m4a[0m
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[32mN[0m [38;5;92mfunky chicken.m4a[0m
|
||||||
|
|
||||||
|
/testcases/git2/ignoreds/nested2:
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[0m[2mI[0m [38;5;92mievan polkka.mp3[0m
|
||||||
|
|
||||||
/testcases/git2/target:
|
/testcases/git2/target:
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m--[0m another ignored file
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[0m[2mI[0m another ignored file
|
||||||
|
|
|
@ -18,6 +18,9 @@
|
||||||
/testcases/git2/ignoreds:
|
/testcases/git2/ignoreds:
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;92mmusic.m4a[0m
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;92mmusic.m4a[0m
|
||||||
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [1;34mnested[0m
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [1;34mnested[0m
|
||||||
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [1;34mnested2[0m
|
||||||
|
|
||||||
/testcases/git2/ignoreds/nested:
|
/testcases/git2/ignoreds/nested:
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;92mfunky chicken.m4a[0m
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;92mfunky chicken.m4a[0m
|
||||||
|
|
||||||
|
/testcases/git2/ignoreds/nested2:
|
||||||
|
|
|
@ -9,8 +9,10 @@
|
||||||
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[32mN[0m [38;5;244m├──[0m [1;34mignoreds[0m
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[32mN[0m [38;5;244m├──[0m [1;34mignoreds[0m
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[32mN[0m [38;5;244m│ ├──[0m [38;5;92mmusic.m4a[0m
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[32mN[0m [38;5;244m│ ├──[0m [38;5;92mmusic.m4a[0m
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[0m[2mI[0m [38;5;244m│ ├──[0m [38;5;92mmusic.mp3[0m
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[0m[2mI[0m [38;5;244m│ ├──[0m [38;5;92mmusic.mp3[0m
|
||||||
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[32mN[0m [38;5;244m│ └──[0m [1;34mnested[0m
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[32mN[0m [38;5;244m│ ├──[0m [1;34mnested[0m
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[0m[2mI[0m [38;5;244m│ ├──[0m [38;5;92m70s grove.mp3[0m
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[0m[2mI[0m [38;5;244m│ │ ├──[0m [38;5;92m70s grove.mp3[0m
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[32mN[0m [38;5;244m│ └──[0m [38;5;92mfunky chicken.m4a[0m
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[32mN[0m [38;5;244m│ │ └──[0m [38;5;92mfunky chicken.m4a[0m
|
||||||
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m--[0m [38;5;244m│ └──[0m [1;34mnested2[0m
|
||||||
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[0m[2mI[0m [38;5;244m│ └──[0m [38;5;92mievan polkka.mp3[0m
|
||||||
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[0m[2mI[0m [38;5;244m└──[0m [1;34mtarget[0m
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[0m[2mI[0m [38;5;244m└──[0m [1;34mtarget[0m
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m--[0m [38;5;244m └──[0m another ignored file
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m-[0m[2mI[0m [38;5;244m └──[0m another ignored file
|
||||||
|
|
|
@ -8,5 +8,6 @@
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m│ └──[0m subfile
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m│ └──[0m subfile
|
||||||
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m└──[0m [1;34mignoreds[0m
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m└──[0m [1;34mignoreds[0m
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m ├──[0m [38;5;92mmusic.m4a[0m
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m ├──[0m [38;5;92mmusic.m4a[0m
|
||||||
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m └──[0m [1;34mnested[0m
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m ├──[0m [1;34mnested[0m
|
||||||
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m └──[0m [38;5;92mfunky chicken.m4a[0m
|
.[1;33mr[31mw[0m[38;5;244m-[33mr[31mw[38;5;244m-[33mr[38;5;244m--[0m [1;32m0[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m │ └──[0m [38;5;92mfunky chicken.m4a[0m
|
||||||
|
[1;34md[33mr[31mw[32mx[0m[33mr[31mw[32mx[33mr[38;5;244m-[32mx[0m [38;5;244m-[0m cassowary [34m 1 Jan 12:34[0m [38;5;244m └──[0m [1;34mnested2[0m
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user