diff --git a/examples/editor-test/src/main.rs b/examples/editor-test/src/main.rs index ea859d5..8f3effe 100644 --- a/examples/editor-test/src/main.rs +++ b/examples/editor-test/src/main.rs @@ -1,7 +1,8 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use cosmic_text::{ - Action, BorrowedWithFontSystem, Buffer, Color, Edit, Editor, FontSystem, Metrics, SwashCache, + Action, BidiParagraphs, BorrowedWithFontSystem, Buffer, Color, Edit, Editor, FontSystem, + Metrics, SwashCache, }; use orbclient::{EventOption, Renderer, Window, WindowFlag}; use std::{env, fs, process, time::Instant}; @@ -83,8 +84,7 @@ fn main() { let test_start = Instant::now(); - //TODO: support bidi - for line in text.lines() { + for line in BidiParagraphs::new(&text) { log::debug!("Line {:?}", line); for grapheme in line.graphemes(true) { diff --git a/src/bidi_para.rs b/src/bidi_para.rs new file mode 100644 index 0000000..fd50d0d --- /dev/null +++ b/src/bidi_para.rs @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use unicode_bidi::{bidi_class, BidiClass, BidiInfo, ParagraphInfo}; + +/// An iterator over the paragraphs in the input text. +/// It is equivalent to [`core::str::Lines`] but follows `unicode-bidi` behaviour. +pub struct BidiParagraphs<'text> { + text: &'text str, + info: alloc::vec::IntoIter, +} + +impl<'text> BidiParagraphs<'text> { + /// Create an iterator to split the input text into paragraphs + /// in accordance with `unicode-bidi` behaviour. + pub fn new(text: &'text str) -> Self { + let info = BidiInfo::new(text, None); + let info = info.paragraphs.into_iter(); + Self { text, info } + } +} + +impl<'text> Iterator for BidiParagraphs<'text> { + type Item = &'text str; + + fn next(&mut self) -> Option { + let para = self.info.next()?; + let paragraph = &self.text[para.range]; + // `para.range` includes the newline that splits the line, so remove it if present + let mut char_indices = paragraph.char_indices(); + if let Some(i) = char_indices.next_back().and_then(|(i, c)| { + // `BidiClass::B` is a Paragraph_Separator (various newline characters) + (bidi_class(c) == BidiClass::B).then_some(i) + }) { + Some(¶graph[0..i]) + } else { + Some(paragraph) + } + } +} diff --git a/src/buffer.rs b/src/buffer.rs index cdf6a11..95ec28d 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -9,8 +9,8 @@ use core::{cmp, fmt}; use unicode_segmentation::UnicodeSegmentation; use crate::{ - Attrs, AttrsList, BorrowedWithFontSystem, BufferLine, Color, FontSystem, LayoutGlyph, - LayoutLine, ShapeLine, Shaping, Wrap, + Attrs, AttrsList, BidiParagraphs, BorrowedWithFontSystem, BufferLine, Color, FontSystem, + LayoutGlyph, LayoutLine, ShapeLine, Shaping, Wrap, }; /// Current cursor location @@ -594,7 +594,7 @@ impl Buffer { shaping: Shaping, ) { self.lines.clear(); - for line in text.lines() { + for line in BidiParagraphs::new(text) { self.lines.push(BufferLine::new( line.to_string(), AttrsList::new(attrs), diff --git a/src/lib.rs b/src/lib.rs index f4b700e..a07eecf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -95,6 +95,9 @@ extern crate alloc; pub use self::attrs::*; mod attrs; +pub use self::bidi_para::*; +mod bidi_para; + pub use self::buffer::*; mod buffer;