use crate::grapheme::{analyze_grapheme, is_joining_type};
use icu_properties::props::JoiningType;
use icu_segmenter::GraphemeClusterSegmenter;
#[derive(Clone, Copy, Debug, Default)]
pub struct JoinedRunSegmenter;
impl JoinedRunSegmenter {
pub const fn new() -> Self {
Self
}
pub fn segment_str(self, input: &str) -> impl Iterator<Item = usize> {
let grapheme_boundaries: Vec<usize> =
GraphemeClusterSegmenter::new().segment_str(input).collect();
let mut boundaries = vec![0];
let mut previous_type: Option<JoiningType> = None;
for pair in grapheme_boundaries.windows(2) {
let grapheme = analyze_grapheme(&input[pair[0]..pair[1]]);
if grapheme.joining_type == JoiningType::Transparent {
continue;
}
let can_join = is_joining_type(grapheme.joining_type);
let continues_run = can_join
&& previous_type.is_some_and(|previous| {
previous != JoiningType::RightJoining
&& grapheme.joining_type != JoiningType::LeftJoining
});
if !continues_run && pair[0] > 0 {
boundaries.push(pair[0]);
}
previous_type = can_join.then_some(grapheme.joining_type);
}
if !input.is_empty() {
boundaries.push(input.len());
}
boundaries.into_iter()
}
pub fn segment_utf16(self, input: &[u16]) -> impl Iterator<Item = usize> {
let text: String = char::decode_utf16(input.iter().copied())
.map(|c| c.unwrap_or(char::REPLACEMENT_CHARACTER))
.collect();
let mut chars = text.chars();
let (mut byte_offset, mut code_unit_offset) = (0, 0);
let boundaries: Vec<usize> = self
.segment_str(&text)
.map(|boundary| {
while byte_offset < boundary {
let c = chars.next().expect("boundaries fall between characters");
byte_offset += c.len_utf8();
code_unit_offset += c.len_utf16();
}
code_unit_offset
})
.collect();
boundaries.into_iter()
}
}