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::{mode::Selection, text::TwoPoints};
25
26#[derive(Clone)]
31pub struct FwdIter<'a> {
32 text: &'a Text,
33 point: Point,
34 init_point: Point,
35 chars: FwdChars<'a>,
36 tags: tags::FwdTags<'a>,
37 conceals: u32,
38
39 main_iter: Option<(Point, FwdChars<'a>, tags::FwdTags<'a>)>,
41 ghost: Option<(Point, usize)>,
42
43 print_ghosts: bool,
45 _conceals: Conceal<'a>,
46}
47
48impl<'a> FwdIter<'a> {
49 pub(super) fn new_at(text: &'a Text, points: TwoPoints) -> Self {
52 let TwoPoints { real, ghost } = points;
53 let point = real.min(text.len());
54
55 let ghost = if let Some(offset) = ghost {
62 let points = text.ghost_max_points_at(real.byte());
63 points.ghost.map(|max| (max.min(offset), 0))
64 } else {
65 let points = text.ghost_max_points_at(real.byte());
66 points.ghost.zip(Some(0))
67 };
68
69 Self {
70 text,
71 point,
72 init_point: point,
73 chars: buf_chars_fwd(text, point.byte()),
74 tags: text.tags_fwd(point.byte(), None),
75 conceals: 0,
76
77 main_iter: None,
78 ghost,
79
80 print_ghosts: true,
81 _conceals: Conceal::All,
82 }
83 }
84
85 pub fn no_conceals(self) -> Self {
91 Self { _conceals: Conceal::None, ..self }
92 }
93
94 pub fn dont_conceal_containing(self, list: &'a [Selection]) -> Self {
101 Self {
102 _conceals: Conceal::Excluding(list),
103 ..self
104 }
105 }
106
107 pub fn no_ghosts(self) -> Self {
111 Self { print_ghosts: false, ..self }
112 }
113
114 pub fn no_tags(self) -> impl Iterator<Item = Item> + 'a {
124 self.filter(|item| item.part.is_char())
125 }
126
127 pub fn skip_to(&mut self, points: TwoPoints) {
131 *self = self.text.iter_fwd(points.max(self.points()))
132 }
133
134 #[inline(always)]
140 pub fn is_on_ghost(&self) -> bool {
141 self.main_iter.is_some()
142 }
143
144 #[inline(always)]
147 pub fn points(&self) -> TwoPoints {
148 if let Some((real, ..)) = self.main_iter.as_ref() {
149 TwoPoints::new(*real, self.ghost.map(|(tg, _)| tg).unwrap())
150 } else {
151 TwoPoints::new_after_ghost(self.point)
152 }
153 }
154
155 pub fn text(&self) -> &'a Text {
157 self.text
158 }
159
160 #[inline(always)]
164 fn handle_meta_tag(&mut self, tag: &RawTag, b: usize) -> bool {
165 match tag {
166 RawTag::Ghost(_, id) => {
167 if !self.print_ghosts || b < self.point.byte() || self.conceals > 0 {
168 return true;
169 }
170 let text = self.text.get_ghost(*id).unwrap();
171
172 let (this_ghost, total_ghost) = if let Some((ghost, dist)) = &mut self.ghost {
173 if ghost.byte() >= *dist + text.len().byte() {
174 *dist += text.len().byte();
175 return true;
176 }
177 (text.point_at_byte(ghost.byte() - *dist), *ghost)
178 } else {
179 (Point::default(), Point::default())
180 };
181
182 let iter = text.iter_fwd(this_ghost.to_two_points_before());
183 let point = std::mem::replace(&mut self.point, this_ghost);
184 let chars = std::mem::replace(&mut self.chars, iter.chars);
185 let tags = std::mem::replace(&mut self.tags, iter.tags);
186
187 self.ghost = Some((total_ghost, total_ghost.byte()));
188 self.main_iter = Some((point, chars, tags));
189 }
190 RawTag::StartConceal(_) => {
191 self.conceals += 1;
192 }
193 RawTag::EndConceal(_) => {
194 self.conceals = self.conceals.saturating_sub(1);
195 if self.conceals == 0 {
196 self.ghost.take_if(|_| self.point.byte() < b);
199 self.point = self.point.max(self.text.point_at_byte(b));
200 self.chars = buf_chars_fwd(self.text, self.point.byte());
201 }
202 }
203 RawTag::ConcealUntil(b) => {
204 let point = self.text.point_at_byte(*b as usize);
205 *self = FwdIter::new_at(self.text, point.to_two_points_before());
206 return false;
207 }
208 RawTag::MainCaret(_)
209 | RawTag::ExtraCaret(_)
210 | RawTag::Spacer(_)
211 | RawTag::ReplaceChar(..)
212 | RawTag::SpawnedWidget(..)
213 if b < self.init_point.byte() => {}
214 _ => return false,
215 }
216
217 true
218 }
219}
220
221impl Iterator for FwdIter<'_> {
222 type Item = Item;
223
224 #[inline]
225 fn next(&mut self) -> Option<Self::Item> {
226 let tag = self.tags.peek();
227
228 if let Some(&(b, tag)) = tag
229 && (b <= self.point.byte() || self.conceals > 0)
230 {
231 self.tags.next();
232
233 if self.handle_meta_tag(&tag, b) {
234 self.next()
235 } else {
236 Some(Item::new(self.points(), Part::from_raw(tag)))
237 }
238 } else if let Some(char) = self.chars.next() {
239 let points = self.points();
240 self.point = self.point.fwd(char);
241
242 self.ghost = match self.main_iter {
243 Some(..) => self.ghost.map(|(g, d)| (g.fwd(char), d + 1)),
244 None => None,
245 };
246
247 Some(Item::new(points, Part::Char(char)))
248 } else if let Some(backup) = self.main_iter.take() {
249 (self.point, self.chars, self.tags) = backup;
250 self.next()
251 } else {
252 None
253 }
254 }
255}
256
257#[derive(Clone)]
262pub struct RevIter<'a> {
263 text: &'a Text,
264 point: Point,
265 init_point: Point,
266 chars: RevChars<'a>,
267 tags: tags::RevTags<'a>,
268 conceals: usize,
269
270 main_iter: Option<(Point, RevChars<'a>, tags::RevTags<'a>)>,
271 ghost: Option<(Point, usize)>,
272
273 print_ghosts: bool,
275 _conceals: Conceal<'a>,
276}
277
278impl<'a> RevIter<'a> {
279 pub(super) fn new_at(text: &'a Text, points: TwoPoints) -> Self {
282 let TwoPoints { real, ghost } = points;
283 let point = real.min(text.len());
284
285 let ghost = ghost.and_then(|offset| {
286 let points = text.ghost_max_points_at(real.byte());
287 points.ghost.map(|max| (max.min(offset), max.byte()))
288 });
289
290 Self {
291 text,
292 point,
293 init_point: point,
294 chars: buf_chars_rev(text, point.byte()),
295 tags: text.tags_rev(point.byte(), None),
296 conceals: 0,
297
298 main_iter: None,
299 ghost,
300
301 print_ghosts: true,
302 _conceals: Conceal::All,
303 }
304 }
305
306 pub fn no_conceals(self) -> Self {
312 Self { _conceals: Conceal::None, ..self }
313 }
314
315 pub fn no_ghosts(self) -> Self {
319 Self { print_ghosts: false, ..self }
320 }
321
322 pub fn no_tags(self) -> impl Iterator<Item = Item> + 'a {
332 self.filter(|item| item.part.is_char())
333 }
334
335 pub fn points(&self) -> TwoPoints {
339 if let Some((real, ..)) = self.main_iter.as_ref() {
340 TwoPoints::new(*real, self.point)
341 } else if let Some((ghost, _)) = self.ghost {
342 TwoPoints::new(self.point, ghost)
343 } else {
344 TwoPoints::new_after_ghost(self.point)
345 }
346 }
347
348 pub fn text(&self) -> &'a Text {
350 self.text
351 }
352
353 pub fn is_on_ghost(&self) -> bool {
357 self.main_iter.is_some()
358 }
359
360 #[inline]
364 fn handled_meta_tag(&mut self, tag: &RawTag, b: usize) -> bool {
365 match tag {
366 RawTag::Ghost(_, id) => {
367 if !self.print_ghosts || b > self.point.byte() || self.conceals > 0 {
368 return true;
369 }
370 let text = self.text.get_ghost(*id).unwrap();
371
372 let (ghost_b, offset) = if let Some((offset, dist)) = &mut self.ghost {
373 if *dist - text.len().byte() >= offset.byte() {
374 *dist -= text.len().byte();
375 return true;
376 }
377 (
378 text.point_at_byte(offset.byte() + text.len().byte() - *dist),
379 *offset,
380 )
381 } else {
382 let this = text.len();
383 let points = self.text.ghost_max_points_at(b);
384 (this, points.ghost.unwrap())
385 };
386
387 let iter = text.iter_rev(ghost_b.to_two_points_before());
388 let point = std::mem::replace(&mut self.point, offset);
389 let chars = std::mem::replace(&mut self.chars, iter.chars);
390 let tags = std::mem::replace(&mut self.tags, iter.tags);
391
392 self.ghost = Some((offset, offset.byte()));
393 self.main_iter = Some((point, chars, tags));
394 }
395
396 RawTag::StartConceal(_) => {
397 self.conceals = self.conceals.saturating_sub(1);
398 if self.conceals == 0 {
399 self.ghost.take_if(|_| b < self.point.byte());
400 self.point = self.point.min(self.text.point_at_byte(b));
401 self.chars = buf_chars_rev(self.text, self.point.byte());
402 }
403 }
404 RawTag::EndConceal(_) => self.conceals += 1,
405 RawTag::ConcealUntil(b) => {
406 let point = self.text.point_at_byte(*b as usize);
407 *self = RevIter::new_at(self.text, point.to_two_points_before());
408 return false;
409 }
410 RawTag::MainCaret(_)
411 | RawTag::ExtraCaret(_)
412 | RawTag::Spacer(_)
413 | RawTag::ReplaceChar(..)
414 | RawTag::SpawnedWidget(..)
415 if b > self.init_point.byte() => {}
416 _ => return false,
417 }
418
419 true
420 }
421}
422
423impl Iterator for RevIter<'_> {
424 type Item = Item;
425
426 #[inline]
427 fn next(&mut self) -> Option<Self::Item> {
428 let tag = self.tags.peek();
429
430 if let Some(&(b, tag)) = tag
431 && (b >= self.point.byte() || self.conceals > 0)
432 {
433 self.tags.next();
434
435 if self.handled_meta_tag(&tag, b) {
436 self.next()
437 } else {
438 Some(Item::new(self.points(), Part::from_raw(tag)))
439 }
440 } else if let Some(char) = self.chars.next() {
441 self.point = self.point.rev(char);
442
443 self.ghost = match self.main_iter {
444 Some(..) => self.ghost.map(|(g, d)| (g.rev(char), d - 1)),
445 None => None,
446 };
447
448 Some(Item::new(self.points(), Part::Char(char)))
449 } else if let Some(last_iter) = self.main_iter.take() {
450 (self.point, self.chars, self.tags) = last_iter;
451 self.next()
452 } else {
453 None
454 }
455 }
456}
457
458fn buf_chars_fwd(text: &Text, b: usize) -> FwdChars<'_> {
459 let [s0, s1] = text
460 .slices(b..)
461 .to_array()
462 .map(|s| unsafe { std::str::from_utf8_unchecked(s) });
463 s0.chars().chain(s1.chars())
464}
465
466fn buf_chars_rev(text: &Text, b: usize) -> RevChars<'_> {
467 let [s0, s1] = text
468 .slices(..b)
469 .to_array()
470 .map(|s| unsafe { std::str::from_utf8_unchecked(s) });
471 s1.chars().rev().chain(s0.chars().rev())
472}
473
474#[derive(Debug, Clone, Copy)]
486pub struct Item {
487 pub real: Point,
489 pub ghost: Option<Point>,
499 pub part: Part,
503}
504
505impl Item {
506 #[inline]
508 const fn new(points: TwoPoints, part: Part) -> Self {
509 let TwoPoints { real, ghost } = points;
510 Self { real, ghost, part }
511 }
512
513 pub const fn is_real(&self) -> bool {
517 self.ghost.is_none()
518 }
519
520 pub const fn as_real_char(self) -> Option<(Point, char)> {
524 let Some(char) = self.part.as_char() else {
525 return None;
526 };
527 if self.ghost.is_none() {
528 Some((self.real, char))
529 } else {
530 None
531 }
532 }
533
534 pub const fn byte(&self) -> usize {
536 self.real.byte()
537 }
538
539 pub const fn char(&self) -> usize {
541 self.real.char()
542 }
543
544 pub const fn line(&self) -> usize {
546 self.real.line()
547 }
548
549 pub const fn points(&self) -> TwoPoints {
551 if let Some(ghost) = self.ghost {
552 TwoPoints::new(self.real, ghost)
553 } else {
554 TwoPoints::new_after_ghost(self.real)
555 }
556 }
557}
558
559#[allow(dead_code)]
561#[derive(Debug, Default, Clone)]
562enum Conceal<'a> {
563 #[default]
564 All,
565 None,
566 Excluding(&'a [Selection]),
567 NotOnLineOf(&'a [Selection]),
568}
569
570type FwdChars<'a> = Chain<Chars<'a>, Chars<'a>>;
571type RevChars<'a> = Chain<Rev<Chars<'a>>, Rev<Chars<'a>>>;
572
573use crate::form::FormId;
574
575#[derive(Debug, Clone, Copy, PartialEq, Eq)]
593pub enum Part {
594 Char(char),
598 PushForm(FormId, u8),
603 PopForm(FormId),
608 MainCaret,
614 ExtraCaret,
620 Spacer,
624 ReplaceChar(char),
626 ToggleStart(ToggleId),
630 ToggleEnd(ToggleId),
634 SpawnedWidget(SpawnId),
638
639 ResetState,
652}
653
654impl Part {
655 #[inline]
657 pub(super) fn from_raw(value: RawTag) -> Self {
658 match value {
659 RawTag::PushForm(_, id, prio) => Part::PushForm(id, prio),
660 RawTag::PopForm(_, id) => Part::PopForm(id),
661 RawTag::MainCaret(_) => Part::MainCaret,
662 RawTag::ExtraCaret(_) => Part::ExtraCaret,
663 RawTag::Spacer(_) => Part::Spacer,
664 RawTag::ReplaceChar(_, char) => Part::ReplaceChar(char),
665 RawTag::StartToggle(_, id) => Part::ToggleStart(id),
666 RawTag::EndToggle(_, id) => Part::ToggleEnd(id),
667 RawTag::ConcealUntil(_) => Part::ResetState,
668 RawTag::SpawnedWidget(_, id) => Part::SpawnedWidget(id),
669 RawTag::StartConceal(_) | RawTag::EndConceal(_) | RawTag::Ghost(..) => {
670 unreachable!("These tags are automatically processed elsewhere.")
671 }
672 }
673 }
674
675 #[must_use]
679 #[inline]
680 pub const fn is_char(&self) -> bool {
681 matches!(self, Part::Char(_))
682 }
683
684 #[inline]
688 pub const fn is_tag(&self) -> bool {
689 !self.is_char()
690 }
691
692 #[inline]
696 pub const fn as_char(&self) -> Option<char> {
697 if let Self::Char(v) = self {
698 Some(*v)
699 } else {
700 None
701 }
702 }
703}