Skip to content

Commit

Permalink
Add styled example
Browse files Browse the repository at this point in the history
  • Loading branch information
robinkrahl committed Jul 1, 2021
1 parent 14fa737 commit a105b03
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 0 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
134 changes: 134 additions & 0 deletions examples/style.rs
Original file line number Diff line number Diff line change
@@ -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<text_style::Style>,
}

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<str> for StyledWord<'_> {
fn as_ref(&self) -> &str {
&self.word
}
}

impl<'a> From<text_style::StyledStr<'a>> 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<usize>, 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!();
}
}

0 comments on commit a105b03

Please sign in to comment.