1use alloc::vec::Vec;
4use unicode_bidi::{bidi_class, BidiClass, BidiInfo, ParagraphInfo};
5
6#[derive(Debug)]
9pub struct BidiParagraphs<'text> {
10 text: &'text str,
11 info: alloc::vec::IntoIter<ParagraphInfo>,
12}
13
14impl<'text> BidiParagraphs<'text> {
15 pub fn new(text: &'text str) -> Self {
18 if text.is_ascii()
20 && !text
21 .chars()
22 .any(|c| c.is_ascii_control() && c != '\n' && c != '\r' && c != '\t')
23 {
24 let mut paragraphs = Vec::new();
27 let mut start = 0;
28
29 for (i, c) in text.char_indices() {
30 if c == '\n' {
31 paragraphs.push(ParagraphInfo {
32 range: start..i,
33 level: unicode_bidi::Level::ltr(),
34 });
35 start = i + 1;
36 }
37 }
38
39 if start < text.len() {
41 paragraphs.push(ParagraphInfo {
42 range: start..text.len(),
43 level: unicode_bidi::Level::ltr(),
44 });
45 }
46
47 let info = paragraphs.into_iter();
48 Self { text, info }
49 } else {
50 let info = BidiInfo::new(text, None);
52 let info = info.paragraphs.into_iter();
53 Self { text, info }
54 }
55 }
56}
57
58impl<'text> Iterator for BidiParagraphs<'text> {
59 type Item = &'text str;
60
61 fn next(&mut self) -> Option<Self::Item> {
62 let para = self.info.next()?;
63 let paragraph = &self.text[para.range];
64 let mut char_indices = paragraph.char_indices();
66 char_indices
67 .next_back()
68 .and_then(|(i, c)| {
69 (bidi_class(c) == BidiClass::B).then_some(i)
71 })
72 .map_or(Some(paragraph), |i| Some(¶graph[0..i]))
73 }
74}