layout/flow/inline/
line_breaker.rs1use std::ops::Range;
6
7use icu_segmenter::LineSegmenter;
8use icu_segmenter::options::LineBreakOptions;
9
10pub(crate) struct LineBreaker {
11 linebreaks: Vec<usize>,
12 current_offset: usize,
13}
14
15impl LineBreaker {
16 pub(crate) fn new(string: &str, options: LineBreakOptions<'_>) -> Self {
17 let line_segmenter = LineSegmenter::new_auto(options);
18 Self {
19 linebreaks: line_segmenter.segment_str(string).skip(1).collect(),
26 current_offset: 0,
27 }
28 }
29
30 pub(crate) fn advance_to_linebreaks_in_range(&mut self, text_range: Range<usize>) -> &[usize] {
31 let linebreaks_in_range = self.linebreaks_in_range_after_current_offset(text_range);
32 self.current_offset = linebreaks_in_range.end;
33 &self.linebreaks[linebreaks_in_range]
34 }
35
36 fn linebreaks_in_range_after_current_offset(&self, text_range: Range<usize>) -> Range<usize> {
37 assert!(text_range.start <= text_range.end);
38
39 let mut linebreaks_range = self.current_offset..self.linebreaks.len();
40
41 while self.linebreaks[linebreaks_range.start] < text_range.start &&
42 linebreaks_range.len() > 1
43 {
44 linebreaks_range.start += 1;
45 }
46
47 let mut ending_linebreak_index = linebreaks_range.start;
48 while self.linebreaks[ending_linebreak_index] < text_range.end &&
49 ending_linebreak_index < self.linebreaks.len() - 1
50 {
51 ending_linebreak_index += 1;
52 }
53 linebreaks_range.end = ending_linebreak_index;
54 linebreaks_range
55 }
56}
57
58#[cfg(test)]
59mod test {
60 use super::*;
61
62 #[test]
63 fn test_linebreaker_ranges() {
64 let linebreaker = LineBreaker::new("abc def", LineBreakOptions::default());
65 assert_eq!(linebreaker.linebreaks, [4, 7]);
66 assert_eq!(
67 linebreaker.linebreaks_in_range_after_current_offset(0..5),
68 0..1
69 );
70 assert_eq!(
72 linebreaker.linebreaks_in_range_after_current_offset(0..7),
73 0..1
74 );
75
76 let linebreaker = LineBreaker::new("abc d def", LineBreakOptions::default());
77 assert_eq!(linebreaker.linebreaks, [4, 6, 9]);
78 assert_eq!(
79 linebreaker.linebreaks_in_range_after_current_offset(0..5),
80 0..1
81 );
82 assert_eq!(
83 linebreaker.linebreaks_in_range_after_current_offset(0..7),
84 0..2
85 );
86 assert_eq!(
87 linebreaker.linebreaks_in_range_after_current_offset(0..9),
88 0..2
89 );
90
91 assert_eq!(
92 linebreaker.linebreaks_in_range_after_current_offset(4..9),
93 0..2
94 );
95
96 std::panic::catch_unwind(|| {
97 let linebreaker = LineBreaker::new("abc def", LineBreakOptions::default());
98 linebreaker.linebreaks_in_range_after_current_offset(5..2);
99 })
100 .expect_err("Reversed range should cause an assertion failure.");
101 }
102
103 #[test]
104 fn test_linebreaker_stateful_advance() {
105 let mut linebreaker = LineBreaker::new("abc d def", LineBreakOptions::default());
106 assert_eq!(linebreaker.linebreaks, [4, 6, 9]);
107 assert!(linebreaker.advance_to_linebreaks_in_range(0..7) == &[4, 6]);
108 assert!(linebreaker.advance_to_linebreaks_in_range(8..9).is_empty());
109
110 assert!(linebreaker.advance_to_linebreaks_in_range(0..9).is_empty());
112
113 linebreaker.current_offset = 0;
114
115 assert!(linebreaker.advance_to_linebreaks_in_range(0..999) == &[4, 6]);
117
118 linebreaker.current_offset = 0;
119
120 std::panic::catch_unwind(|| {
121 let mut linebreaker = LineBreaker::new("abc d def", LineBreakOptions::default());
122 linebreaker.advance_to_linebreaks_in_range(2..0);
123 })
124 .expect_err("Reversed range should cause an assertion failure.");
125 }
126}