From a105b03fec1bf499fcf608b0375a4d17bcd27a2e Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 1 Jul 2021 12:04:22 +0200 Subject: [PATCH] Add styled example --- Cargo.toml | 2 + examples/style.rs | 134 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 examples/style.rs diff --git a/Cargo.toml b/Cargo.toml index 3be33f48..b2c20ce2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,12 +32,14 @@ default = ["unicode-linebreak", "unicode-width", "smawk"] hyphenation = { version = "0.8.2", optional = true, features = ["embed_en-us"] } smawk = { version = "0.3", optional = true } terminal_size = { version = "0.1", optional = true } +text-style = { version = "0.3", features = ["termion"] } unicode-linebreak = { version = "0.1", optional = true } unicode-width = { version= "0.1", optional = true } [dev-dependencies] criterion = "0.3" lipsum = "0.8" +rand = "0.8" unic-emoji-char = "0.9.0" version-sync = "0.9" diff --git a/examples/style.rs b/examples/style.rs new file mode 100644 index 00000000..4e46ee82 --- /dev/null +++ b/examples/style.rs @@ -0,0 +1,134 @@ +use hyphenation::{Language, Load, Standard}; +use rand::Rng as _; +use textwrap::word_separators::WordSeparator as _; + +#[derive(Debug)] +struct StyledWord<'a> { + word: &'a str, + whitespace: &'a str, + hyphen: bool, + style: Option, +} + +impl StyledWord<'_> { + fn render(&self, is_end: bool) { + use text_style::termion::Termion as _; + + print!( + "{}", + text_style::StyledStr::new(self.word, self.style).termion() + ); + + if is_end { + if self.hyphen { + print!("{}", text_style::StyledStr::new("-", self.style).termion()); + } + } else { + print!("{}", self.whitespace); + } + } +} + +impl AsRef for StyledWord<'_> { + fn as_ref(&self) -> &str { + &self.word + } +} + +impl<'a> From> for StyledWord<'a> { + fn from(word: text_style::StyledStr<'a>) -> Self { + let trimmed = word.s.trim_end_matches(' '); + Self { + word: trimmed, + whitespace: &word.s[trimmed.len()..], + hyphen: false, + style: word.style, + } + } +} + +impl textwrap::core::Fragment for StyledWord<'_> { + fn width(&self) -> usize { + self.word.len() + } + + fn whitespace_width(&self) -> usize { + self.whitespace.len() + } + + fn penalty_width(&self) -> usize { + if self.hyphen { + 1 + } else { + 0 + } + } +} + +impl textwrap::word_splitters::Splittable for StyledWord<'_> { + type Output = Self; + + fn split(&self, range: std::ops::Range, keep_ending: bool) -> Self::Output { + let word = &self.word[range]; + Self { + word, + whitespace: if keep_ending { self.whitespace } else { "" }, + hyphen: if keep_ending { + self.hyphen + } else { + !word.ends_with('-') + }, + style: self.style, + } + } +} + +fn generate_style(rng: &mut impl rand::Rng) -> text_style::Style { + let mut style = text_style::Style::default(); + + style.set_bold(rng.gen_bool(0.1)); + style.set_italic(rng.gen_bool(0.1)); + style.set_underline(rng.gen_bool(0.1)); + style.strikethrough(rng.gen_bool(0.01)); + + style.fg = match rng.gen_range(0..100) { + 0..=10 => Some(text_style::AnsiColor::Red), + 11..=20 => Some(text_style::AnsiColor::Green), + 21..=30 => Some(text_style::AnsiColor::Blue), + _ => None, + } + .map(|color| text_style::Color::Ansi { + color, + mode: text_style::AnsiMode::Light, + }); + + style +} + +fn main() { + let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap(); + let mut rng = rand::thread_rng(); + + let text = lipsum::lipsum(rng.gen_range(100..500)); + + let styled = text + .split_inclusive(' ') + .map(|s| text_style::StyledStr::styled(s, generate_style(&mut rng))); + let words: Vec<_> = styled + .flat_map(|s| { + textwrap::word_separators::AsciiSpace + .find_word_ranges(&s.s) + .map(move |range| text_style::StyledStr::new(&s.s[range], s.style)) + }) + .map(StyledWord::from) + .flat_map(|w| textwrap::word_splitters::Fragments::new(w, &dictionary)) + .collect(); + + let lines = textwrap::wrap_algorithms::wrap_first_fit(&words, &[50]); + for line in lines { + for (idx, fragment) in line.into_iter().enumerate() { + fragment.render(idx + 1 == line.len()); + } + println!(); + } +}