Compare commits

...

170 Commits

Author SHA1 Message Date
Aria 753150d374
Merge pull request #1249 from capak07/test#1010
Fixed Say Thanks Button Issue
2024-04-08 19:19:49 +02:00
capak07 d7c9f4e65f Removed Say Thanks link in README 2024-03-21 00:20:25 -03:00
Mélanie Chauvel fb05c421ae exa is unmaintained, please use the active fork eza instead
<https://github.com/eza-community/eza>
2023-09-06 01:14:36 +02:00
Mélanie Chauvel f039202b4f
Merge pull request #1188 from 1stDimension/ci-migrate
Moving actions to dtolnay's version
2023-08-03 19:34:08 +02:00
Mélanie Chauvel 17893b5f57 Upgrade Rust version to fix CI 2023-08-03 18:34:16 +02:00
Mélanie Chauvel 08cc56d7ac
Merge pull request #1219 from MartinNowak/patch-1
fix fish completion for -i/--inode option
2023-08-03 18:08:33 +02:00
Mélanie Chauvel 352d32b60c
Merge pull request #1178 from mtoohey31/fix/obsolete-icons
Replace obsolete icons
2023-08-03 18:06:36 +02:00
Mélanie Chauvel e79f7361a7
Merge pull request #1175 from dcervenkov/patch-1
Change ipynb icon
2023-08-03 18:03:55 +02:00
dawg a4cee84b4b
fix fish completion for -i/--inode option
- looks like a copy&paste error of -g/--group
2023-07-19 23:58:02 +02:00
1stDimension bf41cdefa6
Moving actions to dtolnay's version 2023-05-01 01:41:20 +02:00
Matthew Toohey 94043e1fa8 Replace obsolete icons 2023-04-12 18:55:00 -04:00
Daniel Cervenkov b8cee60acf
Change ipynb icon
Jupyter notebooks are often in the same folder as plain Python files. Having a different icon would be useful. Since there is no Jupyter Notebook icon in Nerd fonts, I chose an icon of a notebook.
2023-03-30 16:59:12 +02:00
Mélanie Chauvel c697d06670
Merge pull request #1125 from Tyrubias/upgrade-and-cleanup
Upgrade both Rust version and edition (and fix some lints)
2023-03-01 15:17:36 +01:00
Victor Song ee67110333 Update README.md 2023-02-28 22:41:51 -05:00
Victor Song 39d15a317d Update GitHub actions Rust version 2023-02-28 22:23:29 -05:00
Victor Song 8d03922e3b Addressed PR comments 2023-02-24 18:45:27 -05:00
Victor Song eba3646b83 Grab bag of miscellaneous fixes 2023-02-24 18:29:37 -05:00
Victor Song 72b2119a34 Simplify boolean assertions in tests 2023-02-24 18:29:37 -05:00
Victor Song bbea87db91 Avoid redundant closures when calling methods 2023-02-24 18:29:37 -05:00
Victor Song 1dc14eaff1 Avoid manually implementing repeat() with iterators 2023-02-24 18:29:37 -05:00
Victor Song d24ca084a3 Use single char pattern for better performance 2023-02-24 18:29:37 -05:00
Victor Song 433a9a52d3 Use mutable slice instead of creating new Vec in filters 2023-02-24 18:29:37 -05:00
Victor Song 7c1878f0e4 Fix borrowing code smells 2023-02-24 18:29:37 -05:00
Victor Song 1f409793ae Use `pointer::cast` instead of `as` pointer casts 2023-02-24 18:29:37 -05:00
Victor Song 19601267cf Convert from bool to u8 using From trait 2023-02-24 18:29:37 -05:00
Victor Song 7595289698 Use `unsigned_abs` instead of casting from i64 to u64 2023-02-24 18:29:37 -05:00
Victor Song 2917062466 Nest OR patterns in match arms 2023-02-24 18:29:37 -05:00
Victor Song 1b844a8dfa Change `pub_enum_variant_names` lint to `enum_variant_names` 2023-02-24 18:29:37 -05:00
Victor Song af267ba638 Use `unwrap_or_else` in build.rs 2023-02-24 18:29:37 -05:00
Victor Song cd715a6e00 Safely derive `Eq` whenever we derive `PartialEq` 2023-02-24 18:29:37 -05:00
Victor Song 89bcc00e32 Upgrade to latest stable Rust and edition 2021 2023-02-24 18:29:37 -05:00
Mélanie Chauvel f3ca1fe6f7
Merge pull request #820 from skyline75489/chesterliu/dev/win-support
Initial support for Windows
2023-02-20 14:19:47 +01:00
Mélanie Chauvel e385cd58da
Merge pull request #1088 from cab-1729/master
Added icon for img files
2022-07-18 18:25:02 +02:00
Mélanie Chauvel 3ca40915ae
Merge pull request #1082 from TygrisIQ/master
updated links with markdown highlighting and always use https
2022-07-18 18:23:10 +02:00
cab-1729 45b6413fd0
Added icon for img files 2022-07-17 16:22:20 +05:30
TygrisIQ 577ac91513
Merge branch 'ogham:master' into master 2022-07-17 09:40:16 +03:00
TygrisIQ d40b7b1ff4
updated unix.stackexchange link from http to https 2022-07-17 09:39:30 +03:00
TygrisIQ 21758c81ea
Update fields.rs
changed unix.stackexchange.com from http to https
2022-07-17 09:35:14 +03:00
Mélanie Chauvel 7e4944c188
Merge pull request #1076 from cab-1729/master
Use Perl icon for .pm and other Perl extensions
2022-07-16 16:17:55 +02:00
TygrisIQ 584b53bb17
updated links with markdown highlighting 2022-07-07 16:00:37 +03:00
TygrisIQ a4b23055a8
updated links with markdown highlighting 2022-07-07 15:59:21 +03:00
TygrisIQ 863d96150d
updated the link with markdown link syntax
updated the link in the comment so it helps with generated docs
2022-07-07 15:18:04 +03:00
cab-1729 a65c52d821
Add files via upload 2022-06-26 21:40:00 +05:30
cab-1729 954462634a
Added icons for .pm similar to .pl 2022-06-17 16:44:39 +05:30
Mélanie Chauvel 8ad8b33573 Fix build on aarch64 and armv7 2022-05-08 03:19:09 +02:00
Mélanie Chauvel 8c2956a8fd Add rust-version in Cargo.toml 2022-05-08 03:13:03 +02:00
Mélanie Chauvel 29422d8c93 Update Ubuntu package link in README 2022-05-08 03:11:17 +02:00
Chester Liu 6fb3740f24
Update src/fs/file.rs 2022-05-06 13:30:35 +08:00
Chester Liu 53cb75cf2b Fix build on Windows 2022-05-06 13:29:10 +08:00
Chester Liu d6732aea10 Merge branch 'master' into chesterliu/dev/win-support 2022-05-06 13:22:24 +08:00
Mélanie Chauvel dceca33779
Merge pull request #1058 from Ryooooooga/feature/go-mod-icons
add icons for go.mod and go.sum
2022-04-29 18:57:23 +02:00
Ryooooooga 6197006d5f
add Go module icons 2022-04-23 10:18:42 +09:00
Mélanie Chauvel 02f44c68d8
Merge pull request #1050 from ewrenge/patch-1
Fix `just` command's unavailable option
2022-04-02 18:12:55 +02:00
ewreurei 98a4431d6a
Fix just command's unavailable option 2022-04-01 22:26:51 +09:00
Mélanie Chauvel fc6a6d0b38
Merge pull request #1045 from sorairolake/format/cpio
Add cpio filetype
2022-03-22 15:17:15 +01:00
Mélanie Chauvel ccc1f9999a
Merge pull request #1046 from sorairolake/format/jp2
Add JPEG 2000 filetype
2022-03-22 15:16:55 +01:00
Shun Sakai bced9841f4 Add JPEG 2000 filetype 2022-03-20 12:36:54 +09:00
Shun Sakai f5bbfa7871 Add cpio filetype 2022-03-18 16:51:06 +09:00
Mélanie Chauvel b869bd06dc
Merge pull request #999 from loicreynier/patch-1
Update Debian info
2022-03-17 19:44:12 +01:00
Mélanie Chauvel f6db28e25a
Merge pull request #1011 from WesleyBatista/patch-1
Make it easy to copy and paste
2022-03-17 19:41:32 +01:00
Mélanie Chauvel 0659c36897
Merge pull request #1008 from jvaverka/patch-1
Update icons.rs
2022-03-17 19:36:25 +01:00
Mélanie Chauvel a58a3313ea
Merge pull request #1013 from ricpelo/fix-icons
Uses same icon for lib, xml & xul files (fix #891)
2022-03-17 19:29:14 +01:00
Philippe Eberli fe64690063
Change icon for gradle files to Java (#1017)
* Change icon for gradle files to Java

Gradle files are for the whole Java ecosystem. Not just Android.

* Change the icon for jar & java to 

Other java related files (class, jad, war) already use this.
2022-03-17 18:05:37 +01:00
Abhilash Balaji c968c388d4
workaround for num_cpus returning 0 (#1034)
workaround for num_cpus returning 0
2022-03-17 14:51:22 +01:00
Ricardo Pérez 400bb0a140 Use same icon for lib, xml & xul files (fix #891) 2022-01-23 19:52:06 +01:00
Wesley f0b9cceb73
Make it easy to copy and paste 2022-01-15 00:25:27 +01:00
Jacob Vaverka e433b3fed0
Update icons.rs
Add Markdown icon for Julia Markdown documents (same convention as for `*.Rmd`)
2022-01-10 11:46:23 -05:00
Christian Höltje 42659f9345
add icon for .bats files (#968)
* add icon for .bats files

This adds the same icon as BASH for `.bats` files.

Co-authored-by: Mélanie Chauvel <perso@hack-libre.org>
2022-01-05 00:10:24 +01:00
Mélanie Chauvel a2f3ff98c2
Merge pull request #993 from cab-1729/master
Adding icons for .part and .torrent files
2022-01-05 00:09:31 +01:00
Mélanie Chauvel 4f919a6bc5
Merge pull request #989 from foundObjects/add-filetypes
Add file icons for zst and Arch Linux PKGBUILD
2022-01-05 00:03:58 +01:00
Mélanie Chauvel 446903ac00
Merge pull request #1002 from thesamesam/gentoo
icons: add Gentoo for .ebuild
2022-01-04 23:39:28 +01:00
Mélanie Chauvel be6feb82d8
Merge pull request #1003 from reishoku/remove-unnested-or-patterns
src/main.rs: remove clippy::unnested_or_patterns
2022-01-04 22:19:13 +01:00
KOSHIKAWA Kenichi 352a1e8f19 src/main.rs: remove clippy::unnested_or_patterns
Signed-off-by: KOSHIKAWA Kenichi <reishoku.misc@pm.me>
2022-01-03 22:57:38 +09:00
Sam James 89d537adb4 icons: add Gentoo for .ebuild
Signed-off-by: Sam James <sam@gentoo.org>
2022-01-03 04:43:29 +00:00
Mélanie Chauvel 4f0275395b
Merge pull request #991 from ashincoder/patch-1
feat: Add julia file extension icon
2021-12-27 18:45:53 +01:00
Loïc c3eb8321ff
Update README.md 2021-12-26 12:26:25 +01:00
cab-1729 091ab51b98
Add files via upload 2021-12-15 21:08:30 +05:30
Ashin Antony 859666d287
feat: Add julia file extension icon
Icon for julia file extension '.jl' is added to the icons list.
2021-12-14 17:46:30 +05:30
Scott B c4b8e7af1a add PKGBUILD file icon 2021-12-10 18:21:03 -08:00
Scott B 69ae5db3b6 add zst file icon 2021-12-10 18:04:57 -08:00
Mélanie Chauvel 3e9de0e7e1
Merge pull request #988 from Eijebong/deps
Update term_grid to 0.2
2021-12-10 17:34:31 +01:00
Bastien Orivel af208285e8 Update term_grid to 0.2 2021-12-10 13:21:13 +01:00
Mélanie Chauvel ef8fd32dc6 Update MSRV and add it in rust-toolchain.toml 2021-12-10 01:02:21 +01:00
Mélanie Chauvel 2ac7024197 Ignore all log files (produced by Vagrant) 2021-12-10 00:16:42 +01:00
Mélanie Chauvel 7c957f95b3
Merge pull request #982 from paper-lark/no-color
NO_COLOR environment variable support
2021-12-06 21:43:55 +01:00
Max Zhuravsky c6874f0b32
[baseline] - fix docs and remove gitignore 2021-12-06 02:06:30 +03:00
Mélanie Chauvel df4fb84ae1 don’t display broken pipe error messages (see rust-lang/rust#46016) 2021-12-05 18:22:38 +01:00
Max Zhuravsky a371c41711
[no-color] - add unit test and doc 2021-11-26 01:45:41 +03:00
Max Zhuravsky aab1d3db59
[no-color] - implement NO_COLOR support 2021-11-25 23:37:02 +03:00
Chester Liu 99d653b7fa Merge branch 'master' into chesterliu/dev/win-support 2021-10-29 15:50:54 +08:00
Mélanie Chauvel b32f441851
Merge pull request #929 from ariasuni/fix-shell-completions-colour-flag
Fix shell completions colour flag
2021-10-11 02:27:30 +02:00
Mélanie Chauvel 0332e0c7f7 Fix bash completion for the colour flag
Tell bash it accepts a value (always, auto, or never)
2021-10-11 02:26:12 +02:00
Mélanie Chauvel 0d645735d7 Add short flags and fix broken flags in bash completion
- Parse help manually to be able to find short options
- Correctly propose options starting with colo[u]r
- Propose short options after - and long after --
2021-10-11 02:26:12 +02:00
Mélanie Chauvel 6af9e221a4 Fix fish completion for the colour flag
Tell fish it accepts a value (always, auto, or never)
2021-10-11 02:26:12 +02:00
Mélanie Chauvel 0cebf3ad1c
Merge pull request #965 from jim4067/lz4-patch
add lz4 file type and icon
2021-10-10 19:51:34 +02:00
jim4067 4220b6f41e add lz4 file type and icon 2021-10-03 13:16:47 +03:00
Mélanie Chauvel a7aca35d97
Merge pull request #957 from hellosway/patch-1
Recognize .jxl and .avif as image files
2021-10-03 01:11:01 +02:00
Mélanie Chauvel 35aeac759e
Merge pull request #964 from jim4067/icons-patch
fix: add compression icon to .tXX files
2021-10-03 00:50:29 +02:00
jim4067 247d1345e7 fix: add compression icon to .tXX files #930 2021-10-02 20:05:32 +03:00
hellosway 1c36b71779
Add avif and jxl icons 2021-09-27 16:36:17 -07:00
hellosway 659def7138
Recognize .jxl and .avif as image files 2021-09-22 17:07:06 -07:00
Mélanie Chauvel ec786201c8
Merge pull request #907 from ariasuni/update-cargo-toml-for-cargo-deb
Update Cargo.toml for cargo-deb
2021-09-17 21:09:26 +02:00
ariasuni d2b6cc9185 Update build.rs to print path when writing version_string.txt fails 2021-09-17 20:58:49 +02:00
ariasuni 75f14d23a3 Update Cargo.toml to make cargo-deb work again 2021-09-17 20:58:49 +02:00
Joshua Gawley 11793973fe
Add Makefile icon (#945)
* Add icon for .mk files
* Updated icon for Makefile
2021-09-03 18:10:15 +02:00
Mélanie Chauvel 257786749f
Merge pull request #870 from ariasuni/fix-panic-on-non-utf8-file-when-using-git
Fix panic on non UTF-8 file when using Git
2021-09-01 22:59:14 +02:00
Mélanie Chauvel fe11b9d319 Fix panic on non UTF-8 file when using Git 2021-09-01 22:42:34 +02:00
Mélanie Chauvel aff35a1643
Merge pull request #940 from izhakjakov/master
Add extension ".bkp" as temp file
2021-09-01 22:38:38 +02:00
Izhak Jakov 5f49a2e840 Add extension ".bkp" as temp file 2021-08-22 21:11:35 -04:00
Mélanie Chauvel 439b629d90
Merge pull request #933 from billrisher/fix/add_podfile_to_immediate
Added 'Podfile' to list of immediate files
2021-08-20 18:18:06 +02:00
Mélanie Chauvel 26b40bf773
Merge pull request #932 from a1346054/master
Minor cleanup
2021-08-18 19:01:19 +02:00
Bill Risher 79cd5d448a Added 'Podfile' to list of immediate files 2021-08-17 21:21:02 -05:00
a1346054 2bef43fb1b fix whitespace 2021-08-14 22:05:22 +00:00
a1346054 91dcf52972 fix shell script issues identified through shellcheck 2021-08-14 22:04:50 +00:00
Mélanie Chauvel 3f24f7cbcf
Merge pull request #903 from xxkfqz/jpgexts
Add more JPG extensions
2021-08-12 17:20:15 +02:00
Mélanie Chauvel 4c8658ab90
Replace Travis CI badge by GitHub Actions one 2021-08-12 17:18:56 +02:00
Mélanie Chauvel e7a477eb15
Merge pull request #926 from Freed-Wu/update-zsh-completion
Update zsh completions for '--color'
2021-08-10 17:10:46 +02:00
Mélanie Chauvel 69d5e1fc11
Merge pull request #928 from ariasuni/use-github-actions
use GitHub Actions instead of Travis CI
2021-08-09 16:05:21 +02:00
Mélanie Chauvel c24afe3a08 use GitHub Actions instead of Travis CI 2021-08-09 16:02:31 +02:00
Mélanie Chauvel 049f766d1d
Merge pull request #920 from j-tai/update-git2
Update git2 dependency (fix build with Rust 1.54)
2021-08-09 15:18:24 +02:00
Freed-Wu 4b6cf1b5a4 Update zsh completions for '--color' 2021-08-07 13:22:15 +08:00
James Tai c46329efb2 Update git2 package 2021-08-01 15:49:19 -07:00
xxkfqz 8de5b97804 fix: more JPG extensions 2021-07-02 15:02:06 +05:00
Mélanie Chauvel dc5c42a0f2
Merge pull request #877 from gleich/master
Add icon for .sty file
2021-06-24 11:41:55 +02:00
Mélanie Chauvel 78b46e219e
Merge pull request #899 from kidonng/patch-1
Fix fish completion for `-H` option
2021-06-24 11:39:29 +02:00
Kid f6b3975562
Fix fish completion for `-H` option 2021-06-24 17:27:45 +08:00
Matthew Gleich 6a07b59a80 Merge branch 'ogham:master' into master 2021-05-28 11:53:05 -04:00
Chester Liu 9881d00d00 Fix build 2021-05-20 10:57:57 +08:00
Mélanie Chauvel a6754f3cc3
Merge pull request #860 from ariasuni/split-completions-directory-for-each-shell
Rename completion scripts according to each shell conventions
2021-05-19 01:00:58 +02:00
Matthew Gleich 56c78400b8
feat: add sty file
Signed-off-by: Matthew Gleich <git@mattglei.ch>
2021-05-18 00:12:17 -04:00
Mélanie Chauvel 42b546606e
Merge pull request #871 from ariasuni/fix-clippy-warnings-for-rust-1.53
Fix clippy warnings for Rust 1.53
2021-05-17 17:11:11 +02:00
Chester Liu e3204a574e Use system path sep in symlink 2021-05-15 22:11:23 +08:00
Chester Liu 23a1c8a41f Merge branch 'master' into chesterliu/dev/win-support 2021-05-15 22:03:21 +08:00
ariasuni 785d6ed991 Fix clippy warnings for Rust 1.53 2021-05-13 02:44:22 +02:00
Mélanie Chauvel b18e93d283
Merge pull request #866 from ariasuni/fix-clippy-warnings
Fix all remaining clippy warnings
2021-05-08 23:03:54 +02:00
ariasuni 045172bd9e Fix all remaining clippy warnings
- Allow clippy::cast_*: generated warnings are mostly useless
- Fix the other warnings so using clippy is actually useful
2021-05-08 18:09:35 +02:00
Mélanie Chauvel f8610d05ae
Merge pull request #865 from ariasuni/filter-rs-cleanup
`src/fs/filter.rs` cleanup
2021-05-08 16:37:19 +02:00
ariasuni 86d5939abe Remove unused function 2021-05-08 16:20:17 +02:00
ariasuni 90416ed3ce Update comments about how the --git-ignore option is handled 2021-05-08 16:20:17 +02:00
ariasuni 7c80070120 Update broken link, remove content copied from said link
The old Google+ post is unavailable. The new link points to an article who saved the content and is also available on Wayback Machine.
2021-05-08 16:20:17 +02:00
Mélanie Chauvel a58ad6487f
Merge pull request #862 from cgzones/clippy
Cleanup clippy warnings
2021-05-08 16:06:49 +02:00
Christian Göttsche ae62f5d18e Cleanup clippy warnings
Drop unused allow overrides
2021-04-30 15:37:43 +02:00
Christian Göttsche d253893614 Cleanup clippy warnings
Enable clippy::missing_errors_doc

warning: docs for function returning `Result` missing `# Errors` section
   --> src/main.rs:164:5
    |
164 | /     pub fn run(mut self) -> io::Result<i32> {
165 | |         debug!("Running with options: {:#?}", self.options);
166 | |
167 | |         let mut files = Vec::new();
...   |
202 | |         self.print_dirs(dirs, no_files, is_only_dir, exit_status)
203 | |     }
    | |_____^
    |
2021-04-30 15:37:39 +02:00
Christian Göttsche 61ec153bcd Cleanup clippy warnings
warning: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
 --> src/output/escape.rs:4:1
  |
4 | pub fn escape<'a>(string: String, bits: &mut Vec<ANSIString<'a>>, good: Style, bad: Style) {
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |

warning: this lifetime isn't used in the function definition
 --> src/output/escape.rs:4:15
  |
4 | pub fn escape<'a>(string: String, bits: &mut Vec<ANSIString<'_>>, good: Style, bad: Style) {
  |               ^^
  |

warning: single-character string constant used as pattern
   --> src/output/table.rs:310:41
    |
310 |                     if file.starts_with(":") {
    |                                         ^^^ help: try using a `char` instead: `':'`
    |

warning: single-character string constant used as pattern
   --> src/output/table.rs:310:41
    |
310 |                     if file.starts_with(":") {
    |                                         ^^^ help: try using a `char` instead: `':'`
    |

warning: methods called `new` usually return `Self`
  --> src/output/render/git.rs:38:5
   |
38 |     fn new(&self) -> Style;
   |     ^^^^^^^^^^^^^^^^^^^^^^^
   |

warning: this lifetime isn't used in the function definition
  --> src/output/icons.rs:40:22
   |
40 | pub fn iconify_style<'a>(style: Style) -> Style {
   |                      ^^
   |

warning: lint `clippy::find_map` has been removed: this lint has been replaced by `manual_find_map`, a more specific lint
  --> src/main.rs:11:10
   |
11 | #![allow(clippy::find_map)]
   |          ^^^^^^^^^^^^^^^^
   |

warning: redundant else block
   --> src/fs/dir.rs:124:18
    |
124 |               else {
    |  __________________^
125 | |                 return None
126 | |             }
    | |_____________^
    |

warning: redundant else block
  --> src/options/view.rs:60:18
   |
60 |               else {
   |  __________________^
61 | |                 // the --tree case is handled by the DirAction parser later
62 | |                 return Ok(Self::Details(details));
63 | |             }
   | |_____________^
   |

warning: all variants have the same postfix: `Bytes`
   --> src/output/table.rs:170:1
    |
170 | / pub enum SizeFormat {
171 | |
172 | |     /// Format the file size using **decimal** prefixes, such as “kilo”,
173 | |     /// “mega”, or “giga”.
...   |
181 | |     JustBytes,
182 | | }
    | |_^
    |

warning: all variants have the same postfix: `Bytes`
   --> src/output/table.rs:171:1
    |
171 | / pub enum SizeFormat {
172 | |
173 | |     /// Format the file size using **decimal** prefixes, such as “kilo”,
174 | |     /// “mega”, or “giga”.
...   |
182 | |     JustBytes,
183 | | }
    | |_^
    |

warning: useless use of `format!`
   --> src/options/mod.rs:181:50
    |
181 |               return Err(OptionsError::Unsupported(format!(
    |  __________________________________________________^
182 | |                 "Options --git and --git-ignore can't be used because `git` feature was disabled in this build of exa"
183 | |             )));
    | |_____________^ help: consider using `.to_string()`: `"Options --git and --git-ignore can't be used because `git` feature was disabled in this build of exa".to_string()`
    |

warning: stripping a prefix manually
   --> src/fs/filter.rs:287:33
    |
287 |         if n.starts_with('.') { &n[1..] }
    |                                 ^^^^^^^
    |

warning: case-sensitive file extension comparison
  --> src/info/filetype.rs:24:19
   |
24 |         file.name.ends_with(".ninja") ||
   |                   ^^^^^^^^^^^^^^^^^^^
   |
2021-04-30 15:37:31 +02:00
ariasuni 4a81d2df91 Rename completion scripts according to each shell conventions 2021-04-27 01:06:43 +02:00
Mélanie Chauvel 95682f5674
Merge pull request #858 from Prunkles/master
Add F# icons
2021-04-27 01:01:41 +02:00
Mélanie Chauvel 6b8d7fcd70
Merge pull request #850 from ariasuni/replace-term_size-by-terminal_size
Replace unmaintained crate `term_size` by `terminal_size`
2021-04-27 00:57:49 +02:00
skyline75489 76e336c757 Merge branch 'master' into chesterliu/dev/win-support 2021-04-27 06:46:30 +08:00
ariasuni a85c72e2a0 Replace unmaintained crate `term_size` by `terminal_size` 2021-04-27 00:16:25 +02:00
Haren S 90b97753ad
Update lockfile (#854)
Update lockfile (exa now requires Rust 1.45.2 or higher)

Co-authored-by: Mélanie Chauvel <perso@hack-libre.org>
2021-04-26 23:37:32 +02:00
Prunkles 7a26b4e0f7 Add F# icons 2021-04-26 08:05:05 +03:00
Chester Liu d6d35bf47e Hide _ prefix files 2021-04-20 17:47:58 +08:00
Chester Liu 777cd7e815 Explicitly enable vt processing on Windows 2021-04-17 17:56:20 +08:00
Chester Liu 8f0e4ccfdd Merge branch 'master' into chesterliu/dev/win-support 2021-04-13 11:27:49 +08:00
skyline75489 0adc5c789b Merge branch 'master' into chesterliu/dev/win-support 2021-04-06 16:13:15 +08:00
skyline75489 8ad46e2ee5 Merge branch 'master' into chesterliu/dev/win-support 2021-04-03 21:12:19 +08:00
Chester Liu 0ea8f17b22 Clean 2021-03-31 11:04:11 +08:00
Chester Liu 00f97a9738 Mimic 'Mode' in gci 2021-03-30 17:13:00 +08:00
Chester Liu 78a3bc9838 Merge branch 'master' into chesterliu/dev/win-support 2021-03-30 11:58:39 +08:00
Chester Liu 9d613016c0 Git works! Hooray! 2021-03-28 11:30:32 +08:00
Chester Liu e874584a55 Try to fix CI 2021-03-28 09:31:03 +08:00
Chester Liu 33dd8fd2ca Clean 2021-03-26 20:34:16 +08:00
Chester Liu 13b3635407 Fix tests 2021-03-26 20:28:23 +08:00
Chester Liu 5503e4756e Clean 2021-03-26 18:45:52 +08:00
Chester Liu 31583691d5 Timestamps 2021-03-26 18:40:22 +08:00
Chester Liu aeb4a679e8 It actually works 2021-03-26 17:47:18 +08:00
Chester Liu e9d0af0343 Restore more 2021-03-26 16:53:31 +08:00
Chester Liu 0e8a4582d0 Restore 2021-03-26 16:50:34 +08:00
Chester Liu 6a642d0f32 merge 2021-03-26 16:37:17 +08:00
Kat Marchán 7f717c3af3 checkpoint 2020-05-02 19:00:06 -07:00
61 changed files with 797 additions and 408 deletions

View File

@ -1,14 +1,8 @@
---
name: Bug report
about: Report a crash, runtime error, or invalid output in exa
name: exa is unmaintained
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:
- The version of exa being used (`exa --version`)
- The command-line arguments you are using
- Your operating system and hardware platform
If its a crash, please include the full text of the crash that gets printed to the screen. If youre seeing unexpected behaviour, a screenshot of the issue will help a lot.
exa is unmaintained, please use the active fork eza instead. <https://github.com/eza-community/eza>
---

View File

@ -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.
---

View File

@ -1 +0,0 @@
blank_issues_enabled: true

View File

@ -1,4 +0,0 @@
---
name: Feature request
about: Request a feature or enhancement to exa
---

View File

@ -1,4 +0,0 @@
---
name: Question
about: Ask a question about exa
---

46
.github/workflows/unit-tests.yml vendored Normal file
View 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
View File

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

View File

@ -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

36
Cargo.lock generated
View File

@ -1,5 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "ansi_term"
version = "0.12.1"
@ -57,7 +59,7 @@ dependencies = [
[[package]]
name = "exa"
version = "0.11.0-pre"
version = "0.10.1"
dependencies = [
"ansi_term",
"datetime",
@ -72,7 +74,7 @@ dependencies = [
"number_prefix",
"scoped_threadpool",
"term_grid",
"term_size",
"terminal_size",
"unicode-width",
"users",
"zoneinfo_compiled",
@ -90,9 +92,9 @@ dependencies = [
[[package]]
name = "git2"
version = "0.13.17"
version = "0.13.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d250f5f82326884bd39c2853577e70a121775db76818ffa452ed1e80de12986"
checksum = "d9831e983241f8c5591ed53f17d874833e2fa82cac2625f3888c50cbfe136cba"
dependencies = [
"bitflags",
"libc",
@ -119,9 +121,9 @@ dependencies = [
[[package]]
name = "idna"
version = "0.2.2"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89829a5d69c23d348314a7ac337fe39173b61149a9864deabd260983aed48c21"
checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
dependencies = [
"matches",
"unicode-bidi",
@ -130,9 +132,9 @@ dependencies = [
[[package]]
name = "jobserver"
version = "0.1.21"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2"
checksum = "972f5ae5d1cb9c6ae417789196c803205313edde988685da5e3aae0827b9e7fd"
dependencies = [
"libc",
]
@ -151,9 +153,9 @@ checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
[[package]]
name = "libgit2-sys"
version = "0.12.18+1.1.0"
version = "0.12.21+1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3da6a42da88fc37ee1ecda212ffa254c25713532980005d5f7c0b0fbe7e6e885"
checksum = "86271bacd72b2b9e854c3dcfb82efd538f15f870e4c11af66900effb462f6825"
dependencies = [
"cc",
"libc",
@ -277,18 +279,18 @@ checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
[[package]]
name = "term_grid"
version = "0.1.7"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "230d3e804faaed5a39b08319efb797783df2fd9671b39b7596490cb486d702cf"
checksum = "a7c9eb7705cb3f0fd71d3955b23db6d372142ac139e8c473952c93bf3c3dc4b7"
dependencies = [
"unicode-width",
]
[[package]]
name = "term_size"
version = "0.3.2"
name = "terminal_size"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9"
checksum = "86ca8ced750734db02076f44132d802af0b33b09942331f4459dde8636fd2406"
dependencies = [
"libc",
"winapi",
@ -357,9 +359,9 @@ dependencies = [
[[package]]
name = "vcpkg"
version = "0.2.11"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb"
checksum = "cbdbff6266a24120518560b5dc983096efb98462e51d0d68169895b237be3e5d"
[[package]]
name = "winapi"

View File

@ -1,11 +1,12 @@
[package]
name = "exa"
description = "A modern replacement for ls"
authors = ["Benjamin Sago <ogham@bsago.me>"]
categories = ["command-line-utilities"]
edition = "2018"
edition = "2021"
rust-version = "1.66.1"
exclude = ["/devtools/*", "/Justfile", "/Vagrantfile", "/screenshots.png"]
readme = "README.md"
homepage = "https://the.exa.website/"
license = "MIT"
repository = "https://github.com/ogham/exa"
@ -27,12 +28,14 @@ natord = "1.0"
num_cpus = "1.10"
number_prefix = "0.4"
scoped_threadpool = "0.1"
term_grid = "0.1"
term_size = "0.3"
term_grid = "0.2.0"
terminal_size = "0.1.16"
unicode-width = "0.1"
users = "0.11"
zoneinfo_compiled = "0.5.1"
[target.'cfg(unix)'.dependencies]
users = "0.11"
[dependencies.datetime]
version = "0.5.2"
default-features = false
@ -63,7 +66,7 @@ lto = true
[package.metadata.deb]
license-file = [ "LICENCE" ]
license-file = [ "LICENCE", "4" ]
depends = "$auto"
extended-description = """
exa is a replacement for ls written in Rust.
@ -72,6 +75,9 @@ section = "utils"
priority = "optional"
assets = [
[ "target/release/exa", "/usr/bin/exa", "0755" ],
[ "contrib/man/exa.1", "/usr/share/man/man1/exa.1", "0644" ],
[ "contrib/completions.bash", "/etc/bash_completion.d/exa", "0644" ],
[ "target/release/../man/exa.1", "/usr/share/man/man1/exa.1", "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" ],
]

View File

@ -1,17 +1,19 @@
# exa is unmaintained, use the [fork eza](https://github.com/eza-community/eza) instead.
(This repository isnt archived because the only person with the rights to do so is unreachable).
---
<div align="center">
<h1>exa</h1>
# exa
[exa](https://the.exa.website/) is a modern replacement for _ls_.
**README Sections:** [Options](#options) — [Installation](#installation) — [Development](#development)
<a href="https://travis-ci.org/github/ogham/exa">
<img src="https://travis-ci.org/ogham/exa.svg?branch=master" alt="Build status" />
</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>
[![Unit tests](https://github.com/ogham/exa/actions/workflows/unit-tests.yml/badge.svg)](https://github.com/ogham/exa/actions/workflows/unit-tests.yml)
[![Say thanks!](https://img.shields.io/badge/Say%20Thanks-!-1EAEDB.svg)]()
</div>
![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.
$ apk add exa
apk add exa
### Arch Linux
On Arch, install the [`exa`](https://www.archlinux.org/packages/community/x86_64/exa/) package.
$ pacman -S exa
pacman -S exa
### Android / Termux
On Android / Termux, install the [`exa`](https://github.com/termux/termux-packages/tree/master/packages/exa) package.
$ pkg install exa
pkg install exa
### Debian
On Debian, install the [`exa`](https://packages.debian.org/unstable/exa) package.
For now, exa is in the _unstable_ repository.
On Debian, install the [`exa`](https://packages.debian.org/stable/exa) package.
$ apt install exa
apt install exa
### Fedora
On Fedora, install the [`exa`](https://src.fedoraproject.org/modules/exa) package.
$ dnf install exa
dnf install exa
### Gentoo
On Gentoo, install the [`sys-apps/exa`](https://packages.gentoo.org/packages/sys-apps/exa) package.
$ emerge sys-apps/exa
emerge sys-apps/exa
### Homebrew
If youre using [Homebrew](https://brew.sh/) on macOS, install the [`exa`](http://formulae.brew.sh/formula/exa) formula.
$ brew install exa
brew install exa
### MacPorts
If you're using [MacPorts](https://www.macports.org/) on macOS, install the [`exa`](https://ports.macports.org/port/exa/summary) port.
$ port install exa
port install exa
### Nix
On nixOS, install the [`exa`](https://github.com/NixOS/nixpkgs/blob/master/pkgs/tools/misc/exa/default.nix) package.
$ nix-env -i exa
nix-env -i exa
### openSUSE
On openSUSE, install the [`exa`](https://software.opensuse.org/package/exa) package.
$ zypper install exa
zypper install exa
### Ubuntu
On Ubuntu 20.10 (Groovy Gorilla) and later, install the [`exa`](https://packages.ubuntu.com/groovy/exa) package.
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
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
@ -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:
$ cargo install exa
cargo install exa
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">
<h1>Development
<a href="https://blog.rust-lang.org/2020/03/12/Rust-1.42.html">
<img src="https://img.shields.io/badge/rustc-1.42+-lightgray.svg" alt="Rust 1.42+" />
<a href="https://blog.rust-lang.org/2023/01/10/Rust-1.66.1.html">
<img src="https://img.shields.io/badge/rustc-1.66.1+-lightgray.svg" alt="Rust 1.66.1+" />
</a>
<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>
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.
Once Rust is installed, you can compile exa with Cargo:
$ cargo build
$ cargo test
cargo build
cargo test
- The [just](https://github.com/casey/just) command runner can be used to run some helpful development commands, in a manner similar to `make`.
Run `just --tasks` to get an overview of whats available.
Run `just --list` to get an overview of whats available.
- If you are compiling a copy for yourself, be sure to run `cargo build --release` or `just build-release` to benefit from release-mode optimisations.
Copy the resulting binary, which will be in the `target/release` directory, into a folder in your `$PATH`.

View File

@ -38,9 +38,10 @@ fn main() -> io::Result<()> {
// We need to create these files in the Cargo output directory.
let out = PathBuf::from(env::var("OUT_DIR").unwrap());
let path = &out.join("version_string.txt");
// 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))?;
Ok(())

View File

@ -8,6 +8,11 @@ _exa()
return
;;
--colour)
COMPREPLY=( $( compgen -W 'always auto never' -- "$cur" ) )
return
;;
-L|--level)
COMPREPLY=( $( compgen -W '{0..9}' -- "$cur" ) )
return
@ -19,19 +24,28 @@ _exa()
;;
-t|--time)
COMPREPLY=( $( compgen -W 'modified changed accessed created --' -- $cur ) )
COMPREPLY=( $( compgen -W 'modified changed accessed created --' -- "$cur" ) )
return
;;
--time-style)
COMPREPLY=( $( compgen -W 'default iso long-iso full-iso --' -- $cur ) )
COMPREPLY=( $( compgen -W 'default iso long-iso full-iso --' -- "$cur" ) )
return
;;
esac
case "$cur" in
# _parse_help doesnt pick up short options when they are on the same line than long options
--*)
# colo[u]r isnt 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" ) )
;;
*)

View File

@ -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 '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 -l 'color' -d "When to use terminal colours"
complete -c exa -l 'colour' -d "When to use terminal colours"
complete -c exa -l 'color-scale' -d "Highlight levels of file sizes distinctly"
complete -c exa -l 'colour-scale' -d "Highlight levels of file sizes distinctly"
complete -c exa -l 'color' \
-l 'colour' -d "When to use terminal colours" -x -a "
always\t'Always use colour'
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 '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 -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 '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 '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'
age\t'Sort by file modified time (newest first)'
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 '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 '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 'H' -l 'links' -d "List each file's number of hard links"
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 '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'
changed\t'Display changed 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 -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 -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'
iso\t'Display brief ISO timestamps'
long-iso\t'Display longer ISO timestaps, up to the minute'

View File

@ -1,7 +1,7 @@
#compdef exa
# 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
# ~/.zshrc.
@ -19,7 +19,7 @@ __exa() {
{-R,--recurse}"[Recurse into directories]" \
{-T,--tree}"[Recurse into directories as a tree]" \
{-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]" \
--icons"[Display icons]" \
--no-icons"[Hide icons]" \

View File

@ -11,17 +11,17 @@ bash /vagrant/devtools/dev-versions.sh
# Configure the Cool Prompt™ (not actually trademarked).
# The Cool Prompt tells you whether youre in debug or strict mode, whether
# you have colours configured, and whether your last command failed.
function nonzero_return() { RETVAL=$?; [ $RETVAL -ne 0 ] && echo "$RETVAL "; }
function debug_mode() { [ "$EXA_DEBUG" == "trace" ] && echo -n "trace-"; [ -n "$EXA_DEBUG" ] && echo "debug "; }
function strict_mode() { [ -n "$EXA_STRICT" ] && echo "strict "; }
function lsc_mode() { [ -n "$LS_COLORS" ] && echo "lsc "; }
function exac_mode() { [ -n "$EXA_COLORS" ] && echo "exac "; }
nonzero_return() { RETVAL=$?; [ "$RETVAL" -ne 0 ] && echo "$RETVAL "; }
debug_mode() { [ "$EXA_DEBUG" == "trace" ] && echo -n "trace-"; [ -n "$EXA_DEBUG" ] && echo "debug "; }
strict_mode() { [ -n "$EXA_STRICT" ] && echo "strict "; }
lsc_mode() { [ -n "$LS_COLORS" ] && echo "lsc "; }
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\] "
# The debug function lets you switch debug mode on and off.
# Turn it on if you need to see exas debugging logs.
function debug () {
debug() {
case "$1" in
""|"on") export EXA_DEBUG=1 ;;
"off") export EXA_DEBUG= ;;
@ -33,11 +33,12 @@ function debug () {
# The strict function lets you switch strict mode on and off.
# Turn it on if youd like exas command-line arguments checked.
function strict () {
case "$1" in "on") export EXA_STRICT=1 ;;
strict() {
case "$1" in
"on") export EXA_STRICT=1 ;;
"off") export EXA_STRICT= ;;
"") [ -n "$EXA_STRICT" ] && echo "strict on" || echo "strict off" ;;
*) echo "Usage: strict on|off"; return 1 ;;
"") [ -n "$EXA_STRICT" ] && echo "strict on" || echo "strict off" ;;
*) echo "Usage: strict on|off"; return 1 ;;
esac;
}
@ -45,7 +46,7 @@ function strict () {
# environment variables. Theres also a hacker theme which turns everything
# green, which is usually used for checking that all colour codes work, and
# for looking cool while you phreak some mainframes or whatever.
function colors () {
colors() {
case "$1" in
"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"

View File

@ -252,7 +252,7 @@ sudo chown $FIXED_USER:$FIXED_USER -R "$TEST_ROOT/attributes"
# A sample Git repository
# 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"
cd "$TEST_ROOT/git"
git init >/dev/null
@ -281,7 +281,7 @@ sudo chown $FIXED_USER:$FIXED_USER -R "$TEST_ROOT/git"
# A second Git repository
# 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"
cd "$TEST_ROOT/git2"
git init >/dev/null
@ -321,7 +321,7 @@ sudo chown $FIXED_USER:$FIXED_USER -R "$TEST_ROOT/git2"
# A third Git repository
# 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"
cd "$TEST_ROOT/git3"
git init >/dev/null
@ -334,6 +334,20 @@ find "$TEST_ROOT/git3" -exec touch {} -h -t $FIXED_DATE \;
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.
# We need to set the permissions of `.` and `..` because they actually
# get displayed in the output here, so this has to come last.

View File

@ -9,7 +9,7 @@ set -e
# Linux check!
uname=`uname -s`
uname=$(uname -s)
if [[ "$uname" != "Linux" ]]; then
echo "Gotta be on Linux to run this (detected '$uname')!"
exit 1
@ -29,8 +29,8 @@ fi
# Weekly builds have a bit more information in their version number (see build.rs).
if [[ "$1" == "--weekly" ]]; then
git_hash=`GIT_DIR=/vagrant/.git git rev-parse --short --verify HEAD`
date=`date +"%Y-%m-%d"`
git_hash=$(GIT_DIR=/vagrant/.git git rev-parse --short --verify HEAD)
date=$(date +"%Y-%m-%d")
echo "Building exa weekly v$exa_version, date $date, Git hash $git_hash"
else
echo "Building exa v$exa_version"
@ -57,9 +57,10 @@ strip -v "$exa_linux_binary"
# the binaries can have consistent names, and its still possible to tell
# different *downloads* apart.
echo -e "\n\033[4mZipping binary...\033[0m"
if [[ "$1" == "--weekly" ]]
then exa_linux_zip="/vagrant/exa-linux-x86_64-${exa_version}-${date}-${git_hash}.zip"
else exa_linux_zip="/vagrant/exa-linux-x86_64.zip"
if [[ "$1" == "--weekly" ]]; then
exa_linux_zip="/vagrant/exa-linux-x86_64-${exa_version}-${date}-${git_hash}.zip"
else
exa_linux_zip="/vagrant/exa-linux-x86_64.zip"
fi
rm -vf "$exa_linux_zip"
zip -j "$exa_linux_zip" "$exa_linux_binary"

View File

@ -11,7 +11,7 @@ set -e
# 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.
uname=`uname -s`
uname=$(uname -s)
if [[ "$uname" != "Darwin" ]]; then
echo "Gotta be on Darwin to run this (detected '$uname')!"
exit 1
@ -36,8 +36,8 @@ fi
# Weekly builds have a bit more information in their version number (see build.rs).
if [[ "$1" == "--weekly" ]]; then
git_hash=`GIT_DIR=$exa_root/.git git rev-parse --short --verify HEAD`
date=`date +"%Y-%m-%d"`
git_hash=$(GIT_DIR=$exa_root/.git git rev-parse --short --verify HEAD)
date=$(date +"%Y-%m-%d")
echo "Building exa weekly v$exa_version, date $date, Git hash $git_hash"
else
echo "Building exa v$exa_version"
@ -65,9 +65,10 @@ echo "strip $exa_macos_binary"
# the binaries can have consistent names, and its still possible to tell
# different *downloads* apart.
echo -e "\n\033[4mZipping binary...\033[0m"
if [[ "$1" == "--weekly" ]]
then 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"
if [[ "$1" == "--weekly" ]]; then
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"
fi
rm -vf "$exa_macos_zip" | sed 's/^/removing /'
zip -j "$exa_macos_zip" "$exa_macos_binary"

View File

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

2
rust-toolchain.toml Normal file
View File

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

View File

@ -111,6 +111,13 @@ impl<'dir, 'ig> Files<'dir, 'ig> {
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 {
let git_status = self.git.map(|g| g.get(path, false)).unwrap_or_default();
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)
.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
/// entries in particular are “extra-hidden”: `.` and `..`, which only become
/// visible after an extra `-a` option.
#[derive(PartialEq, Debug, Copy, Clone)]
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum DotFilter {
/// Shows files, dotfiles, and `.` and `..`.

View File

@ -19,7 +19,7 @@
/// 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
/// directories inline, with their contents immediately underneath.
#[derive(PartialEq, Debug, Copy, Clone)]
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum DirAction {
/// This directory should be listed along with the regular files, instead
@ -51,14 +51,14 @@ impl DirAction {
match self {
Self::AsFile => true,
Self::Recurse(o) => o.tree,
_ => false,
Self::List => false,
}
}
}
/// The options that determine how to recurse into a directory.
#[derive(PartialEq, Debug, Copy, Clone)]
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub struct RecurseOptions {
/// Whether recursion should be done as a tree or as multiple individual

View File

@ -1,5 +1,8 @@
//! 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::sync::Mutex;
@ -205,6 +208,11 @@ fn repo_to_statuses(repo: &git2::Repository, workdir: &Path) -> Git {
match repo.statuses(None) {
Ok(es) => {
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 elem = (path, e.status());
statuses.push(elem);
@ -288,6 +296,7 @@ impl Git {
/// Paths need to be absolute for them to be compared properly, otherwise
/// youd ask a repo about “./README.md” but it only knows about
/// “/vagrant/README.md”, prefixed by the workdir.
#[cfg(unix)]
fn reorient(path: &Path) -> PathBuf {
use std::env::current_dir;
@ -300,6 +309,14 @@ fn reorient(path: &Path) -> PathBuf {
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.
fn working_tree_status(status: git2::Status) -> f::GitStatus {
match status {

View File

@ -167,7 +167,7 @@ mod lister {
unsafe {
listxattr(
c_path.as_ptr(),
buf.as_mut_ptr() as *mut c_char,
buf.as_mut_ptr().cast::<c_char>(),
bufsize as size_t,
self.c_flags,
)
@ -178,7 +178,7 @@ mod lister {
unsafe {
getxattr(
c_path.as_ptr(),
buf.as_ptr() as *const c_char,
buf.as_ptr().cast::<c_char>(),
ptr::null_mut(),
0,
0,
@ -246,7 +246,7 @@ mod lister {
unsafe {
listxattr(
c_path.as_ptr() as *const _,
c_path.as_ptr().cast(),
ptr::null_mut(),
0,
)
@ -261,8 +261,8 @@ mod lister {
unsafe {
listxattr(
c_path.as_ptr() as *const _,
buf.as_mut_ptr() as *mut c_char,
c_path.as_ptr().cast(),
buf.as_mut_ptr().cast(),
bufsize as size_t,
)
}
@ -276,8 +276,8 @@ mod lister {
unsafe {
getxattr(
c_path.as_ptr() as *const _,
buf.as_ptr() as *const c_char,
c_path.as_ptr().cast(),
buf.as_ptr().cast(),
ptr::null_mut(),
0,
)

View File

@ -82,13 +82,27 @@ pub struct Permissions {
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 details view. These values are fused together to make the output a
/// little more compressed.
#[derive(Copy, Clone)]
pub struct PermissionsPlus {
pub file_type: Type,
#[cfg(unix)]
pub permissions: Permissions,
#[cfg(windows)]
pub attributes: Attributes,
pub xattrs: bool,
}
@ -162,7 +176,7 @@ pub enum Size {
/// data is rarely useful — I cant think of a time when Ive seen it and
/// 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,
/// This file is a block or character device, so instead of a size, print
@ -196,7 +210,7 @@ pub struct Time {
/// A files 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
/// this enum.
#[derive(PartialEq, Copy, Clone)]
#[derive(PartialEq, Eq, Copy, Clone)]
pub enum GitStatus {
/// This file hasnt changed since the last commit.

View File

@ -1,7 +1,10 @@
//! Files, and methods and fields to access their metadata.
use std::io;
#[cfg(unix)]
use std::os::unix::fs::{FileTypeExt, MetadataExt, PermissionsExt};
#[cfg(windows)]
use std::os::windows::fs::MetadataExt;
use std::path::{Path, PathBuf};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
@ -78,11 +81,11 @@ impl<'dir> File<'dir> {
let metadata = std::fs::symlink_metadata(&path)?;
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>> {
let path = parent_dir.path.to_path_buf();
let path = parent_dir.path.clone();
let ext = File::ext(&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
/// current user. An executable file has a different purpose from an
/// executable directory, so they should be highlighted differently.
#[cfg(unix)]
pub fn is_executable_file(&self) -> bool {
let bit = modes::USER_EXECUTE;
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.
#[cfg(unix)]
pub fn is_pipe(&self) -> bool {
self.metadata.file_type().is_fifo()
}
/// Whether this file is a char device on the filesystem.
#[cfg(unix)]
pub fn is_char_device(&self) -> bool {
self.metadata.file_type().is_char_device()
}
/// Whether this file is a block device on the filesystem.
#[cfg(unix)]
pub fn is_block_device(&self) -> bool {
self.metadata.file_type().is_block_device()
}
/// Whether this file is a socket on the filesystem.
#[cfg(unix)]
pub fn is_socket(&self) -> bool {
self.metadata.file_type().is_socket()
}
@ -213,13 +221,13 @@ impl<'dir> File<'dir> {
path.to_path_buf()
}
else if let Some(dir) = self.parent_dir {
dir.join(&*path)
dir.join(path)
}
else if let Some(parent) = self.path.parent() {
parent.join(&*path)
parent.join(path)
}
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
/// with multiple links much more often. Thus, it should get highlighted
/// more attentively.
#[cfg(unix)]
pub fn links(&self) -> f::Links {
let count = self.metadata.nlink();
@ -280,6 +289,7 @@ impl<'dir> File<'dir> {
}
/// This files inode.
#[cfg(unix)]
pub fn inode(&self) -> f::Inode {
f::Inode(self.metadata.ino())
}
@ -287,6 +297,7 @@ impl<'dir> File<'dir> {
/// This files number of filesystem blocks.
///
/// (Not the size of each block, which we dont actually report on)
#[cfg(unix)]
pub fn blocks(&self) -> f::Blocks {
if self.is_file() || self.is_link() {
f::Blocks::Some(self.metadata.blocks())
@ -297,11 +308,13 @@ impl<'dir> File<'dir> {
}
/// The ID of the user that own this file.
#[cfg(unix)]
pub fn user(&self) -> f::User {
f::User(self.metadata.uid())
}
/// The ID of the group that owns this file.
#[cfg(unix)]
pub fn group(&self) -> f::Group {
f::Group(self.metadata.gid())
}
@ -314,6 +327,7 @@ impl<'dir> File<'dir> {
///
/// Block and character devices return their device IDs, because they
/// usually just have a file size of zero.
#[cfg(unix)]
pub fn size(&self) -> f::Size {
if self.is_directory() {
f::Size::None
@ -335,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 files last modified timestamp, if available on this platform.
pub fn modified_time(&self) -> Option<SystemTime> {
self.metadata.modified().ok()
}
/// This files last changed timestamp, if available on this platform.
#[cfg(unix)]
pub fn changed_time(&self) -> Option<SystemTime> {
let (mut sec, mut nanosec) = (self.metadata.ctime(), self.metadata.ctime_nsec());
@ -350,7 +375,7 @@ impl<'dir> File<'dir> {
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)
}
else {
@ -359,6 +384,11 @@ impl<'dir> File<'dir> {
}
}
#[cfg(windows)]
pub fn changed_time(&self) -> Option<SystemTime> {
return self.modified_time()
}
/// This files last accessed timestamp, if available on this platform.
pub fn accessed_time(&self) -> Option<SystemTime> {
self.metadata.accessed().ok()
@ -374,6 +404,7 @@ impl<'dir> File<'dir> {
/// 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
/// ls puts this character there.
#[cfg(unix)]
pub fn type_char(&self) -> f::Type {
if self.is_file() {
f::Type::File
@ -401,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 files permissions, with flags for each bit.
#[cfg(unix)]
pub fn permissions(&self) -> f::Permissions {
let bits = self.metadata.mode();
let has_bit = |bit| bits & bit == bit;
@ -425,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 files extension is any of the strings that get passed in.
///
/// This will always return `false` if the file has no extension.
@ -482,6 +543,7 @@ impl<'dir> FileTarget<'dir> {
/// More readable aliases for the permission bits exposed by libc.
#[allow(trivial_numeric_casts)]
#[cfg(unix)]
mod modes {
// The `libc::mode_t` types actual type varies, but the value returned
@ -559,6 +621,7 @@ mod filename_test {
}
#[test]
#[cfg(unix)]
fn topmost() {
assert_eq!("/", File::filename(Path::new("/")))
}

View File

@ -2,8 +2,8 @@
use std::cmp::Ordering;
use std::iter::FromIterator;
#[cfg(unix)]
use std::os::unix::fs::MetadataExt;
use std::path::Path;
use crate::fs::DotFilter;
use crate::fs::File;
@ -23,7 +23,7 @@ use crate::fs::File;
/// 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
/// performing the comparison.
#[derive(PartialEq, Debug, Clone)]
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct FileFilter {
/// 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,
/// 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
/// 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.
/// [Linux History: How Dot Files Became Hidden Files](https://linux-audit.com/linux-history-how-dot-files-became-hidden-files/)
pub dot_filter: DotFilter,
/// Glob patterns to ignore. Any file name that matches *any* of these
@ -82,9 +59,6 @@ pub struct FileFilter {
pub ignore_patterns: IgnorePatterns,
/// 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 theres no `.git` folder present.
pub git_ignore: GitIgnore,
}
@ -115,7 +89,7 @@ impl FileFilter {
}
/// 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>>
{
files.sort_by(|a, b| {
@ -139,7 +113,7 @@ impl FileFilter {
/// User-supplied field to sort by.
#[derive(PartialEq, Debug, Copy, Clone)]
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum SortField {
/// Dont apply any sorting. This is usually used as an optimisation in
@ -157,6 +131,7 @@ pub enum SortField {
/// The files inode, which usually corresponds to the order in which
/// files were created on the filesystem, more or less.
#[cfg(unix)]
FileInode,
/// 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
/// timestamp in certain circumstances. This has become common enough that
/// its now expected behaviour!
/// http://unix.stackexchange.com/a/8842
/// <https://unix.stackexchange.com/a/8842>
AccessedDate,
/// The time the file was changed (the “ctime”).
@ -182,7 +157,7 @@ pub enum SortField {
/// changed — its permissions, owners, or link count.
///
/// 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,
/// 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
/// into account? I gave up and just named these two variants after the
/// effects they have.
#[derive(PartialEq, Debug, Copy, Clone)]
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum SortCase {
/// 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::Size => a.metadata.len().cmp(&b.metadata.len()),
#[cfg(unix)]
Self::FileInode => a.metadata.ino().cmp(&b.metadata.ino()),
Self::ModifiedDate => a.modified_time().cmp(&b.modified_time()),
Self::AccessedDate => a.accessed_time().cmp(&b.accessed_time()),
@ -284,8 +260,10 @@ impl SortField {
}
fn strip_dot(n: &str) -> &str {
if n.starts_with('.') { &n[1..] }
else { n }
match n.strip_prefix('.') {
Some(s) => s,
None => n,
}
}
}
@ -293,7 +271,7 @@ impl SortField {
/// The **ignore patterns** are a list of globs that are tested against
/// each filename, and if any of them match, that file isnt displayed.
/// 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 {
patterns: Vec<glob::Pattern>,
}
@ -345,35 +323,20 @@ impl IgnorePatterns {
fn is_ignored(&self, file: &str) -> bool {
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`
// isnt probably means its in the wrong place
}
/// Whether to ignore or display files that are mentioned in `.gitignore` files.
#[derive(PartialEq, Debug, Copy, Clone)]
/// Whether to ignore or display files that Git would ignore.
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum GitIgnore {
/// Ignore files that Git would ignore. This means doing a check for a
/// `.gitignore` file, possibly recursively up the filesystem tree.
/// Ignore files that Git would ignore.
CheckAndIgnore,
/// Display files, even if Git would ignore them.
Off,
}
// This is not fully baked yet. The `ignore` crate lists a lot more files that
// we arent 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)]
@ -383,31 +346,31 @@ mod test_ignores {
#[test]
fn empty_matches_nothing() {
let pats = IgnorePatterns::empty();
assert_eq!(false, pats.is_ignored("nothing"));
assert_eq!(false, pats.is_ignored("test.mp3"));
assert!(!pats.is_ignored("nothing"));
assert!(!pats.is_ignored("test.mp3"));
}
#[test]
fn ignores_a_glob() {
let (pats, fails) = IgnorePatterns::parse_from_iter(vec![ "*.mp3" ]);
assert!(fails.is_empty());
assert_eq!(false, pats.is_ignored("nothing"));
assert_eq!(true, pats.is_ignored("test.mp3"));
assert!(!pats.is_ignored("nothing"));
assert!(pats.is_ignored("test.mp3"));
}
#[test]
fn ignores_an_exact_filename() {
let (pats, fails) = IgnorePatterns::parse_from_iter(vec![ "nothing" ]);
assert!(fails.is_empty());
assert_eq!(true, pats.is_ignored("nothing"));
assert_eq!(false, pats.is_ignored("test.mp3"));
assert!(pats.is_ignored("nothing"));
assert!(!pats.is_ignored("test.mp3"));
}
#[test]
fn ignores_both() {
let (pats, fails) = IgnorePatterns::parse_from_iter(vec![ "nothing", "*.mp3" ]);
assert!(fails.is_empty());
assert_eq!(true, pats.is_ignored("nothing"));
assert_eq!(true, pats.is_ignored("test.mp3"));
assert!(pats.is_ignored("nothing"));
assert!(pats.is_ignored("test.mp3"));
}
}

View File

@ -11,7 +11,7 @@ use crate::output::icons::FileIcon;
use crate::theme::FileColours;
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Default, PartialEq, Eq)]
pub struct FileExtensions;
impl FileExtensions {
@ -19,13 +19,14 @@ impl FileExtensions {
/// An “immediate” file is something that can be run or activated somehow
/// in order to kick off the build of a project. Its usually only present
/// in directories full of source code.
#[allow(clippy::case_sensitive_file_extension_comparisons)]
fn is_immediate(&self, file: &File<'_>) -> bool {
file.name.to_lowercase().starts_with("readme") ||
file.name.ends_with(".ninja") ||
file.name_is_one_of( &[
"Makefile", "Cargo.toml", "SConstruct", "CMakeLists.txt",
"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",
"Justfile", "Procfile", "Dockerfile", "Containerfile", "Vagrantfile", "Brewfile",
"Gemfile", "Pipfile", "build.sbt", "mix.exs", "bsconfig.json", "tsconfig.json",
@ -34,10 +35,11 @@ impl FileExtensions {
fn is_image(&self, file: &File<'_>) -> bool {
file.extension_is_one_of( &[
"png", "jpeg", "jpg", "gif", "bmp", "tiff", "tif",
"ppm", "pgm", "pbm", "pnm", "webp", "raw", "arw",
"svg", "stl", "eps", "dvi", "ps", "cbr", "jpf",
"cbz", "xpm", "ico", "cr2", "orf", "nef", "heif",
"png", "jfi", "jfif", "jif", "jpe", "jpeg", "jpg", "gif", "bmp",
"tiff", "tif", "ppm", "pgm", "pbm", "pnm", "webp", "raw", "arw",
"svg", "stl", "eps", "dvi", "ps", "cbr", "jpf", "cbz", "xpm",
"ico", "cr2", "orf", "nef", "heif", "avif", "jxl", "j2k", "jp2",
"j2c", "jpx",
])
}
@ -79,14 +81,14 @@ impl FileExtensions {
file.extension_is_one_of( &[
"zip", "tar", "Z", "z", "gz", "bz2", "a", "ar", "7z",
"iso", "dmg", "tc", "rar", "par", "tgz", "xz", "txz",
"lz", "tlz", "lzma", "deb", "rpm", "zst",
"lz", "tlz", "lzma", "deb", "rpm", "zst", "lz4", "cpio",
])
}
fn is_temp(&self, file: &File<'_>) -> bool {
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 {

View File

@ -7,18 +7,19 @@
#![warn(unused)]
#![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::find_map)]
#![allow(clippy::map_unwrap_or)]
#![allow(clippy::match_same_arms)]
#![allow(clippy::missing_const_for_fn)]
#![allow(clippy::missing_errors_doc)]
#![allow(clippy::module_name_repetitions)]
#![allow(clippy::must_use_candidate)]
#![allow(clippy::non_ascii_literal)]
#![allow(clippy::option_if_let_else)]
#![allow(clippy::too_many_lines)]
#![allow(clippy::unused_self)]
#![allow(clippy::upper_case_acronyms)]
#![allow(clippy::wildcard_imports)]
use std::env;
@ -48,10 +49,20 @@ mod theme;
fn main() {
use std::process::exit;
#[cfg(unix)]
unsafe {
libc::signal(libc::SIGPIPE, libc::SIG_DFL);
}
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();
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) => {
// List the current directory by default.
@ -155,6 +166,9 @@ fn git_options(options: &Options, args: &[&OsStr]) -> Option<GitCache> {
}
impl<'args> Exa<'args> {
/// # Errors
///
/// Will return `Err` if printing to stderr fails.
pub fn run(mut self) -> io::Result<i32> {
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 = 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)
}
@ -306,7 +320,7 @@ impl<'args> Exa<'args> {
let git_ignoring = self.options.filter.git_ignore == GitIgnore::CheckAndIgnore;
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)
}
}

View File

@ -7,7 +7,7 @@ use crate::options::parser::{Arg, Flag, ParseError};
/// Something wrong with the combination of options the user has picked.
#[derive(PartialEq, Debug)]
#[derive(PartialEq, Eq, Debug)]
pub enum OptionsError {
/// There was an error (from `getopts`) parsing the arguments.
@ -44,7 +44,7 @@ pub enum OptionsError {
}
/// The source of a string that failed to be parsed as a number.
#[derive(PartialEq, Debug)]
#[derive(PartialEq, Eq, Debug)]
pub enum NumberSource {
/// It came... from a command-line argument!
@ -119,7 +119,7 @@ impl OptionsError {
/// A list of legal choices for an argument-taking option.
#[derive(PartialEq, Debug)]
#[derive(PartialEq, Eq, Debug)]
pub struct Choices(pub &'static [&'static str]);
impl fmt::Display for Choices {

View File

@ -88,6 +88,7 @@ impl SortField {
"cr" | "created" => {
Self::CreatedDate
}
#[cfg(unix)]
"inode" => {
Self::FileInode
}
@ -294,7 +295,6 @@ mod test {
mod ignore_patterns {
use super::*;
use std::iter::FromIterator;
use glob;
fn pat(string: &'static str) -> glob::Pattern {
glob::Pattern::new(string).unwrap()

View File

@ -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
/// on which features are enabled and whether the user only wants to
/// see one sections help.
#[derive(PartialEq, Debug, Copy, Clone)]
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub struct HelpString;
impl HelpString {

View File

@ -178,7 +178,7 @@ impl Options {
fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
if cfg!(not(feature = "git")) &&
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"
)));
}
@ -216,7 +216,7 @@ pub mod test {
use crate::options::parser::{Arg, MatchedFlags};
use std::ffi::OsStr;
#[derive(PartialEq, Debug)]
#[derive(PartialEq, Eq, Debug)]
pub enum Strictnesses {
Last,
Complain,
@ -228,14 +228,14 @@ pub mod test {
/// both, then both should resolve to the same result.
///
/// 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>
where F: Fn(&MatchedFlags<'_>) -> T
{
use self::Strictnesses::*;
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();
if strictnesses == Last || strictnesses == Both {

View File

@ -52,7 +52,7 @@ pub type Values = &'static [&'static str];
/// A **flag** is either of the two argument types, because they have to
/// be in the same array together.
#[derive(PartialEq, Debug, Copy, Clone)]
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum Flag {
Short(ShortArg),
Long(LongArg),
@ -77,7 +77,7 @@ impl fmt::Display for Flag {
}
/// Whether redundant arguments should be considered a problem.
#[derive(PartialEq, Debug, Copy, Clone)]
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum Strictness {
/// Throw an error when an argument doesnt 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
/// arguments.
#[derive(Copy, Clone, PartialEq, Debug)]
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum TakesValue {
/// 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 users input strings.
#[derive(PartialEq, Debug, Copy, Clone)]
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub struct Arg {
/// The short argument that matches it, if any.
@ -136,7 +136,7 @@ impl fmt::Display for Arg {
/// Literally just several args.
#[derive(PartialEq, Debug)]
#[derive(PartialEq, Eq, Debug)]
pub struct Args(pub &'static [&'static Arg]);
impl Args {
@ -146,8 +146,6 @@ impl Args {
pub fn parse<'args, I>(&self, inputs: I, strictness: Strictness) -> Result<Matches<'args>, ParseError>
where I: IntoIterator<Item = &'args OsStr>
{
use std::os::unix::ffi::OsStrExt;
let mut parsing = true;
// The results that get built up.
@ -159,7 +157,7 @@ impl Args {
// doesnt have one in its string so it needs the next one.
let mut inputs = inputs.into_iter();
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 “--”.
// 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 its a long argument.
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 theres an equals in it, then the string before the
// equals will be the flags name, and the string after it
@ -221,7 +219,7 @@ impl Args {
// If the string starts with *one* dash then its one or more
// short arguments.
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 theres an equals in it, then the argument immediately
// before the equals was the one that has the value, with the
@ -236,7 +234,7 @@ impl Args {
// its an error if any of the first set of arguments actually
// takes a value.
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...
for byte in other_args {
@ -291,7 +289,7 @@ impl Args {
TakesValue::Optional(values) => {
if index < bytes.len() - 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;
}
else if let Some(next_arg) = inputs.next() {
@ -342,7 +340,7 @@ impl Args {
/// The **matches** are the result of parsing the users command-line strings.
#[derive(PartialEq, Debug)]
#[derive(PartialEq, Eq, Debug)]
pub struct Matches<'args> {
/// The flags that were parsed from the users input.
@ -353,7 +351,7 @@ pub struct Matches<'args> {
pub frees: Vec<&'args OsStr>,
}
#[derive(PartialEq, Debug)]
#[derive(PartialEq, Eq, Debug)]
pub struct MatchedFlags<'args> {
/// The individual flags from the users input, in the order they were
@ -432,7 +430,7 @@ impl<'a> MatchedFlags<'a> {
.filter(|tuple| tuple.1.is_some() && predicate(&tuple.0))
.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 {
@ -464,7 +462,7 @@ impl<'a> MatchedFlags<'a> {
/// A problem with the users input that meant it couldnt be parsed into a
/// coherent list of arguments.
#[derive(PartialEq, Debug)]
#[derive(PartialEq, Eq, Debug)]
pub enum ParseError {
/// 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
/// either side. Returns `None` if theres no equals or a string is missing.
fn split_on_equals(input: &OsStr) -> Option<(&OsStr, &OsStr)> {
use std::os::unix::ffi::OsStrExt;
if let Some(index) = input.as_bytes().iter().position(|elem| *elem == b'=') {
let (before, after) = input.as_bytes().split_at(index);
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);
// The after string contains the = that we need to remove.
if ! before.is_empty() && after.len() >= 2 {
return Some((OsStr::from_bytes(before),
OsStr::from_bytes(&after[1..])))
return Some((bytes_to_os_str(before),
bytes_to_os_str(&after[1..])))
}
}
@ -722,6 +743,6 @@ mod matches_test {
fn no_count() {
let flags = MatchedFlags { flags: Vec::new(), strictness: Strictness::UseLastArguments };
assert_eq!(flags.has(&COUNT).unwrap(), false);
assert!(!flags.has(&COUNT).unwrap());
}
}

View File

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

View File

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

View File

@ -8,7 +8,7 @@ use crate::options::flags;
use crate::options::parser::MatchedFlags;
#[derive(PartialEq, Debug, Copy, Clone)]
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub struct VersionString;
// There were options here once, but there arent anymore!

View File

@ -32,13 +32,10 @@ impl Mode {
let flag = matches.has_where_any(|f| f.matches(&flags::LONG) || f.matches(&flags::ONE_LINE)
|| f.matches(&flags::GRID) || f.matches(&flags::TREE));
let flag = match flag {
Some(f) => f,
None => {
Self::strict_check_long_flags(matches)?;
let grid = grid::Options::deduce(matches)?;
return Ok(Self::Grid(grid));
}
let flag = if let Some(f) = flag { f } else {
Self::strict_check_long_flags(matches)?;
let grid = grid::Options::deduce(matches)?;
return Ok(Self::Grid(grid));
};
if flag.matches(&flags::LONG)
@ -57,10 +54,9 @@ impl Mode {
let grid_details = grid_details::Options { grid, details, row_threshold };
return Ok(Self::GridDetails(grid_details));
}
else {
// the --tree case is handled by the DirAction parser later
return Ok(Self::Details(details));
}
// the --tree case is handled by the DirAction parser later
return Ok(Self::Details(details));
}
Self::strict_check_long_flags(matches)?;
@ -195,7 +191,7 @@ impl TableOptions {
let size_format = SizeFormat::deduce(matches)?;
let user_format = UserFormat::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 })
}
}
@ -215,7 +211,7 @@ impl Columns {
let filesize = ! matches.has(&flags::NO_FILESIZE)?;
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 })
}
}
@ -382,7 +378,7 @@ mod test {
($name:ident: $type:ident <- $inputs:expr; $stricts:expr => err $result:expr) => {
/// Special macro for testing Err results.
/// This is needed because sometimes the Ok type doesnt implement PartialEq.
/// This is needed because sometimes the Ok type doesnt implement `PartialEq`.
#[test]
fn $name() {
for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| $type::deduce(mf)) {
@ -393,7 +389,7 @@ mod test {
($name:ident: $type:ident <- $inputs:expr; $stricts:expr => like $pat:pat) => {
/// 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]
fn $name() {
for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| $type::deduce(mf)) {

View File

@ -77,11 +77,9 @@ impl TextCell {
///
/// 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();
let spaces: String = " ".repeat(count);
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
/// 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);
impl<'a> From<&'a str> for DisplayWidth {

View File

@ -91,7 +91,7 @@ use crate::theme::Theme;
///
/// Almost all the heavy lifting is done in a Table object, which handles the
/// columns for each row.
#[derive(PartialEq, Debug)]
#[derive(PartialEq, Eq, Debug)]
pub struct Options {
/// Options specific to drawing a table.
@ -147,7 +147,11 @@ impl<'a> AsRef<File<'a>> for Egg<'a> {
impl<'a> Render<'a> {
pub fn render<W: Write>(mut self, w: &mut W) -> io::Result<()> {
let mut pool = Pool::new(num_cpus::get() as u32);
let n_cpus = match num_cpus::get() as u32 {
0 => 1,
n => n,
};
let mut pool = Pool::new(n_cpus);
let mut rows = Vec::new();
if let Some(ref table) = self.opts.table {
@ -157,7 +161,7 @@ impl<'a> Render<'a> {
(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 {
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 {
use crate::output::file_name::Colours;
let error_message = match path {
Some(path) => format!("<{}: {}>", path.display(), error),
None => format!("<{}>", error),
let error_message = if let Some(path) = path {
format!("<{}: {}>", path.display(), error)
} else {
format!("<{}>", error)
};
// TODO: broken_symlink() doesnt quite seem like the right name for

View File

@ -1,7 +1,7 @@
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) {
bits.push(good.paint(string));
return;

View File

@ -54,7 +54,7 @@ enum LinkStyle {
/// Whether to append file class characters to the file names.
#[derive(PartialEq, Debug, Copy, Clone)]
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum Classify {
/// Just display the file names, without any characters.
@ -73,7 +73,7 @@ impl Default for Classify {
/// Whether and how to show icons.
#[derive(PartialEq, Debug, Copy, Clone)]
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum ShowIcons {
/// Dont show icons at all.
@ -226,7 +226,7 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
let coconut = parent.components().count();
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 {
escape(
@ -235,12 +235,13 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
self.colours.symlink_path(),
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 files type has one associated with it.
#[cfg(unix)]
fn classify_char(&self, file: &File<'_>) -> Option<&'static str> {
if file.is_executable_file() {
Some("*")
@ -262,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 files
/// name using the given set of colours.
///
@ -301,11 +315,16 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
match self.file {
f if f.is_directory() => self.colours.directory(),
#[cfg(unix)]
f if f.is_executable_file() => self.colours.executable_file(),
f if f.is_link() => self.colours.symlink(),
#[cfg(unix)]
f if f.is_pipe() => self.colours.pipe(),
#[cfg(unix)]
f if f.is_block_device() => self.colours.block_device(),
#[cfg(unix)]
f if f.is_char_device() => self.colours.char_device(),
#[cfg(unix)]
f if f.is_socket() => self.colours.socket(),
f if ! f.is_file() => self.colours.special(),
_ => self.colours.colour_file(self.file),

View File

@ -8,7 +8,7 @@ use crate::output::file_name::Options as FileStyle;
use crate::theme::Theme;
#[derive(PartialEq, Debug, Copy, Clone)]
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub struct Options {
pub across: bool,
}
@ -46,6 +46,7 @@ impl<'a> Render<'a> {
grid.add(tg::Cell {
contents: filename.strings().to_string(),
width: *filename.width(),
alignment: tg::Alignment::Left,
});
}

View File

@ -18,7 +18,7 @@ use crate::output::tree::{TreeParams, TreeDepth};
use crate::theme::Theme;
#[derive(PartialEq, Debug)]
#[derive(PartialEq, Eq, Debug)]
pub struct Options {
pub grid: GridOptions,
pub details: DetailsOptions,
@ -39,7 +39,7 @@ impl Options {
/// small directory of four files in four columns, the files just look spaced
/// out and its harder to see whats going on. So it can be enabled just for
/// larger directory listings.
#[derive(PartialEq, Debug, Copy, Clone)]
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum RowThreshold {
/// Only use grid-details view if it would result in at least this many
@ -158,7 +158,7 @@ impl<'a> Render<'a> {
.collect::<Vec<_>>();
let mut last_working_grid = self.make_grid(1, options, &file_names, rows.clone(), &drender);
if file_names.len() == 1 {
return Some((last_working_grid, 1));
}
@ -176,7 +176,7 @@ impl<'a> Render<'a> {
if the_grid_fits {
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 };
// If weve figured out how many columns can fit in the users terminal,
@ -202,7 +202,7 @@ impl<'a> Render<'a> {
(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();
if self.details.header {
@ -263,6 +263,7 @@ impl<'a> Render<'a> {
let cell = grid::Cell {
contents: ANSIStrings(&column[row].contents).to_string(),
width: *column[row].width,
alignment: grid::Alignment::Left,
};
grid.add(cell);
@ -276,6 +277,7 @@ impl<'a> Render<'a> {
let cell = grid::Cell {
contents: ANSIStrings(&cell.contents).to_string(),
width: *cell.width,
alignment: grid::Alignment::Left,
};
grid.add(cell);

View File

@ -37,7 +37,7 @@ impl Icons {
/// - If neither is set, just use the default style.
/// - Attributes such as bold or underline should not be used to paint the
/// 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)
.map(Style::from)
.unwrap_or_default()
@ -69,7 +69,9 @@ lazy_static! {
m.insert("Dockerfile", '\u{f308}'); // 
m.insert("ds_store", '\u{f179}'); // 
m.insert("gitignore_global", '\u{f1d3}'); // 
m.insert("gradle", '\u{e70e}'); // 
m.insert("go.mod", '\u{e626}'); // 
m.insert("go.sum", '\u{e626}'); // 
m.insert("gradle", '\u{e256}'); // 
m.insert("gruntfile.coffee", '\u{e611}'); // 
m.insert("gruntfile.js", '\u{e611}'); // 
m.insert("gruntfile.ls", '\u{e611}'); // 
@ -80,9 +82,10 @@ lazy_static! {
m.insert("include", '\u{e5fc}'); // 
m.insert("lib", '\u{f121}'); // 
m.insert("localized", '\u{f179}'); // 
m.insert("Makefile", '\u{e779}'); // 
m.insert("Makefile", '\u{f489}'); // 
m.insert("node_modules", '\u{e718}'); // 
m.insert("npmignore", '\u{e71e}'); // 
m.insert("PKGBUILD", '\u{f303}'); // 
m.insert("rubydoc", '\u{e73b}'); // 
m.insert("yarn.lock", '\u{e718}'); // 
@ -110,6 +113,7 @@ pub fn icon_for_file(file: &File<'_>) -> char {
"apk" => '\u{e70e}', // 
"apple" => '\u{f179}', // 
"avi" => '\u{f03d}', // 
"avif" => '\u{f1c5}', // 
"avro" => '\u{e60b}', // 
"awk" => '\u{f489}', // 
"bash" => '\u{f489}', // 
@ -117,6 +121,7 @@ pub fn icon_for_file(file: &File<'_>) -> char {
"bash_profile" => '\u{f489}', // 
"bashrc" => '\u{f489}', // 
"bat" => '\u{f17a}', // 
"bats" => '\u{f489}', // 
"bmp" => '\u{f1c5}', // 
"bz" => '\u{f410}', // 
"bz2" => '\u{f410}', // 
@ -133,14 +138,15 @@ pub fn icon_for_file(file: &File<'_>) -> char {
"coffee" => '\u{f0f4}', // 
"conf" => '\u{e615}', // 
"cp" => '\u{e61d}', // 
"cpio" => '\u{f410}', // 
"cpp" => '\u{e61d}', // 
"cs" => '\u{f81a}', // 
"cs" => '\u{f031b}', // 󰌛
"csh" => '\u{f489}', // 
"cshtml" => '\u{f1fa}', // 
"csproj" => '\u{f81a}', // 
"csproj" => '\u{f031b}', // 󰌛
"css" => '\u{e749}', // 
"csv" => '\u{f1c3}', // 
"csx" => '\u{f81a}', // 
"csx" => '\u{f031b}', // 󰌛
"cxx" => '\u{e61d}', // 
"d" => '\u{e7af}', // 
"dart" => '\u{e798}', // 
@ -155,6 +161,7 @@ pub fn icon_for_file(file: &File<'_>) -> char {
"DS_store" => '\u{f179}', // 
"dump" => '\u{f1c0}', // 
"ebook" => '\u{e28b}', // 
"ebuild" => '\u{f30d}', // 
"editorconfig" => '\u{e615}', // 
"ejs" => '\u{e618}', // 
"elm" => '\u{e62c}', // 
@ -170,6 +177,9 @@ pub fn icon_for_file(file: &File<'_>) -> char {
"flac" => '\u{f001}', // 
"flv" => '\u{f03d}', // 
"font" => '\u{f031}', // 
"fs" => '\u{e7a7}', // 
"fsi" => '\u{e7a7}', // 
"fsx" => '\u{e7a7}', // 
"gdoc" => '\u{f1c2}', // 
"gem" => '\u{e21e}', // 
"gemfile" => '\u{e21e}', // 
@ -181,7 +191,7 @@ pub fn icon_for_file(file: &File<'_>) -> char {
"gitignore" => '\u{f1d3}', // 
"gitmodules" => '\u{f1d3}', // 
"go" => '\u{e626}', // 
"gradle" => '\u{e70e}', // 
"gradle" => '\u{e256}', // 
"groovy" => '\u{e775}', // 
"gsheet" => '\u{f1c3}', // 
"gslides" => '\u{f1c4}', // 
@ -196,28 +206,41 @@ pub fn icon_for_file(file: &File<'_>) -> char {
"hxx" => '\u{f0fd}', // 
"ico" => '\u{f1c5}', // 
"image" => '\u{f1c5}', // 
"img" => '\u{e271}', // 
"iml" => '\u{e7b5}', // 
"ini" => '\u{f17a}', // 
"ipynb" => '\u{e606}', // 
"ipynb" => '\u{e678}', // 
"iso" => '\u{e271}', // 
"j2c" => '\u{f1c5}', // 
"j2k" => '\u{f1c5}', // 
"jad" => '\u{e256}', // 
"jar" => '\u{e204}', // 
"java" => '\u{e204}', // 
"jar" => '\u{e256}', // 
"java" => '\u{e256}', // 
"jfi" => '\u{f1c5}', // 
"jfif" => '\u{f1c5}', // 
"jif" => '\u{f1c5}', // 
"jl" => '\u{e624}', // 
"jmd" => '\u{f48a}', // 
"jp2" => '\u{f1c5}', // 
"jpe" => '\u{f1c5}', // 
"jpeg" => '\u{f1c5}', // 
"jpg" => '\u{f1c5}', // 
"jpx" => '\u{f1c5}', // 
"js" => '\u{e74e}', // 
"json" => '\u{e60b}', // 
"jsx" => '\u{e7ba}', // 
"jxl" => '\u{f1c5}', // 
"ksh" => '\u{f489}', // 
"latex" => '\u{f034}', // 
"less" => '\u{e758}', // 
"lhs" => '\u{e777}', // 
"license" => '\u{f718}', // 
"license" => '\u{f0219}', // 󰈙
"localized" => '\u{f179}', // 
"lock" => '\u{f023}', // 
"log" => '\u{f18d}', // 
"lua" => '\u{e620}', // 
"lz" => '\u{f410}', // 
"lz4" => '\u{f410}', // 
"lzh" => '\u{f410}', // 
"lzma" => '\u{f410}', // 
"lzo" => '\u{f410}', // 
@ -227,6 +250,7 @@ pub fn icon_for_file(file: &File<'_>) -> char {
"markdown" => '\u{f48a}', // 
"md" => '\u{f48a}', // 
"mjs" => '\u{e74e}', // 
"mk" => '\u{f489}', // 
"mkd" => '\u{f48a}', // 
"mkv" => '\u{f03d}', // 
"mobi" => '\u{e28b}', // 
@ -236,7 +260,7 @@ pub fn icon_for_file(file: &File<'_>) -> char {
"msi" => '\u{e70f}', // 
"mustache" => '\u{e60f}', // 
"nix" => '\u{f313}', // 
"node" => '\u{f898}', // 
"node" => '\u{f0399}', // 󰎙
"npmignore" => '\u{e71e}', // 
"odp" => '\u{f1c4}', // 
"ods" => '\u{f1c3}', // 
@ -244,11 +268,15 @@ pub fn icon_for_file(file: &File<'_>) -> char {
"ogg" => '\u{f001}', // 
"ogv" => '\u{f03d}', // 
"otf" => '\u{f031}', // 
"part" => '\u{f43a}', // 
"patch" => '\u{f440}', // 
"pdf" => '\u{f1c1}', // 
"php" => '\u{e73d}', // 
"pl" => '\u{e769}', // 
"plx" => '\u{e769}', // 
"pm" => '\u{e769}', // 
"png" => '\u{f1c5}', // 
"pod" => '\u{e769}', // 
"ppt" => '\u{f1c4}', // 
"pptx" => '\u{f1c4}', // 
"procfile" => '\u{e21e}', // 
@ -276,7 +304,7 @@ pub fn icon_for_file(file: &File<'_>) -> char {
"rspec_parallel"=> '\u{e21e}', // 
"rspec_status" => '\u{e21e}', // 
"rss" => '\u{f09e}', // 
"rtf" => '\u{f718}', // 
"rtf" => '\u{f0219}', // 󰈙
"ru" => '\u{e21e}', // 
"rubydoc" => '\u{e73b}', // 
"sass" => '\u{e603}', // 
@ -289,28 +317,34 @@ pub fn icon_for_file(file: &File<'_>) -> char {
"so" => '\u{f17c}', // 
"sql" => '\u{f1c0}', // 
"sqlite3" => '\u{e7c4}', // 
"sty" => '\u{f034}', // 
"styl" => '\u{e600}', // 
"stylus" => '\u{e600}', // 
"svg" => '\u{f1c5}', // 
"swift" => '\u{e755}', // 
"t" => '\u{e769}', // 
"tar" => '\u{f410}', // 
"taz" => '\u{f410}', // 
"tbz" => '\u{f410}', // 
"tbz2" => '\u{f410}', // 
"tex" => '\u{f034}', // 
"tgz" => '\u{f410}', // 
"tiff" => '\u{f1c5}', // 
"tlz" => '\u{f410}', // 
"toml" => '\u{e615}', // 
"torrent" => '\u{e275}', // 
"ts" => '\u{e628}', // 
"tsv" => '\u{f1c3}', // 
"tsx" => '\u{e7ba}', // 
"ttf" => '\u{f031}', // 
"twig" => '\u{e61c}', // 
"txt" => '\u{f15c}', // 
"txz" => '\u{f410}', // 
"tz" => '\u{f410}', // 
"tzo" => '\u{f410}', // 
"video" => '\u{f03d}', // 
"vim" => '\u{e62b}', // 
"vue" => '\u{fd42}', // ﵂
"vue" => '\u{f0844}', // 󰡄
"war" => '\u{e256}', // 
"wav" => '\u{f001}', // 
"webm" => '\u{f03d}', // 
@ -321,8 +355,8 @@ pub fn icon_for_file(file: &File<'_>) -> char {
"xhtml" => '\u{f13b}', // 
"xls" => '\u{f1c3}', // 
"xlsx" => '\u{f1c3}', // 
"xml" => '\u{fabf}', // 謹
"xul" => '\u{fabf}', // 謹
"xml" => '\u{f05c0}', // 󰗀
"xul" => '\u{f05c0}', // 󰗀
"xz" => '\u{f410}', // 
"yaml" => '\u{f481}', // 
"yml" => '\u{f481}', // 
@ -330,6 +364,7 @@ pub fn icon_for_file(file: &File<'_>) -> char {
"zsh" => '\u{f489}', // 
"zsh-theme" => '\u{f489}', // 
"zshrc" => '\u{f489}', // 
"zst" => '\u{f410}', // 
_ => '\u{f15b}' // 
}
}

View File

@ -26,7 +26,7 @@ pub struct View {
/// The **mode** is the “type” of output.
#[derive(PartialEq, Debug)]
#[derive(PartialEq, Eq, Debug)]
#[allow(clippy::large_enum_variant)]
pub enum Mode {
Grid(grid::Options),
@ -37,7 +37,7 @@ pub enum Mode {
/// The width of the terminal requested by the user.
#[derive(PartialEq, Debug, Copy, Clone)]
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum TerminalWidth {
/// The user requested this specific number of columns.
@ -55,7 +55,7 @@ impl TerminalWidth {
match self {
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()),
}
}
}

View File

@ -43,7 +43,7 @@ pub mod test {
let blox = f::Blocks::None;
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 expected = TextCell::paint_str(Red.blink(), "3005");
assert_eq!(expected, blox.render(&TestColours).into());
assert_eq!(expected, blox.render(&TestColours));
}
}

View File

@ -35,6 +35,7 @@ impl f::GitStatus {
pub trait Colours {
fn not_modified(&self) -> Style;
#[allow(clippy::new_ret_no_self)]
fn new(&self) -> Style;
fn modified(&self) -> Style;
fn deleted(&self) -> Style;
@ -84,7 +85,7 @@ pub mod test {
].into(),
};
assert_eq!(expected, stati.render(&TestColours).into())
assert_eq!(expected, stati.render(&TestColours))
}
@ -103,6 +104,6 @@ pub mod test {
].into(),
};
assert_eq!(expected, stati.render(&TestColours).into())
assert_eq!(expected, stati.render(&TestColours))
}
}

View File

@ -21,8 +21,8 @@ pub mod test {
#[test]
fn blocklessness() {
let io = f::Inode(1414213);
let io = f::Inode(1_414_213);
let expected = TextCell::paint_str(Cyan.underline(), "1414213");
assert_eq!(expected, io.render(Cyan.underline()).into());
assert_eq!(expected, io.render(Cyan.underline()));
}
}

View File

@ -52,7 +52,7 @@ pub mod test {
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]
@ -67,7 +67,7 @@ pub mod test {
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]
@ -82,6 +82,6 @@ pub mod test {
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()));
}
}

View File

@ -7,7 +7,9 @@ pub use self::filetype::Colours as FiletypeColours;
mod git;
pub use self::git::Colours as GitColours;
#[cfg(unix)]
mod groups;
#[cfg(unix)]
pub use self::groups::Colours as GroupColours;
mod inode;
@ -26,7 +28,9 @@ mod times;
pub use self::times::Render as TimeRender;
// times does too
#[cfg(unix)]
mod users;
#[cfg(unix)]
pub use self::users::Colours as UserColours;
mod octal;

View File

@ -6,7 +6,7 @@ use crate::output::cell::TextCell;
impl f::OctalPermissions {
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 {
@ -40,7 +40,7 @@ pub mod test {
let octal = f::OctalPermissions{ permissions: bits };
let expected = TextCell::paint_str(Purple.bold(), "0755");
assert_eq!(expected, octal.render(Purple.bold()).into());
assert_eq!(expected, octal.render(Purple.bold()));
}
#[test]
@ -54,7 +54,7 @@ pub mod test {
let octal = f::OctalPermissions{ permissions: bits };
let expected = TextCell::paint_str(Purple.bold(), "0644");
assert_eq!(expected, octal.render(Purple.bold()).into());
assert_eq!(expected, octal.render(Purple.bold()));
}
#[test]
@ -68,7 +68,7 @@ pub mod test {
let octal = f::OctalPermissions{ permissions: bits };
let expected = TextCell::paint_str(Purple.bold(), "0600");
assert_eq!(expected, octal.render(Purple.bold()).into());
assert_eq!(expected, octal.render(Purple.bold()));
}
#[test]
@ -82,7 +82,7 @@ pub mod test {
let octal = f::OctalPermissions{ permissions: bits };
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 expected = TextCell::paint_str(Purple.bold(), "2777");
assert_eq!(expected, octal.render(Purple.bold()).into());
assert_eq!(expected, octal.render(Purple.bold()));
}
#[test]
@ -111,6 +111,6 @@ pub mod test {
let octal = f::OctalPermissions{ permissions: bits };
let expected = TextCell::paint_str(Purple.bold(), "1777");
assert_eq!(expected, octal.render(Purple.bold()).into());
assert_eq!(expected, octal.render(Purple.bold()));
}
}

View File

@ -6,6 +6,7 @@ use crate::output::render::FiletypeColours;
impl f::PermissionsPlus {
#[cfg(unix)]
pub fn render<C: Colours+FiletypeColours>(&self, colours: &C) -> TextCell {
let mut chars = vec![ self.file_type.render(colours) ];
chars.extend(self.permissions.render(colours, self.file_type.is_regular_file()));
@ -22,6 +23,17 @@ impl f::PermissionsPlus {
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 {
fn dash(&self) -> Style;

View File

@ -46,7 +46,7 @@ impl f::Size {
} else {
numerics.format_int(n.round() as isize)
};
TextCell {
// symbol is guaranteed to be ASCII since unit prefixes are hardcoded.
width: DisplayWidth::from(&*number) + symbol.len(),
@ -153,7 +153,7 @@ pub mod test {
#[test]
fn file_bytes() {
let directory = f::Size::Some(1048576);
let directory = f::Size::Some(1_048_576);
let expected = TextCell {
width: DisplayWidth::from(9),
contents: vec![

View File

@ -1,6 +1,7 @@
use std::cmp::max;
use std::env;
use std::ops::Deref;
#[cfg(unix)]
use std::sync::{Mutex, MutexGuard};
use datetime::TimeZone;
@ -8,6 +9,7 @@ use zoneinfo_compiled::{CompiledData, Result as TZResult};
use lazy_static::lazy_static;
use log::*;
#[cfg(unix)]
use users::UsersCache;
use crate::fs::{File, fields as f};
@ -19,7 +21,7 @@ use crate::theme::Theme;
/// Options for displaying a table.
#[derive(PartialEq, Debug)]
#[derive(PartialEq, Eq, Debug)]
pub struct Options {
pub size_format: SizeFormat,
pub time_format: TimeFormat,
@ -28,7 +30,8 @@ pub struct Options {
}
/// 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 {
/// At least one of these timestamps will be shown.
@ -53,10 +56,12 @@ impl Columns {
let mut columns = Vec::with_capacity(4);
if self.inode {
#[cfg(unix)]
columns.push(Column::Inode);
}
if self.octal {
#[cfg(unix)]
columns.push(Column::Octal);
}
@ -65,6 +70,7 @@ impl Columns {
}
if self.links {
#[cfg(unix)]
columns.push(Column::HardLinks);
}
@ -73,14 +79,17 @@ impl Columns {
}
if self.blocks {
#[cfg(unix)]
columns.push(Column::Blocks);
}
if self.user {
#[cfg(unix)]
columns.push(Column::User);
}
if self.group {
#[cfg(unix)]
columns.push(Column::Group);
}
@ -115,12 +124,18 @@ pub enum Column {
Permissions,
FileSize,
Timestamp(TimeType),
#[cfg(unix)]
Blocks,
#[cfg(unix)]
User,
#[cfg(unix)]
Group,
#[cfg(unix)]
HardLinks,
#[cfg(unix)]
Inode,
GitStatus,
#[cfg(unix)]
Octal,
}
@ -135,6 +150,7 @@ pub enum Alignment {
impl Column {
/// Get the alignment this column should use.
#[cfg(unix)]
pub fn alignment(self) -> Alignment {
match self {
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
/// to have a header row printed.
pub fn header(self) -> &'static str {
match self {
#[cfg(unix)]
Self::Permissions => "Permissions",
#[cfg(windows)]
Self::Permissions => "Mode",
Self::FileSize => "Size",
Self::Timestamp(t) => t.header(),
#[cfg(unix)]
Self::Blocks => "Blocks",
#[cfg(unix)]
Self::User => "User",
#[cfg(unix)]
Self::Group => "Group",
#[cfg(unix)]
Self::HardLinks => "Links",
#[cfg(unix)]
Self::Inode => "inode",
Self::GitStatus => "Git",
#[cfg(unix)]
Self::Octal => "Octal",
}
}
@ -166,7 +200,8 @@ impl Column {
/// Formatting options for file sizes.
#[derive(PartialEq, Debug, Copy, Clone)]
#[allow(clippy::enum_variant_names)]
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum SizeFormat {
/// Format the file size using **decimal** prefixes, such as “kilo”,
@ -182,7 +217,7 @@ pub enum SizeFormat {
}
/// Formatting options for user and group.
#[derive(PartialEq, Debug, Copy, Clone)]
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum UserFormat {
/// The UID / GID
Numeric,
@ -199,7 +234,7 @@ impl Default for SizeFormat {
/// The types of a files time fields. These three fields are standard
/// across most (all?) operating systems.
#[derive(PartialEq, Debug, Copy, Clone)]
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum TimeType {
/// The files modified time (`st_mtime`).
@ -234,7 +269,7 @@ impl TimeType {
///
/// There should always be at least one of these — theres no way to disable
/// the time columns entirely (yet).
#[derive(PartialEq, Debug, Copy, Clone)]
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
#[allow(clippy::struct_excessive_bools)]
pub struct TimeTypes {
pub modified: bool,
@ -272,10 +307,12 @@ pub struct Environment {
tz: Option<TimeZone>,
/// Mapping cache of user IDs to usernames.
#[cfg(unix)]
users: Mutex<UsersCache>,
}
impl Environment {
#[cfg(unix)]
pub fn lock_users(&self) -> MutexGuard<'_, UsersCache> {
self.users.lock().unwrap()
}
@ -294,21 +331,23 @@ impl Environment {
let numeric = locale::Numeric::load_user_locale()
.unwrap_or_else(|_| locale::Numeric::english());
#[cfg(unix)]
let users = Mutex::new(UsersCache::new());
Self { tz, numeric, users }
Self { numeric, tz, #[cfg(unix)] users }
}
}
#[cfg(unix)]
fn determine_time_zone() -> TZResult<TimeZone> {
if let Ok(file) = env::var("TZ") {
TimeZone::from_file({
if file.starts_with("/") {
if file.starts_with('/') {
file
} else {
format!("/usr/share/zoneinfo/{}", {
if file.starts_with(":") {
file.replacen(":", "", 1)
if file.starts_with(':') {
file.replacen(':', "", 1)
} else {
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! {
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 {
f::PermissionsPlus {
file_type: file.type_char(),
#[cfg(unix)]
permissions: file.permissions(),
#[cfg(windows)]
attributes: file.attributes(),
xattrs,
}
}
#[cfg(unix)]
fn octal_permissions(&self, file: &File<'_>) -> f::OctalPermissions {
f::OctalPermissions {
permissions: file.permissions(),
@ -405,24 +473,30 @@ impl<'a, 'f> Table<'a> {
Column::FileSize => {
file.size().render(self.theme, self.size_format, &self.env.numeric)
}
#[cfg(unix)]
Column::HardLinks => {
file.links().render(self.theme, &self.env.numeric)
}
#[cfg(unix)]
Column::Inode => {
file.inode().render(self.theme.ui.inode)
}
#[cfg(unix)]
Column::Blocks => {
file.blocks().render(self.theme)
}
#[cfg(unix)]
Column::User => {
file.user().render(self.theme, &*self.env.lock_users(), self.user_format)
}
#[cfg(unix)]
Column::Group => {
file.group().render(self.theme, &*self.env.lock_users(), self.user_format)
}
Column::GitStatus => {
self.git_status(file).render(self.theme)
}
#[cfg(unix)]
Column::Octal => {
self.octal_permissions(file).render(self.theme.ui.octal)
}

View File

@ -25,7 +25,7 @@ use unicode_width::UnicodeWidthStr;
///
/// Currently exa does not support *custom* styles, where the user enters a
/// 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 {
/// The **default format** uses the users locale to print month names,
@ -87,7 +87,7 @@ fn default_zoned(time: SystemTime, zone: &TimeZone) -> String {
}
fn get_dateformat(date: &LocalDateTime) -> &'static DateFormat<'static> {
match (is_recent(&date), *MAXIMUM_MONTH_WIDTH) {
match (is_recent(date), *MAXIMUM_MONTH_WIDTH) {
(true, 4) => &FOUR_WIDE_DATE_TIME,
(true, 5) => &FIVE_WIDE_DATE_TIME,
(true, _) => &OTHER_WIDE_DATE_TIME,

View File

@ -39,7 +39,7 @@
//! each directory)
#[derive(PartialEq, Debug, Copy, Clone)]
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum TreePart {
/// Rightmost column, *not* the last in the directory.
@ -253,19 +253,19 @@ mod iter_test {
#[test]
fn test_iteration() {
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();
assert_eq!(&"first", next.1);
assert_eq!(false, next.0.last);
assert!(!next.0.last);
let next = iter.next().unwrap();
assert_eq!(&"middle", next.1);
assert_eq!(false, next.0.last);
assert!(!next.0.last);
let next = iter.next().unwrap();
assert_eq!(&"last", next.1);
assert_eq!(true, next.0.last);
assert!(next.0.last);
assert!(iter.next().is_none());
}
@ -273,7 +273,7 @@ mod iter_test {
#[test]
fn test_empty() {
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());
}
}

View File

@ -14,7 +14,7 @@ pub use self::lsc::LSColors;
mod default_theme;
#[derive(PartialEq, Debug)]
#[derive(PartialEq, Eq, Debug)]
pub struct Options {
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
/// 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.
#[derive(PartialEq, Debug, Copy, Clone)]
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum UseColours {
/// Display them even when output isnt going to a terminal.
@ -44,13 +44,13 @@ pub enum UseColours {
Never,
}
#[derive(PartialEq, Debug, Copy, Clone)]
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum ColourScale {
Fixed,
Gradient,
}
#[derive(PartialEq, Debug, Default)]
#[derive(PartialEq, Eq, Debug, Default)]
pub struct Definitions {
pub ls: Option<String>,
pub exa: Option<String>,
@ -229,6 +229,7 @@ impl render::GitColours for Theme {
fn conflicted(&self) -> Style { self.ui.git.conflicted }
}
#[cfg(unix)]
impl render::GroupColours for Theme {
fn yours(&self) -> Style { self.ui.users.group_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::*;
match prefix {
None => self.ui.size.number_byte,
Some(Kilo) | Some(Kibi) => self.ui.size.number_kilo,
Some(Mega) | Some(Mebi) => self.ui.size.number_mega,
Some(Giga) | Some(Gibi) => self.ui.size.number_giga,
Some(_) => self.ui.size.number_huge,
Some(Kilo | Kibi) => self.ui.size.number_kilo,
Some(Mega | Mebi) => self.ui.size.number_mega,
Some(Giga | Gibi) => self.ui.size.number_giga,
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::*;
match prefix {
None => self.ui.size.unit_byte,
Some(Kilo) | Some(Kibi) => self.ui.size.unit_kilo,
Some(Mega) | Some(Mebi) => self.ui.size.unit_mega,
Some(Giga) | Some(Gibi) => self.ui.size.unit_giga,
Some(_) => self.ui.size.unit_huge,
Some(Kilo | Kibi) => self.ui.size.unit_kilo,
Some(Mega | Mebi) => self.ui.size.unit_mega,
Some(Giga | Gibi) => self.ui.size.unit_giga,
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 }
}
#[cfg(unix)]
impl render::UserColours for Theme {
fn you(&self) -> Style { self.ui.users.user_you }
fn someone_else(&self) -> Style { self.ui.users.user_someone_else }

View File

@ -144,6 +144,18 @@ status = 0
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
[[cmd]]

View File

@ -0,0 +1 @@
.rw-rw-r-- 0 cassowary  1 Jan 12:34 -N P\u{8}<30>UUU