Repository: eza-community/eza (language: rust)

Issue:
feat: Do not colorize the output when stdout is not connected to a real terminal when `--color=auto` or `--color=automatic`  is set.
coreutils `ls` and [exa](https://github.com/ogham/exa) has a `--color=auto` option that disables colorization when the output is redirected or piped.  
This is very helpful when you want to combine `eza` with other CLI/TUI tools. I am listing a few use cases

- `eza --color=auto --online *.py| fzf` to make a fuzzy search on the `eza` output.
- `eza --color=auto | rg SOMEFANCYREGEX` to do subsequent search in the result.
- `eza --color=auto | nvim -` to edit the output manually. 
- `eza > eza-ouput` to save the output as a text file.

In these cases, you don't want the ANSI controll characters to be included. 

`ls --color=auto` dose exactly the thing we want. Since `eza` is intended to be a replacement of  `ls`, we should support similar functionality.

----

Relavent issue: https://github.com/eza-community/eza/issues/433
bug: `--color=automatic` dose not work
When piping output of `eza` to another program or simply redirecting stdout to  another file, color controll characters present in the output. The expected behavior is that colorization is disabled when `eza` detect that it is not printing things to pty/tty. 

To reproduce:

```fish
eza --color=automatic --online | fzf
eza --color=automatic --online > eza-out; cat ./eza-out
```

You can see things like `[1;34mvar[0m`.

In contrast, when using coreutils ls, no color should be there.

- The version of eza being used (`eza --version`): `v0.13.1 [+git]`
- The command-line arguments you are using: `eza --color=automatic --oneline`
- Your operating system and hardware platform: `Arch Linux, x86_64`
- This bug is reproduced on a clean container using the `docker.io/ubuntu:latest` image, so it is not my env/config file causing the problem.


Discussion / hints:
Well, I think that is change in `terminal_size` causing this problem.  

Previsouly the `terminal_size::terminal_size()` function is simply `terminal_size_using_fd()` on `stdout`, which is what we need here.
Later on, it is changed to query `terminal_size_using_fd()` on `stdout, stderr, stdin`.  
The commit that did this is: https://github.com/eminence/terminal-size/commit/08f0e73926c11adc3105dbf4eb84dd8c9e6c873a

To correct the behavior, we should query `terminal_size_using_fd()` on `libc::STDOUT_FIILENO`.
I'll create a PR to fix this soon.
As you can observe from e.g. the fish completions:
```
✦ ❯ eza --color=always 
--color=always  (Always use colour)  --color=auto  (Use colour if standard output is a terminal)  --color=never  (Never use colour)
```
It uses color if standard output is a terminal, making this expected behavior.
If you want, you can add it to the man pages to clear up confusions, or change this to a feature request. LMK if you want to continue and I'll reopen.
Interesting, I rember that `exa`  handle colorizaton diifferently.
I'll make it a feature request.
> Interesting, I rember that `exa` handle colorizaton diifferently. I'll make it a feature request.

Okay! 

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index 578c48c6d..42fe75127 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -380,6 +380,7 @@ dependencies = [
  "trycmd",
  "unicode-width",
  "uzers",
+ "windows-sys",
  "zoneinfo_compiled",
 ]
 
diff --git a/Cargo.toml b/Cargo.toml
index 965b20ea2..fe0fc8613 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -101,6 +101,9 @@ proc-mounts = "0.3"
 [target.'cfg(unix)'.dependencies]
 uzers = "0.11.3"
 
+[target.'cfg(target_os = "windows")'.dependencies]
+windows-sys = "0.48.0"
+
 [build-dependencies]
 chrono = { version = "0.4.31", default-features = false, features = ["clock"] }
 
diff --git a/README.md b/README.md
index 683f4cedb..39a512b9d 100644
--- a/README.md
+++ b/README.md
@@ -334,7 +334,7 @@ These options are available when running with `--long` (`-l`):
 
 Some of the options accept parameters:
 
-- Valid **--color** options are **always**, **automatic**, and **never**.
+- Valid **--co{u}lor** options are **always**, **automatic** (or **auto** for short), and **never**.
 - Valid sort fields are **accessed**, **changed**, **created**, **extension**, **Extension**, **inode**, **modified**, **name**, **Name**, **size**, **type**, and **none**. Fields starting with a capital letter sort uppercase before lowercase. The modified field has the aliases **date**, **time**, and **newest**, while its reverse has the aliases **age** and **oldest**.
 - Valid time fields are **modified**, **changed**, **accessed**, and **created**.
 - Valid time styles are **default**, **iso**, **long-iso**, **full-iso**, and **relative**.
diff --git a/completions/bash/eza b/completions/bash/eza
index b22473501..df739d480 100644
--- a/completions/bash/eza
+++ b/completions/bash/eza
@@ -9,7 +9,7 @@ _eza() {
             ;;
 
         --colour)
-            mapfile -t COMPREPLY < <(compgen -W 'always auto never' -- "$cur")
+            mapfile -t COMPREPLY < <(compgen -W 'always automatic auto never' -- "$cur")
             return
             ;;
 
diff --git a/completions/fish/eza.fish b/completions/fish/eza.fish
index 577569cff..124400b12 100644
--- a/completions/fish/eza.fish
+++ b/completions/fish/eza.fish
@@ -14,6 +14,7 @@ complete -c eza -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'
+    automatic\t'Use colour if standard output is a terminal'
     never\t'Never use colour'
 "
 complete -c eza -l color-scale \
diff --git a/completions/zsh/_eza b/completions/zsh/_eza
index 26c5e1474..63982e118 100644
--- a/completions/zsh/_eza
+++ b/completions/zsh/_eza
@@ -19,7 +19,7 @@ __eza() {
         {-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]:(when):(always auto never)" \
+        --colo{,u}r="[When to use terminal colours]:(when):(always auto automatic never)" \
         --colo{,u}r-scale"[Highlight levels of file sizes distinctly]" \
         --icons"[Display icons]" \
         --no-icons"[Hide icons]" \
diff --git a/man/eza.1.md b/man/eza.1.md
index acfcf05bc..ac48402dc 100644
--- a/man/eza.1.md
+++ b/man/eza.1.md
@@ -62,9 +62,15 @@ DISPLAY OPTIONS
 `-x`, `--across`
 : Sort the grid across, rather than downwards.
 
-`--color`, `--colour=WHEN`
-: When to use terminal colours.
-Valid settings are ‘`always`’, ‘`automatic`’, and ‘`never`’.
+`--color=WHEN`, `--colour=WHEN`
+: When to use terminal colours (using ANSI escape code to colorize the output).
+
+Valid settings are ‘`always`’, ‘`automatic`’ (or ‘`auto`’ for short), and ‘`never`’.
+The default value is ‘`automatic`’.
+
+The default behavior (‘`automatic`’ or ‘`auto`’) is to colorize the output only when the standard output is connected to a real terminal. If the output of `eza` is redirected to a file or piped into another program, terminal colors will not be used. Setting this option to ‘`always`’ causes `eza` to always output terminal color, while ‘`never`’ disables the use of terminal color.
+
+Manually setting this option overrides `NO_COLOR` environment.
 
 `--color-scale`, `--colour-scale`
 : Colour file sizes on a scale.
diff --git a/src/main.rs b/src/main.rs
index ef7bb2c44..a37781a63 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -23,7 +23,7 @@
 
 use std::env;
 use std::ffi::{OsStr, OsString};
-use std::io::{self, ErrorKind, Write};
+use std::io::{self, ErrorKind, IsTerminal, Write};
 use std::path::{Component, PathBuf};
 use std::process::exit;
 
@@ -58,6 +58,8 @@ fn main() {
         warn!("Failed to enable ANSI support: {}", e);
     }
 
+    let stdout_istty = io::stdout().is_terminal();
+
     let args: Vec<_> = env::args_os().skip(1).collect();
     match Options::parse(args.iter().map(std::convert::AsRef::as_ref), &LiveVars) {
         OptionsResult::Ok(options, mut input_paths) => {
@@ -71,9 +73,7 @@ fn main() {
             let writer = io::stdout();
 
             let console_width = options.view.width.actual_terminal_width();
-            let theme = options
-                .theme
-                .to_theme(terminal_size::terminal_size().is_some());
+            let theme = options.theme.to_theme(stdout_istty);
             let exa = Exa {
                 options,
                 writer,
diff --git a/src/output/mod.rs b/src/output/mod.rs
index b3fc57d15..feb633681 100644
--- a/src/output/mod.rs
+++ b/src/output/mod.rs
@@ -50,10 +50,26 @@ impl TerminalWidth {
         // terminal, but we’re only interested in stdout because it’s
         // where the output goes.
 
+        #[cfg(unix)]
+        let stdout_term_width = {
+            use std::os::fd::AsRawFd;
+            terminal_size::terminal_size_using_fd(std::io::stdout().as_raw_fd())
+                .map(|(w, _h)| w.0 as _)
+        };
+        #[cfg(windows)]
+        let stdout_term_width = {
+            use std::os::windows::io::RawHandle;
+            use windows_sys::Win32::System::Console::{GetStdHandle, STD_OUTPUT_HANDLE};
+            terminal_size::terminal_size_using_handle(unsafe {
+                GetStdHandle(STD_OUTPUT_HANDLE) as RawHandle
+            })
+            .map(|(w, _h)| w.0 as _)
+        };
+
         #[rustfmt::skip]
         return match self {
             Self::Set(width)  => Some(width),
-            Self::Automatic   => terminal_size::terminal_size().map(|(w, _)| w.0.into()),
+            Self::Automatic   => stdout_term_width,
         };
     }
 }


Test patch: