Properly indenting strikethrough text
noib3 opened this issue · comments
Riccardo Mazzarini commented
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);
}
Martin Geisler commented
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.