Repository: sharkdp/fd (language: rust)

Issue:
Colourful output on Windows (in Git Bash)
I've seen the talks leading up to colour output support, namely #129 #137

However, [`bat`](http://github.com/sharkdp/bat) works and emits a colourful output, by default. Below is `bat ~/Desktop/desktop.ini`:

![`bat` output with colours working](https://user-images.githubusercontent.com/1969470/63160946-42dfe780-c01f-11e9-8b67-8cab4a74f0db.png)

It seems colourful output *can* be supported.

I'm using Git Bash 2.20-something. Git Bash can be set up to run with *MinTTY*, which gets you a nice terminal emulator, unlike that of the default `cmd`. Git Bash is the nicest and best way to get Bash shell environment running for Windows, and this is like the only Shell I use on Windows, so it's surprising that if `bat` colours work, `fd` does not.


Discussion / hints:
Thank you for the feedback.

How do you run `fd`? What does the output look like? On Windows, the `LS_COLORS` environment variable is typically not set, so we would only expect to get (default) colors for directories (and symlinks).

Could you try to run `fd --color=always …` and pipe the output to a file? This way, we could see if any ANSI escape codes are generated.
Meanwhile, I checked, and the output of `bat` in a standard `cmd.exe` looks ugly. So this request could be specific to Git Bash.

I have `LS_COLORS`, because this is a Bash shell running inside a Windows process.

Both `fd` and `fd --color=always` produces the same output:
![image](https://user-images.githubusercontent.com/1969470/63256470-edeade00-c277-11e9-839f-5204122146d5.png)

Could you please run
```
fd --color=always > output.txt
```
and check if the `output.txt` file includes ANSI escape sequences?

If not, could you please show the content of `LS_COLORS`?
No escape sequences, only normal characters.

> 17:59 ~ $ echo $LS_COLORS
> `rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01: or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32 :*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=0 1;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*. zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.x z=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31 :*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01 ;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz =01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:* .jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm =01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35: *.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=0 1;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:* .m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01; 35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl= 01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.o gv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36 :*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=0 0;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:`



(I guess this is just a copy-and-paste error, but there shouldn't be any spaces in `$LS_COLORS`)

Could you please run `fd` in a folder where you would definitely expect some entries to be colorized (e.g. with subdirectories)? In your screenshot above, I wouldn't expect anything to be colorized.
Note that the comment on  #129 about termcolor does not apply any more: termcolor supports truecolors and ansi256 colors.
@sharkdp I had no better idea to put it up on GitHub because the output is a single line, which would look idiotic with a horizontal scrollbar.
This is the default `LS_COLOURS` you get on a Debian(?)-based machine? I use no customisations to it, none of my (userspace) `.bash` or `.profile` files contain any changes from that.

Ran it with subdirectories. No escape sequences or anything is printed. Ran it with hidden directories, something with `README.md`, it's all the same.

Feels like the binary doesn't want to output colours at all.

`bat`'s output is full of escape sequences, to the point that the `bat` binary works with this MinTTY shell, but doesn't work at all with the traditional `cmd.exe`, the escape sequences garble the output up.
Which Windows version are you on?

Could you please show the output of (run this in a new, temporary directory):
```bash
mkdir test_directory
touch test.md test.txt

fd --color=always | cat -A
echo
export LS_COLORS="di=01;34:*.md=01;31"
fd --color=always | cat -A
```
![image](https://user-images.githubusercontent.com/1969470/65061880-1b13d480-d97b-11e9-8bfa-37b795bdcbba.png)

Win7 x64 SP1 updated a few months ago.

The terminal emulator is not the standard `cmd.exe` it's coming from MinTTY via *Git Bash* `git version 2.20.1.windows.1`.
Ok, thank you.

The reason that this happens is the following:
https://github.com/sharkdp/fd/blob/b027b2c32285253b5866f934acc66fcd695ea649/src/main.rs#L125-L129

https://github.com/sharkdp/fd/blob/b027b2c32285253b5866f934acc66fcd695ea649/src/main.rs#L133-L134

If you have `--color=always`, `colored_output` should be `true` first. But then, in the second part which is only enabled on Windows, we call `ansi_term::enable_ansi_support()` which enables ANSI escape sequences in PowerShell/`cmd.exe` on Windows 10(!). If this function fails (presumably on Windows 7), the `.is_ok()` call returns `false`, and `colored_output` is set to `false` for the remainder of the `fd` run.

So even though your terminal emulator would actually already support ANSI escape sequences, `fd` switches off color support because "enabling ANSI escape sequences" failed.

I think there are a few ways forward:

* Ignore the result of `ansi_term::enable_ansi_support().is_ok()` if `--color=always` is enabled. However, this would force users MinTTY users to use `--color=always` all the time, which is not a good idea (when you want to pipe the `fd` output to another program).
* Use an (existing?) environment variable to toggle between using `enable_ansi_support()` or not. That would certainly work, but would add additional documentation overhead and would not work out of the box.
* Add special handling for Windows terminal emulators like MinTTY which would basically skip the whole `enable_ansi_support()` part. If there is an easy way to detect such terminals (without adding specific detection logic for each and every terminal emulator), that would be a good solution.
* Try to move this kind of logic into `ansi_term`...?
* ....
You have managed to turn off escape sequences in `bat` when its output is going away on a pipe. Same logic could work here, and thus you don't need to specify `always` and use it as a default *unless* a pipe happens?
Yes, of course. We already do this. That's the `_ => atty::is(Stream::Stdout)` part which triggers in the "auto" case and checks whether `Stdout` is connected to a TTY (interactive terminal).

The problem is the second part. So far, we wanted to *disable* color support on Windows if `ansi_term::enable_ansi_support()` failed. This is useful for old Windows versions with `cmd.exe`, for example.
The way that `bat` "solves" this is another possibility:

* Simply ignore the result of `ansi_term::enable_ansi_support().is_ok()` in all cases. This would mean that colors would show up on MinTTY. However, it would also mean that ANSI escape sequences show up in `cmd.exe` (and PowerShell?) on older Windows versions.
Just ran into this.

I think `fd` is trying to be too smart here.

`fd` needs to account for situations where users operating doesnt support
ANSI (Windows 7, 8), but their terminal does (Mintty).

Currently that case is not handled. `fd` assumes that if users OS doesnt have
built in support, then they have no way to see color, regardless of what 3rd
party solutions might exist.

Im not sure how to fix it, but `fd` should be accounting for a users terminal
capabilities rather than OS capabilities.

I would be okay with the `bat` solution. This would be a regression for users with old Windows versions, but I guess if we have to choose, we rather provide a good default behavior for users with a recent version of Windows.
fixed via #570 
This has now been released in [fd v8.0](https://github.com/sharkdp/fd/releases/tag/v8.0.0).
Unfortunately, I'm going to re-open this, as I had to revert the original change in #597 due to multiple bug reports from users of older Windows versions.

As of [fd 8.1.0](https://github.com/sharkdp/fd/releases/tag/v8.1.0), this will be relevant again.
Windows 10 supports terminal colors starting with builds >= 16257

This should be able to retrieve the build number but I'm not sure what the exact output looks like: https://docs.rs/sys-info/0.6.1/sys_info/fn.os_release.html
I think `ansi_term:enable_ansi_support` does the right thing on older Windows versions that don't support vt escape sequences, so you can probably use this code instead:

```rust
    #[cfg(windows)]
    let colored_output = ansi_term::enable_ansi_support().is_ok();
```
I compiled fd with @lzybkr's suggestion and it works on my up-to-date Windows 10 machine. It just needs to be tested on an older distribution of Windows I assume.
@lzybkr @ImportTaste Thank you for your feedback. I'm not sure I can follow.

We already use this code in the current version of `fd`:
https://github.com/sharkdp/fd/blob/78cde856b1064ea458f6ec7dda8c66f560aac94c/src/main.rs#L160-L161

This means that we will disable color support if `enable_ansi_support()` returns an error (which it does, on older Windows versions).

What I thought this issue is really about: there are some terminals that DO support ANSI escape sequences, even on older Windows versions (I have explained this here: https://github.com/sharkdp/fd/issues/469#issuecomment-532314748). At the moment, users will not see colored output on these terminals, due to `enable_ansi_support()` returning an error.

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cbd45e074..846a688c6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,9 @@
 
 ## Features
 ## Bugfixes
+
+- Support colored output on older Windows versions if either (1) `--color=always` is set or (2) the `TERM` environment variable is set. See #469
+
 ## Changes
 ## Other
 
diff --git a/src/main.rs b/src/main.rs
index 6ffa27749..4c95f06a2 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -148,18 +148,22 @@ fn run() -> Result<ExitCode> {
     let case_sensitive = !matches.is_present("ignore-case")
         && (matches.is_present("case-sensitive") || pattern_has_uppercase_char(&pattern_regex));
 
+    #[cfg(windows)]
+    let ansi_colors_support =
+        ansi_term::enable_ansi_support().is_ok() || std::env::var_os("TERM").is_some();
+
+    #[cfg(not(windows))]
+    let ansi_colors_support = true;
+
     let interactive_terminal = atty::is(Stream::Stdout);
     let colored_output = match matches.value_of("color") {
         Some("always") => true,
         Some("never") => false,
-        _ => env::var_os("NO_COLOR").is_none() && interactive_terminal,
+        _ => ansi_colors_support && env::var_os("NO_COLOR").is_none() && interactive_terminal,
     };
 
     let path_separator = matches.value_of("path-separator").map(|str| str.to_owned());
 
-    #[cfg(windows)]
-    let colored_output = colored_output && ansi_term::enable_ansi_support().is_ok();
-
     let ls_colors = if colored_output {
         Some(LsColors::from_env().unwrap_or_default())
     } else {


Test patch: