Search code examples
rustterminalzsh

Coloured terminal output not working as it should using the termcolor crate


I have been playing around with the colorterm crate. I've written a simple program in VS code here:

use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor, BufferWriter};
use std::io::Write;

fn main() {
    let quote: &str = "A donkey, a donkey, my kingdom for a donkey!";

    let colors: Vec<(u8, u8, u8)> = vec![
        (50, 168, 82),
        (255, 129, 3),
        (32, 72, 145),
        (237, 226, 17), 
        (48, 176, 161), 
        (166, 28, 111),
        (230, 18, 36)
    ];

    let mut stdout = StandardStream::stdout(ColorChoice::Always);
    for color in colors {
        stdout.set_color(ColorSpec::new().set_fg(
            Some(Color::Rgb(
                color.0,
                color.1,
                color.2
            ))
        ))
            .expect("Failed to set color");
        write!(&mut stdout, "{}", quote).expect("failed to write");
        println!();
    }
}

Everything was going fine testing the output in the VS integrated terminal

VS code integrated terminal output

However, when I test it on my OS terminal (the Terminal.app that comes with MacOS) I get the following:

enter image description here

The results have the incorrect colours, are italicised in one instance or have no color (well grey I suppose) at all.

I'm thinking this is less a problem with the crate and more with my terminal config? I use Mac OSX with a zsh shell for my normal terminal, no fancy configs but I do use oh my zsh normally (I tried this on a shell without oh my zsh and got the same result). My first feeling was that it was something to do with the environment variable mentioned in the docs here however I've got TERM set to xterm-256color. I think the answer may lay in the configuration difference between the VS code integrated terminal (there must be something right?) and the vanilla zsh terminal. If anyone is more familiar with the differences between the two, or if you have encountered any similar problems with the crate, or I am missing something entirely here I would be grateful for some help.


Solution

  • Ok, just for completeness I managed to find the answer to this with some digging. The vanilla Terminal.app doesn't support truecolor; checking COLORTERM variables for each terminal (terminal.app, vs code intergrated, and iterm2) clarified this. For terminals that don't support truecolor we can convert our rbg values into a single u8 ansi256 value (for conversion I used this library) and use the appropriate variation of the Color enum to output our coloured content like so:

    ...
    
    println!("{}", val);
        let mut stdout = StandardStream::stdout(ColorChoice::Always);
        for color in colors {
            let ansi_val: u8 = rgb_to_ansi256(color.0, color.1, color.2);
            stdout.set_color(ColorSpec::new().set_fg(
                Some(Color::Ansi256(ansi_val))
            ))
                .expect("Failed to set color");
            write!(&mut stdout, "{}", quote).expect("failed to write");
            println!();
        }