1use std::{
16 iter::{Chain, Rev},
17 str::Chars,
18};
19
20use super::{
21 Point, SpawnId, Text, ToggleId,
22 tags::{self, RawTag},
23};
24use crate::text::{TwoPoints, tags::InnerTags};
25
26#[derive(Clone)]
31pub struct FwdIter<'t> {
32 text: &'t Text,
33 point: Point,
34 init_point: Point,
35 chars: FwdChars<'t>,
36 tags: tags::FwdTags<'t>,
37 conceals: u32,
38
39 main_iter: Option<MainIter<FwdChars<'t>, tags::FwdTags<'t>>>,
41 ghost: Option<(Point, usize)>,
42}
43
44impl<'t> FwdIter<'t> {
45 #[track_caller]
48 pub(super) fn new_at(text: &'t Text, points: TwoPoints, is_ghost: bool) -> Self {
49 let TwoPoints { real, ghost } = points;
50 let point = real.min(if is_ghost {
51 text.last_point()
52 } else {
53 text.end_point()
54 });
55
56 let ghost = if let Some(offset) = ghost {
63 let points = text.ghost_max_points_at(real.byte());
64 points.ghost.map(|max| (max.min(offset), 0))
65 } else {
66 let points = text.ghost_max_points_at(real.byte());
67 points.ghost.zip(Some(0))
68 };
69
70 Self {
71 text,
72 point,
73 init_point: point,
74 chars: buf_chars_fwd(text, point.byte(), is_ghost),
75 tags: text.tags_fwd(point.byte(), None),
76 conceals: 0,
77
78 main_iter: None,
79 ghost,
80 }
81 }
82
83 #[inline(always)]
89 pub fn is_on_ghost(&self) -> bool {
90 self.main_iter.is_some()
91 }
92
93 #[inline(always)]
96 pub fn points(&self) -> TwoPoints {
97 if let Some(MainIter { point, .. }) = self.main_iter.as_ref() {
98 TwoPoints::new(*point, self.ghost.map(|(tg, _)| tg).unwrap())
99 } else {
100 TwoPoints::new_after_ghost(self.point)
101 }
102 }
103
104 pub fn text(&self) -> &Text {
106 self.text
107 }
108
109 #[inline(always)]
113 fn handle_meta_tag(&mut self, tag: &RawTag, b: usize) -> bool {
114 match tag {
115 RawTag::Ghost(_, id) => {
116 if b < self.point.byte() || self.conceals > 0 {
117 return true;
118 }
119 let text = self.text.get_ghost(*id).unwrap();
120
121 let (this_ghost, total_ghost) = if let Some((ghost, dist)) = &mut self.ghost {
122 if ghost.byte() >= *dist + text.last_point().byte() {
123 *dist += text.last_point().byte();
124 return true;
125 }
126 (text.point_at_byte(ghost.byte() - *dist), *ghost)
127 } else {
128 (Point::default(), Point::default())
129 };
130
131 let iter = FwdIter::new_at(text, this_ghost.to_two_points_before(), true);
132 let point = std::mem::replace(&mut self.point, this_ghost);
133 let init_point = std::mem::replace(&mut self.init_point, this_ghost);
134 let chars = std::mem::replace(&mut self.chars, iter.chars);
135 let tags = std::mem::replace(&mut self.tags, iter.tags);
136
137 self.ghost = Some((total_ghost, total_ghost.byte()));
138 self.main_iter = Some(MainIter { point, init_point, chars, tags });
139 }
140 RawTag::StartConceal(_) => {
141 self.conceals += 1;
142 }
143 RawTag::EndConceal(_) => {
144 self.conceals = self.conceals.saturating_sub(1);
145 if self.conceals == 0 {
146 self.ghost.take_if(|_| self.point.byte() < b);
149 self.point = self.point.max(self.text.point_at_byte(b));
150 self.chars = buf_chars_fwd(self.text, self.point.byte(), false);
151 }
152 }
153 RawTag::ConcealUntil(b) => {
154 let point = self.text.point_at_byte(*b as usize);
155 *self = FwdIter::new_at(self.text, point.to_two_points_before(), false);
156 return false;
157 }
158 RawTag::Spacer(_) | RawTag::SwapChar(..) | RawTag::SpawnedWidget(..)
159 if b < self.init_point.byte() => {}
160 _ => return false,
161 }
162
163 true
164 }
165}
166
167impl<'t> Iterator for FwdIter<'t> {
168 type Item = TextPlace<'t>;
169
170 #[inline]
171 fn next(&mut self) -> Option<Self::Item> {
172 let tag = self.tags.peek();
173
174 if let Some(&(b, tag)) = tag
175 && (b <= self.point.byte() || self.conceals > 0)
176 {
177 self.tags.next();
178
179 if self.handle_meta_tag(&tag, b) {
180 self.next()
181 } else {
182 let tags = &self.text.0.tags;
183 Some(TextPlace::new(self.points(), TextPart::from_raw(tags, tag)))
184 }
185 } else if let Some(char) = self.chars.next() {
186 let points = self.points();
187 self.point = self.point.fwd(char);
188
189 self.ghost = match self.main_iter {
190 Some(..) => self.ghost.map(|(g, d)| (g.fwd(char), d + 1)),
191 None => None,
192 };
193
194 Some(TextPlace::new(points, TextPart::Char(char)))
195 } else if let Some(main_iter) = self.main_iter.take() {
196 self.point = main_iter.point;
197 self.init_point = main_iter.init_point;
198 self.chars = main_iter.chars;
199 self.tags = main_iter.tags;
200
201 self.next()
202 } else {
203 None
204 }
205 }
206}
207
208#[derive(Clone)]
213pub struct RevIter<'t> {
214 text: &'t Text,
215 point: Point,
216 init_point: Point,
217 chars: RevChars<'t>,
218 tags: tags::RevTags<'t>,
219 conceals: usize,
220
221 main_iter: Option<MainIter<RevChars<'t>, tags::RevTags<'t>>>,
222 ghost: Option<(Point, usize)>,
223}
224
225impl<'t> RevIter<'t> {
226 #[track_caller]
229 pub(super) fn new_at(text: &'t Text, points: TwoPoints) -> Self {
230 let TwoPoints { real, ghost } = points;
231 let point = real.min(text.end_point());
232
233 let ghost = ghost.and_then(|offset| {
234 let points = text.ghost_max_points_at(real.byte());
235 points.ghost.map(|max| (max.min(offset), max.byte()))
236 });
237
238 Self {
239 text,
240 point,
241 init_point: point,
242 chars: buf_chars_rev(text, point.byte()),
243 tags: text.tags_rev(point.byte(), None),
244 conceals: 0,
245
246 main_iter: None,
247 ghost,
248 }
249 }
250
251 pub fn points(&self) -> TwoPoints {
255 if let Some(MainIter { point, .. }) = self.main_iter.as_ref() {
256 TwoPoints::new(*point, self.point)
257 } else if let Some((ghost, _)) = self.ghost {
258 TwoPoints::new(self.point, ghost)
259 } else {
260 TwoPoints::new_after_ghost(self.point)
261 }
262 }
263
264 pub fn text(&self) -> &'t Text {
266 self.text
267 }
268
269 pub fn is_on_ghost(&self) -> bool {
273 self.main_iter.is_some()
274 }
275
276 #[inline]
280 fn handled_meta_tag(&mut self, tag: &RawTag, b: usize) -> bool {
281 match tag {
282 RawTag::Ghost(_, id) => {
283 if b > self.point.byte() || self.conceals > 0 {
284 return true;
285 }
286 let text = self.text.get_ghost(*id).unwrap();
287
288 let (ghost_b, this_ghost) = if let Some((offset, dist)) = &mut self.ghost {
289 if *dist - text.last_point().byte() >= offset.byte() {
290 *dist -= text.last_point().byte();
291 return true;
292 }
293 (
294 text.point_at_byte(offset.byte() + text.last_point().byte() - *dist),
295 *offset,
296 )
297 } else {
298 let this = text.last_point();
299 let points = self.text.ghost_max_points_at(b);
300 (this, points.ghost.unwrap())
301 };
302
303 let iter = text.iter_rev(ghost_b.to_two_points_before());
304 let point = std::mem::replace(&mut self.point, this_ghost);
305 let init_point = std::mem::replace(&mut self.init_point, this_ghost);
306 let chars = std::mem::replace(&mut self.chars, iter.chars);
307 let tags = std::mem::replace(&mut self.tags, iter.tags);
308
309 self.ghost = Some((this_ghost, this_ghost.byte()));
310 self.main_iter = Some(MainIter { point, init_point, chars, tags });
311 }
312
313 RawTag::StartConceal(_) => {
314 self.conceals = self.conceals.saturating_sub(1);
315 if self.conceals == 0 {
316 self.ghost.take_if(|_| b < self.point.byte());
317 self.point = self.point.min(self.text.point_at_byte(b));
318 self.chars = buf_chars_rev(self.text, self.point.byte());
319 }
320 }
321 RawTag::EndConceal(_) => self.conceals += 1,
322 RawTag::ConcealUntil(b) => {
323 let point = self.text.point_at_byte(*b as usize);
324 *self = RevIter::new_at(self.text, point.to_two_points_before());
325 return false;
326 }
327 RawTag::Spacer(_) | RawTag::SwapChar(..) | RawTag::SpawnedWidget(..)
328 if b > self.init_point.byte() => {}
329 _ => return false,
330 }
331
332 true
333 }
334}
335
336impl<'t> Iterator for RevIter<'t> {
337 type Item = TextPlace<'t>;
338
339 #[inline]
340 fn next(&mut self) -> Option<Self::Item> {
341 let tag = self.tags.peek();
342
343 if let Some(&(b, tag)) = tag
344 && (b >= self.point.byte() || self.conceals > 0)
345 {
346 self.tags.next();
347
348 if self.handled_meta_tag(&tag, b) {
349 self.next()
350 } else {
351 let tags = &self.text.0.tags;
352 Some(TextPlace::new(self.points(), TextPart::from_raw(tags, tag)))
353 }
354 } else if let Some(char) = self.chars.next() {
355 self.point = self.point.rev(char);
356
357 self.ghost = match self.main_iter {
358 Some(..) => self.ghost.map(|(g, d)| (g.rev(char), d - 1)),
359 None => None,
360 };
361
362 Some(TextPlace::new(self.points(), TextPart::Char(char)))
363 } else if let Some(main_iter) = self.main_iter.take() {
364 self.point = main_iter.point;
365 self.init_point = main_iter.init_point;
366 self.chars = main_iter.chars;
367 self.tags = main_iter.tags;
368
369 self.next()
370 } else {
371 None
372 }
373 }
374}
375
376fn buf_chars_fwd(text: &Text, b: usize, minus_last_nl: bool) -> FwdChars<'_> {
377 let [s0, s1] = text
378 .slices(b..text.len() - minus_last_nl as usize)
379 .map(|s| unsafe { std::str::from_utf8_unchecked(s) });
380 s0.chars().chain(s1.chars())
381}
382
383fn buf_chars_rev(text: &Text, b: usize) -> RevChars<'_> {
384 let [s0, s1] = text
385 .slices(..b)
386 .map(|s| unsafe { std::str::from_utf8_unchecked(s) });
387 s1.chars().rev().chain(s0.chars().rev())
388}
389
390#[derive(Debug, Clone, Copy)]
402pub struct TextPlace<'t> {
403 pub real: Point,
405 pub ghost: Option<Point>,
415 pub part: TextPart<'t>,
419}
420
421impl<'t> TextPlace<'t> {
422 #[inline]
424 const fn new(points: TwoPoints, part: TextPart<'t>) -> Self {
425 let TwoPoints { real, ghost } = points;
426 Self { real, ghost, part }
427 }
428
429 pub const fn is_real(&self) -> bool {
433 self.ghost.is_none()
434 }
435
436 pub const fn as_real_char(self) -> Option<(Point, char)> {
440 let Some(char) = self.part.as_char() else {
441 return None;
442 };
443 if self.ghost.is_none() {
444 Some((self.real, char))
445 } else {
446 None
447 }
448 }
449
450 pub const fn byte(&self) -> usize {
452 self.real.byte()
453 }
454
455 pub const fn char(&self) -> usize {
457 self.real.char()
458 }
459
460 pub const fn line(&self) -> usize {
462 self.real.line()
463 }
464
465 pub const fn points(&self) -> TwoPoints {
467 if let Some(ghost) = self.ghost {
468 TwoPoints::new(self.real, ghost)
469 } else {
470 TwoPoints::new_after_ghost(self.real)
471 }
472 }
473}
474
475type FwdChars<'t> = Chain<Chars<'t>, Chars<'t>>;
476type RevChars<'t> = Chain<Rev<Chars<'t>>, Rev<Chars<'t>>>;
477
478use crate::form::FormId;
479
480#[derive(Debug, Clone, Copy, PartialEq, Eq)]
498#[doc(hidden)]
499pub enum TextPart<'t> {
500 Char(char),
504 PushForm(FormId, u8),
509 PopForm(FormId),
514 Spacer,
518 SwapChar(char),
520 ToggleStart(ToggleId),
522 ToggleEnd(ToggleId),
524 SpawnedWidget(SpawnId),
528
529 Inlay(&'t Text),
531
532 ResetState,
545}
546
547impl<'t> TextPart<'t> {
548 #[inline]
550 pub(super) fn from_raw(tags: &'t InnerTags, value: RawTag) -> Self {
551 match value {
552 RawTag::PushForm(_, id, prio) => Self::PushForm(id, prio),
553 RawTag::PopForm(_, id) => Self::PopForm(id),
554 RawTag::Spacer(_) => Self::Spacer,
555 RawTag::SwapChar(_, char) => Self::SwapChar(char),
556 RawTag::StartToggle(_, id) => Self::ToggleStart(id),
557 RawTag::EndToggle(_, id) => Self::ToggleEnd(id),
558 RawTag::ConcealUntil(_) => Self::ResetState,
559 RawTag::SpawnedWidget(_, id) => Self::SpawnedWidget(id),
560 RawTag::Inlay(_, id) => Self::Inlay(tags.get_ghost(id).unwrap()),
561 RawTag::StartConceal(_) | RawTag::EndConceal(_) | RawTag::Ghost(..) => {
562 unreachable!("These tags are automatically processed elsewhere.")
563 }
564 }
565 }
566
567 #[must_use]
571 #[inline]
572 pub const fn is_char(&self) -> bool {
573 matches!(self, TextPart::Char(_))
574 }
575
576 #[inline]
580 pub const fn is_tag(&self) -> bool {
581 !self.is_char()
582 }
583
584 #[inline]
588 pub const fn as_char(&self) -> Option<char> {
589 if let Self::Char(v) = self {
590 Some(*v)
591 } else {
592 None
593 }
594 }
595}
596
597#[derive(Debug, Clone)]
598struct MainIter<Chars, Tags> {
599 point: Point,
600 init_point: Point,
601 chars: Chars,
602 tags: Tags,
603}