mirror of
https://github.com/Llewellynvdm/exa.git
synced 2024-11-26 13:56:27 +00:00
Merge branch 'split-up-details'
This commit is contained in:
commit
e1d2c3f46e
91
Cargo.lock
generated
91
Cargo.lock
generated
@ -2,13 +2,13 @@
|
|||||||
name = "exa"
|
name = "exa"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ansi_term 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"bitflags 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"datetime 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"datetime 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"git2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"git2 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"locale 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"locale 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"natord 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"natord 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"num_cpus 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num_cpus 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -38,7 +38,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ansi_term"
|
name = "ansi_term"
|
||||||
version = "0.7.0"
|
version = "0.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -53,10 +53,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cmake"
|
name = "cmake"
|
||||||
version = "0.1.11"
|
version = "0.1.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gcc 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gcc 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -66,21 +66,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"locale 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"locale 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"num 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pad 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pad 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"regex 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
"regex 0.1.47 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"tz 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"tz 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gcc"
|
name = "gcc"
|
||||||
version = "0.3.20"
|
version = "0.3.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"advapi32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"advapi32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gdi32-sys"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getopts"
|
name = "getopts"
|
||||||
version = "0.2.14"
|
version = "0.2.14"
|
||||||
@ -88,12 +96,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "git2"
|
name = "git2"
|
||||||
version = "0.3.3"
|
version = "0.3.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libgit2-sys 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libgit2-sys 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"url 0.2.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
"url 0.2.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -118,19 +126,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.2"
|
version = "0.2.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libgit2-sys"
|
name = "libgit2-sys"
|
||||||
version = "0.3.8"
|
version = "0.3.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cmake 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cmake 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libssh2-sys 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libssh2-sys 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libz-sys 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libz-sys 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"openssl-sys 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"openssl-sys 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pkg-config 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pkg-config 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -147,10 +155,10 @@ name = "libssh2-sys"
|
|||||||
version = "0.1.34"
|
version = "0.1.34"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cmake 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cmake 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libz-sys 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libz-sys 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"openssl-sys 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"openssl-sys 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pkg-config 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pkg-config 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -161,8 +169,8 @@ name = "libz-sys"
|
|||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gcc 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gcc 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pkg-config 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pkg-config 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -172,7 +180,7 @@ version = "0.1.8"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"num 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -185,7 +193,7 @@ name = "memchr"
|
|||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -195,10 +203,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num"
|
name = "num"
|
||||||
version = "0.1.28"
|
version = "0.1.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rand 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -208,7 +216,7 @@ version = "0.2.10"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -217,17 +225,19 @@ name = "number_prefix"
|
|||||||
version = "0.2.5"
|
version = "0.2.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl-sys"
|
name = "openssl-sys"
|
||||||
version = "0.7.1"
|
version = "0.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gdi32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libressl-pnacl-sys 2.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libressl-pnacl-sys 2.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pkg-config 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pkg-config 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"user32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -253,17 +263,17 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.3.12"
|
version = "0.3.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"advapi32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"advapi32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "0.1.43"
|
version = "0.1.47"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"aho-corasick 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -307,7 +317,7 @@ name = "tempdir"
|
|||||||
version = "0.3.4"
|
version = "0.3.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rand 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -341,12 +351,21 @@ dependencies = [
|
|||||||
"uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
"uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "user32-sys"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "users"
|
name = "users"
|
||||||
version = "0.4.4"
|
version = "0.4.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -354,7 +373,7 @@ name = "uuid"
|
|||||||
version = "0.1.18"
|
version = "0.1.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rand 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ authors = [ "ogham@bsago.me" ]
|
|||||||
name = "exa"
|
name = "exa"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ansi_term = "0.7.0"
|
ansi_term = "0.7.1"
|
||||||
bitflags = "0.1"
|
bitflags = "0.1"
|
||||||
datetime = "0.4.1"
|
datetime = "0.4.1"
|
||||||
getopts = "0.2.14"
|
getopts = "0.2.14"
|
||||||
|
13
src/file.rs
13
src/file.rs
@ -7,8 +7,6 @@ use std::io::Result as IOResult;
|
|||||||
use std::os::unix::fs::{MetadataExt, PermissionsExt};
|
use std::os::unix::fs::{MetadataExt, PermissionsExt};
|
||||||
use std::path::{Component, Path, PathBuf};
|
use std::path::{Component, Path, PathBuf};
|
||||||
|
|
||||||
use unicode_width::UnicodeWidthStr;
|
|
||||||
|
|
||||||
use dir::Dir;
|
use dir::Dir;
|
||||||
|
|
||||||
use self::fields as f;
|
use self::fields as f;
|
||||||
@ -180,22 +178,13 @@ impl<'dir> File<'dir> {
|
|||||||
path_prefix
|
path_prefix
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The Unicode 'display width' of the filename.
|
|
||||||
///
|
|
||||||
/// This is related to the number of graphemes in the string: most
|
|
||||||
/// characters are 1 columns wide, but in some contexts, certain
|
|
||||||
/// characters are actually 2 columns wide.
|
|
||||||
pub fn file_name_width(&self) -> usize {
|
|
||||||
UnicodeWidthStr::width(&self.name[..])
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Assuming the current file is a symlink, follows the link and
|
/// Assuming the current file is a symlink, follows the link and
|
||||||
/// returns a File object from the path the link points to.
|
/// returns a File object from the path the link points to.
|
||||||
///
|
///
|
||||||
/// If statting the file fails (usually because the file on the
|
/// If statting the file fails (usually because the file on the
|
||||||
/// other end doesn't exist), returns the *filename* of the file
|
/// other end doesn't exist), returns the *filename* of the file
|
||||||
/// that should be there.
|
/// that should be there.
|
||||||
pub fn link_target(&self) -> Result<File, String> {
|
pub fn link_target(&self) -> Result<File<'dir>, String> {
|
||||||
let path = match fs::read_link(&self.path) {
|
let path = match fs::read_link(&self.path) {
|
||||||
Ok(path) => path,
|
Ok(path) => path,
|
||||||
Err(_) => return Err(self.name.clone()),
|
Err(_) => return Err(self.name.clone()),
|
||||||
|
@ -1,31 +1,7 @@
|
|||||||
use ansi_term::Style;
|
|
||||||
|
|
||||||
use file::File;
|
use file::File;
|
||||||
use colours::Colours;
|
|
||||||
|
|
||||||
|
|
||||||
pub fn file_colour(colours: &Colours, file: &File) -> Style {
|
pub trait FileTypes {
|
||||||
match file {
|
|
||||||
f if f.is_directory() => colours.filetypes.directory,
|
|
||||||
f if f.is_executable_file() => colours.filetypes.executable,
|
|
||||||
f if f.is_link() => colours.filetypes.symlink,
|
|
||||||
f if !f.is_file() => colours.filetypes.special,
|
|
||||||
f if f.is_immediate() => colours.filetypes.immediate,
|
|
||||||
f if f.is_image() => colours.filetypes.image,
|
|
||||||
f if f.is_video() => colours.filetypes.video,
|
|
||||||
f if f.is_music() => colours.filetypes.music,
|
|
||||||
f if f.is_lossless() => colours.filetypes.lossless,
|
|
||||||
f if f.is_crypto() => colours.filetypes.crypto,
|
|
||||||
f if f.is_document() => colours.filetypes.document,
|
|
||||||
f if f.is_compressed() => colours.filetypes.compressed,
|
|
||||||
f if f.is_temp() => colours.filetypes.temp,
|
|
||||||
f if f.is_compiled() => colours.filetypes.compiled,
|
|
||||||
_ => colours.filetypes.normal,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
trait FileTypes {
|
|
||||||
fn is_immediate(&self) -> bool;
|
fn is_immediate(&self) -> bool;
|
||||||
fn is_image(&self) -> bool;
|
fn is_image(&self) -> bool;
|
||||||
fn is_video(&self) -> bool;
|
fn is_video(&self) -> bool;
|
||||||
|
@ -26,7 +26,6 @@ use dir::Dir;
|
|||||||
use file::File;
|
use file::File;
|
||||||
use options::{Options, View};
|
use options::{Options, View};
|
||||||
|
|
||||||
mod colours;
|
|
||||||
mod dir;
|
mod dir;
|
||||||
mod feature;
|
mod feature;
|
||||||
mod file;
|
mod file;
|
||||||
@ -135,8 +134,8 @@ impl Exa {
|
|||||||
match self.options.view {
|
match self.options.view {
|
||||||
View::Grid(g) => g.view(&files),
|
View::Grid(g) => g.view(&files),
|
||||||
View::Details(d) => d.view(dir, files),
|
View::Details(d) => d.view(dir, files),
|
||||||
View::GridDetails(gd) => gd.view(dir, &files),
|
View::GridDetails(gd) => gd.view(dir, files),
|
||||||
View::Lines(l) => l.view(&files),
|
View::Lines(l) => l.view(files),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,10 @@ use std::os::unix::fs::MetadataExt;
|
|||||||
use getopts;
|
use getopts;
|
||||||
use natord;
|
use natord;
|
||||||
|
|
||||||
use colours::Colours;
|
|
||||||
use feature::xattr;
|
use feature::xattr;
|
||||||
use file::File;
|
use file::File;
|
||||||
use output::{Grid, Details, GridDetails, Lines};
|
use output::{Grid, Details, GridDetails, Lines};
|
||||||
|
use output::Colours;
|
||||||
use output::column::{Columns, TimeTypes, SizeFormat};
|
use output::column::{Columns, TimeTypes, SizeFormat};
|
||||||
use term::dimensions;
|
use term::dimensions;
|
||||||
|
|
||||||
|
205
src/output/cell.rs
Normal file
205
src/output/cell.rs
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
//! The `TextCell` type for the details and lines views.
|
||||||
|
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
use ansi_term::{Style, ANSIString, ANSIStrings};
|
||||||
|
use unicode_width::UnicodeWidthStr;
|
||||||
|
|
||||||
|
|
||||||
|
/// An individual cell that holds text in a table, used in the details and
|
||||||
|
/// lines views to store ANSI-terminal-formatted data before it is printed.
|
||||||
|
///
|
||||||
|
/// A text cell is made up of zero or more strings coupled with the
|
||||||
|
/// pre-computed length of all the strings combined. When constructing details
|
||||||
|
/// or grid-details tables, the length will have to be queried multiple times,
|
||||||
|
/// so it makes sense to cache it.
|
||||||
|
///
|
||||||
|
/// (This used to be called `Cell`, but was renamed because there’s a Rust
|
||||||
|
/// type by that name too.)
|
||||||
|
#[derive(PartialEq, Debug, Clone, Default)]
|
||||||
|
pub struct TextCell {
|
||||||
|
|
||||||
|
/// The contents of this cell, as a vector of ANSI-styled strings.
|
||||||
|
pub contents: TextCellContents,
|
||||||
|
|
||||||
|
/// The Unicode “display width” of this cell.
|
||||||
|
pub width: DisplayWidth,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for TextCell {
|
||||||
|
type Target = TextCellContents;
|
||||||
|
|
||||||
|
fn deref<'a>(&'a self) -> &'a Self::Target {
|
||||||
|
&self.contents
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TextCell {
|
||||||
|
|
||||||
|
/// Creates a new text cell that holds the given text in the given style,
|
||||||
|
/// computing the Unicode width of the text.
|
||||||
|
pub fn paint(style: Style, text: String) -> Self {
|
||||||
|
let width = DisplayWidth::from(&*text);
|
||||||
|
|
||||||
|
TextCell {
|
||||||
|
contents: vec![ style.paint(text) ].into(),
|
||||||
|
width: width,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new text cell that holds the given text in the given style,
|
||||||
|
/// computing the Unicode width of the text. (This could be merged with
|
||||||
|
/// `paint`, but.)
|
||||||
|
pub fn paint_str(style: Style, text: &'static str) -> Self {
|
||||||
|
let width = DisplayWidth::from(text);
|
||||||
|
|
||||||
|
TextCell {
|
||||||
|
contents: vec![ style.paint(text) ].into(),
|
||||||
|
width: width,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new “blank” text cell that contains a single hyphen in the
|
||||||
|
/// given style, which should be the “punctuation” style from a `Colours`
|
||||||
|
/// value.
|
||||||
|
///
|
||||||
|
/// This is used in place of empty table cells, as it is easier to read
|
||||||
|
/// tabular data when there is *something* in each cell.
|
||||||
|
pub fn blank(style: Style) -> Self {
|
||||||
|
TextCell {
|
||||||
|
contents: vec![ style.paint("-") ].into(),
|
||||||
|
width: DisplayWidth::from(1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds the given number of unstyled spaces after this cell.
|
||||||
|
///
|
||||||
|
/// This method allocates a `String` to hold the spaces.
|
||||||
|
pub fn add_spaces(&mut self, count: usize) {
|
||||||
|
use std::iter::repeat;
|
||||||
|
|
||||||
|
(*self.width) += count;
|
||||||
|
|
||||||
|
let spaces: String = repeat(' ').take(count).collect();
|
||||||
|
self.contents.0.push(Style::default().paint(spaces));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds the contents of another `ANSIString` to the end of this cell.
|
||||||
|
pub fn push(&mut self, string: ANSIString<'static>, extra_width: usize) {
|
||||||
|
self.contents.0.push(string);
|
||||||
|
(*self.width) += extra_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds all the contents of another `TextCell` to the end of this cell.
|
||||||
|
pub fn append(&mut self, other: TextCell) {
|
||||||
|
(*self.width) += *other.width;
|
||||||
|
self.contents.0.extend(other.contents.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// I’d like to eventually abstract cells so that instead of *every* cell
|
||||||
|
// storing a vector, only variable-length cells would, and individual cells
|
||||||
|
// would just store an array of a fixed length (which would usually be just 1
|
||||||
|
// or 2), which wouldn’t require a heap allocation.
|
||||||
|
//
|
||||||
|
// For examples, look at the `render_*` methods in the `Table` object in the
|
||||||
|
// details view:
|
||||||
|
//
|
||||||
|
// - `render_blocks`, `inode`, and `links` will always return a
|
||||||
|
// one-string-long TextCell;
|
||||||
|
// - `render_size` will return one or two strings in a TextCell, depending on
|
||||||
|
// the size and whether one is present;
|
||||||
|
// - `render_permissions` will return ten or eleven strings;
|
||||||
|
// - `filename` and `symlink_filename` in the output module root return six or
|
||||||
|
// five strings.
|
||||||
|
//
|
||||||
|
// In none of these cases are we dealing with a *truly variable* number of
|
||||||
|
// strings: it is only when the strings are concatenated together do we need a
|
||||||
|
// growable, heap-allocated buffer.
|
||||||
|
//
|
||||||
|
// So it would be nice to abstract the `TextCell` type so instead of a `Vec`,
|
||||||
|
// it can use anything of type `T: IntoIterator<Item=ANSIString<’static>>`.
|
||||||
|
// This would allow us to still hold all the data, but allocate less.
|
||||||
|
//
|
||||||
|
// But exa still has bugs and I need to fix those first :(
|
||||||
|
|
||||||
|
|
||||||
|
/// The contents of a text cell, as a vector of ANSI-styled strings.
|
||||||
|
///
|
||||||
|
/// It’s possible to use this type directly in the case where you want a
|
||||||
|
/// `TextCell` but aren’t concerned with tracking its width, because it occurs
|
||||||
|
/// in the final cell of a table or grid and there’s no point padding it. This
|
||||||
|
/// happens when dealing with file names.
|
||||||
|
#[derive(PartialEq, Debug, Clone, Default)]
|
||||||
|
pub struct TextCellContents(Vec<ANSIString<'static>>);
|
||||||
|
|
||||||
|
impl From<Vec<ANSIString<'static>>> for TextCellContents {
|
||||||
|
fn from(strings: Vec<ANSIString<'static>>) -> TextCellContents {
|
||||||
|
TextCellContents(strings)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for TextCellContents {
|
||||||
|
type Target = [ANSIString<'static>];
|
||||||
|
|
||||||
|
fn deref<'a>(&'a self) -> &'a Self::Target {
|
||||||
|
&*self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No DerefMut implementation here -- it would be publicly accessible, and as
|
||||||
|
// the contents only get changed in this module, the mutators in the struct
|
||||||
|
// above can just access the value directly.
|
||||||
|
|
||||||
|
impl TextCellContents {
|
||||||
|
|
||||||
|
/// Produces an `ANSIStrings` value that can be used to print the styled
|
||||||
|
/// values of this cell as an ANSI-terminal-formatted string.
|
||||||
|
pub fn strings(&self) -> ANSIStrings {
|
||||||
|
ANSIStrings(&self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// The Unicode “display width” of a string.
|
||||||
|
///
|
||||||
|
/// This is related to the number of *graphemes* of a string, rather than the
|
||||||
|
/// number of *characters*, or *bytes*: although most characters are one
|
||||||
|
/// column wide, a few can be two columns wide, and this is important to note
|
||||||
|
/// when calculating widths for displaying tables in a terminal.
|
||||||
|
///
|
||||||
|
/// This type is used to ensure that the width, rather than the length, is
|
||||||
|
/// used when constructing a `TextCell` -- it's too easy to write something
|
||||||
|
/// like `file_name.len()` and assume it will work!
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
#[derive(PartialEq, Debug, Clone, Copy, Default)]
|
||||||
|
pub struct DisplayWidth(usize);
|
||||||
|
|
||||||
|
impl<'a> From<&'a str> for DisplayWidth {
|
||||||
|
fn from(input: &'a str) -> DisplayWidth {
|
||||||
|
DisplayWidth(UnicodeWidthStr::width(input))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<usize> for DisplayWidth {
|
||||||
|
fn from(width: usize) -> DisplayWidth {
|
||||||
|
DisplayWidth(width)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for DisplayWidth {
|
||||||
|
type Target = usize;
|
||||||
|
|
||||||
|
fn deref<'a>(&'a self) -> &'a Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for DisplayWidth {
|
||||||
|
fn deref_mut<'a>(&'a mut self) -> &'a mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,3 @@
|
|||||||
use ansi_term::Style;
|
|
||||||
use unicode_width::UnicodeWidthStr;
|
|
||||||
|
|
||||||
use dir::Dir;
|
use dir::Dir;
|
||||||
|
|
||||||
|
|
||||||
@ -194,38 +191,3 @@ impl Default for TimeTypes {
|
|||||||
TimeTypes { accessed: false, modified: true, created: false }
|
TimeTypes { accessed: false, modified: true, created: false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Clone)]
|
|
||||||
pub struct Cell {
|
|
||||||
pub length: usize,
|
|
||||||
pub text: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Cell {
|
|
||||||
pub fn empty() -> Cell {
|
|
||||||
Cell {
|
|
||||||
text: String::new(),
|
|
||||||
length: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn paint(style: Style, string: &str) -> Cell {
|
|
||||||
Cell {
|
|
||||||
text: style.paint(string).to_string(),
|
|
||||||
length: UnicodeWidthStr::width(string),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_spaces(&mut self, count: usize) {
|
|
||||||
self.length += count;
|
|
||||||
for _ in 0 .. count {
|
|
||||||
self.text.push(' ');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn append(&mut self, other: &Cell) {
|
|
||||||
self.length += other.length;
|
|
||||||
self.text.push_str(&*other.text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,10 @@
|
|||||||
use colours::Colours;
|
|
||||||
use file::File;
|
|
||||||
use filetype::file_colour;
|
|
||||||
|
|
||||||
use term_grid as grid;
|
use term_grid as grid;
|
||||||
|
|
||||||
|
use file::File;
|
||||||
|
use output::DisplayWidth;
|
||||||
|
use output::colours::Colours;
|
||||||
|
use super::file_colour;
|
||||||
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Debug, Copy, Clone)]
|
||||||
pub struct Grid {
|
pub struct Grid {
|
||||||
@ -27,7 +28,7 @@ impl Grid {
|
|||||||
for file in files.iter() {
|
for file in files.iter() {
|
||||||
grid.add(grid::Cell {
|
grid.add(grid::Cell {
|
||||||
contents: file_colour(&self.colours, file).paint(&*file.name).to_string(),
|
contents: file_colour(&self.colours, file).paint(&*file.name).to_string(),
|
||||||
width: file.file_name_width(),
|
width: *DisplayWidth::from(&*file.name),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use std::iter::repeat;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use ansi_term::ANSIStrings;
|
||||||
use users::OSUsers;
|
use users::OSUsers;
|
||||||
use term_grid as grid;
|
use term_grid as grid;
|
||||||
|
|
||||||
@ -7,8 +8,9 @@ use dir::Dir;
|
|||||||
use feature::xattr::FileAttributes;
|
use feature::xattr::FileAttributes;
|
||||||
use file::File;
|
use file::File;
|
||||||
|
|
||||||
use output::column::{Column, Cell};
|
use output::cell::TextCell;
|
||||||
use output::details::{Details, Table};
|
use output::column::Column;
|
||||||
|
use output::details::{Details, Table, Environment};
|
||||||
use output::grid::Grid;
|
use output::grid::Grid;
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Debug, Copy, Clone)]
|
||||||
@ -25,19 +27,33 @@ fn file_has_xattrs(file: &File) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl GridDetails {
|
impl GridDetails {
|
||||||
pub fn view(&self, dir: Option<&Dir>, files: &[File]) {
|
pub fn view(&self, dir: Option<&Dir>, files: Vec<File>) {
|
||||||
let columns_for_dir = match self.details.columns {
|
let columns_for_dir = match self.details.columns {
|
||||||
Some(cols) => cols.for_dir(dir),
|
Some(cols) => cols.for_dir(dir),
|
||||||
None => Vec::new(),
|
None => Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut first_table = Table::with_options(self.details.colours, columns_for_dir.clone());
|
let env = Arc::new(Environment::default());
|
||||||
let cells: Vec<_> = files.iter().map(|file| first_table.cells_for_file(file, file_has_xattrs(file))).collect();
|
|
||||||
|
|
||||||
let mut last_working_table = self.make_grid(1, &*columns_for_dir, files, cells.clone());
|
let (cells, file_names) = {
|
||||||
|
|
||||||
|
let first_table = self.make_table(env.clone(), &*columns_for_dir);
|
||||||
|
|
||||||
|
let cells = files.iter()
|
||||||
|
.map(|file| first_table.cells_for_file(file, file_has_xattrs(file)))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let file_names = files.into_iter()
|
||||||
|
.map(|file| first_table.filename_cell(file, false))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
(cells, file_names)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut last_working_table = self.make_grid(env.clone(), 1, &columns_for_dir, &file_names, cells.clone());
|
||||||
|
|
||||||
for column_count in 2.. {
|
for column_count in 2.. {
|
||||||
let grid = self.make_grid(column_count, &*columns_for_dir, files, cells.clone());
|
let grid = self.make_grid(env.clone(), column_count, &columns_for_dir, &file_names, cells.clone());
|
||||||
|
|
||||||
let the_grid_fits = {
|
let the_grid_fits = {
|
||||||
let d = grid.fit_into_columns(column_count);
|
let d = grid.fit_into_columns(column_count);
|
||||||
@ -54,14 +70,24 @@ impl GridDetails {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_table(&self, columns_for_dir: &[Column]) -> Table<OSUsers> {
|
fn make_table<'a>(&'a self, env: Arc<Environment<OSUsers>>, columns_for_dir: &'a [Column]) -> Table<OSUsers> {
|
||||||
let mut table = Table::with_options(self.details.colours, columns_for_dir.into());
|
let mut table = Table {
|
||||||
|
columns: columns_for_dir,
|
||||||
|
opts: &self.details,
|
||||||
|
env: env,
|
||||||
|
|
||||||
|
rows: Vec::new(),
|
||||||
|
};
|
||||||
|
|
||||||
if self.details.header { table.add_header() }
|
if self.details.header { table.add_header() }
|
||||||
table
|
table
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_grid(&self, column_count: usize, columns_for_dir: &[Column], files: &[File], cells: Vec<Vec<Cell>>) -> grid::Grid {
|
fn make_grid<'a>(&'a self, env: Arc<Environment<OSUsers>>, column_count: usize, columns_for_dir: &'a [Column], file_names: &[TextCell], cells: Vec<Vec<TextCell>>) -> grid::Grid {
|
||||||
let mut tables: Vec<_> = repeat(()).map(|_| self.make_table(columns_for_dir)).take(column_count).collect();
|
let mut tables = Vec::new();
|
||||||
|
for _ in 0 .. column_count {
|
||||||
|
tables.push(self.make_table(env.clone(), columns_for_dir));
|
||||||
|
}
|
||||||
|
|
||||||
let mut num_cells = cells.len();
|
let mut num_cells = cells.len();
|
||||||
if self.details.header {
|
if self.details.header {
|
||||||
@ -71,7 +97,7 @@ impl GridDetails {
|
|||||||
let original_height = divide_rounding_up(cells.len(), column_count);
|
let original_height = divide_rounding_up(cells.len(), column_count);
|
||||||
let height = divide_rounding_up(num_cells, column_count);
|
let height = divide_rounding_up(num_cells, column_count);
|
||||||
|
|
||||||
for (i, (file, row)) in files.iter().zip(cells.into_iter()).enumerate() {
|
for (i, (file_name, row)) in file_names.iter().zip(cells.into_iter()).enumerate() {
|
||||||
let index = if self.grid.across {
|
let index = if self.grid.across {
|
||||||
i % column_count
|
i % column_count
|
||||||
}
|
}
|
||||||
@ -79,10 +105,10 @@ impl GridDetails {
|
|||||||
i / original_height
|
i / original_height
|
||||||
};
|
};
|
||||||
|
|
||||||
tables[index].add_file_with_cells(row, file, 0, false, false);
|
tables[index].add_file_with_cells(row, file_name.clone(), 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
let columns: Vec<_> = tables.iter().map(|t| t.print_table()).collect();
|
let columns: Vec<_> = tables.into_iter().map(|t| t.print_table()).collect();
|
||||||
|
|
||||||
let direction = if self.grid.across { grid::Direction::LeftToRight }
|
let direction = if self.grid.across { grid::Direction::LeftToRight }
|
||||||
else { grid::Direction::TopToBottom };
|
else { grid::Direction::TopToBottom };
|
||||||
@ -97,8 +123,8 @@ impl GridDetails {
|
|||||||
for column in columns.iter() {
|
for column in columns.iter() {
|
||||||
if row < column.len() {
|
if row < column.len() {
|
||||||
let cell = grid::Cell {
|
let cell = grid::Cell {
|
||||||
contents: column[row].text.clone(),
|
contents: ANSIStrings(&column[row].contents).to_string(),
|
||||||
width: column[row].length,
|
width: *column[row].width,
|
||||||
};
|
};
|
||||||
|
|
||||||
grid.add(cell);
|
grid.add(cell);
|
||||||
@ -110,8 +136,8 @@ impl GridDetails {
|
|||||||
for column in columns.iter() {
|
for column in columns.iter() {
|
||||||
for cell in column.iter() {
|
for cell in column.iter() {
|
||||||
let cell = grid::Cell {
|
let cell = grid::Cell {
|
||||||
contents: cell.text.clone(),
|
contents: ANSIStrings(&cell.contents).to_string(),
|
||||||
width: cell.length,
|
width: *cell.width,
|
||||||
};
|
};
|
||||||
|
|
||||||
grid.add(cell);
|
grid.add(cell);
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
use colours::Colours;
|
use ansi_term::ANSIStrings;
|
||||||
|
|
||||||
use file::File;
|
use file::File;
|
||||||
|
|
||||||
use super::filename;
|
use super::filename;
|
||||||
|
use super::colours::Colours;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
@ -11,9 +13,9 @@ pub struct Lines {
|
|||||||
|
|
||||||
/// The lines view literally just displays each file, line-by-line.
|
/// The lines view literally just displays each file, line-by-line.
|
||||||
impl Lines {
|
impl Lines {
|
||||||
pub fn view(&self, files: &[File]) {
|
pub fn view(&self, files: Vec<File>) {
|
||||||
for file in files {
|
for file in files {
|
||||||
println!("{}", filename(file, &self.colours, true));
|
println!("{}", ANSIStrings(&filename(file, &self.colours, true)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,42 +1,73 @@
|
|||||||
use ansi_term::ANSIStrings;
|
use ansi_term::Style;
|
||||||
|
|
||||||
use colours::Colours;
|
|
||||||
use file::File;
|
use file::File;
|
||||||
use filetype::file_colour;
|
|
||||||
|
|
||||||
|
pub use self::cell::{TextCell, TextCellContents, DisplayWidth};
|
||||||
|
pub use self::colours::Colours;
|
||||||
pub use self::details::Details;
|
pub use self::details::Details;
|
||||||
|
pub use self::grid_details::GridDetails;
|
||||||
pub use self::grid::Grid;
|
pub use self::grid::Grid;
|
||||||
pub use self::lines::Lines;
|
pub use self::lines::Lines;
|
||||||
pub use self::grid_details::GridDetails;
|
|
||||||
|
|
||||||
mod grid;
|
mod grid;
|
||||||
pub mod details;
|
pub mod details;
|
||||||
mod lines;
|
mod lines;
|
||||||
mod grid_details;
|
mod grid_details;
|
||||||
pub mod column;
|
pub mod column;
|
||||||
|
mod cell;
|
||||||
|
mod colours;
|
||||||
|
mod tree;
|
||||||
|
|
||||||
|
pub fn filename(file: File, colours: &Colours, links: bool) -> TextCellContents {
|
||||||
pub fn filename(file: &File, colours: &Colours, links: bool) -> String {
|
|
||||||
if links && file.is_link() {
|
if links && file.is_link() {
|
||||||
symlink_filename(file, colours)
|
symlink_filename(file, colours)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let style = file_colour(colours, file);
|
vec![
|
||||||
style.paint(&*file.name).to_string()
|
file_colour(colours, &file).paint(file.name)
|
||||||
|
].into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn symlink_filename(file: &File, colours: &Colours) -> String {
|
fn symlink_filename(file: File, colours: &Colours) -> TextCellContents {
|
||||||
match file.link_target() {
|
match file.link_target() {
|
||||||
Ok(target) => format!("{} {} {}",
|
Ok(target) => vec![
|
||||||
file_colour(colours, file).paint(&*file.name),
|
file_colour(colours, &file).paint(file.name),
|
||||||
colours.punctuation.paint("->"),
|
Style::default().paint(" "),
|
||||||
ANSIStrings(&[ colours.symlink_path.paint(target.path_prefix()),
|
colours.punctuation.paint("->"),
|
||||||
file_colour(colours, &target).paint(target.name) ])),
|
Style::default().paint(" "),
|
||||||
|
colours.symlink_path.paint(target.path_prefix()),
|
||||||
|
file_colour(colours, &target).paint(target.name)
|
||||||
|
].into(),
|
||||||
|
|
||||||
Err(filename) => format!("{} {} {}",
|
Err(filename) => vec![
|
||||||
file_colour(colours, file).paint(&*file.name),
|
file_colour(colours, &file).paint(file.name),
|
||||||
colours.broken_arrow.paint("->"),
|
Style::default().paint(" "),
|
||||||
colours.broken_filename.paint(filename)),
|
colours.broken_arrow.paint("->"),
|
||||||
|
Style::default().paint(" "),
|
||||||
|
colours.broken_filename.paint(filename),
|
||||||
|
].into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn file_colour(colours: &Colours, file: &File) -> Style {
|
||||||
|
use filetype::FileTypes;
|
||||||
|
|
||||||
|
match file {
|
||||||
|
f if f.is_directory() => colours.filetypes.directory,
|
||||||
|
f if f.is_executable_file() => colours.filetypes.executable,
|
||||||
|
f if f.is_link() => colours.filetypes.symlink,
|
||||||
|
f if !f.is_file() => colours.filetypes.special,
|
||||||
|
f if f.is_immediate() => colours.filetypes.immediate,
|
||||||
|
f if f.is_image() => colours.filetypes.image,
|
||||||
|
f if f.is_video() => colours.filetypes.video,
|
||||||
|
f if f.is_music() => colours.filetypes.music,
|
||||||
|
f if f.is_lossless() => colours.filetypes.lossless,
|
||||||
|
f if f.is_crypto() => colours.filetypes.crypto,
|
||||||
|
f if f.is_document() => colours.filetypes.document,
|
||||||
|
f if f.is_compressed() => colours.filetypes.compressed,
|
||||||
|
f if f.is_temp() => colours.filetypes.temp,
|
||||||
|
f if f.is_compiled() => colours.filetypes.compiled,
|
||||||
|
_ => colours.filetypes.normal,
|
||||||
|
}
|
||||||
|
}
|
175
src/output/tree.rs
Normal file
175
src/output/tree.rs
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
//! Tree structures, such as `├──` or `└──`, used in a tree view.
|
||||||
|
//!
|
||||||
|
//! ## Constructing Tree Views
|
||||||
|
//!
|
||||||
|
//! When using the `--tree` argument, instead of a vector of cells, each row
|
||||||
|
//! has a `depth` field that indicates how far deep in the tree it is: the top
|
||||||
|
//! level has depth 0, its children have depth 1, and *their* children have
|
||||||
|
//! depth 2, and so on.
|
||||||
|
//!
|
||||||
|
//! On top of this, it also has a `last` field that specifies whether this is
|
||||||
|
//! the last row of this particular consecutive set of rows. This doesn’t
|
||||||
|
//! affect the file’s information; it’s just used to display a different set of
|
||||||
|
//! Unicode tree characters! The resulting table looks like this:
|
||||||
|
//!
|
||||||
|
//! ┌───────┬───────┬───────────────────────┐
|
||||||
|
//! │ Depth │ Last │ Output │
|
||||||
|
//! ├───────┼───────┼───────────────────────┤
|
||||||
|
//! │ 0 │ │ documents │
|
||||||
|
//! │ 1 │ false │ ├── this_file.txt │
|
||||||
|
//! │ 1 │ false │ ├── that_file.txt │
|
||||||
|
//! │ 1 │ false │ ├── features │
|
||||||
|
//! │ 2 │ false │ │ ├── feature_1.rs │
|
||||||
|
//! │ 2 │ false │ │ ├── feature_2.rs │
|
||||||
|
//! │ 2 │ true │ │ └── feature_3.rs │
|
||||||
|
//! │ 1 │ true │ └── pictures │
|
||||||
|
//! │ 2 │ false │ ├── garden.jpg │
|
||||||
|
//! │ 2 │ false │ ├── flowers.jpg │
|
||||||
|
//! │ 2 │ false │ ├── library.png │
|
||||||
|
//! │ 2 │ true │ └── space.tiff │
|
||||||
|
//! └───────┴───────┴───────────────────────┘
|
||||||
|
//!
|
||||||
|
//! Creating the table like this means that each file has to be tested to see
|
||||||
|
//! if it’s the last one in the group. This is usually done by putting all the
|
||||||
|
//! files in a vector beforehand, getting its length, then comparing the index
|
||||||
|
//! of each file to see if it’s the last one. (As some files may not be
|
||||||
|
//! successfully `stat`ted, we don’t know how many files are going to exist in
|
||||||
|
//! each directory)
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug, Clone)]
|
||||||
|
pub enum TreePart {
|
||||||
|
|
||||||
|
/// Rightmost column, *not* the last in the directory.
|
||||||
|
Edge,
|
||||||
|
|
||||||
|
/// Not the rightmost column, and the directory has not finished yet.
|
||||||
|
Line,
|
||||||
|
|
||||||
|
/// Rightmost column, and the last in the directory.
|
||||||
|
Corner,
|
||||||
|
|
||||||
|
/// Not the rightmost column, and the directory *has* finished.
|
||||||
|
Blank,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TreePart {
|
||||||
|
|
||||||
|
/// Turn this tree part into ASCII-licious box drawing characters!
|
||||||
|
/// (Warning: not actually ASCII)
|
||||||
|
pub fn ascii_art(&self) -> &'static str {
|
||||||
|
match *self {
|
||||||
|
TreePart::Edge => "├──",
|
||||||
|
TreePart::Line => "│ ",
|
||||||
|
TreePart::Corner => "└──",
|
||||||
|
TreePart::Blank => " ",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// A **tree trunk** builds up arrays of tree parts over multiple depths.
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct TreeTrunk {
|
||||||
|
|
||||||
|
/// A stack tracks which tree characters should be printed. It’s
|
||||||
|
/// necessary to maintain information about the previously-printed
|
||||||
|
/// lines, as the output will change based on any previous entries.
|
||||||
|
stack: Vec<TreePart>,
|
||||||
|
|
||||||
|
/// A tuple for the last ‘depth’ and ‘last’ parameters that are passed in.
|
||||||
|
last_params: Option<(usize, bool)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TreeTrunk {
|
||||||
|
|
||||||
|
/// Calculates the tree parts for an entry at the given depth and
|
||||||
|
/// last-ness. The depth is used to determine where in the stack the tree
|
||||||
|
/// part should be inserted, and the last-ness is used to determine which
|
||||||
|
/// type of tree part to insert.
|
||||||
|
///
|
||||||
|
/// This takes a `&mut self` because the results of each file are stored
|
||||||
|
/// and used in future rows.
|
||||||
|
pub fn new_row(&mut self, depth: usize, last: bool) -> &[TreePart] {
|
||||||
|
|
||||||
|
// If this isn’t our first iteration, then update the tree parts thus
|
||||||
|
// far to account for there being another row after it.
|
||||||
|
if let Some((last_depth, last_last)) = self.last_params {
|
||||||
|
self.stack[last_depth] = if last_last { TreePart::Blank } else { TreePart::Line };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the stack has enough space, then add or modify another
|
||||||
|
// part into it.
|
||||||
|
self.stack.resize(depth + 1, TreePart::Edge);
|
||||||
|
self.stack[depth] = if last { TreePart::Corner } else { TreePart::Edge };
|
||||||
|
self.last_params = Some((depth, last));
|
||||||
|
|
||||||
|
// Return the tree parts as a slice of the stack.
|
||||||
|
//
|
||||||
|
// Ignoring the first component is specific to exa: when a user prints
|
||||||
|
// a tree view for multiple directories, we don’t want there to be a
|
||||||
|
// ‘zeroth level’ connecting the initial directories. Otherwise, not
|
||||||
|
// only are unrelated directories seemingly connected to each other,
|
||||||
|
// but the tree part of the first row doesn’t connect to anything:
|
||||||
|
//
|
||||||
|
// with [0..] with [1..]
|
||||||
|
// ========== ==========
|
||||||
|
// ├──folder folder
|
||||||
|
// │ └──file └──file
|
||||||
|
// └──folder folder
|
||||||
|
// └──file └──file
|
||||||
|
&self.stack[1..]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_at_first() {
|
||||||
|
let mut tt = TreeTrunk::default();
|
||||||
|
assert_eq!(tt.new_row(0, true), &[]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn one_child() {
|
||||||
|
let mut tt = TreeTrunk::default();
|
||||||
|
assert_eq!(tt.new_row(0, true), &[]);
|
||||||
|
assert_eq!(tt.new_row(1, true), &[ TreePart::Corner ]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn two_children() {
|
||||||
|
let mut tt = TreeTrunk::default();
|
||||||
|
assert_eq!(tt.new_row(0, true), &[]);
|
||||||
|
assert_eq!(tt.new_row(1, false), &[ TreePart::Edge ]);
|
||||||
|
assert_eq!(tt.new_row(1, true), &[ TreePart::Corner ]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn two_times_two_children() {
|
||||||
|
let mut tt = TreeTrunk::default();
|
||||||
|
assert_eq!(tt.new_row(0, false), &[]);
|
||||||
|
assert_eq!(tt.new_row(1, false), &[ TreePart::Edge ]);
|
||||||
|
assert_eq!(tt.new_row(1, true), &[ TreePart::Corner ]);
|
||||||
|
|
||||||
|
assert_eq!(tt.new_row(0, true), &[]);
|
||||||
|
assert_eq!(tt.new_row(1, false), &[ TreePart::Edge ]);
|
||||||
|
assert_eq!(tt.new_row(1, true), &[ TreePart::Corner ]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn two_times_two_nested_children() {
|
||||||
|
let mut tt = TreeTrunk::default();
|
||||||
|
assert_eq!(tt.new_row(0, true), &[]);
|
||||||
|
|
||||||
|
assert_eq!(tt.new_row(1, false), &[ TreePart::Edge ]);
|
||||||
|
assert_eq!(tt.new_row(2, false), &[ TreePart::Line, TreePart::Edge ]);
|
||||||
|
assert_eq!(tt.new_row(2, true), &[ TreePart::Line, TreePart::Corner ]);
|
||||||
|
|
||||||
|
assert_eq!(tt.new_row(1, true), &[ TreePart::Corner ]);
|
||||||
|
assert_eq!(tt.new_row(2, false), &[ TreePart::Blank, TreePart::Edge ]);
|
||||||
|
assert_eq!(tt.new_row(2, true), &[ TreePart::Blank, TreePart::Corner ]);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user