Parcourir la source

Merge branch 'main' into dependabot/cargo/num_cpus-1.16.0

Christina Sørensen il y a 2 ans
Parent
commit
a030c580dd

+ 1 - 0
.envrc

@@ -0,0 +1 @@
+use flake .

+ 1 - 0
.gitignore

@@ -22,3 +22,4 @@ parts
 prime
 stage
 *.snap
+.direnv

+ 8 - 8
Cargo.lock

@@ -87,9 +87,9 @@ dependencies = [
 
 [[package]]
 name = "git2"
-version = "0.16.1"
+version = "0.17.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ccf7f68c2995f392c49fffb4f95ae2c873297830eb25c6bc4c114ce8f4562acc"
+checksum = "7b989d6a7ca95a362cf2cfc5ad688b3a467be1f87e480b8dad07fee8c79b0044"
 dependencies = [
  "bitflags",
  "libc",
@@ -139,15 +139,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.93"
+version = "0.2.147"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
+checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
 
 [[package]]
 name = "libgit2-sys"
-version = "0.14.2+1.5.1"
+version = "0.15.2+1.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f3d95f6b51075fe9810a7ae22c7095f12b98005ab364d8544797a825ce946a4"
+checksum = "a80df2e11fb4a61f4ba2ab42dbe7f74468da143f1a75c74e11dee7c813f694fa"
 dependencies = [
  "cc",
  "libc",
@@ -327,9 +327,9 @@ dependencies = [
 
 [[package]]
 name = "unicode-width"
-version = "0.1.8"
+version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
 
 [[package]]
 name = "url"

+ 3 - 3
Cargo.toml

@@ -7,9 +7,9 @@ edition = "2021"
 rust-version = "1.70.0"
 exclude = ["/devtools/*", "/Justfile", "/Vagrantfile", "/screenshots.png"]
 readme = "README.md"
-homepage = "https://github.com/cafkafk/eza"
+homepage = "https://github.com/eza-community/eza"
 license = "MIT"
-repository = "https://github.com/cafkafk/eza"
+repository = "https://github.com/eza-community/eza"
 version = "0.10.7"
 
 
@@ -43,7 +43,7 @@ default-features = false
 features = ["format"]
 
 [dependencies.git2]
-version = "0.16"
+version = "0.17"
 optional = true
 default-features = false
 

+ 9 - 9
README.md

@@ -9,9 +9,9 @@ eza is a modern, maintained replacement for ls, built on [exa](https://github.co
 [![Built with Nix](https://img.shields.io/badge/Built_With-Nix-5277C3.svg?logo=nixos&labelColor=73C3D5)](https://nixos.org)
 [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](code_of_conduct.md)
 
-[![Unit tests](https://github.com/cafkafk/eza/actions/workflows/unit-tests.yml/badge.svg)](https://github.com/cafkafk/eza/actions/workflows/unit-tests.yml)
+[![Unit tests](https://github.com/eza-community/eza/actions/workflows/unit-tests.yml/badge.svg)](https://github.com/eza-community/eza/actions/workflows/unit-tests.yml)
 ![Crates.io](https://img.shields.io/crates/v/eza?link=https%3A%2F%2Fcrates.io%2Fcrates%2Feza)
-![Crates.io](https://img.shields.io/crates/l/eza?link=https%3A%2F%2Fgithub.com%2Fcafkafk%2Feza%2Fblob%2Fmain%2FLICENCE)
+![Crates.io](https://img.shields.io/crates/l/eza?link=https%3A%2F%2Fgithub.com%2Feza-community%2Feza%2Fblob%2Fmain%2FLICENCE)
 
 </div>
 
@@ -31,12 +31,12 @@ By deliberately making some decisions differently, eza attempts to be a more fea
 
 **eza** features not in exa (non-exhaustive):
 
- -   Fixes [“The Grid Bug”](https://github.com/cafkafk/eza/issues/66#issuecomment-1656758327) introduced in exa 2021.
+ -   Fixes [“The Grid Bug”](https://github.com/eza-community/eza/issues/66#issuecomment-1656758327) introduced in exa 2021.
  -   Hyperlink support.
  -   Selinux context output.
  -   Git repo status output.
  -   Human readable relative dates.
- -   Several security fixes (see [dependabot](https://github.com/cafkafk/eza/security/dependabot?q=is%3Aclosed))
+ -   Several security fixes (see [dependabot](https://github.com/eza-community/eza/security/dependabot?q=is%3Aclosed))
  -   Many smaller bug fixes/changes!
 
 ---
@@ -49,11 +49,11 @@ By deliberately making some decisions differently, eza attempts to be a more fea
 
 If you already have Nix setup with flake support, you can try out eza with the `nix run` command:
 
-    nix run github:cafkafk/eza
+    nix run github:eza-community/eza
 
 Nix will build eza and run it. 
 
-If you want to pass arguments this way, use e.g. `nix run github:cafkafk/eza -- -ol`.
+If you want to pass arguments this way, use e.g. `nix run github:eza-community/eza -- -ol`.
 
 <a id="installation">
 <h1>Installation</h1>
@@ -75,7 +75,7 @@ Cargo will build the `eza` binary and place it in `$HOME/.local/share/cargo/bin/
 
 If you already have a Rust environment set up, you can use the `cargo install` command in your local clone of the repo:
 
-    git clone https://github.com/cafkafk/eza.git
+    git clone https://github.com/eza-community/eza.git
     cd eza
     cargo install --path .
 
@@ -158,7 +158,7 @@ These options are available when running with `--long` (`-l`):
 - **-H**, **--links**: list each file’s number of hard links
 - **-i**, **--inode**: list each file’s inode number
 - **-m**, **--modified**: use the modified timestamp field
-- **-S**, **--blocks**: list each file’s number of file system blocks
+- **-S**, **--blocksize**: show size of allocated file system blocks
 - **-t**, **--time=(field)**: which timestamp field to use
 - **-u**, **--accessed**: use the accessed timestamp field
 - **-U**, **--created**: use the created timestamp field
@@ -192,7 +192,7 @@ Some of the options accept parameters:
     <img src="https://img.shields.io/badge/rustc-1.63.0+-lightgray.svg" alt="Rust 1.63.0+" />
 </a>
 
-<a href="https://github.com/cafkafk/eza/blob/master/LICENCE">
+<a href="https://github.com/eza-community/eza/blob/master/LICENCE">
     <img src="https://img.shields.io/badge/licence-MIT-green" alt="MIT Licence" />
 </a>
 </h1></a>

+ 1 - 1
build.rs

@@ -23,7 +23,7 @@ fn main() -> io::Result<()> {
     #![allow(clippy::write_with_newline)]
 
     let tagline = "eza - A modern, maintained replacement for ls";
-    let url     = "https://github.com/cafkafk/eza";
+    let url     = "https://github.com/eza-community/eza";
 
     let ver =
         if is_debug_build() {

+ 1 - 1
completions/fish/eza.fish

@@ -64,7 +64,7 @@ complete -c eza -s g -l group -d "List each file's group"
 complete -c eza -s h -l header -d "Add a header row to each column"
 complete -c eza -s H -l links -d "List each file's number of hard links"
 complete -c eza -s i -l inode -d "List each file's inode number"
-complete -c eza -s S -l blocks -d "List each file's number of filesystem blocks"
+complete -c eza -s S -l blocksize -d "List each file's size of allocated file system blocks"
 complete -c eza -s t -l time -d "Which timestamp field to list" -x -a "
     modified\t'Display modified time'
     changed\t'Display changed time'

+ 1 - 1
completions/zsh/_eza

@@ -43,7 +43,7 @@ __eza() {
         {-i,--inode}"[List each file's inode number]" \
         {-m,--modified}"[Use the modified timestamp field]" \
         {-n,--numeric}"[List numeric user and group IDs.]" \
-        {-S,--blocks}"[List each file's number of filesystem blocks]" \
+        {-S,--blocksize}"[List each file's size of allocated file system blocks.]" \
         {-t,--time}="[Which time field to show]:(time field):(accessed changed created modified)" \
         --time-style="[How to format timestamps]:(time style):(default iso long-iso full-iso relative)" \
         --no-permissions"[Suppress the permissions field]" \

+ 1 - 1
flake.nix

@@ -69,7 +69,7 @@
 
         # For `nix develop`:
         devShells.default = pkgs.mkShell {
-          nativeBuildInputs = with pkgs; [toolchain];
+          nativeBuildInputs = with pkgs; [toolchain just pandoc];
         };
 
         # for `nix flake check`

+ 5 - 5
man/eza.1.md

@@ -79,7 +79,7 @@ Valid settings are ‘`always`’, ‘`automatic`’, and ‘`never`’.
 : Display entries as hyperlinks
 
 `-w`, `--width=COLS`
-Set screen width in columns.
+: Set screen width in columns.
 
 
 FILTERING AND SORTING OPTIONS
@@ -152,8 +152,8 @@ These options are available when running with `--long` (`-l`):
 `-n`, `--numeric`
 : List numeric user and group IDs.
 
-`-S`, `--blocks`
-: List each file’s number of file system blocks.
+`-S`, `--blocksize`
+: List each file’s size of allocated file system blocks.
 
 `-t`, `--time=WORD`
 : Which timestamp field to list.
@@ -267,8 +267,8 @@ AUTHOR
 
 eza is maintained by Christina Sørensen and many other contributors.
 
-**Source code:** `https://github.com/cafkafk/eza` \
-**Contributors:** `https://github.com/cafkafk/eza/graphs/contributors`
+**Source code:** `https://github.com/eza-community/eza` \
+**Contributors:** `https://github.com/eza-community/eza/graphs/contributors`
 
 Our infinite thanks to Benjamin ‘ogham’ Sago and all the other contributors of exa, from which eza was forked.
 

+ 5 - 2
man/eza_colors.5.md

@@ -193,6 +193,9 @@ LIST OF CODES
 `gt`
 : a modified metadata flag in Git
 
+`gi`
+: an ignored flag in Git
+
 `xx`
 : “punctuation”, including many background UI elements
 
@@ -271,8 +274,8 @@ AUTHOR
 
 eza is maintained by Christina Sørensen and many other contributors.
 
-**Source code:** `https://github.com/cafkafk/eza` \
-**Contributors:** `https://github.com/cafkafk/eza/graphs/contributors`
+**Source code:** `https://github.com/eza-community/eza` \
+**Contributors:** `https://github.com/eza-community/eza/graphs/contributors`
 
 Our infinite thanks to Benjamin ‘ogham’ Sago and all the other contributors of exa, from which eza was forked.
 

+ 3 - 8
src/fs/fields.rs

@@ -11,14 +11,9 @@
 //! The `output::details` module, among others, uses these types to render and
 //! display the information as formatted strings.
 
-// C-style `blkcnt_t` types don’t follow Rust’s rules!
 #![allow(non_camel_case_types)]
 #![allow(clippy::struct_excessive_bools)]
 
-
-/// The type of a file’s block count.
-pub type blkcnt_t = u64;
-
 /// The type of a file’s group ID.
 pub type gid_t = u32;
 
@@ -137,12 +132,12 @@ pub struct Links {
 pub struct Inode(pub ino_t);
 
 
-/// The number of blocks that a file takes up on the filesystem, if any.
+/// A file's size of allocated file system blocks.
 #[derive(Copy, Clone)]
-pub enum Blocks {
+pub enum Blocksize {
 
     /// This file has the given number of blocks.
-    Some(blkcnt_t),
+    Some(u64),
 
     /// This file isn’t of a type that can take up blocks.
     None,

+ 7 - 6
src/fs/file.rs

@@ -345,16 +345,17 @@ impl<'dir> File<'dir> {
         f::Inode(self.metadata.ino())
     }
 
-    /// This file’s number of filesystem blocks.
-    ///
-    /// (Not the size of each block, which we don’t actually report on)
+    /// This actual size the file takes up on disk, in bytes.
     #[cfg(unix)]
-    pub fn blocks(&self) -> f::Blocks {
+    pub fn blocksize(&self) -> f::Blocksize {
         if self.is_file() || self.is_link() {
-            f::Blocks::Some(self.metadata.blocks())
+            // Note that metadata.blocks returns the number of blocks
+            // for 512 byte blocks according to the POSIX standard
+            // even though the physical block size may be different.
+            f::Blocksize::Some(self.metadata.blocks() * 512)
         }
         else {
-            f::Blocks::None
+            f::Blocksize::None
         }
     }
 

+ 2 - 2
src/main.rs

@@ -75,7 +75,7 @@ fn main() {
             let writer = io::stdout();
 
             let console_width = options.view.width.actual_terminal_width();
-            let theme = options.theme.to_theme(console_width.is_some());
+            let theme = options.theme.to_theme(terminal_size::terminal_size().is_some());
             let exa = Exa { options, writer, input_paths, theme, console_width, git };
 
             match exa.run() {
@@ -104,7 +104,7 @@ fn main() {
         }
 
         OptionsResult::InvalidOptions(error) => {
-            eprintln!("exa: {error}");
+            eprintln!("eza: {error}");
 
             if let Some(s) = error.suggestion() {
                 eprintln!("{s}");

+ 2 - 2
src/options/flags.rs

@@ -49,7 +49,7 @@ pub static INODE:      Arg = Arg { short: Some(b'i'), long: "inode",      takes_
 pub static LINKS:      Arg = Arg { short: Some(b'H'), long: "links",      takes_value: TakesValue::Forbidden };
 pub static MODIFIED:   Arg = Arg { short: Some(b'm'), long: "modified",   takes_value: TakesValue::Forbidden };
 pub static CHANGED:    Arg = Arg { short: None,       long: "changed",    takes_value: TakesValue::Forbidden };
-pub static BLOCKS:     Arg = Arg { short: Some(b'S'), long: "blocks",     takes_value: TakesValue::Forbidden };
+pub static BLOCKSIZE:  Arg = Arg { short: Some(b'S'), long: "blocksize",  takes_value: TakesValue::Forbidden };
 pub static TIME:       Arg = Arg { short: Some(b't'), long: "time",       takes_value: TakesValue::Necessary(Some(TIMES)) };
 pub static ACCESSED:   Arg = Arg { short: Some(b'u'), long: "accessed",   takes_value: TakesValue::Forbidden };
 pub static CREATED:    Arg = Arg { short: Some(b'U'), long: "created",    takes_value: TakesValue::Forbidden };
@@ -84,7 +84,7 @@ pub static ALL_ARGS: Args = Args(&[
     &IGNORE_GLOB, &GIT_IGNORE, &ONLY_DIRS,
 
     &BINARY, &BYTES, &GROUP, &NUMERIC, &HEADER, &ICONS, &INODE, &LINKS, &MODIFIED, &CHANGED,
-    &BLOCKS, &TIME, &ACCESSED, &CREATED, &TIME_STYLE, &HYPERLINK,
+    &BLOCKSIZE, &TIME, &ACCESSED, &CREATED, &TIME_STYLE, &HYPERLINK,
     &NO_PERMISSIONS, &NO_FILESIZE, &NO_USER, &NO_TIME, &NO_ICONS,
 
     &GIT, &GIT_REPOS, &GIT_REPOS_NO_STAT, &EXTENDED, &OCTAL, &SECURITY_CONTEXT

+ 3 - 3
src/options/help.rs

@@ -6,11 +6,11 @@ use crate::options::parser::MatchedFlags;
 
 
 static USAGE_PART1: &str = "Usage:
-  exa [options] [files...]
+  eza [options] [files...]
 
 META OPTIONS
   -?, --help         show list of command-line options
-  -v, --version      show version of exa
+  -v, --version      show version of eza
 
 DISPLAY OPTIONS
   -1, --oneline      display one entry per line
@@ -51,7 +51,7 @@ LONG VIEW OPTIONS
   -i, --inode              list each file's inode number
   -m, --modified           use the modified timestamp field
   -n, --numeric            list numeric user and group IDs
-  -S, --blocks             show number of file system blocks
+  -S, --blocksize          show size of allocated file system blocks
   -t, --time FIELD         which timestamp field to list (modified, accessed, created)
   -u, --accessed           use the accessed timestamp field
   -U, --created            use the created timestamp field

+ 22 - 22
src/options/view.rs

@@ -82,7 +82,7 @@ impl Mode {
         // user about flags that won’t have any effect.
         if matches.is_strict() {
             for option in &[ &flags::BINARY, &flags::BYTES, &flags::INODE, &flags::LINKS,
-                             &flags::HEADER, &flags::BLOCKS, &flags::TIME, &flags::GROUP, &flags::NUMERIC ] {
+                             &flags::HEADER, &flags::BLOCKSIZE, &flags::TIME, &flags::GROUP, &flags::NUMERIC ] {
                 if matches.has(option)? {
                     return Err(OptionsError::Useless(option, false, &flags::LONG));
                 }
@@ -223,7 +223,7 @@ impl Columns {
         let subdir_git_repos = matches.has(&flags::GIT_REPOS)?;
         let subdir_git_repos_no_stat = !subdir_git_repos && matches.has(&flags::GIT_REPOS_NO_STAT)?;
 
-        let blocks           = matches.has(&flags::BLOCKS)?;
+        let blocksize        = matches.has(&flags::BLOCKSIZE)?;
         let group            = matches.has(&flags::GROUP)?;
         let inode            = matches.has(&flags::INODE)?;
         let links            = matches.has(&flags::LINKS)?;
@@ -234,7 +234,7 @@ impl Columns {
         let filesize =    ! matches.has(&flags::NO_FILESIZE)?;
         let user =        ! matches.has(&flags::NO_USER)?;
 
-        Ok(Self { time_types, inode, links, blocks, group, git, subdir_git_repos, subdir_git_repos_no_stat, octal, security_context, permissions, filesize, user })
+        Ok(Self { time_types, inode, links, blocksize, group, git, subdir_git_repos, subdir_git_repos_no_stat, octal, security_context, permissions, filesize, user })
     }
 }
 
@@ -375,7 +375,7 @@ mod test {
                                    &flags::TIME,   &flags::MODIFIED, &flags::CHANGED,
                                    &flags::CREATED, &flags::ACCESSED,
                                    &flags::HEADER, &flags::GROUP,  &flags::INODE, &flags::GIT,
-                                   &flags::LINKS,  &flags::BLOCKS, &flags::LONG,  &flags::LEVEL,
+                                   &flags::LINKS,  &flags::BLOCKSIZE, &flags::LONG,  &flags::LEVEL,
                                    &flags::GRID,   &flags::ACROSS, &flags::ONE_LINE, &flags::TREE,
                                    &flags::NUMERIC ];
 
@@ -580,26 +580,26 @@ mod test {
         test!(long_across:   Mode <- ["--long", "--across"],   None;  Last => like Ok(Mode::Details(_)));
 
         // Options that do nothing without --long
-        test!(just_header:   Mode <- ["--header"],   None;  Last => like Ok(Mode::Grid(_)));
-        test!(just_group:    Mode <- ["--group"],    None;  Last => like Ok(Mode::Grid(_)));
-        test!(just_inode:    Mode <- ["--inode"],    None;  Last => like Ok(Mode::Grid(_)));
-        test!(just_links:    Mode <- ["--links"],    None;  Last => like Ok(Mode::Grid(_)));
-        test!(just_blocks:   Mode <- ["--blocks"],   None;  Last => like Ok(Mode::Grid(_)));
-        test!(just_binary:   Mode <- ["--binary"],   None;  Last => like Ok(Mode::Grid(_)));
-        test!(just_bytes:    Mode <- ["--bytes"],    None;  Last => like Ok(Mode::Grid(_)));
-        test!(just_numeric:  Mode <- ["--numeric"],  None;  Last => like Ok(Mode::Grid(_)));
+        test!(just_header:   Mode <- ["--header"],    None;  Last => like Ok(Mode::Grid(_)));
+        test!(just_group:    Mode <- ["--group"],     None;  Last => like Ok(Mode::Grid(_)));
+        test!(just_inode:    Mode <- ["--inode"],     None;  Last => like Ok(Mode::Grid(_)));
+        test!(just_links:    Mode <- ["--links"],     None;  Last => like Ok(Mode::Grid(_)));
+        test!(just_blocks:   Mode <- ["--blocksize"], None;  Last => like Ok(Mode::Grid(_)));
+        test!(just_binary:   Mode <- ["--binary"],    None;  Last => like Ok(Mode::Grid(_)));
+        test!(just_bytes:    Mode <- ["--bytes"],     None;  Last => like Ok(Mode::Grid(_)));
+        test!(just_numeric:  Mode <- ["--numeric"],   None;  Last => like Ok(Mode::Grid(_)));
 
         #[cfg(feature = "git")]
-        test!(just_git:      Mode <- ["--git"],    None;  Last => like Ok(Mode::Grid(_)));
-
-        test!(just_header_2: Mode <- ["--header"],   None;  Complain => err OptionsError::Useless(&flags::HEADER,  false, &flags::LONG));
-        test!(just_group_2:  Mode <- ["--group"],    None;  Complain => err OptionsError::Useless(&flags::GROUP,   false, &flags::LONG));
-        test!(just_inode_2:  Mode <- ["--inode"],    None;  Complain => err OptionsError::Useless(&flags::INODE,   false, &flags::LONG));
-        test!(just_links_2:  Mode <- ["--links"],    None;  Complain => err OptionsError::Useless(&flags::LINKS,   false, &flags::LONG));
-        test!(just_blocks_2: Mode <- ["--blocks"],   None;  Complain => err OptionsError::Useless(&flags::BLOCKS,  false, &flags::LONG));
-        test!(just_binary_2: Mode <- ["--binary"],   None;  Complain => err OptionsError::Useless(&flags::BINARY,  false, &flags::LONG));
-        test!(just_bytes_2:  Mode <- ["--bytes"],    None;  Complain => err OptionsError::Useless(&flags::BYTES,   false, &flags::LONG));
-        test!(just_numeric2: Mode <- ["--numeric"],  None;  Complain => err OptionsError::Useless(&flags::NUMERIC, false, &flags::LONG));
+        test!(just_git:      Mode <- ["--git"],       None;  Last => like Ok(Mode::Grid(_)));
+
+        test!(just_header_2: Mode <- ["--header"],    None;  Complain => err OptionsError::Useless(&flags::HEADER,  false, &flags::LONG));
+        test!(just_group_2:  Mode <- ["--group"],     None;  Complain => err OptionsError::Useless(&flags::GROUP,   false, &flags::LONG));
+        test!(just_inode_2:  Mode <- ["--inode"],     None;  Complain => err OptionsError::Useless(&flags::INODE,   false, &flags::LONG));
+        test!(just_links_2:  Mode <- ["--links"],     None;  Complain => err OptionsError::Useless(&flags::LINKS,   false, &flags::LONG));
+        test!(just_blocks_2: Mode <- ["--blocksize"], None;  Complain => err OptionsError::Useless(&flags::BLOCKSIZE,  false, &flags::LONG));
+        test!(just_binary_2: Mode <- ["--binary"],    None;  Complain => err OptionsError::Useless(&flags::BINARY,  false, &flags::LONG));
+        test!(just_bytes_2:  Mode <- ["--bytes"],     None;  Complain => err OptionsError::Useless(&flags::BYTES,   false, &flags::LONG));
+        test!(just_numeric2: Mode <- ["--numeric"],   None;  Complain => err OptionsError::Useless(&flags::NUMERIC, false, &flags::LONG));
 
         #[cfg(feature = "git")]
         test!(just_git_2:    Mode <- ["--git"],    None;  Complain => err OptionsError::Useless(&flags::GIT,    false, &flags::LONG));

+ 19 - 3
src/output/grid_details.rs

@@ -8,9 +8,10 @@ use term_grid as grid;
 use crate::fs::{Dir, File};
 use crate::fs::feature::git::GitCache;
 use crate::fs::filter::FileFilter;
-use crate::output::cell::TextCell;
+use crate::output::cell::{TextCell, DisplayWidth};
 use crate::output::details::{Options as DetailsOptions, Row as DetailsRow, Render as DetailsRender};
 use crate::output::file_name::Options as FileStyle;
+use crate::output::file_name::{ShowIcons, EmbedHyperlinks};
 use crate::output::grid::Options as GridOptions;
 use crate::output::table::{Table, Row as TableRow, Options as TableOptions};
 use crate::output::tree::{TreeParams, TreeDepth};
@@ -153,8 +154,23 @@ impl<'a> Render<'a> {
                        .collect::<Vec<_>>();
 
         let file_names = self.files.iter()
-                             .map(|file| self.file_style.for_file(file, self.theme).paint().promote())
-                             .collect::<Vec<_>>();
+            .map(|file| {
+                let filename = self.file_style.for_file(file, self.theme);
+                let contents = filename.paint();
+                let width = match (filename.options.embed_hyperlinks, filename.options.show_icons) {
+                    (EmbedHyperlinks::On, ShowIcons::On(spacing)) => filename.bare_width() + 1 + (spacing as usize),
+                    (EmbedHyperlinks::On, ShowIcons::Off) => filename.bare_width(),
+                    (EmbedHyperlinks::Off, _) => *contents.width(),
+                };
+
+                TextCell {
+                    contents,
+                    // with hyperlink escape sequences,
+                    // the actual *contents.width() is larger than actually needed, so we take only the filename
+                    width: DisplayWidth::from(width),
+                }
+            })
+            .collect::<Vec<_>>();
 
         let mut last_working_grid = self.make_grid(1, options, &file_names, rows.clone(), &drender);
 

+ 104 - 21
src/output/render/blocks.rs

@@ -1,22 +1,67 @@
 use ansi_term::Style;
+use locale::Numeric as NumericLocale;
+use number_prefix::Prefix;
 
 use crate::fs::fields as f;
-use crate::output::cell::TextCell;
-
-
-impl f::Blocks {
-    pub fn render<C: Colours>(&self, colours: &C) -> TextCell {
-        match self {
-            Self::Some(blk)  => TextCell::paint(colours.block_count(), blk.to_string()),
-            Self::None       => TextCell::blank(colours.no_blocks()),
+use crate::output::cell::{TextCell, DisplayWidth};
+use crate::output::table::SizeFormat;
+
+
+impl f::Blocksize {
+    pub fn render<C: Colours>(self, colours: &C, size_format: SizeFormat, numerics: &NumericLocale) -> TextCell {
+        use number_prefix::NumberPrefix;
+
+        let size = match self {
+            Self::Some(s)             => s,
+            Self::None                => return TextCell::blank(colours.no_blocksize()),
+        };
+
+        let result = match size_format {
+            SizeFormat::DecimalBytes  => NumberPrefix::decimal(size as f64),
+            SizeFormat::BinaryBytes   => NumberPrefix::binary(size as f64),
+            SizeFormat::JustBytes     => {
+
+                // Use the binary prefix to select a style.
+                let prefix = match NumberPrefix::binary(size as f64) {
+                    NumberPrefix::Standalone(_)   => None,
+                    NumberPrefix::Prefixed(p, _)  => Some(p),
+                };
+
+                // But format the number directly using the locale.
+                let string = numerics.format_int(size);
+
+                return TextCell::paint(colours.blocksize(prefix), string);
+            }
+        };
+
+        let (prefix, n) = match result {
+            NumberPrefix::Standalone(b)   => return TextCell::paint(colours.blocksize(None), numerics.format_int(b)),
+            NumberPrefix::Prefixed(p, n)  => (p, n),
+        };
+
+        let symbol = prefix.symbol();
+        let number = if n < 10_f64 {
+            numerics.format_float(n, 1)
+        } 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(),
+            contents: vec![
+                colours.blocksize(Some(prefix)).paint(number),
+                colours.unit(Some(prefix)).paint(symbol),
+            ].into(),
         }
     }
 }
 
 
 pub trait Colours {
-    fn block_count(&self) -> Style;
-    fn no_blocks(&self) -> Style;
+    fn blocksize(&self, prefix: Option<Prefix>) -> Style;
+    fn unit(&self, prefix: Option<Prefix>)      -> Style;
+    fn no_blocksize(&self)                      -> Style;
 }
 
 
@@ -26,32 +71,70 @@ pub mod test {
     use ansi_term::Colour::*;
 
     use super::Colours;
-    use crate::output::cell::TextCell;
+    use crate::output::cell::{TextCell, DisplayWidth};
+    use crate::output::table::SizeFormat;
     use crate::fs::fields as f;
 
+    use locale::Numeric as NumericLocale;
+    use number_prefix::Prefix;
 
     struct TestColours;
 
     impl Colours for TestColours {
-        fn block_count(&self) -> Style { Red.blink() }
-        fn no_blocks(&self)   -> Style { Green.italic() }
+        fn blocksize(&self, _prefix: Option<Prefix>) -> Style { Fixed(66).normal() }
+        fn unit(&self, _prefix: Option<Prefix>)      -> Style { Fixed(77).bold() }
+        fn no_blocksize(&self)                       -> Style { Black.italic() }
     }
 
 
     #[test]
-    fn blocklessness() {
-        let blox = f::Blocks::None;
-        let expected = TextCell::blank(Green.italic());
+    fn directory() {
+        let directory = f::Blocksize::None;
+        let expected = TextCell::blank(Black.italic());
+        assert_eq!(expected, directory.render(&TestColours, SizeFormat::JustBytes, &NumericLocale::english()))
+    }
+
 
-        assert_eq!(expected, blox.render(&TestColours));
+    #[test]
+    fn file_decimal() {
+        let directory = f::Blocksize::Some(2_100_000);
+        let expected = TextCell {
+            width: DisplayWidth::from(4),
+            contents: vec![
+                Fixed(66).paint("2.1"),
+                Fixed(77).bold().paint("M"),
+            ].into(),
+        };
+
+        assert_eq!(expected, directory.render(&TestColours, SizeFormat::DecimalBytes, &NumericLocale::english()))
     }
 
 
     #[test]
-    fn blockfulity() {
-        let blox = f::Blocks::Some(3005);
-        let expected = TextCell::paint_str(Red.blink(), "3005");
+    fn file_binary() {
+        let directory = f::Blocksize::Some(1_048_576);
+        let expected = TextCell {
+            width: DisplayWidth::from(5),
+            contents: vec![
+                Fixed(66).paint("1.0"),
+                Fixed(77).bold().paint("Mi"),
+            ].into(),
+        };
+
+        assert_eq!(expected, directory.render(&TestColours, SizeFormat::BinaryBytes, &NumericLocale::english()))
+    }
 
-        assert_eq!(expected, blox.render(&TestColours));
+
+    #[test]
+    fn file_bytes() {
+        let directory = f::Blocksize::Some(1_048_576);
+        let expected = TextCell {
+            width: DisplayWidth::from(9),
+            contents: vec![
+                Fixed(66).paint("1,048,576"),
+            ].into(),
+        };
+
+        assert_eq!(expected, directory.render(&TestColours, SizeFormat::JustBytes, &NumericLocale::english()))
     }
 }

+ 8 - 8
src/output/table.rs

@@ -48,7 +48,7 @@ pub struct Columns {
     // The rest are just on/off
     pub inode: bool,
     pub links: bool,
-    pub blocks: bool,
+    pub blocksize: bool,
     pub group: bool,
     pub git: bool,
     pub subdir_git_repos: bool,
@@ -89,9 +89,9 @@ impl Columns {
             columns.push(Column::FileSize);
         }
 
-        if self.blocks {
+        if self.blocksize {
             #[cfg(unix)]
-            columns.push(Column::Blocks);
+            columns.push(Column::Blocksize);
         }
 
         if self.user {
@@ -148,7 +148,7 @@ pub enum Column {
     FileSize,
     Timestamp(TimeType),
     #[cfg(unix)]
-    Blocks,
+    Blocksize,
     #[cfg(unix)]
     User,
     #[cfg(unix)]
@@ -184,7 +184,7 @@ impl Column {
             Self::FileSize   |
             Self::HardLinks  |
             Self::Inode      |
-            Self::Blocks     |
+            Self::Blocksize  |
             Self::GitStatus  => Alignment::Right,
             Self::Timestamp(_) | 
             _                => Alignment::Left,
@@ -211,7 +211,7 @@ impl Column {
             Self::FileSize      => "Size",
             Self::Timestamp(t)  => t.header(),
             #[cfg(unix)]
-            Self::Blocks        => "Blocks",
+            Self::Blocksize     => "Blocksize",
             #[cfg(unix)]
             Self::User          => "User",
             #[cfg(unix)]
@@ -515,8 +515,8 @@ impl<'a> Table<'a> {
                 file.inode().render(self.theme.ui.inode)
             }
             #[cfg(unix)]
-            Column::Blocks => {
-                file.blocks().render(self.theme)
+            Column::Blocksize => {
+                file.blocksize().render(self.theme, self.size_format, &self.env.numeric)
             }
             #[cfg(unix)]
             Column::User => {

+ 25 - 2
src/theme/mod.rs

@@ -202,8 +202,31 @@ impl ExtensionMappings {
 
 
 impl render::BlocksColours for Theme {
-    fn block_count(&self)  -> Style { self.ui.blocks }
-    fn no_blocks(&self)    -> Style { self.ui.punctuation }
+    fn blocksize(&self, prefix: Option<number_prefix::Prefix>) -> Style {
+        use number_prefix::Prefix::*;
+
+        match prefix {
+            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,
+        }
+    }
+
+    fn unit(&self, prefix: Option<number_prefix::Prefix>) -> Style {
+        use number_prefix::Prefix::*;
+
+        match prefix {
+            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,
+        }
+    }
+
+    fn no_blocksize(&self) -> Style { self.ui.punctuation }
 }
 
 impl render::FiletypeColours for Theme {

+ 1 - 0
src/theme/ui_styles.rs

@@ -199,6 +199,7 @@ impl UiStyles {
             "gd" => self.git.deleted              = pair.to_style(),
             "gv" => self.git.renamed              = pair.to_style(),
             "gt" => self.git.typechange           = pair.to_style(),
+            "gi" => self.git.ignored              = pair.to_style(),
 
             "xx" => self.punctuation              = pair.to_style(),
             "da" => self.date                     = pair.to_style(),

+ 1 - 1
xtests/outputs/help.ansitxt

@@ -41,7 +41,7 @@ LONG VIEW OPTIONS
   -i, --inode              list each file's inode number
   -m, --modified           use the modified timestamp field
   -n, --numeric            list numeric user and group IDs
-  -S, --blocks             show number of file system blocks
+  -S, --blocksize          show size of allocated file system blocks
   -t, --time FIELD         which timestamp field to list (modified, accessed, created)
   -u, --accessed           use the accessed timestamp field
   -U, --created            use the created timestamp field