layout/flow/inline/
line_breaker.rs1use std::ops::Range;
6
7use icu_segmenter::LineSegmenter;
8use icu_segmenter::options::LineBreakOptions;
9use servo_base::text::Utf8CodeUnits;
10
11pub(crate) struct LineBreaker {
12 linebreaks: Vec<Utf8CodeUnits>,
13 current_linebreak_offset: usize,
14 additional_break_at_start: Option<Utf8CodeUnits>,
16}
17
18impl LineBreaker {
19 pub(crate) fn new(string: &str, options: LineBreakOptions<'_>) -> Self {
20 let line_segmenter = LineSegmenter::new_auto(options);
21 Self {
22 linebreaks: line_segmenter
29 .segment_str(string)
30 .skip(1)
31 .map(|offset| Utf8CodeUnits(offset as u32))
32 .collect(),
33 current_linebreak_offset: 0,
34 additional_break_at_start: None,
35 }
36 }
37
38 pub(crate) fn advance_to_linebreaks_in_range(
39 &mut self,
40 text_range: Range<Utf8CodeUnits>,
41 ) -> &[Utf8CodeUnits] {
42 let linebreaks_in_range = self.linebreaks_in_range_after_current_offset(text_range);
43 self.current_linebreak_offset = linebreaks_in_range.end;
44 &self.linebreaks[linebreaks_in_range]
45 }
46
47 fn linebreaks_in_range_after_current_offset(
48 &self,
49 text_range: Range<Utf8CodeUnits>,
50 ) -> Range<usize> {
51 assert!(text_range.start <= text_range.end);
52
53 let mut linebreaks_range = self.current_linebreak_offset..self.linebreaks.len();
54
55 while self.linebreaks[linebreaks_range.start] < text_range.start &&
56 linebreaks_range.len() > 1
57 {
58 linebreaks_range.start += 1;
59 }
60
61 let mut ending_linebreak_index = linebreaks_range.start;
62 while self.linebreaks[ending_linebreak_index] < text_range.end &&
63 ending_linebreak_index < self.linebreaks.len() - 1
64 {
65 ending_linebreak_index += 1;
66 }
67 linebreaks_range.end = ending_linebreak_index;
68 linebreaks_range
69 }
70
71 pub(crate) fn set_additional_break_at_start(&mut self, index: Utf8CodeUnits) {
72 debug_assert!(self.additional_break_at_start.is_none());
73 self.additional_break_at_start = Some(index);
74 }
75
76 pub(crate) fn take_additional_break_at_start(&mut self) -> Option<Utf8CodeUnits> {
77 self.additional_break_at_start.take()
78 }
79}
80
81#[cfg(test)]
82mod test {
83 use super::*;
84
85 fn linebreaks_in_range_after_current_offset(
86 linebreaker: &LineBreaker,
87 range: Range<u32>,
88 ) -> Range<usize> {
89 linebreaker.linebreaks_in_range_after_current_offset(
90 Utf8CodeUnits(range.start)..Utf8CodeUnits(range.end),
91 )
92 }
93
94 #[test]
95 fn test_linebreaker_ranges() {
96 let linebreaker = LineBreaker::new("abc def", LineBreakOptions::default());
97 assert_eq!(linebreaker.linebreaks, [Utf8CodeUnits(4), Utf8CodeUnits(7)]);
98 assert_eq!(
99 linebreaks_in_range_after_current_offset(&linebreaker, 0..5),
100 0..1
101 );
102 assert_eq!(
104 linebreaks_in_range_after_current_offset(&linebreaker, 0..7),
105 0..1
106 );
107
108 let linebreaker = LineBreaker::new("abc d def", LineBreakOptions::default());
109 assert_eq!(
110 linebreaker.linebreaks,
111 [Utf8CodeUnits(4), Utf8CodeUnits(6), Utf8CodeUnits(9)]
112 );
113 assert_eq!(
114 linebreaks_in_range_after_current_offset(&linebreaker, 0..5),
115 0..1
116 );
117 assert_eq!(
118 linebreaks_in_range_after_current_offset(&linebreaker, 0..7),
119 0..2
120 );
121 assert_eq!(
122 linebreaks_in_range_after_current_offset(&linebreaker, 0..9),
123 0..2
124 );
125
126 assert_eq!(
127 linebreaks_in_range_after_current_offset(&linebreaker, 4..9),
128 0..2
129 );
130
131 std::panic::catch_unwind(|| {
132 let linebreaker = LineBreaker::new("abc def", LineBreakOptions::default());
133 linebreaks_in_range_after_current_offset(&linebreaker, 5..2);
134 })
135 .expect_err("Reversed range should cause an assertion failure.");
136 }
137
138 fn advance_to_linebreaks_in_range(
139 linebreaker: &mut LineBreaker,
140 range: Range<u32>,
141 ) -> &[Utf8CodeUnits] {
142 linebreaker
143 .advance_to_linebreaks_in_range(Utf8CodeUnits(range.start)..Utf8CodeUnits(range.end))
144 }
145
146 #[test]
147 fn test_linebreaker_stateful_advance() {
148 let mut linebreaker = LineBreaker::new("abc d def", LineBreakOptions::default());
149 assert_eq!(
150 linebreaker.linebreaks,
151 [Utf8CodeUnits(4), Utf8CodeUnits(6), Utf8CodeUnits(9)]
152 );
153 assert!(
154 advance_to_linebreaks_in_range(&mut linebreaker, 0..7) ==
155 &[Utf8CodeUnits(4), Utf8CodeUnits(6)]
156 );
157 assert!(advance_to_linebreaks_in_range(&mut linebreaker, 8..9).is_empty());
158
159 assert!(advance_to_linebreaks_in_range(&mut linebreaker, 0..9).is_empty());
161
162 linebreaker.current_linebreak_offset = 0;
163
164 assert!(
166 advance_to_linebreaks_in_range(&mut linebreaker, 0..999) ==
167 &[Utf8CodeUnits(4), Utf8CodeUnits(6)]
168 );
169
170 linebreaker.current_linebreak_offset = 0;
171
172 std::panic::catch_unwind(|| {
173 let mut linebreaker = LineBreaker::new("abc d def", LineBreakOptions::default());
174 advance_to_linebreaks_in_range(&mut linebreaker, 2..0);
175 })
176 .expect_err("Reversed range should cause an assertion failure.");
177 }
178}