zhiburt / ansi-str

This is a library for work with colored and formatted strings on ANSI terminals.
MIT License
2 stars 0 forks source link

optimize ansi escape sequences #7

Open fdncred opened 6 months ago

fdncred commented 6 months ago

I've been looking around for something to optimize ansi escape sequences and I thought of your ansi crates and tabled.

I can't remember if we've talked about this before but It would be nice to have something that receives a string with ansi escapes and optimizes that string to use as few ansi escape characters as possible while maintaining the same output.

Here's a simple example in nushell.

This will print foo in green bold text.

"\e[32m\e[1mfoo\e[0m"

However, it's not optimized. One of these below would require less characters.

"\e[1;32mfoo\e[0m"
"\e[32;1mfoo\e[0m"

It's not that big of a deal when it's just green bold but looking at my prompt string, you can see that it's pretty optimized but it's big. More could be done to optimize this.

\e[38;5;10m\e]133;A\e\\e[48;2;206;215;207;38;2;8;8;8m  \e[48;2;52;101;164;38;2;206;215;207m  \e[48;2;52;101;164;38;2;188;188;188m~\e[0m\e[48;2;52;101;164;38;2;188;188;188m/\e[48;2;52;101;164;38;2;188;188;188ms\e[0m\e[48;2;52;101;164;38;2;188;188;188m/\e[1;48;2;52;101;164;38;2;228;228;228mnushell\e[0m\e[48;2;52;101;164;38;2;206;215;207m \e[48;2;78;154;6;38;2;52;101;164m \e[48;2;78;154;6;38;2;12;12;12m main \e[48;2;196;160;0;38;2;78;154;6m \e[0m\e[48;2;196;160;0;38;2;0;0;0m≢1\e[0m\e[1;48;2;196;160;0;38;2;0;0;0m*\e[0m\e[48;2;12;12;12;38;2;196;160;0m\e[0m\e]133;B\e\\e[38;5;14m\e[1;32m
❯ \e[0m\e[38;5;5m\e7\e[29;100H\e[38;2;96;96;96m\e[49m 0823 \e[0m\e[48;2;12;12;12;38;2;211;215;207m\e[48;2;211;215;207;38;2;12;12;12m 07:34:48 AM \e[0m\e8\e[0m\e[0m\e7\e8\e[6 q\e[?25h

This is the image of the prompt. image

If I counted correctly, there are 734 characters counting \e as 1 since it represents the escape character. There are 11 ansi resets \e[0m which I think could probably be reduced to 3 or 4. So, it could take this string to somewhere around 700 chars. Every little bit helps but if you're constructing this prompt by hand, you could easily have 2 or 3 times more characters due to lack of optimization.

So, I was wondering if in your ansi work, you've implemented or found such a crate to optimize ansi escape sequences?

zhiburt commented 6 months ago

Hi @fdncred

I think it's a good proposition.

If my memory serves me well; it was discussed a long time ago, in regard of nushell. At least I was thinking of adding this method into ansi-str, but to be honest I don't remember the outcome :smile:

I've just checked that it could be achieved via existing structure.

pub fn main() {
    cmp_text("\u{1b}[1m\u{1b}[31;46m\u{1b}[1m\u{1b}[31;46m\u{1b}[1m\u{1b}[31;46m\u{1b}[1m\u{1b}[31;46mWhen the night has come\u{1b}[0m\u{1b}[0m\u{1b}[0m\u{1b}[0m\u{1b}[0m\u{1b}[0m\u{1b}[0m\u{1b}[0m");
}

fn cmp_text(text: &str) {
    use std::fmt::Write;

    let text1 = ansi_str::get_blocks(text).fold(String::new(), |mut acc, b| {
        let _ = write!(
            &mut acc,
            "{}{}{}",
            b.style().start(),
            b.text(),
            b.style().end()
        );
        acc
    });

    println!("ORIGIN => {text}");
    println!("ORIGIN => {text:?}");
    println!("CONVERTED => {text1}");
    println!("CONVERTED => {text1:?}");
    println!("RESULT => equal={} origin-len={} converted-len={}", text == text1, text.len(), text1.len());
}

Which results in

origin => "\u{1b}[1m\u{1b}[31;46m\u{1b}[1m\u{1b}[31;46m\u{1b}[1m\u{1b}[31;46m\u{1b}[1m\u{1b}[31;46mWhen the night has come\u{1b}[0m\u{1b}[0m\u{1b}[0m\u{1b}[0m\u{1b}[0m\u{1b}[0m\u{1b}[0m\u{1b}[0m"
result => "\u{1b}[1m\u{1b}[31m\u{1b}[46mWhen the night has come\u{1b}[22m\u{1b}[39m\u{1b}[49m"
origin-len=103 result-len=52

TLDR;

Seems like it already possible, the only thing is that probably we shall add a flag to squash continues ANSI sequences together (for prefix and suffix). So we would get smth. like for my example, with a length of 44 instead of 52.

"\u{1b}[1;31;46mWhen the night has come\u{1b}[22;39;49m"

Note: For your data; the code above produces even more lengthy string exactly cause of extensive \e production.

PS: Yesss I haven't forgot about emojie issue; sorry took longer than shall to

zhiburt commented 6 months ago

So yes I'll take a look at it

fdncred commented 6 months ago

Thanks @zhiburt. This is about nushell too. I've thought about adding such a functionality to nushell before it prints things out or perhaps a plugin. Or maybe a separate command like ansi optimize too? So, I have some ideas of where to use it.