Introducing Colored: the most simple way to add colors in your term!

I just published Colored 1.0, the most simple way to add colors in your term.

    extern crate colored;
    use colored::*;

    fn main() {
        println!("{} {} {}",
                    "this is blue".blue(),
                    "this is red on blue".red().on_blue()
                    "this is yellow and bold".yellow().bold()
         );
    }

Simple as that. Support for windows is planned, very soon, so brace yourself. :slight_smile:

2 Likes

Oh boy, yet another library for terminal colors that of course completely ignores Windows!

4 Likes

Windows support is definitely on the list. I considered the ansi support as being sufficient for releasing a 1.0 as the API won't change.

I need to dig MSDN a little, but it will come in the next weeks.

It'll certainly be tricky to support Windows. For one thing color and such is done via API calls, not by inserting special characters into the output. Since your API relies on fmt::Display to be usable with println!, this means you have no control over what is being written to and when. You have no idea if it is being formatted to a string or to the console, so you can't call the right API at the right time. The term crate is able to support Windows because you have to go through term to write to the console, so it knows what it is writing to and is thus able to call the right APIs at the right time to get the desired colors.

Yeah, I'm with @retep998 on this; your API design is fundamentally incompatible with how Windows does console colours (unless you're willing to do incredibly nasty things like unilaterally redirect all output on program startup). You can't just assume something will work cross platform until you actually try it... because whatever OS you haven't tested on will almost certainly chuck a spanner in the works.

That reminds me... I should dig up and finish that ANSI interpreter crate I very nearly finished that redirects all output on program startup...

I plan to support Windows by attaching a Colorize implementor to a term handle and using a slightly different workflow. It won't be as handy as a Display, but it will work.

ANSI terms are priorized, and the API will stay for them. People who want to use the cross-platform api will have a less handy api, and they won't be able to println!("{} {}" "b".blue(), "r".red()) but hey, that's life.

I think we can work out something not too bad and keeping the "string".style() idea.

Please don't forget a way to turn color off, preferably via some configuration file in the user's home directory.
Instead of setting colors directly it would also be better to provide a level of indirection, so that users can configure the colors according to their setup, in particular for a bright terminal background color.

Better term handling is sorely needed in the Rust tools. They all use a combination of the term crate and hand-rolled tty checks. Adding nice console styling has to be open coded with a bunch of tty checks plus activating and deactivating colors. All the open-coding makes it hard to be motivated to make the console output really special. I'd like some sort of templating language that allows embedding styles, that falls back to unstyled when there's no tty.

Another thing to consider for further work is that none of the existing solutions support mingw's mintty console.

Good luck!

Hi @starblue thanks for your comment.

Please don't forget a way to turn color off, preferably via some configuration file in the user's home directory.

Great idea. But I am not fan of the config file option. What about an env variable ? Could be RUST_NOCOLOR. By the way, any particular reason why you want this feature ?

Instead of setting colors directly it would also be better to provide a level of indirection, so that users can configure the colors according to their setup, in particular for a bright terminal background color.

All terminal colors can be configured. Either by .Xresources or by using the GUI configuration of the terminal.
As for Colored, I respect the default color setup when no styling is applied, and respect the choice of color when the library user ask for a color.

If an user use a particularly exotic color for his term, I am affraid it's the responsibility of the user to adjust its color palette to a correct setting.

It would be nice if there were some standard way that all tools obey, something like COLOR=never.

Obviously because I don't want colors on my console.

I consider colors in console output a bad idea, because the background color is set by the user. For example, on my light gray background bright green is pretty much unreadable.

It is a different story when a program like aptitude uses curses to control both foreground and background.

Hi @brson ! Thanks for your interest !

Better term handling is sorely needed in the Rust tools. They all use a combination of the term crate and hand-rolled tty checks

I think there is two separate issues, and it's important to not mix them together.

For one, there is the ability to style the output of a terminal program for a rust programmer. This should be the most simple and accessible and I propose here a straightforward API, some would even say naive. I think naivety and simplicity is best here as it's welcoming for programmers.

The second issue, is effectively displaying these colors in a way the terminal emulator understand. This is far more tricky to do great in a general manner. Here the term crate has a great place and it should be the favorite place to put tty checks and cross-platform handling as much homogeneous as possible.

I would be glad to contribute as much as I can to term and integrate it in colored, anyway.

All the open-coding makes it hard to be motivated to make the console output really special.

Could you develop your thought ? I think having a zero-effort API for coloring thing on linux whose future is undefined, and a clear path of upgrade when the need for cross-platform become apparent, it seems to be the best of both worlds as I see it.

I'd like some sort of templating language that allows embedding styles, that falls back to unstyled when there's no tty.

It seems... actually a good idea ! Does it already exists somewhere or did you just made up ?

I really suggest you to craft a color palette with all foreground colors at a color, and all the background colors at your light gray background. You will never have anymore issue with terminal coloring.

Here is a website which can help you with that: https://terminal.sexy/

1 Like

As I intimated above, I have a WIP ANSI interpreter for Windows that works by replacing the default console handles before std acquires them. This has two parts: an ANSI escape parser, and the actual Win32 interpreter.

One thing I was considering doing was expanding it so that the interpreter can be used elsewhere for other purposes, like stripping escape codes on the fly, or translating them to something else (like HTML). That might help with the "lots of manual TTY checks" problem, by letting you just spew a rainbow cornucopia to stdout, and let a higher level take care of making the output plain and boring again.

If there's interest, I can move it back on to the priority list. :slight_smile:

As far as I can tell there is no concept of foreground and background in the palette. The light colors are used for text in the console by some programs, and for background by aptitude. Making them darker and somewhat better to read on the console decreases readability in aptitude.

Black is still much better to read, so I prefer colors off (it also feels less noisy and cluttered to me). Since you shouldn't put essential information in color (there are color-blind people who can't see it) that shouldn't be a big loss, just a loss in emphasis on some text.

This is just some console code I have on hand:

    let mut t = term::stderr().unwrap();
    if tty::stderr_isatty() { let _ = t.fg(term::color::BRIGHT_GREEN); }
    let _ = write!(t, "info: ");
    if tty::stderr_isatty() { let _ = t.reset(); }
    let _ = t.write_fmt(args);
    let _ = write!(t, "\n");

Every time you want to style the output you have to check for ttyness, then issue color commands, then reset. This means that every time I want to display colors to the screen I need to restate this type of logic. I'd rather just say 'output this string, these substrings of which have these colors, but only if there's a tty'. I'd basically rather have a terminal-aware format! macro.

Why everytime ? How would it change during the life of the program ?

If supporting Windows precludes things like ls -l --color | less -R on other platforms, perhaps some tools will do well to ignore it.

By the way, any particular reason why you want this feature ?

Another reason: redirecting an ANSI formatted output to a file will include the ANSI control codes which is just garbage.

It won't. One doesn't need to literalyl query isatty every time you print; it could be stored to a bool for later, but a flag still has to be checked.

Yeah, I am totally sold on the idea. I will introduce the env control variables RUST_NOCOLOR and RUST_FORCECOLOR and talk the authors of ansi_term and term-painter to introduce similar behavior. Probably first talking, then implementing, though.

@brson: I was thinking so. Great.