anstyle icon indicating copy to clipboard operation
anstyle copied to clipboard

ANSI color is not retained across multiple `print(ln)!`'s in Windows legacy console

Open nooriro opened this issue 1 year ago • 1 comments

Cargo.toml

[package]
name = "anstream-test"
version = "0.1.0"
edition = "2021"

[dependencies]
anstream = "0.6.18"

src/main.rs

use anstream::{print, println};

fn main() {
    print!("\x1b[90m(print!) This text is displayed in gray.\n");
    print!("(print!) This text should also be displayed in gray.\n");
    println!("\x1b[90m(println!) This text is displayed in gray.");
    println!("(println!) This text should also be displayed in gray.\x1b[0m");
}

Steps to reproduce

Run cargo run in CMD/PowerShell with 'Legacy Console mode' enabled.

anstream-win-legacyconsole-01-241121

Expected result

All the lines should be displayed in gray, just like in the non-legacy Windows Console.

anstream-win-legacyconsole-02-241121 anstream-win-legacyconsole-03-241121

Actual result

Only the lines that output \x1b[90m are displayed in gray (1st and 3rd lines).

anstream-win-legacyconsole-04-241121

Environment

  • Windows 10 22H2 Home x64 10.0.19045.5011
  • Visual Studio Community 2022 17.11.5 / Windows 11 SDK 10.0.26100.0
  • Default host: x86_64-pc-windows-msvc
  • Active toolchain: stable-x86_64-pc-windows-msvc (default) / rustc 1.82.0 (f6e511eec 2024-10-15)

nooriro avatar Nov 22 '24 01:11 nooriro

Each println! call will call stdout https://github.com/rust-cli/anstyle/blob/fabe0c31e56a8f4cec5bda325dfafbe773ef6621/crates/anstream/src/_macros.rs#L130-L151

Each stdout call creates a new AutoStream https://github.com/rust-cli/anstyle/blob/fabe0c31e56a8f4cec5bda325dfafbe773ef6621/crates/anstream/src/lib.rs#L70-L73

AutoStream is stateless, forwarding on calls https://github.com/rust-cli/anstyle/blob/fabe0c31e56a8f4cec5bda325dfafbe773ef6621/crates/anstream/src/auto.rs#L256-L262

WinconStream is stateful but only within an instance and not across instances https://github.com/rust-cli/anstyle/blob/fabe0c31e56a8f4cec5bda325dfafbe773ef6621/crates/anstream/src/wincon.rs#L115-L131

The next layer down of WinconStream only deals with global for tracking the original colors of the terminal https://github.com/rust-cli/anstyle/blob/fabe0c31e56a8f4cec5bda325dfafbe773ef6621/crates/anstyle-wincon/src/stream.rs#L140-L150

During one of the redesigns, I had explored keeping global state to "remember" where we left off between instances of WinconStream I think the big problem I ran into was that the stream would be unlocked and I couldn't guarantee what happened to the output between instances. This was assuming someone was explicitly acquiring and releasing the AutoStream for complete messages. The interaction of this with println, especially with a user intentionally bleeding output across calls, was overlooked in this case.

The easy workaround is to call stdout() and write to it, especially locking it. Writing to locked output is already a best practice because of how slow individual print calls can be from acquiring and releasing the lock (Rust-wide and not just our wrappers).

As for fixing this, we'll need to think on this more of what we want to optimize for and what is the right answer.

epage avatar Nov 22 '24 15:11 epage