1use std::{
16 iter::{Chain, Rev},
17 str::Chars,
18};
19
20use super::{
21 Point, SpawnId, Text,
22 tags::{self, RawTag},
23};
24use crate::{
25 form::MaskId,
26 text::{TwoPoints, tags::InnerTags},
27};
28
29#[derive(Clone)]
34pub struct FwdIter<'t> {
35 text: &'t Text,
36 point: Point,
37 init_point: Point,
38 chars: FwdChars<'t>,
39 tags: tags::FwdTags<'t>,
40 conceals: u32,
41
42 main_iter: Option<MainIter<FwdChars<'t>, tags::FwdTags<'t>>>,
44 ghost: Option<(Point, usize)>,
45}
46
47impl<'t> FwdIter<'t> {
48 #[track_caller]
51 pub(super) fn new_at(text: &'t Text, points: TwoPoints, is_ghost: bool) -> Self {
52 let TwoPoints { real, ghost } = points;
53 let point = real.min(if is_ghost {
54 text.last_point()
55 } else {
56 text.end_point()
57 });
58
59 let ghost = if let Some(offset) = ghost {
66 let points = text.ghost_max_points_at(real.byte());
67 points.ghost.map(|max| (max.min(offset), 0))
68 } else {
69 let points = text.ghost_max_points_at(real.byte());
70 points.ghost.zip(Some(0))
71 };
72
73 Self {
74 text,
75 point,
76 init_point: point,
77 chars: buf_chars_fwd(text, point.byte(), is_ghost),
78 tags: text.tags_fwd(point.byte(), None),
79 conceals: 0,
80
81 main_iter: None,
82 ghost,
83 }
84 }
85
86 #[inline(always)]
92 pub fn is_on_ghost(&self) -> bool {
93 self.main_iter.is_some()
94 }
95
96 #[inline(always)]
99 pub fn points(&self) -> TwoPoints {
100 if let Some(MainIter { point, .. }) = self.main_iter.as_ref() {
101 TwoPoints::new(*point, self.ghost.map(|(tg, _)| tg).unwrap())
102 } else {
103 TwoPoints::new_after_ghost(self.point)
104 }
105 }
106
107 pub fn text(&self) -> &Text {
109 self.text
110 }
111
112 #[inline(always)]
116 fn handle_meta_tag(&mut self, tag: &RawTag, b: usize) -> bool {
117 match tag {
118 RawTag::Inlay(_, id) => {
119 if b < self.point.byte() || self.conceals > 0 {
120 return true;
121 }
122 let text = self.text.get_ghost(*id).unwrap();
123
124 let (this_ghost, total_ghost) = if let Some((ghost, dist)) = &mut self.ghost {
125 if ghost.byte() >= *dist + text.last_point().byte() {
126 *dist += text.last_point().byte();
127 return true;
128 }
129 (text.point_at_byte(ghost.byte() - *dist), *ghost)
130 } else {
131 (Point::default(), Point::default())
132 };
133
134 let iter = FwdIter::new_at(text, this_ghost.to_two_points_before(), true);
135 let point = std::mem::replace(&mut self.point, this_ghost);
136 let init_point = std::mem::replace(&mut self.init_point, this_ghost);
137 let chars = std::mem::replace(&mut self.chars, iter.chars);
138 let tags = std::mem::replace(&mut self.tags, iter.tags);
139
140 self.ghost = Some((total_ghost, total_ghost.byte()));
141 self.main_iter = Some(MainIter { point, init_point, chars, tags });
142 }
143 RawTag::StartConceal(_) => self.conceals += 1,
144 RawTag::EndConceal(_) => {
145 self.conceals = self.conceals.saturating_sub(1);
146 if self.conceals == 0 {
147 self.ghost.take_if(|_| self.point.byte() < b);
150 self.point = self.point.max(self.text.point_at_byte(b));
151 self.chars = buf_chars_fwd(self.text, self.point.byte(), false);
152 }
153 }
154 RawTag::ConcealUntil(b) => {
155 let point = self.text.point_at_byte(*b as usize);
156 *self = FwdIter::new_at(self.text, point.to_two_points_before(), false);
157 return false;
158 }
159 RawTag::Spacer(_) | RawTag::SpawnedWidget(..) | RawTag::Overlay(..)
160 if b < self.init_point.byte() => {}
161 RawTag::StartToggle(..) | RawTag::EndToggle(..) => {}
162 _ => return false,
163 }
164
165 true
166 }
167}
168
169impl<'t> Iterator for FwdIter<'t> {
170 type Item = TextPlace<'t>;
171
172 #[inline]
173 fn next(&mut self) -> Option<Self::Item> {
174 if let Some((b, tag)) = self
175 .tags
176 .next_if(|(b, _)| *b <= self.point.byte() || self.conceals > 0)
177 {
178 if self.handle_meta_tag(&tag, b) {
179 self.next()
180 } else {
181 let tags = &self.text.0.tags;
182 Some(TextPlace::new(self.points(), TextPart::from_raw(tags, tag)))
183 }
184 } else if let Some(char) = self.chars.next() {
185 let points = self.points();
186 self.point = self.point.fwd(char);
187
188 self.ghost = match self.main_iter {
189 Some(..) => self.ghost.map(|(g, d)| (g.fwd(char), d + 1)),
190 None => None,
191 };
192
193 Some(TextPlace::new(points, TextPart::Char(char)))
194 } else if let Some(main_iter) = self.main_iter.take() {
195 self.point = main_iter.point;
196 self.init_point = main_iter.init_point;
197 self.chars = main_iter.chars;
198 self.tags = main_iter.tags;
199
200 self.next()
201 } else {
202 None
203 }
204 }
205}
206
207#[derive(Clone)]
212pub struct RevIter<'t> {
213 text: &'t Text,
214 point: Point,
215 init_point: Point,
216 chars: RevChars<'t>,
217 tags: tags::RevTags<'t>,
218 conceals: usize,
219
220 main_iter: Option<MainIter<RevChars<'t>, tags::RevTags<'t>>>,
221 ghost: Option<(Point, usize)>,
222}
223
224impl<'t> RevIter<'t> {
225 #[track_caller]
228 pub(super) fn new_at(text: &'t Text, points: TwoPoints) -> Self {
229 let TwoPoints { real, ghost } = points;
230 let point = real.min(text.end_point());
231
232 let ghost = ghost.and_then(|offset| {
233 let points = text.ghost_max_points_at(real.byte());
234 points.ghost.map(|max| (max.min(offset), max.byte()))
235 });
236
237 Self {
238 text,
239 point,
240 init_point: point,
241 chars: buf_chars_rev(text, point.byte()),
242 tags: text.tags_rev(point.byte(), None),
243 conceals: 0,
244
245 main_iter: None,
246 ghost,
247 }
248 }
249
250 pub fn points(&self) -> TwoPoints {
254 if let Some(MainIter { point, .. }) = self.main_iter.as_ref() {
255 TwoPoints::new(*point, self.point)
256 } else if let Some((ghost, _)) = self.ghost {
257 TwoPoints::new(self.point, ghost)
258 } else {
259 TwoPoints::new_after_ghost(self.point)
260 }
261 }
262
263 pub fn text(&self) -> &'t Text {
265 self.text
266 }
267
268 pub fn is_on_ghost(&self) -> bool {
272 self.main_iter.is_some()
273 }
274
275 #[inline]
279 fn handled_meta_tag(&mut self, tag: &RawTag, b: usize) -> bool {
280 match tag {
281 RawTag::Inlay(_, id) => {
282 if b > self.point.byte() || self.conceals > 0 {
283 return true;
284 }
285 let text = self.text.get_ghost(*id).unwrap();
286
287 let (ghost_b, this_ghost) = if let Some((offset, dist)) = &mut self.ghost {
288 if *dist - text.last_point().byte() >= offset.byte() {
289 *dist -= text.last_point().byte();
290 return true;
291 }
292 (
293 text.point_at_byte(offset.byte() + text.last_point().byte() - *dist),
294 *offset,
295 )
296 } else {
297 let this = text.last_point();
298 let points = self.text.ghost_max_points_at(b);
299 (this, points.ghost.unwrap())
300 };
301
302 let iter = text.iter_rev(ghost_b.to_two_points_before());
303 let point = std::mem::replace(&mut self.point, this_ghost);
304 let init_point = std::mem::replace(&mut self.init_point, this_ghost);
305 let chars = std::mem::replace(&mut self.chars, iter.chars);
306 let tags = std::mem::replace(&mut self.tags, iter.tags);
307
308 self.ghost = Some((this_ghost, this_ghost.byte()));
309 self.main_iter = Some(MainIter { point, init_point, chars, tags });
310 }
311
312 RawTag::StartConceal(_) => {
313 self.conceals = self.conceals.saturating_sub(1);
314 if self.conceals == 0 {
315 self.ghost.take_if(|_| b < self.point.byte());
316 self.point = self.point.min(self.text.point_at_byte(b));
317 self.chars = buf_chars_rev(self.text, self.point.byte());
318 }
319 }
320 RawTag::EndConceal(_) => self.conceals += 1,
321 RawTag::ConcealUntil(b) => {
322 let point = self.text.point_at_byte(*b as usize);
323 *self = RevIter::new_at(self.text, point.to_two_points_before());
324 return false;
325 }
326 RawTag::Spacer(_) | RawTag::SpawnedWidget(..) | RawTag::Overlay(..)
327 if b > self.init_point.byte() => {}
328 RawTag::StartToggle(..) | RawTag::EndToggle(..) => {}
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 if let Some((b, tag)) = self
342 .tags
343 .next_if(|(b, _)| *b >= self.point.byte() || self.conceals > 0)
344 {
345 if self.handled_meta_tag(&tag, b) {
346 self.next()
347 } else {
348 let tags = &self.text.0.tags;
349 Some(TextPlace::new(self.points(), TextPart::from_raw(tags, tag)))
350 }
351 } else if let Some(char) = self.chars.next() {
352 self.point = self.point.rev(char);
353
354 self.ghost = match self.main_iter {
355 Some(..) => self.ghost.map(|(g, d)| (g.rev(char), d - 1)),
356 None => None,
357 };
358
359 Some(TextPlace::new(self.points(), TextPart::Char(char)))
360 } else if let Some(main_iter) = self.main_iter.take() {
361 self.point = main_iter.point;
362 self.init_point = main_iter.init_point;
363 self.chars = main_iter.chars;
364 self.tags = main_iter.tags;
365
366 self.next()
367 } else {
368 None
369 }
370 }
371}
372
373fn buf_chars_fwd(text: &Text, b: usize, minus_last_nl: bool) -> FwdChars<'_> {
374 let [s0, s1] = text
375 .slices(b..text.len() - minus_last_nl as usize)
376 .map(|s| unsafe { std::str::from_utf8_unchecked(s) });
377 s0.chars().chain(s1.chars())
378}
379
380fn buf_chars_rev(text: &Text, b: usize) -> RevChars<'_> {
381 let [s0, s1] = text
382 .slices(..b)
383 .map(|s| unsafe { std::str::from_utf8_unchecked(s) });
384 s1.chars().rev().chain(s0.chars().rev())
385}
386
387#[derive(Debug, Clone, Copy)]
399pub struct TextPlace<'t> {
400 pub real: Point,
402 pub ghost: Option<Point>,
412 pub part: TextPart<'t>,
416}
417
418impl<'t> TextPlace<'t> {
419 #[inline]
421 const fn new(points: TwoPoints, part: TextPart<'t>) -> Self {
422 let TwoPoints { real, ghost } = points;
423 Self { real, ghost, part }
424 }
425
426 pub const fn is_real(&self) -> bool {
430 self.ghost.is_none()
431 }
432
433 pub const fn as_real_char(self) -> Option<(Point, char)> {
437 let Some(char) = self.part.as_char() else {
438 return None;
439 };
440 if self.ghost.is_none() {
441 Some((self.real, char))
442 } else {
443 None
444 }
445 }
446
447 pub const fn byte(&self) -> usize {
449 self.real.byte()
450 }
451
452 pub const fn char(&self) -> usize {
454 self.real.char()
455 }
456
457 pub const fn line(&self) -> usize {
459 self.real.line()
460 }
461
462 pub const fn points(&self) -> TwoPoints {
464 if let Some(ghost) = self.ghost {
465 TwoPoints::new(self.real, ghost)
466 } else {
467 TwoPoints::new_after_ghost(self.real)
468 }
469 }
470}
471
472type FwdChars<'t> = Chain<Chars<'t>, Chars<'t>>;
473type RevChars<'t> = Chain<Rev<Chars<'t>>, Rev<Chars<'t>>>;
474
475use crate::form::FormId;
476
477#[derive(Debug, Clone, Copy, PartialEq, Eq)]
495#[doc(hidden)]
496pub enum TextPart<'t> {
497 Char(char),
501 PushForm(FormId, u8),
506 PopForm(FormId),
511 Spacer,
515 Overlay(&'t Text),
517 SpawnedWidget(SpawnId),
521 PushMask(MaskId),
527 PopMask(MaskId),
529 ResetState,
542}
543
544impl<'t> TextPart<'t> {
545 #[inline]
547 pub(super) fn from_raw(tags: &'t InnerTags, value: RawTag) -> Self {
548 match value {
549 RawTag::PushForm(_, id, prio) => Self::PushForm(id, prio),
550 RawTag::PopForm(_, id) => Self::PopForm(id),
551 RawTag::Spacer(_) => Self::Spacer,
552 RawTag::ConcealUntil(_) => Self::ResetState,
553 RawTag::SpawnedWidget(_, id) => Self::SpawnedWidget(id),
554 RawTag::Overlay(_, id) => Self::Overlay(tags.get_ghost(id).unwrap()),
555 RawTag::PushMask(_, id) => Self::PushMask(id),
556 RawTag::PopMask(_, id) => Self::PopMask(id),
557 RawTag::StartConceal(_)
558 | RawTag::EndConceal(_)
559 | RawTag::Inlay(..)
560 | RawTag::StartToggle(..)
561 | RawTag::EndToggle(..) => {
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}