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