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()),
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 {
123 self.filter(|item| item.part.is_char())
124 }
125
126 pub fn skip_to(&mut self, points: TwoPoints) {
130 *self = self.text.iter_fwd(points.max(self.points()))
131 }
132
133 #[inline(always)]
139 pub fn is_on_ghost(&self) -> bool {
140 self.main_iter.is_some()
141 }
142
143 #[inline(always)]
146 pub fn points(&self) -> TwoPoints {
147 if let Some((real, ..)) = self.main_iter.as_ref() {
148 TwoPoints::new(*real, self.ghost.map(|(tg, _)| tg).unwrap())
149 } else {
150 TwoPoints::new_after_ghost(self.point)
151 }
152 }
153
154 pub fn text(&self) -> &'a Text {
156 self.text
157 }
158
159 #[inline(always)]
163 fn handle_special_tag(&mut self, tag: &RawTag, b: usize) -> bool {
164 match tag {
165 RawTag::Ghost(_, id) => {
166 if !self.print_ghosts || b < self.point.byte() || self.conceals > 0 {
167 return true;
168 }
169 let text = self.text.get_ghost(*id).unwrap();
170
171 let (this_ghost, total_ghost) = if let Some((ghost, dist)) = &mut self.ghost {
172 if ghost.byte() >= *dist + text.len().byte() {
173 *dist += text.len().byte();
174 return true;
175 }
176 (text.point_at_byte(ghost.byte() - *dist), *ghost)
177 } else {
178 (Point::default(), Point::default())
179 };
180
181 let iter = text.iter_fwd(this_ghost.to_two_points_before());
182 let point = std::mem::replace(&mut self.point, this_ghost);
183 let chars = std::mem::replace(&mut self.chars, iter.chars);
184 let tags = std::mem::replace(&mut self.tags, iter.tags);
185
186 self.ghost = Some((total_ghost, total_ghost.byte()));
187 self.main_iter = Some((point, chars, tags));
188 }
189 RawTag::StartConceal(_) => {
190 self.conceals += 1;
191 }
192 RawTag::EndConceal(_) => {
193 self.conceals = self.conceals.saturating_sub(1);
194 if self.conceals == 0 {
195 self.ghost.take_if(|_| self.point.byte() < b);
198 self.point = self.point.max(self.text.point_at_byte(b));
199 self.chars = buf_chars_fwd(self.text, self.point.byte());
200 }
201 }
202 RawTag::ConcealUntil(b) => {
203 let point = self.text.point_at_byte(*b as usize);
204 *self = FwdIter::new_at(self.text, point.to_two_points_before());
205 return false;
206 }
207 RawTag::MainCaret(_)
208 | RawTag::ExtraCaret(_)
209 | RawTag::Spacer(_)
210 | RawTag::SpawnedWidget(..)
211 if b < self.init_point.byte() => {}
212 _ => return false,
213 }
214
215 true
216 }
217}
218
219impl Iterator for FwdIter<'_> {
220 type Item = Item;
221
222 #[inline]
223 fn next(&mut self) -> Option<Self::Item> {
224 let tag = self.tags.peek();
225
226 if let Some(&(b, tag)) = tag
227 && (b <= self.point.byte() || self.conceals > 0)
228 {
229 self.tags.next();
230
231 if self.handle_special_tag(&tag, b) {
232 self.next()
233 } else {
234 Some(Item::new(self.points(), Part::from_raw(tag)))
235 }
236 } else if let Some(char) = self.chars.next() {
237 let points = self.points();
238 self.point = self.point.fwd(char);
239
240 self.ghost = match self.main_iter {
241 Some(..) => self.ghost.map(|(g, d)| (g.fwd(char), d + 1)),
242 None => None,
243 };
244
245 Some(Item::new(points, Part::Char(char)))
246 } else if let Some(backup) = self.main_iter.take() {
247 (self.point, self.chars, self.tags) = backup;
248 self.next()
249 } else {
250 None
251 }
252 }
253}
254
255#[derive(Clone)]
260pub struct RevIter<'a> {
261 text: &'a Text,
262 point: Point,
263 init_point: Point,
264 chars: RevChars<'a>,
265 tags: tags::RevTags<'a>,
266 conceals: usize,
267
268 main_iter: Option<(Point, RevChars<'a>, tags::RevTags<'a>)>,
269 ghost: Option<(Point, usize)>,
270
271 print_ghosts: bool,
273 _conceals: Conceal<'a>,
274}
275
276impl<'a> RevIter<'a> {
277 pub(super) fn new_at(text: &'a Text, points: TwoPoints) -> Self {
280 let TwoPoints { real, ghost } = points;
281 let point = real.min(text.len());
282
283 let ghost = ghost.and_then(|offset| {
284 let points = text.ghost_max_points_at(real.byte());
285 points.ghost.map(|max| (max.min(offset), max.byte()))
286 });
287
288 Self {
289 text,
290 point,
291 init_point: point,
292 chars: buf_chars_rev(text, point.byte()),
293 tags: text.tags_rev(point.byte()),
294 conceals: 0,
295
296 main_iter: None,
297 ghost,
298
299 print_ghosts: true,
300 _conceals: Conceal::All,
301 }
302 }
303
304 pub fn no_conceals(self) -> Self {
310 Self { _conceals: Conceal::None, ..self }
311 }
312
313 pub fn no_ghosts(self) -> Self {
317 Self { print_ghosts: false, ..self }
318 }
319
320 pub fn no_tags(self) -> impl Iterator<Item = Item> + 'a {
329 self.filter(|item| item.part.is_char())
330 }
331
332 pub fn points(&self) -> TwoPoints {
336 if let Some((real, ..)) = self.main_iter.as_ref() {
337 TwoPoints::new(*real, self.point)
338 } else if let Some((ghost, _)) = self.ghost {
339 TwoPoints::new(self.point, ghost)
340 } else {
341 TwoPoints::new_after_ghost(self.point)
342 }
343 }
344
345 pub fn text(&self) -> &'a Text {
347 self.text
348 }
349
350 pub fn is_on_ghost(&self) -> bool {
354 self.main_iter.is_some()
355 }
356
357 #[inline]
361 fn handled_meta_tag(&mut self, tag: &RawTag, b: usize) -> bool {
362 match tag {
363 RawTag::Ghost(_, id) => {
364 if !self.print_ghosts || b > self.point.byte() || self.conceals > 0 {
365 return true;
366 }
367 let text = self.text.get_ghost(*id).unwrap();
368
369 let (ghost_b, offset) = if let Some((offset, dist)) = &mut self.ghost {
370 if *dist - text.len().byte() >= offset.byte() {
371 *dist -= text.len().byte();
372 return true;
373 }
374 (
375 text.point_at_byte(offset.byte() + text.len().byte() - *dist),
376 *offset,
377 )
378 } else {
379 let this = text.len();
380 let points = self.text.ghost_max_points_at(b);
381 (this, points.ghost.unwrap())
382 };
383
384 let iter = text.iter_rev(ghost_b.to_two_points_before());
385 let point = std::mem::replace(&mut self.point, offset);
386 let chars = std::mem::replace(&mut self.chars, iter.chars);
387 let tags = std::mem::replace(&mut self.tags, iter.tags);
388
389 self.ghost = Some((offset, offset.byte()));
390 self.main_iter = Some((point, chars, tags));
391 }
392
393 RawTag::StartConceal(_) => {
394 self.conceals = self.conceals.saturating_sub(1);
395 if self.conceals == 0 {
396 self.ghost.take_if(|_| b < self.point.byte());
397 self.point = self.point.min(self.text.point_at_byte(b));
398 self.chars = buf_chars_rev(self.text, self.point.byte());
399 }
400 }
401 RawTag::EndConceal(_) => self.conceals += 1,
402 RawTag::ConcealUntil(b) => {
403 let point = self.text.point_at_byte(*b as usize);
404 *self = RevIter::new_at(self.text, point.to_two_points_before());
405 return false;
406 }
407 RawTag::MainCaret(_)
408 | RawTag::ExtraCaret(_)
409 | RawTag::Spacer(_)
410 | RawTag::SpawnedWidget(..)
411 if b > self.init_point.byte() => {}
412 _ => return false,
413 }
414
415 true
416 }
417}
418
419impl Iterator for RevIter<'_> {
420 type Item = Item;
421
422 #[inline]
423 fn next(&mut self) -> Option<Self::Item> {
424 let tag = self.tags.peek();
425
426 if let Some(&(b, tag)) = tag
427 && (b >= self.point.byte() || self.conceals > 0)
428 {
429 self.tags.next();
430
431 if self.handled_meta_tag(&tag, b) {
432 self.next()
433 } else {
434 Some(Item::new(self.points(), Part::from_raw(tag)))
435 }
436 } else if let Some(char) = self.chars.next() {
437 self.point = self.point.rev(char);
438
439 self.ghost = match self.main_iter {
440 Some(..) => self.ghost.map(|(g, d)| (g.rev(char), d - 1)),
441 None => None,
442 };
443
444 Some(Item::new(self.points(), Part::Char(char)))
445 } else if let Some(last_iter) = self.main_iter.take() {
446 (self.point, self.chars, self.tags) = last_iter;
447 self.next()
448 } else {
449 None
450 }
451 }
452}
453
454fn buf_chars_fwd(text: &Text, b: usize) -> FwdChars<'_> {
455 let [s0, s1] = text
456 .slices(b..)
457 .to_array()
458 .map(|s| unsafe { std::str::from_utf8_unchecked(s) });
459 s0.chars().chain(s1.chars())
460}
461
462fn buf_chars_rev(text: &Text, b: usize) -> RevChars<'_> {
463 let [s0, s1] = text
464 .slices(..b)
465 .to_array()
466 .map(|s| unsafe { std::str::from_utf8_unchecked(s) });
467 s1.chars().rev().chain(s0.chars().rev())
468}
469
470#[derive(Debug, Clone, Copy)]
482pub struct Item {
483 pub real: Point,
485 pub ghost: Option<Point>,
495 pub part: Part,
499}
500
501impl Item {
502 #[inline]
504 const fn new(points: TwoPoints, part: Part) -> Self {
505 let TwoPoints { real, ghost } = points;
506 Self { real, ghost, part }
507 }
508
509 pub const fn is_real(&self) -> bool {
513 self.ghost.is_none()
514 }
515
516 pub const fn as_real_char(self) -> Option<(Point, char)> {
520 let Some(char) = self.part.as_char() else {
521 return None;
522 };
523 if self.ghost.is_none() {
524 Some((self.real, char))
525 } else {
526 None
527 }
528 }
529
530 pub const fn byte(&self) -> usize {
532 self.real.byte()
533 }
534
535 pub const fn char(&self) -> usize {
537 self.real.char()
538 }
539
540 pub const fn line(&self) -> usize {
542 self.real.line()
543 }
544
545 pub const fn points(&self) -> TwoPoints {
547 if let Some(ghost) = self.ghost {
548 TwoPoints::new(self.real, ghost)
549 } else {
550 TwoPoints::new_after_ghost(self.real)
551 }
552 }
553}
554
555#[allow(dead_code)]
557#[derive(Debug, Default, Clone)]
558enum Conceal<'a> {
559 #[default]
560 All,
561 None,
562 Excluding(&'a [Selection]),
563 NotOnLineOf(&'a [Selection]),
564}
565
566type FwdChars<'a> = Chain<Chars<'a>, Chars<'a>>;
567type RevChars<'a> = Chain<Rev<Chars<'a>>, Rev<Chars<'a>>>;
568
569use crate::form::FormId;
570
571#[derive(Debug, Clone, Copy, PartialEq, Eq)]
589pub enum Part {
590 Char(char),
594 PushForm(FormId, u8),
599 PopForm(FormId),
604 MainCaret,
610 ExtraCaret,
616 AlignLeft,
618 AlignCenter,
620 AlignRight,
622 Spacer,
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::StartAlignCenter(_) => Part::AlignCenter,
664 RawTag::EndAlignCenter(_) => Part::AlignLeft,
665 RawTag::StartAlignRight(_) => Part::AlignRight,
666 RawTag::EndAlignRight(_) => Part::AlignLeft,
667 RawTag::Spacer(_) => Part::Spacer,
668 RawTag::StartToggle(_, id) => Part::ToggleStart(id),
669 RawTag::EndToggle(_, id) => Part::ToggleEnd(id),
670 RawTag::ConcealUntil(_) => Part::ResetState,
671 RawTag::SpawnedWidget(_, id) => Part::SpawnedWidget(id),
672 RawTag::StartConceal(_) | RawTag::EndConceal(_) | RawTag::Ghost(..) => {
673 unreachable!("These tags are automatically processed elsewhere.")
674 }
675 }
676 }
677
678 #[must_use]
682 #[inline]
683 pub const fn is_char(&self) -> bool {
684 matches!(self, Part::Char(_))
685 }
686
687 #[inline]
691 pub const fn as_char(&self) -> Option<char> {
692 if let Self::Char(v) = self {
693 Some(*v)
694 } else {
695 None
696 }
697 }
698
699 #[inline]
703 pub const fn is_tag(&self) -> bool {
704 !self.is_char()
705 }
706}