duat_term/area/
mod.rs

1mod iter;
2
3use std::{cell::{Cell, RefCell}, fmt::Alignment, rc::Rc, sync::Arc};
4
5use crossterm::cursor;
6use duat_core::{
7    cfg::PrintCfg,
8    context::bincode::{Decode, Encode},
9    form::Painter,
10    text::{FwdIter, Item, Part, Point, RevIter, Text, txt},
11    ui::{self, Axis, Caret, Constraint, MutArea, PushSpecs, SpawnSpecs},
12};
13use iter::{print_iter, print_iter_indented, rev_print_iter};
14
15use crate::{
16    AreaId, CStyle, Mutex,
17    layout::{Layout, Rect, transfer_vars},
18    print::Gaps,
19    queue, style,
20};
21
22#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
23pub struct Coord {
24    pub y: u32,
25    pub x: u32,
26}
27
28impl std::fmt::Debug for Coord {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        f.write_fmt(format_args!("x: {}, y: {}", self.x, self.y))
31    }
32}
33
34impl Coord {
35    pub fn new(x: u32, y: u32) -> Coord {
36        Coord { x, y }
37    }
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
41pub struct Coords {
42    pub tl: Coord,
43    pub br: Coord,
44}
45impl Coords {
46    pub fn new(tl: Coord, br: Coord) -> Self {
47        Coords { tl, br }
48    }
49
50    pub fn width(&self) -> u32 {
51        self.br.x - self.tl.x
52    }
53
54    pub fn height(&self) -> u32 {
55        self.br.y - self.tl.y
56    }
57
58    pub fn intersects(&self, other: Self) -> bool {
59        let and_tl = self.tl.max(other.tl);
60        let and_br = self.br.min(other.br);
61        if and_tl.x > and_br.x || and_tl.y > and_br.y {
62            return false;
63        }
64
65        let and_coords = Coords::new(and_tl, and_br);
66        and_coords.width() > 0 && and_coords.height() > 0
67    }
68}
69
70#[derive(Clone)]
71pub struct Area {
72    layouts: Rc<RefCell<Vec<Layout>>>,
73    pub id: AreaId,
74    ansi_codes: Arc<Mutex<micromap::Map<CStyle, String, 16>>>,
75}
76
77impl PartialEq for Area {
78    fn eq(&self, other: &Self) -> bool {
79        self.id == other.id
80    }
81}
82
83impl Area {
84    pub fn new(id: AreaId, layouts: Rc<RefCell<Vec<Layout>>>) -> Self {
85        Self { layouts, id, ansi_codes: Arc::default() }
86    }
87
88    fn print<'a>(
89        &self,
90        text: &mut Text,
91        cfg: PrintCfg,
92        mut painter: Painter,
93        mut f: impl FnMut(&Caret, &Item) + 'a,
94    ) {
95        let layouts = self.layouts.borrow();
96        let mut ansi_codes = self.ansi_codes.lock().unwrap();
97
98        let layout = get_layout(&layouts, self.id).unwrap();
99        let is_active = layout.active_id() == self.id;
100
101        let (mut lines, iter) = {
102            let rect = layout.get(self.id).unwrap();
103
104            let info = {
105                let mut info = rect.print_info().unwrap().get();
106                info.fix(text);
107                rect.print_info().unwrap().set(info);
108                info
109            };
110
111            let lines = layout.printer.lines(rect.var_points(), info.x_shift, cfg);
112            if lines.coords().width() == 0 || lines.coords().height() == 0 {
113                return;
114            }
115
116            let iter = {
117                let line_start = text.visual_line_start(info.s_points);
118                let iter = text.iter_fwd(line_start);
119                print_iter(iter, lines.cap(), cfg, info.s_points)
120            };
121
122            (lines, iter)
123        };
124
125        let mut style_was_set = false;
126        enum Cursor {
127            Main,
128            Extra,
129        }
130
131        let lines_left = {
132            // The y here represents the bottom of the current row of cells.
133            let tl_y = lines.coords().tl.y;
134            let mut y = tl_y;
135            let mut cursor = None;
136
137            for (caret, item) in iter {
138                f(&caret, &item);
139
140                let Caret { x, len, wrap } = caret;
141                let Item { part, .. } = item;
142
143                if wrap {
144                    if y > lines.coords().tl.y {
145                        lines.end_line(&mut ansi_codes, &painter);
146                    }
147                    if y == lines.coords().br.y {
148                        break;
149                    }
150                    (0..x).for_each(|_| lines.push_char(' ', 1));
151                    style!(lines, &mut ansi_codes, painter.absolute_style());
152                    y += 1
153                }
154
155                match part {
156                    Part::Char(char) => {
157                        if style_was_set {
158                            style!(lines, &mut ansi_codes, painter.relative_style());
159                            style_was_set = false;
160                        }
161                        match char {
162                            '\t' => (0..len).for_each(|_| lines.push_char(' ', 1)),
163                            '\n' => {}
164                            char => lines.push_char(char, len),
165                        }
166                        if let Some(cursor) = cursor.take() {
167                            match cursor {
168                                Cursor::Main => painter.remove_main_caret(),
169                                Cursor::Extra => painter.remove_extra_caret(),
170                            }
171                            style!(lines, &mut ansi_codes, painter.relative_style())
172                        }
173                    }
174                    Part::PushForm(id) => {
175                        painter.apply(id);
176                        style_was_set = true;
177                    }
178                    Part::PopForm(id) => {
179                        painter.remove(id);
180                        style_was_set = true;
181                    }
182                    Part::MainCaret => {
183                        if let Some(shape) = painter.main_cursor()
184                            && is_active
185                        {
186                            lines.show_real_cursor();
187                            queue!(lines, shape, cursor::SavePosition);
188                        } else {
189                            cursor = Some(Cursor::Main);
190                            lines.hide_real_cursor();
191                            painter.apply_main_cursor();
192                            style_was_set = true;
193                        }
194                    }
195                    Part::ExtraCaret => {
196                        cursor = Some(Cursor::Extra);
197                        painter.apply_extra_cursor();
198                        style_was_set = true;
199                    }
200                    Part::AlignLeft if !cfg.wrap_method.is_no_wrap() => {
201                        lines.realign(Alignment::Left)
202                    }
203                    Part::AlignCenter if !cfg.wrap_method.is_no_wrap() => {
204                        lines.realign(Alignment::Center)
205                    }
206                    Part::AlignRight if !cfg.wrap_method.is_no_wrap() => {
207                        lines.realign(Alignment::Right)
208                    }
209                    Part::Spacer if !cfg.wrap_method.is_no_wrap() => {
210                        lines.add_spacer();
211                    }
212                    Part::ResetState => {
213                        style!(lines, &mut ansi_codes, painter.reset())
214                    }
215                    Part::ToggleStart(_) | Part::ToggleEnd(_) => todo!(),
216                    _ => {}
217                }
218            }
219
220            lines.end_line(&mut ansi_codes, &painter);
221
222            lines.coords().br.y - y
223        };
224
225        for _ in 0..lines_left {
226            lines.end_line(&mut ansi_codes, &painter);
227        }
228
229        layout.printer.send(self.id, lines);
230    }
231}
232
233impl ui::RawArea for Area {
234    type Cache = PrintInfo;
235    type PrintInfo = PrintInfo;
236    type Ui = crate::Ui;
237
238    /////////// Modification
239
240    fn bisect(
241        area: MutArea<Self>,
242        specs: PushSpecs,
243        cluster: bool,
244        on_files: bool,
245        cache: PrintInfo,
246    ) -> (Area, Option<Area>) {
247        let mut layouts = area.layouts.borrow_mut();
248        let layout = get_layout_mut(&mut layouts, area.id).unwrap();
249
250        let (child, parent) = layout.bisect(area.id, specs, cluster, on_files, cache);
251
252        (
253            Self::new(child, area.layouts.clone()),
254            parent.map(|parent| Self::new(parent, area.layouts.clone())),
255        )
256    }
257
258    fn delete(area: MutArea<Self>) -> Option<Self> {
259        let mut layouts = area.layouts.borrow_mut();
260        // This Area may have already been deleted, so a Layout may not be
261        // found.
262        let layout = get_layout_mut(&mut layouts, area.id)?;
263        layout
264            .delete(area.id)
265            .map(|id| Self::new(id, area.layouts.clone()))
266    }
267
268    fn swap(lhs: MutArea<Self>, rhs: &Self) {
269        let mut layouts = lhs.layouts.borrow_mut();
270        let lhs_lay = get_layout_pos(&layouts, lhs.id).unwrap();
271        let rhs_lay = get_layout_pos(&layouts, rhs.id).unwrap();
272
273        if lhs_lay == rhs_lay {
274            let layout = &mut layouts[lhs_lay];
275            let lhs_id = layout.rects.get_cluster_master(lhs.id).unwrap_or(lhs.id);
276            let rhs_id = layout.rects.get_cluster_master(rhs.id).unwrap_or(rhs.id);
277            if lhs_id == rhs_id {
278                return;
279            }
280            layout.swap(lhs_id, rhs_id);
281        } else {
282            let [lhs_lay, rhs_lay] = layouts.get_disjoint_mut([lhs_lay, rhs_lay]).unwrap();
283            let lhs_id = lhs_lay.rects.get_cluster_master(lhs.id).unwrap_or(lhs.id);
284            let rhs_id = rhs_lay.rects.get_cluster_master(rhs.id).unwrap_or(rhs.id);
285
286            let lhs_rect = lhs_lay.rects.get_mut(lhs_id).unwrap();
287            let rhs_rect = rhs_lay.rects.get_mut(rhs_id).unwrap();
288
289            let lhs_p = &lhs_lay.printer;
290            let rhs_p = &rhs_lay.printer;
291            transfer_vars(lhs_p, rhs_p, lhs_rect);
292            transfer_vars(rhs_p, lhs_p, rhs_rect);
293
294            std::mem::swap(lhs_rect, rhs_rect);
295
296            lhs_lay.reset_eqs(rhs_id);
297            rhs_lay.reset_eqs(lhs_id);
298        }
299
300        for lay in [lhs_lay, rhs_lay] {
301            layouts[lay].printer.update(false);
302        }
303    }
304
305    fn spawn_floating(area: MutArea<Self>, specs: SpawnSpecs) -> Result<Self, Text> {
306        let mut layouts = area.layouts.borrow_mut();
307        let layout = get_layout_mut(&mut layouts, area.id).unwrap();
308
309        Ok(Self::new(
310            layout.new_floating(area.id, specs),
311            area.layouts.clone(),
312        ))
313    }
314
315    fn spawn_floating_at(
316        _area: MutArea<Self>,
317        _specs: SpawnSpecs,
318        _at: impl duat_core::text::TwoPoints,
319        _text: &Text,
320        _cfg: PrintCfg,
321    ) -> Result<Self, Text> {
322        todo!()
323    }
324
325    fn constrain_hor(&self, cons: impl IntoIterator<Item = Constraint>) -> Result<(), Text> {
326        let cons = {
327            let mut cons: Vec<Constraint> = cons.into_iter().collect();
328            cons.sort_unstable();
329            cons
330        };
331
332        let mut layouts = self.layouts.borrow_mut();
333        let layout = get_layout_mut(&mut layouts, self.id).unwrap();
334        let old_cons = layout
335            .rects
336            .get_constraints_mut(self.id)
337            .ok_or_else(|| txt!("Area has no parents, so it can't be constrained"))?
338            .clone();
339
340        if old_cons.on(Axis::Horizontal).eq(cons.iter().cloned()) {
341            return Ok(());
342        };
343
344        *layout.rects.get_constraints_mut(self.id).unwrap() = {
345            let (cons, old_eqs) = old_cons.replace(cons.into_iter(), Axis::Horizontal);
346
347            let (_, parent) = layout.get_parent(self.id).unwrap();
348            let rect = layout.get(self.id).unwrap();
349
350            let (cons, new_eqs) = cons.apply(rect, parent.id(), &layout.rects);
351            layout.printer.replace_and_update(old_eqs, new_eqs, false);
352            cons
353        };
354
355        Ok(())
356    }
357
358    fn constrain_ver(&self, cons: impl IntoIterator<Item = Constraint>) -> Result<(), Text> {
359        let cons = {
360            let mut cons: Vec<Constraint> = cons.into_iter().collect();
361            cons.sort_unstable();
362            cons
363        };
364
365        let mut layouts = self.layouts.borrow_mut();
366        let layout = get_layout_mut(&mut layouts, self.id).unwrap();
367        let old_cons = layout
368            .rects
369            .get_constraints_mut(self.id)
370            .ok_or_else(|| txt!("Area has no parents, so it can't be constrained"))?
371            .clone();
372
373        if old_cons.on(Axis::Vertical).eq(cons.iter().cloned()) {
374            return Ok(());
375        };
376
377        *layout.rects.get_constraints_mut(self.id).unwrap() = {
378            let (cons, old_eqs) = old_cons.replace(cons.into_iter(), Axis::Vertical);
379
380            let (_, parent) = layout.get_parent(self.id).unwrap();
381            let rect = layout.get(self.id).unwrap();
382
383            let (cons, new_eqs) = cons.apply(rect, parent.id(), &layout.rects);
384            layout.printer.replace_and_update(old_eqs, new_eqs, false);
385            cons
386        };
387
388        Ok(())
389    }
390
391    fn hide(&self) -> Result<(), Text> {
392        let mut layouts = self.layouts.borrow_mut();
393        let layout = get_layout_mut(&mut layouts, self.id).unwrap();
394        let mut old_cons = layout
395            .rects
396            .get_constraints_mut(self.id)
397            .ok_or_else(|| txt!("Area has no parents, so it can't be constrained"))?
398            .clone();
399
400        if old_cons.is_hidden {
401            return Ok(());
402        };
403
404        *layout.rects.get_constraints_mut(self.id).unwrap() = {
405            let old_eqs = old_cons.get_eqs();
406            old_cons.is_hidden = true;
407
408            let (_, parent) = layout.get_parent(self.id).unwrap();
409            let rect = layout.get(self.id).unwrap();
410
411            let (cons, new_eqs) = old_cons.apply(rect, parent.id(), &layout.rects);
412            layout.printer.replace_and_update(old_eqs, new_eqs, false);
413            cons
414        };
415
416        Ok(())
417    }
418
419    fn reveal(&self) -> Result<(), Text> {
420        let mut layouts = self.layouts.borrow_mut();
421        let layout = get_layout_mut(&mut layouts, self.id).unwrap();
422        let mut old_cons = layout
423            .rects
424            .get_constraints_mut(self.id)
425            .ok_or_else(|| txt!("Area has no parents, so it can't be constrained"))?
426            .clone();
427
428        if !old_cons.is_hidden {
429            return Ok(());
430        };
431
432        *layout.rects.get_constraints_mut(self.id).unwrap() = {
433            let old_eqs = old_cons.get_eqs();
434            old_cons.is_hidden = false;
435
436            let (_, parent) = layout.get_parent(self.id).unwrap();
437            let rect = layout.get(self.id).unwrap();
438
439            let (cons, new_eqs) = old_cons.apply(rect, parent.id(), &layout.rects);
440            layout.printer.replace_and_update(old_eqs, new_eqs, false);
441            cons
442        };
443
444        Ok(())
445    }
446
447    fn request_width_to_fit(&self, _text: &str) -> Result<(), Text> {
448        todo!();
449    }
450
451    fn scroll_ver(&self, text: &Text, dist: i32, cfg: PrintCfg) {
452        if dist == 0 {
453            return;
454        }
455
456        let layouts = self.layouts.borrow();
457        let layout = get_layout(&layouts, self.id).unwrap();
458        let rect = layout.get(self.id).unwrap();
459
460        let (mut info, width, height) = {
461            let coords = layout.printer.coords(rect.var_points(), false);
462            let info = rect.print_info().unwrap().get();
463            (info, coords.width(), coords.height())
464        };
465
466        let cap = cfg.wrap_width(width);
467
468        if dist > 0 {
469            let line_start = print_iter(text.iter_fwd(info.s_points), cap, cfg, info.s_points)
470                .filter_map(|(caret, item)| caret.wrap.then_some(item.points()))
471                .nth(dist as usize)
472                .unwrap_or_default();
473
474            let max_s_points = max_s_points(text, cfg, height, cap);
475
476            if line_start < max_s_points {
477                info.s_points = line_start;
478                info.e_points = None;
479            } else {
480                info.s_points = max_s_points;
481                info.e_points = Some(text.len_points());
482            }
483        } else {
484            info.s_points = rev_print_iter(text.iter_rev(info.s_points), cap, cfg)
485                .filter_map(|(caret, item)| caret.wrap.then_some(item.points()))
486                .nth(dist.unsigned_abs() as usize - 1)
487                .unwrap_or_default();
488            info.e_points = None;
489        }
490
491        rect.print_info().unwrap().set(info);
492    }
493
494    ////////// Printing
495
496    fn scroll_around_point(&self, text: &Text, point: Point, cfg: PrintCfg) {
497        let layouts = self.layouts.borrow();
498        let layout = get_layout(&layouts, self.id).unwrap();
499        let rect = layout.get(self.id).unwrap();
500
501        let (mut info, width, height) = {
502            let coords = layout.printer.coords(rect.var_points(), false);
503            let info = rect.print_info().unwrap().get();
504            (info, coords.width(), coords.height())
505        };
506
507        if width > 0 && height > 0 {
508            scroll_ver_around(&mut info, width, height, point, text, cfg);
509            scroll_hor_around(&mut info, width, point, text, cfg);
510        }
511
512        info.prev_main = point;
513        info.e_points = None;
514
515        rect.print_info().unwrap().set(info);
516    }
517
518    fn scroll_to_points(
519        &self,
520        text: &Text,
521        points: impl duat_core::text::TwoPoints,
522        cfg: PrintCfg,
523    ) {
524        let layouts = self.layouts.borrow();
525        let layout = get_layout(&layouts, self.id).unwrap();
526        let rect = layout.get(self.id).unwrap();
527
528        let (mut info, width, height) = {
529            let coords = layout.printer.coords(rect.var_points(), false);
530            let info = rect.print_info().unwrap().get();
531            (info, coords.width(), coords.height())
532        };
533
534        let cap = cfg.wrap_width(width);
535
536        let line_start = rev_print_iter(text.iter_rev(points), cap, cfg)
537            .filter_map(|(caret, item)| caret.wrap.then_some(item.points()))
538            .next()
539            .unwrap_or_default();
540
541        let max_line_start = max_s_points(text, cfg, height, cap);
542
543        if line_start < max_line_start {
544            info.s_points = line_start;
545            info.e_points = None;
546        } else {
547            info.s_points = max_line_start;
548            info.e_points = Some(text.len_points());
549        }
550
551        rect.print_info().unwrap().set(info);
552    }
553
554    fn set_as_active(&self) {
555        let mut layouts = self.layouts.borrow_mut();
556        get_layout_mut(&mut layouts, self.id).unwrap().active_id = self.id;
557    }
558
559    fn print(&self, text: &mut Text, cfg: PrintCfg, painter: Painter) {
560        self.print(text, cfg, painter, |_, _| {})
561    }
562
563    fn print_with<'a>(
564        &self,
565        text: &mut Text,
566        cfg: PrintCfg,
567        painter: Painter,
568        f: impl FnMut(&Caret, &Item) + 'a,
569    ) {
570        self.print(text, cfg, painter, f)
571    }
572
573    ////////// Queries
574
575    fn set_print_info(&self, info: Self::PrintInfo) {
576        let layouts = self.layouts.borrow();
577        let layout = get_layout(&layouts, self.id).unwrap();
578        layout.get(self.id).unwrap().print_info().unwrap().set(info);
579    }
580
581    fn print_iter<'a>(
582        &self,
583        iter: FwdIter<'a>,
584        cfg: PrintCfg,
585    ) -> impl Iterator<Item = (Caret, Item)> + Clone + 'a {
586        let points = iter.points();
587        print_iter(iter, cfg.wrap_width(self.width()), cfg, points)
588    }
589
590    fn rev_print_iter<'a>(
591        &self,
592        iter: RevIter<'a>,
593        cfg: PrintCfg,
594    ) -> impl Iterator<Item = (Caret, Item)> + Clone + 'a {
595        rev_print_iter(iter, cfg.wrap_width(self.width()), cfg)
596    }
597
598    fn has_changed(&self) -> bool {
599        let layouts = self.layouts.borrow();
600        if let Some(layout) = get_layout(&layouts, self.id)
601            && let Some(rect) = layout.get(self.id)
602        {
603            rect.has_changed(layout)
604        } else {
605            true
606        }
607    }
608
609    fn is_master_of(&self, other: &Self) -> bool {
610        if other.id == self.id {
611            return true;
612        }
613        let layouts = self.layouts.borrow();
614        let layout = get_layout(&layouts, self.id).unwrap();
615        let mut parent_id = other.id;
616        while let Some((_, parent)) = layout.get_parent(parent_id) {
617            parent_id = parent.id();
618            if parent.id() == self.id {
619                return true;
620            }
621        }
622
623        parent_id == self.id
624    }
625
626    fn get_cluster_master(&self) -> Option<Self> {
627        let layouts = self.layouts.borrow();
628        get_layout(&layouts, self.id)
629            .unwrap()
630            .rects
631            .get_cluster_master(self.id)
632            .map(|id| Self::new(id, self.layouts.clone()))
633    }
634
635    fn cache(&self) -> Option<Self::Cache> {
636        let layouts = self.layouts.borrow();
637        get_rect(&layouts, self.id)
638            .unwrap()
639            .print_info()
640            .map(Cell::get)
641    }
642
643    fn width(&self) -> u32 {
644        let layouts = self.layouts.borrow();
645        let layout = get_layout(&layouts, self.id).unwrap();
646        let rect = layout.get(self.id).unwrap();
647        let coords = layout.printer.coords(rect.var_points(), false);
648        coords.width()
649    }
650
651    fn height(&self) -> u32 {
652        let layouts = self.layouts.borrow();
653        let layout = get_layout(&layouts, self.id).unwrap();
654        let rect = layout.get(self.id).unwrap();
655        let coords = layout.printer.coords(rect.var_points(), false);
656        coords.height()
657    }
658
659    fn start_points(&self, _text: &Text, _cfg: PrintCfg) -> (Point, Option<Point>) {
660        let layouts = self.layouts.borrow();
661        layouted::start_points(self, &layouts)
662    }
663
664    fn end_points(&self, text: &Text, cfg: PrintCfg) -> (Point, Option<Point>) {
665        let layouts = self.layouts.borrow();
666        layouted::end_points(self, &layouts, text, cfg)
667    }
668
669    fn print_info(&self) -> Self::PrintInfo {
670        let layouts = self.layouts.borrow();
671        get_rect(&layouts, self.id)
672            .unwrap()
673            .print_info()
674            .unwrap()
675            .get()
676    }
677
678    fn is_active(&self) -> bool {
679        get_layout(&self.layouts.borrow(), self.id)
680            .unwrap()
681            .active_id
682            == self.id
683    }
684}
685
686// NOTE: The defaultness in here, when it comes to `last_main`, may
687// cause issues in the future.
688/// Information about how to print the file on the `Label`.
689#[derive(Default, Clone, Copy, PartialEq, Eq, Debug, Encode, Decode)]
690#[bincode(crate = "duat_core::context::bincode")]
691pub struct PrintInfo {
692    s_points: (Point, Option<Point>),
693    x_shift: u32,
694    prev_main: Point,
695    e_points: Option<(Point, Option<Point>)>,
696    vert_dist: u32,
697}
698
699impl PrintInfo {
700    fn fix(&mut self, text: &Text) {
701        let max = text.len().min(self.s_points.0);
702        let (_, max_ghost) = text.ghost_max_points_at(max.byte());
703        self.s_points = (max, self.s_points.1.min(max_ghost))
704    }
705}
706
707/// Scrolls down until the gap between the main cursor and the
708/// bottom of the widget is equal to `config.scrolloff.y_gap`.
709fn scroll_ver_around(
710    info: &mut PrintInfo,
711    width: u32,
712    height: u32,
713    point: Point,
714    text: &Text,
715    cfg: PrintCfg,
716) {
717    if info.prev_main == point {
718        return;
719    }
720
721    let points = text.ghost_max_points_at(point.byte());
722    let after = text
723        .points_after(points)
724        .unwrap_or_else(|| text.len_points());
725
726    let cap = cfg.wrap_width(width);
727
728    let mut below_dist = 0;
729    let mut total_dist = 0;
730    let mut iter = rev_print_iter(text.iter_rev(after), cap, cfg)
731        .filter_map(|(caret, item)| caret.wrap.then_some(item.points()))
732        .inspect(|points| {
733            total_dist += 1;
734            below_dist += (*points >= info.s_points) as u32;
735        });
736
737    let target = if info.prev_main > point {
738        cfg.scrolloff.y as usize
739    } else {
740        height.saturating_sub(cfg.scrolloff.y as u32 + 1) as usize
741    };
742    let first = iter.nth(target).unwrap_or_default();
743
744    if (info.prev_main > point && first <= info.s_points)
745        || (info.prev_main < point && first >= info.s_points)
746    {
747        info.s_points = first;
748        info.vert_dist = total_dist - 1;
749    } else if info.prev_main > point {
750        iter.take_while(|points| *points >= info.s_points)
751            .for_each(|_| {});
752
753        info.vert_dist = below_dist - 1;
754    }
755}
756
757/// Scrolls the file horizontally, usually when no wrapping is
758/// being used.
759fn scroll_hor_around(info: &mut PrintInfo, width: u32, p: Point, text: &Text, cfg: PrintCfg) {
760    let cap = cfg.wrap_width(width);
761    // Quick shortcut to avoid iteration.
762    if cap <= width {
763        info.x_shift = 0;
764        return;
765    }
766
767    let (max_shift, start, end) = {
768        let points = text.ghost_max_points_at(p.byte());
769        let after = text
770            .points_after(points)
771            .unwrap_or_else(|| text.len_points());
772
773        let mut iter = rev_print_iter(text.iter_rev(after), cap, cfg);
774
775        let (points, start, end, wrap) = iter
776            .find_map(|(Caret { x, len, wrap }, item)| {
777                let points = item.points();
778                item.part.as_char().and(Some((points, x, x + len, wrap)))
779            })
780            .unwrap_or(((Point::default(), None), 0, 0, true));
781
782        let (line_len, gaps) = {
783            let mut gaps = Gaps::OnRight;
784            let (indent, points) = if wrap {
785                (start, points)
786            } else {
787                iter.find_map(|(caret, item)| caret.wrap.then_some((caret.x, item.points())))
788                    .unwrap()
789            };
790
791            let len = print_iter_indented(text.iter_fwd(points), cap, cfg, indent)
792                .inspect(|(_, Item { part, real, .. })| match part {
793                    Part::AlignLeft => gaps = Gaps::OnRight,
794                    Part::AlignCenter => gaps = Gaps::OnSides,
795                    Part::AlignRight => gaps = Gaps::OnLeft,
796                    Part::Spacer => gaps.add_spacer(real.byte()),
797                    _ => {}
798                })
799                .take_while(|(caret, item)| !caret.wrap || item.points() == points)
800                .last()
801                .map(|(Caret { x, len, .. }, _)| x + len)
802                .unwrap_or(0);
803
804            (len, gaps)
805        };
806
807        let diff = match &gaps {
808            Gaps::OnRight => 0,
809            Gaps::OnLeft => cap - line_len,
810            Gaps::OnSides => (cap - line_len) / 2,
811            Gaps::Spacers(bytes) => {
812                let spaces = gaps.get_spaces(cap - line_len);
813                bytes
814                    .iter()
815                    .take_while(|b| **b <= p.byte())
816                    .zip(spaces)
817                    .fold(0, |prev_len, (_, len)| prev_len + len)
818            }
819        };
820
821        (line_len + diff, start + diff, end + diff)
822    };
823
824    info.x_shift = info
825        .x_shift
826        .min(start.saturating_sub(cfg.scrolloff.x as u32))
827        .max(if cfg.force_scrolloff {
828            (end + cfg.scrolloff.x as u32).saturating_sub(width)
829        } else {
830            (end + cfg.scrolloff.x as u32)
831                .min(max_shift)
832                .saturating_sub(width)
833        });
834}
835
836mod layouted {
837    use duat_core::{
838        cfg::PrintCfg,
839        text::{Item, Point, Text},
840        ui::Caret,
841    };
842
843    use super::{Area, get_layout, get_rect, iter::print_iter};
844    use crate::layout::Layout;
845
846    pub fn start_points(area: &Area, layouts: &[Layout]) -> (Point, Option<Point>) {
847        let rect = get_rect(layouts, area.id).unwrap();
848        let info = rect.print_info().unwrap().get();
849        info.s_points
850    }
851
852    pub fn end_points(
853        area: &Area,
854        layouts: &[Layout],
855        text: &Text,
856        cfg: PrintCfg,
857    ) -> (Point, Option<Point>) {
858        let (mut info, coords) = {
859            let layout = get_layout(layouts, area.id).unwrap();
860            let rect = layout.get(area.id).unwrap();
861            let info = rect.print_info().unwrap();
862            (info.get(), layout.printer.coords(rect.var_points(), false))
863        };
864
865        if let Some(last) = info.e_points {
866            return last;
867        }
868
869        let (line_start, mut y) = if let Some(cursors) = text.selections()
870            && let Some(main) = cursors.get_main()
871            && main.caret() == info.prev_main
872        {
873            (text.visual_line_start(info.prev_main), info.vert_dist)
874        } else {
875            (text.visual_line_start(info.s_points), 0)
876        };
877
878        let iter = text.iter_fwd(line_start);
879
880        let iter = print_iter(iter, cfg.wrap_width(coords.width()), cfg, info.s_points);
881        let mut points = info.s_points;
882
883        for (Caret { wrap, .. }, Item { part, real, ghost }) in iter {
884            if wrap {
885                if y == coords.height() {
886                    break;
887                }
888                if part.is_char() {
889                    y += 1
890                }
891            } else {
892                points = (real, ghost);
893            }
894        }
895
896        info.e_points = Some(points);
897        let layout = get_layout(layouts, area.id).unwrap();
898        let rect = layout.get(area.id).unwrap();
899        rect.print_info().unwrap().set(info);
900
901        points
902    }
903}
904
905fn get_rect(layouts: &[Layout], id: AreaId) -> Option<&Rect> {
906    layouts.iter().find_map(|l| l.get(id))
907}
908
909fn get_layout(layouts: &[Layout], id: AreaId) -> Option<&Layout> {
910    layouts.iter().find(|l| l.get(id).is_some())
911}
912
913fn get_layout_mut(layouts: &mut [Layout], id: AreaId) -> Option<&mut Layout> {
914    layouts.iter_mut().find(|l| l.get(id).is_some())
915}
916
917fn get_layout_pos(layouts: &[Layout], id: AreaId) -> Option<usize> {
918    layouts.iter().position(|l| l.get(id).is_some())
919}
920
921fn max_s_points(text: &Text, cfg: PrintCfg, height: u32, cap: u32) -> (Point, Option<Point>) {
922    rev_print_iter(text.iter_rev(text.len_points()), cap, cfg)
923        .filter_map(|(caret, item)| caret.wrap.then_some(item.points()))
924        .nth(if cfg.allow_overscroll {
925            cfg.scrolloff.y.saturating_sub(1) as usize
926        } else {
927            height.saturating_sub(1) as usize
928        })
929        .unwrap_or_default()
930}