rust-cli / anstyle

ANSI text styling
https://docs.rs/anstyle
Other
119 stars 19 forks source link

anstream println field witdth is not aware of color codes #167

Open VorpalBlade opened 9 months ago

VorpalBlade commented 9 months ago

I would expect the following (rather silly minimal case) to align columns properly:

use anstream::println;
use anstyle::AnsiColor;
use anstyle::Reset;

enum MyLogLevel {
    Ok,
    Warn,
    Error,
}

impl std::fmt::Display for MyLogLevel {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            MyLogLevel::Ok => write!(f, "{}Ok{}", AnsiColor::Green.render_fg(), Reset.render()),
            MyLogLevel::Warn => write!(
                f,
                "{}Warning{}",
                AnsiColor::Yellow.render_fg(),
                Reset.render()
            ),
            MyLogLevel::Error => write!(f, "{}Error{}", AnsiColor::Red.render_fg(), Reset.render()),
        }
    }
}

fn main() {
    println!("{:<10} Everything is fine", "Ok");
    println!("{:<10} Be careful", "Warning");
    println!("{:<10} BAD BAD BAD", "Error");

    println!("{:<10} Everything is fine", MyLogLevel::Ok);
    println!("{:<10} Be careful", MyLogLevel::Warn);
    println!("{:<10} BAD BAD BAD", MyLogLevel::Error);
}

It prints:

Ok         Everything is fine
Warning    Be careful
Error      BAD BAD BAD
Ok Everything is fine
Warning Be careful
Error BAD BAD BAD

This is unexpected. So it seems the anstream println is not properly aware of how wide colour codes are.

epage commented 9 months ago

I ran into something sort-of similar in env_logger. The way I worked around it is controlling what i used write! on vs what I called .fmt(f) on, see https://github.com/rust-cli/env_logger/commit/303a9c0ed288c5c7c15b6a74e29bfc1467bf9eab

From that experience, I think the problem is that write! doesn't handle any of the format specifiers itself and does not forward them on. I reproduced this with std at https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=0409fd1ae8ddd4d6b6e98ec9903dce7e

So to get his to work, you'd need to do something similar to what I did in env_logger, use write! for the styling and .fmt(f) on the text.

VorpalBlade commented 9 months ago

@epage Thank you, this does indeed work! Perhaps it should be documented in anstyle somewhere? It seems like a bit of a gotcha.

epage commented 8 months ago

I'm not sure how to fit this in. This is a problem specifically when you are creating a wrapper type for Display and is a problem independent of whether anstyle is in use or not.

VorpalBlade commented 8 months ago

That is a fair point. I would assume it is somewhat common to abstract away and use traits though. The code ends up a lot nicer that way.