1use std::{
16 iter::{Chain, Rev},
17 str::Chars,
18};
19
20use super::{
21 Point, Text, ToggleId, TwoPoints,
22 tags::{self, RawTag},
23};
24use crate::mode::Cursor;
25
26#[derive(Debug, Clone, Copy)]
27pub struct Item {
28 pub real: Point,
29 pub ghost: Option<Point>,
30 pub part: Part,
31}
32
33impl Item {
34 pub fn is_real(&self) -> bool {
35 self.ghost.is_none()
36 }
37
38 pub fn as_real_char(self) -> Option<(Point, char)> {
39 if self.ghost.is_none() {
40 Some(self.real).zip(self.part.as_char())
41 } else {
42 None
43 }
44 }
45
46 pub fn byte(&self) -> usize {
47 self.real.byte()
48 }
49
50 pub fn char(&self) -> usize {
51 self.real.char()
52 }
53
54 pub fn line(&self) -> usize {
55 self.real.line()
56 }
57
58 pub fn points(&self) -> (Point, Option<Point>) {
59 (self.real, self.ghost)
60 }
61
62 pub fn lines(&self) -> (usize, Option<usize>) {
63 (self.real.line(), self.ghost.map(|g| g.line()))
64 }
65
66 #[inline]
67 fn new(tp: impl TwoPoints, part: Part) -> Self {
68 let (real, ghost) = tp.to_points();
69 Self { real, ghost, part }
70 }
71}
72
73#[derive(Clone)]
78pub struct FwdIter<'a> {
79 text: &'a Text,
80 point: Point,
81 init_point: Point,
82 chars: FwdChars<'a>,
83 tags: tags::FwdTags<'a>,
84 conceals: u32,
85
86 main_iter: Option<(Point, FwdChars<'a>, tags::FwdTags<'a>)>,
88 ghost: Option<(Point, usize)>,
89
90 print_ghosts: bool,
92 _conceals: Conceal<'a>,
93}
94
95impl<'a> FwdIter<'a> {
96 pub(super) fn new_at(text: &'a Text, tp: impl TwoPoints) -> Self {
97 let (r, g) = tp.to_points();
98 let point = r.min(text.len());
99
100 let ghost = g.and_then(|offset| {
101 let (_, max) = text.ghost_max_points_at(r.byte());
102 max.map(|max| (max.min(offset), 0))
103 });
104
105 Self {
106 text,
107 point,
108 init_point: point,
109 chars: buf_chars_fwd(text, point.byte()),
110 tags: text.tags_fwd(point.byte()),
111 conceals: 0,
112
113 main_iter: None,
114 ghost,
115
116 print_ghosts: true,
117 _conceals: Conceal::All,
118 }
119 }
120
121 pub fn no_conceals(self) -> Self {
122 Self { _conceals: Conceal::None, ..self }
123 }
124
125 pub fn dont_conceal_containing(self, list: &'a [Cursor]) -> Self {
126 Self {
127 _conceals: Conceal::Excluding(list),
128 ..self
129 }
130 }
131
132 pub fn no_ghosts(self) -> Self {
133 Self { print_ghosts: false, ..self }
134 }
135
136 pub fn no_tags(self) -> impl Iterator<Item = Item> + 'a {
137 self.filter(|item| item.part.is_char())
138 }
139
140 pub fn skip_to(&mut self, tp: impl TwoPoints) {
141 *self = self.text.iter_fwd(tp.to_points().max(self.points()))
142 }
143
144 #[inline]
145 fn handle_special_tag(&mut self, tag: &RawTag, b: usize) -> bool {
146 match tag {
147 RawTag::GhostText(_, id) => {
148 if !self.print_ghosts || b < self.point.byte() || self.conceals > 0 {
149 return true;
150 }
151 let text = self.text.get_ghost(*id).unwrap();
152
153 let (this_ghost, total_ghost) = if let Some((ghost, dist)) = &mut self.ghost {
154 if ghost.byte() >= *dist + text.len().byte() {
155 *dist += text.len().byte();
156 return true;
157 }
158 (text.point_at(ghost.byte() - *dist), *ghost)
159 } else {
160 (Point::default(), Point::default())
161 };
162
163 let iter = text.iter_fwd(this_ghost);
164 let point = std::mem::replace(&mut self.point, this_ghost);
165 let chars = std::mem::replace(&mut self.chars, iter.chars);
166 let tags = std::mem::replace(&mut self.tags, iter.tags);
167
168 self.ghost = Some((total_ghost, total_ghost.byte()));
169 self.main_iter = Some((point, chars, tags));
170 }
171 RawTag::StartConceal(_) => {
172 self.conceals += 1;
173 }
174 RawTag::EndConceal(_) => {
175 self.conceals = self.conceals.saturating_sub(1);
176 if self.conceals == 0 {
177 self.ghost.take_if(|_| self.point.byte() < b);
180 self.point = self.point.max(self.text.point_at(b));
181 self.chars = buf_chars_fwd(self.text, self.point.byte());
182 }
183 }
184 RawTag::ConcealUntil(b) => {
185 let point = self.text.point_at(*b as usize);
186 *self = FwdIter::new_at(self.text, point);
187 return false;
188 }
189 RawTag::MainCursor(_) | RawTag::ExtraCursor(_) | RawTag::Spacer(_)
190 if b < self.init_point.byte() => {}
191 _ => return false,
192 }
193
194 true
195 }
196
197 pub fn on_ghost(&self) -> bool {
198 self.main_iter.is_some()
199 }
200
201 pub fn points(&self) -> (Point, Option<Point>) {
202 if let Some((real, ..)) = self.main_iter.as_ref() {
203 (*real, self.ghost.map(|(tg, _)| tg))
204 } else {
205 (self.point, self.ghost.map(|(p, _)| p))
206 }
207 }
208}
209
210impl Iterator for FwdIter<'_> {
211 type Item = Item;
212
213 #[inline]
214 fn next(&mut self) -> Option<Self::Item> {
215 let tag = self.tags.peek();
216
217 if let Some(&(b, tag)) = tag
218 && (b <= self.point.byte() || self.conceals > 0)
219 {
220 self.tags.next();
221
222 if self.handle_special_tag(&tag, b) {
223 self.next()
224 } else {
225 Some(Item::new(self.points(), Part::from_raw(tag)))
226 }
227 } else if let Some(char) = self.chars.next() {
228 let points = self.points();
229 self.point = self.point.fwd(char);
230
231 self.ghost = match self.main_iter {
232 Some(..) => self.ghost.map(|(g, d)| (g.fwd(char), d + char.len_utf8())),
233 None => None,
234 };
235
236 Some(Item::new(points, Part::Char(char)))
237 } else if let Some(backup) = self.main_iter.take() {
238 (self.point, self.chars, self.tags) = backup;
239 self.next()
240 } else {
241 None
242 }
243 }
244}
245
246#[derive(Clone)]
251pub struct RevIter<'a> {
252 text: &'a Text,
253 point: Point,
254 init_point: Point,
255 chars: RevChars<'a>,
256 tags: tags::RevTags<'a>,
257 conceals: usize,
258
259 main_iter: Option<(Point, RevChars<'a>, tags::RevTags<'a>)>,
260 ghost: Option<(Point, usize)>,
261
262 print_ghosts: bool,
264 _conceals: Conceal<'a>,
265}
266
267impl<'a> RevIter<'a> {
268 pub(super) fn new_at(text: &'a Text, tp: impl TwoPoints) -> Self {
269 let (r, g) = tp.to_points();
270 let point = r.min(text.len());
271
272 let ghost = g.and_then(|offset| {
273 let (_, max) = text.ghost_max_points_at(r.byte());
274 max.map(|max| (max.min(offset), max.byte()))
275 });
276
277 Self {
278 text,
279 point,
280 init_point: point,
281 chars: buf_chars_rev(text, point.byte()),
282 tags: text.tags_rev(point.byte()),
283 conceals: 0,
284
285 main_iter: None,
286 ghost,
287
288 print_ghosts: true,
289 _conceals: Conceal::All,
290 }
291 }
292
293 pub fn no_conceals(self) -> Self {
294 Self { _conceals: Conceal::None, ..self }
295 }
296
297 pub fn no_ghosts(self) -> Self {
298 Self { print_ghosts: false, ..self }
299 }
300
301 pub fn no_tags(self) -> impl Iterator<Item = Item> + 'a {
302 self.filter(|item| item.part.is_char())
303 }
304
305 #[inline]
306 fn handled_meta_tag(&mut self, tag: &RawTag, b: usize) -> bool {
307 match tag {
308 RawTag::GhostText(_, id) => {
309 if !self.print_ghosts || b > self.point.byte() || self.conceals > 0 {
310 return true;
311 }
312 let text = self.text.get_ghost(*id).unwrap();
313
314 let (ghost_b, offset) = if let Some((offset, dist)) = &mut self.ghost {
315 if *dist - text.len().byte() >= offset.byte() {
316 *dist -= text.len().byte();
317 return true;
318 }
319 (
320 text.point_at(offset.byte() + text.len().byte() - *dist),
321 *offset,
322 )
323 } else {
324 let this = text.len();
325 let (_, max) = self.text.ghost_max_points_at(b);
326 (this, max.unwrap())
327 };
328
329 let iter = text.iter_rev(ghost_b);
330 let point = std::mem::replace(&mut self.point, offset);
331 let chars = std::mem::replace(&mut self.chars, iter.chars);
332 let tags = std::mem::replace(&mut self.tags, iter.tags);
333
334 self.ghost = Some((offset, offset.byte()));
335 self.main_iter = Some((point, chars, tags));
336 }
337
338 RawTag::StartConceal(_) => {
339 self.conceals = self.conceals.saturating_sub(1);
340 if self.conceals == 0 {
341 self.ghost.take_if(|_| b < self.point.byte());
342 self.point = self.point.min(self.text.point_at(b));
343 self.chars = buf_chars_rev(self.text, self.point.byte());
344 }
345 }
346 RawTag::EndConceal(_) => self.conceals += 1,
347 RawTag::ConcealUntil(b) => {
348 let point = self.text.point_at(*b as usize);
349 *self = RevIter::new_at(self.text, point);
350 return false;
351 }
352 RawTag::MainCursor(_) | RawTag::ExtraCursor(_) | RawTag::Spacer(_)
353 if b > self.init_point.byte() => {}
354 _ => return false,
355 }
356
357 true
358 }
359
360 pub fn points(&self) -> (Point, Option<Point>) {
361 if let Some((real, ..)) = self.main_iter.as_ref() {
362 (*real, Some(self.point))
363 } else {
364 (self.point, self.ghost.map(|(p, _)| p))
365 }
366 }
367
368 pub fn is_on_ghost(&self) -> bool {
369 self.main_iter.is_some()
370 }
371}
372
373impl Iterator for RevIter<'_> {
374 type Item = Item;
375
376 #[inline]
377 fn next(&mut self) -> Option<Self::Item> {
378 let tag = self.tags.peek();
379
380 if let Some(&(b, tag)) = tag
381 && (b >= self.point.byte() || self.conceals > 0)
382 {
383 self.tags.next();
384
385 if self.handled_meta_tag(&tag, b) {
386 self.next()
387 } else {
388 Some(Item::new(self.points(), Part::from_raw(tag)))
389 }
390 } else if let Some(char) = self.chars.next() {
391 self.point = self.point.rev(char);
392
393 self.ghost = match self.main_iter {
394 Some(..) => self.ghost.map(|(g, d)| (g.rev(char), d - char.len_utf8())),
395 None => None,
396 };
397
398 Some(Item::new(self.points(), Part::Char(char)))
399 } else if let Some(last_iter) = self.main_iter.take() {
400 (self.point, self.chars, self.tags) = last_iter;
401 self.next()
402 } else {
403 None
404 }
405 }
406}
407
408fn buf_chars_fwd(text: &Text, b: usize) -> FwdChars {
409 let [s0, s1] = text.strs(b..).to_array();
410 s0.chars().chain(s1.chars())
411}
412
413fn buf_chars_rev(text: &Text, b: usize) -> RevChars {
414 let [s0, s1] = text.strs(..b).to_array();
415 s1.chars().rev().chain(s0.chars().rev())
416}
417
418#[allow(dead_code)]
420#[derive(Debug, Default, Clone)]
421enum Conceal<'a> {
422 #[default]
423 All,
424 None,
425 Excluding(&'a [Cursor]),
426 NotOnLineOf(&'a [Cursor]),
427}
428
429type FwdChars<'a> = Chain<Chars<'a>, Chars<'a>>;
430type RevChars<'a> = Chain<Rev<Chars<'a>>, Rev<Chars<'a>>>;
431
432use crate::form::FormId;
433
434#[derive(Debug, Clone, Copy, PartialEq, Eq)]
452pub enum Part {
453 Char(char),
454 PushForm(FormId),
455 PopForm(FormId),
456 MainCursor,
457 ExtraCursor,
458 AlignLeft,
459 AlignCenter,
460 AlignRight,
461 Spacer,
462 ToggleStart(ToggleId),
463 ToggleEnd(ToggleId),
464 ResetState,
465}
466
467impl Part {
468 #[inline]
470 pub(super) fn from_raw(value: RawTag) -> Self {
471 match value {
472 RawTag::PushForm(_, id, _) => Part::PushForm(id),
473 RawTag::PopForm(_, id) => Part::PopForm(id),
474 RawTag::MainCursor(_) => Part::MainCursor,
475 RawTag::ExtraCursor(_) => Part::ExtraCursor,
476 RawTag::StartAlignCenter(_) => Part::AlignCenter,
477 RawTag::EndAlignCenter(_) => Part::AlignLeft,
478 RawTag::StartAlignRight(_) => Part::AlignRight,
479 RawTag::EndAlignRight(_) => Part::AlignLeft,
480 RawTag::Spacer(_) => Part::Spacer,
481 RawTag::ToggleStart(_, id) => Part::ToggleStart(id),
482 RawTag::ToggleEnd(_, id) => Part::ToggleEnd(id),
483 RawTag::ConcealUntil(_) => Part::ResetState,
484 RawTag::StartConceal(_) | RawTag::EndConceal(_) | RawTag::GhostText(..) => {
485 unreachable!("These tags are automatically processed elsewhere.")
486 }
487 }
488 }
489
490 #[must_use]
494 #[inline]
495 pub fn is_char(&self) -> bool {
496 matches!(self, Part::Char(_))
497 }
498
499 #[inline]
503 pub fn as_char(&self) -> Option<char> {
504 if let Self::Char(v) = self {
505 Some(*v)
506 } else {
507 None
508 }
509 }
510
511 #[inline]
515 pub fn is_tag(&self) -> bool {
516 !self.is_char()
517 }
518}