mgeisler / textwrap

An efficient and powerful Rust library for word wrapping text.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Properly indenting strikethrough text

noib3 opened this issue · comments

Is there a way to integrate nicely w/ the colored crate when it comes to indenting strikethrough text without affecting the leading whitespace?

Here's an example of what I mean

use colored::Colorize;
use textwrap::indent;

fn main() {
    let msg = indent(&("Hello,\nWorld!".strikethrough().to_string()), "  ");
    println!("{}", msg);
}

which produces
2022-01-14_23-04

Hi @noib3,

That is an interesting question 😄

There is no easy or built-in solution for this, I'm afraid. However, if you have access to the original ColoredString, then you could go all in and reconstruct the necessary ANSI codes for stopping and starting the colors and effects... something like this:

fn stop_start_style(text: &str, color: &ColoredString) -> String {
    // Start and stop code for each style. The colored crate doesn't
    // seem to have functionality to disable
    let all_styles = [
        (Styles::Bold, "1", "21"),
        (Styles::Dimmed, "2", "22"),
        (Styles::Underline, "3", "23"),
        (Styles::Reversed, "4", "24"),
        (Styles::Italic, "5", "25"),
        (Styles::Blink, "7", "27"),
        (Styles::Hidden, "8", "28"),
        (Styles::Strikethrough, "9", "29"),
    ];

    // Stop colors with the default fg and bg colors.
    let mut stop = String::from("39;49");
    // Restart fg and bg colors.
    let mut start = format!(
        "{};{}",
        color.fgcolor().map_or("39".into(), |c| c.to_fg_str()),
        color.bgcolor().map_or("49".into(), |c| c.to_bg_str())
    );
    // Stop and start styles.
    for (s, start_num, stop_num) in all_styles {
        if color.style().contains(s) {
            stop.push(';');
            stop.push_str(stop_num);
            start.push(';');
            start.push_str(start_num);
        }
    }

    format!("\x1B[{}m{}\x1B[{}m", stop, text, start)
}

fn main() {
    let text = "Hello\nWorld!".strikethrough().red();
    let msg = indent(
        &text.to_string(),
        &format!("{}", stop_start_style("////", &text)),
    );
    println!("{msg}");
}

That's probably a bit overkill, but the above seems to work in a few tests.