duat_term/area/
mod.rs

1mod iter;
2
3use std::{fmt::Alignment, io::Write};
4
5use crossterm::cursor;
6use duat_core::{
7    cache::{Deserialize, Serialize},
8    cfg::{IterCfg, PrintCfg},
9    data::RwData,
10    form::Painter,
11    text::{Item, Iter, Part, Point, RevIter, Text},
12    ui::{self, Area as UiArea, Axis, Caret, Constraint, DuatPermission, PushSpecs},
13};
14use iter::{print_iter, print_iter_indented, rev_print_iter};
15
16use crate::{
17    AreaId, ConstraintErr,
18    layout::{Layout, Rect},
19    print::Printer,
20    queue, style,
21};
22
23#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
24pub struct Coord {
25    pub y: u32,
26    pub x: u32,
27}
28
29impl std::fmt::Debug for Coord {
30    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31        f.write_fmt(format_args!("x: {}, y: {}", self.x, self.y))
32    }
33}
34
35impl Coord {
36    pub fn new(x: u32, y: u32) -> Coord {
37        Coord { x, y }
38    }
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
42pub struct Coords {
43    tl: Coord,
44    br: Coord,
45}
46impl Coords {
47    pub fn new(tl: Coord, br: Coord) -> Self {
48        Coords { tl, br }
49    }
50
51    pub fn width(&self) -> u32 {
52        self.br.x - self.tl.x
53    }
54
55    pub fn height(&self) -> u32 {
56        self.br.y - self.tl.y
57    }
58
59    pub fn tl(&self) -> Coord {
60        self.tl
61    }
62
63    pub fn br(&self) -> Coord {
64        self.br
65    }
66}
67
68#[derive(Clone)]
69pub struct Area {
70    layouts: RwData<Vec<Layout>>,
71    pub id: AreaId,
72}
73
74impl PartialEq for Area {
75    fn eq(&self, other: &Self) -> bool {
76        self.id == other.id
77    }
78}
79
80impl Area {
81    pub fn new(id: AreaId, layouts: RwData<Vec<Layout>>) -> Self {
82        Self { layouts, id }
83    }
84
85    fn print<'a>(
86        &self,
87        text: &mut Text,
88        cfg: PrintCfg,
89        painter: Painter,
90        f: impl FnMut(&Caret, &Item) + 'a,
91    ) {
92        if text.needs_update() {
93            let (first_point, _) = self.first_points(text, cfg);
94            let (last_point, _) = self.last_points(text, cfg);
95            text.update_range((first_point, last_point));
96        }
97
98        let layouts = self.layouts.read();
99        let layout = get_layout(&layouts, self.id).unwrap();
100        let is_active = layout.active_id() == self.id;
101        let Some((sender, info)) = layout.get(self.id).and_then(|rect| {
102            let sender = rect.sender();
103            let info = rect.print_info();
104            sender.zip(info).map(|(sender, info)| {
105                let mut info = info.write();
106                info.fix(text);
107                (sender, *info)
108            })
109        }) else {
110            return;
111        };
112
113        let (iter, cfg) = {
114            let line_start = text.visual_line_start(info.points);
115            (text.iter_fwd(line_start), IterCfg::new(cfg).outsource_lfs())
116        };
117
118        let cap = cfg.wrap_width(sender.coords().width());
119        let iter = print_iter(iter, cap, cfg, info.points);
120
121        let mut lines = sender.lines(info.x_shift, cap);
122        let mut cur_style = None;
123
124        enum Cursor {
125            Main,
126            Extra,
127        }
128
129        let lines_left = {
130            let (mut painter, mut f) = (painter, f);
131            // The y here represents the bottom of the current row of cells.
132            let tl_y = sender.coords().tl.y;
133            let mut y = tl_y;
134            let mut cursor = None;
135
136            for (caret, item) in iter {
137                f(&caret, &item);
138
139                let Caret { x, len, wrap } = caret;
140                let Item { part, .. } = item;
141
142                if wrap {
143                    if y > sender.coords().tl.y {
144                        lines.flush().unwrap();
145                    }
146                    if y == sender.coords().br.y {
147                        break;
148                    }
149                    (0..x).for_each(|_| lines.push_char(' ', 1));
150                    style!(lines, painter.make_style());
151                    y += 1
152                }
153
154                match part {
155                    Part::Char(char) => {
156                        painter.confirm_printing();
157                        if let Some(style) = cur_style.take() {
158                            style!(lines, style);
159                        }
160                        match char {
161                            '\t' => (0..len).for_each(|_| lines.push_char(' ', 1)),
162                            '\n' => {}
163                            char => lines.push_char(char, len),
164                        }
165                        if let Some(cursor) = cursor.take() {
166                            style!(lines, match cursor {
167                                Cursor::Main => painter.remove_main_cursor(),
168                                Cursor::Extra => painter.remove_extra_cursor(),
169                            })
170                        }
171                    }
172                    Part::PushForm(id) => {
173                        cur_style = Some(painter.apply(id));
174                    }
175                    Part::PopForm(id) => {
176                        cur_style = Some(painter.remove(id));
177                    }
178                    Part::MainCursor => {
179                        if let Some(shape) = painter.main_cursor()
180                            && is_active
181                        {
182                            lines.show_real_cursor();
183                            queue!(lines, shape, cursor::SavePosition);
184                        } else {
185                            cursor = Some(Cursor::Main);
186                            lines.hide_real_cursor();
187                            cur_style = Some(painter.apply_main_cursor());
188                        }
189                    }
190                    Part::ExtraCursor => {
191                        cursor = Some(Cursor::Extra);
192                        cur_style = Some(painter.apply_extra_cursor());
193                    }
194                    Part::AlignLeft if !cfg.wrap_method().is_no_wrap() => {
195                        lines.realign(Alignment::Left)
196                    }
197                    Part::AlignCenter if !cfg.wrap_method().is_no_wrap() => {
198                        lines.realign(Alignment::Center)
199                    }
200                    Part::AlignRight if !cfg.wrap_method().is_no_wrap() => {
201                        lines.realign(Alignment::Right)
202                    }
203                    Part::ResetState => {
204                        style!(lines, painter.reset())
205                    }
206                    Part::ToggleStart(_) => todo!(),
207                    Part::ToggleEnd(_) => todo!(),
208                    _ => {}
209                }
210            }
211
212            if !lines.is_empty() {
213                lines.flush().unwrap();
214            }
215
216            sender.coords().br.y - y
217        };
218
219        for _ in 0..lines_left {
220            lines.flush().unwrap();
221        }
222
223        sender.send(lines);
224    }
225}
226
227impl ui::Area for Area {
228    type Cache = PrintInfo;
229    type ConstraintChangeErr = ConstraintErr;
230    type PrintInfo = PrintInfo;
231    type Ui = crate::Ui;
232
233    /////////// Modification
234
235    fn bisect(
236        &self,
237        specs: PushSpecs,
238        cluster: bool,
239        on_files: bool,
240        cache: PrintInfo,
241        _: DuatPermission,
242    ) -> (Area, Option<Area>) {
243        let mut layouts = self.layouts.write();
244        let layout = get_layout_mut(&mut layouts, self.id).unwrap();
245
246        let (child, parent) = layout.bisect(self.id, specs, cluster, on_files, cache);
247
248        (
249            Area::new(child, self.layouts.clone()),
250            parent.map(|parent| Area::new(parent, self.layouts.clone())),
251        )
252    }
253
254    fn delete(&self, _: DuatPermission) -> Option<Self> {
255        let mut layouts = self.layouts.write();
256        // This Area may have already been deleted, so a Layout may not be
257        // found.
258        get_layout_mut(&mut layouts, self.id)?
259            .delete(self.id)
260            .map(|id| Area::new(id, self.layouts.clone()))
261    }
262
263    fn swap(&self, rhs: &Self, _: DuatPermission) {
264        let mut layouts = self.layouts.write();
265        let lhs_win = get_layout_pos(&layouts, self.id).unwrap();
266        let rhs_win = get_layout_pos(&layouts, rhs.id).unwrap();
267
268        if lhs_win == rhs_win {
269            let layout = get_layout_mut(&mut layouts, self.id).unwrap();
270            let lhs_id = layout.rects.get_cluster_master(self.id).unwrap_or(self.id);
271            let rhs_id = layout.rects.get_cluster_master(rhs.id).unwrap_or(rhs.id);
272            if lhs_id == rhs_id {
273                return;
274            }
275            layout.swap(lhs_id, rhs_id);
276        } else {
277            let [lhs_lay, rhs_lay] = layouts.get_disjoint_mut([lhs_win, rhs_win]).unwrap();
278            let lhs_id = lhs_lay.rects.get_cluster_master(self.id).unwrap_or(self.id);
279            let rhs_id = rhs_lay.rects.get_cluster_master(rhs.id).unwrap_or(rhs.id);
280
281            let lhs_rect = lhs_lay.rects.get_mut(lhs_id).unwrap();
282            let rhs_rect = rhs_lay.rects.get_mut(rhs_id).unwrap();
283
284            let mut lhs_p = lhs_lay.printer.write();
285            let mut rhs_p = rhs_lay.printer.write();
286            transfer_vars_and_recvs(&mut lhs_p, &mut rhs_p, lhs_rect);
287            transfer_vars_and_recvs(&mut rhs_p, &mut lhs_p, rhs_rect);
288            drop((lhs_p, rhs_p));
289
290            std::mem::swap(lhs_rect, rhs_rect);
291
292            lhs_lay.reset_eqs(rhs_id);
293            rhs_lay.reset_eqs(lhs_id);
294        }
295    }
296
297    fn constrain_ver(&self, con: Constraint) -> Result<(), ConstraintErr> {
298        let mut layouts = self.layouts.write();
299        let layout = get_layout_mut(&mut layouts, self.id).unwrap();
300        let cons = layout
301            .rects
302            .get_constraints_mut(self.id)
303            .ok_or(ConstraintErr::NoParent)?
304            .clone();
305
306        if let Some(ver) = cons.on(Axis::Vertical)
307            && ver == con
308        {
309            return Ok(());
310        };
311
312        *layout.rects.get_constraints_mut(self.id).unwrap() = {
313            let mut p = layout.printer.write();
314            let cons = cons.replace(con, Axis::Vertical, &mut p);
315
316            let (_, parent) = layout.get_parent(self.id).unwrap();
317            let rect = layout.get(self.id).unwrap();
318
319            let cons = cons.apply(rect, parent.id(), &layout.rects, &mut p);
320            p.flush_equalities().unwrap();
321            cons
322        };
323
324        Ok(())
325    }
326
327    fn constrain_hor(&self, con: Constraint) -> Result<(), ConstraintErr> {
328        let mut layouts = self.layouts.write();
329        let layout = get_layout_mut(&mut layouts, self.id).unwrap();
330        let cons = layout
331            .rects
332            .get_constraints_mut(self.id)
333            .ok_or(ConstraintErr::NoParent)?
334            .clone();
335
336        if let Some(hor) = cons.on(Axis::Horizontal)
337            && hor == con
338        {
339            return Ok(());
340        };
341
342        *layout.rects.get_constraints_mut(self.id).unwrap() = {
343            let mut p = layout.printer.write();
344            let cons = cons.replace(con, Axis::Horizontal, &mut p);
345
346            let (_, parent) = layout.get_parent(self.id).unwrap();
347            let rect = layout.get(self.id).unwrap();
348
349            let cons = cons.apply(rect, parent.id(), &layout.rects, &mut p);
350            p.flush_equalities().unwrap();
351            cons
352        };
353
354        Ok(())
355    }
356
357    fn restore_constraints(&self) -> Result<(), Self::ConstraintChangeErr> {
358        todo!();
359    }
360
361    fn request_width_to_fit(&self, _text: &str) -> Result<(), Self::ConstraintChangeErr> {
362        todo!();
363    }
364
365    fn scroll_around_point(&self, text: &Text, point: Point, cfg: PrintCfg) {
366        let (info, w, h) = {
367            let layouts = self.layouts.read();
368            let rect = get_rect(&layouts, self.id).unwrap();
369            let info = rect.print_info().unwrap();
370            let info = info.read();
371            let width = rect.br().x - rect.tl().x;
372            let height = rect.br().y - rect.tl().y;
373            (*info, width, height)
374        };
375
376        let info = scroll_ver_around(info, w, h, point, text, IterCfg::new(cfg).outsource_lfs());
377        let info = scroll_hor_around(info, w, point, text, IterCfg::new(cfg).outsource_lfs());
378
379        let layouts = self.layouts.read();
380        let rect = get_rect(&layouts, self.id).unwrap();
381        let mut old_info = rect.print_info().unwrap().write();
382        *old_info = info;
383        old_info.prev_main = point;
384        old_info.last_points = None;
385    }
386
387    fn set_as_active(&self) {
388        let mut layouts = self.layouts.write();
389        get_layout_mut(&mut layouts, self.id).unwrap().active_id = self.id;
390    }
391
392    ////////// Printing
393
394    fn print(&self, text: &mut Text, cfg: PrintCfg, painter: Painter) {
395        self.print(text, cfg, painter, |_, _| {})
396    }
397
398    fn print_with<'a>(
399        &self,
400        text: &mut Text,
401        cfg: PrintCfg,
402        painter: Painter,
403        f: impl FnMut(&Caret, &Item) + 'a,
404    ) {
405        self.print(text, cfg, painter, f)
406    }
407
408    fn set_print_info(&self, info: Self::PrintInfo) {
409        let layouts = self.layouts.read();
410        let layout = get_layout(&layouts, self.id).unwrap();
411        *layout.get(self.id).unwrap().print_info().unwrap().write() = info;
412    }
413
414    fn print_iter<'a>(
415        &self,
416        iter: Iter<'a>,
417        cfg: IterCfg,
418    ) -> impl Iterator<Item = (Caret, Item)> + Clone + 'a {
419        let points = iter.points();
420        print_iter(iter, cfg.wrap_width(self.width()), cfg, points)
421    }
422
423    fn print_iter_from_top<'a>(
424        &self,
425        text: &'a Text,
426        cfg: IterCfg,
427    ) -> impl Iterator<Item = (Caret, Item)> + Clone + 'a {
428        let (info, width) = {
429            let layouts = self.layouts.read();
430            let rect = get_rect(&layouts, self.id).unwrap();
431            let info = rect.print_info().unwrap();
432            let info = info.read();
433            (*info, rect.width())
434        };
435        let line_start = text.visual_line_start(info.points);
436        let iter = text.iter_fwd(line_start);
437
438        print_iter(iter, cfg.wrap_width(width), cfg, info.points)
439    }
440
441    fn rev_print_iter<'a>(
442        &self,
443        iter: RevIter<'a>,
444        cfg: IterCfg,
445    ) -> impl Iterator<Item = (Caret, Item)> + Clone + 'a {
446        rev_print_iter(iter, cfg.wrap_width(self.width()), cfg)
447    }
448
449    fn print_info(&self) -> Self::PrintInfo {
450        let layouts = self.layouts.read();
451        *get_rect(&layouts, self.id)
452            .unwrap()
453            .print_info()
454            .unwrap()
455            .read()
456    }
457
458    ////////// Queries
459
460    fn has_changed(&self) -> bool {
461        let layouts = self.layouts.read();
462        get_rect(&layouts, self.id).unwrap().has_changed()
463    }
464
465    fn is_master_of(&self, other: &Self) -> bool {
466        if other.id == self.id {
467            return true;
468        }
469        let layouts = self.layouts.read();
470        let layout = get_layout(&layouts, self.id).unwrap();
471        let mut parent_id = other.id;
472        while let Some((_, parent)) = layout.get_parent(parent_id) {
473            parent_id = parent.id();
474            if parent.id() == self.id {
475                return true;
476            }
477        }
478
479        parent_id == self.id
480    }
481
482    fn get_cluster_master(&self) -> Option<Self> {
483        let layouts = self.layouts.read();
484        get_layout(&layouts, self.id)
485            .unwrap()
486            .rects
487            .get_cluster_master(self.id)
488            .map(|id| Area::new(id, self.layouts.clone()))
489    }
490
491    fn cache(&self) -> Option<Self::Cache> {
492        let layouts = self.layouts.read();
493        get_rect(&layouts, self.id)
494            .unwrap()
495            .print_info()
496            .map(|info| *info.read())
497    }
498
499    fn width(&self) -> u32 {
500        get_rect(&self.layouts.read(), self.id).unwrap().width()
501    }
502
503    fn height(&self) -> u32 {
504        get_rect(&self.layouts.read(), self.id).unwrap().height()
505    }
506
507    fn first_points(&self, _text: &Text, _cfg: PrintCfg) -> (Point, Option<Point>) {
508        let layouts = self.layouts.read();
509        let rect = get_rect(&layouts, self.id).unwrap();
510        let info = rect.print_info().unwrap();
511        let info = info.read();
512        info.points
513    }
514
515    fn last_points(&self, text: &Text, cfg: PrintCfg) -> (Point, Option<Point>) {
516        let layouts = self.layouts.read();
517        let rect = get_rect(&layouts, self.id).unwrap();
518        let (height, width) = (rect.height(), rect.width());
519        let info = rect.print_info().unwrap();
520        let mut info = info.write();
521
522        if let Some(last) = info.last_points {
523            return last;
524        }
525
526        let line_start = text.visual_line_start(info.points);
527        let iter = text.iter_fwd(line_start);
528        let cfg = IterCfg::new(cfg);
529
530        let iter = print_iter(iter, cfg.wrap_width(width), cfg, info.points);
531        let mut points = info.points;
532        let mut y = 0;
533
534        for (Caret { wrap, .. }, Item { part, real, ghost }) in iter {
535            if wrap {
536                if y == height {
537                    break;
538                }
539                if part.is_char() {
540                    y += 1
541                }
542            } else {
543                points = (real, ghost);
544            }
545        }
546
547        info.last_points = Some(points);
548
549        points
550    }
551
552    fn is_active(&self) -> bool {
553        get_layout(&self.layouts.read(), self.id).unwrap().active_id == self.id
554    }
555}
556
557// NOTE: The defaultness in here, when it comes to `last_main`, may
558// cause issues in the future.
559/// Information about how to print the file on the `Label`.
560#[derive(Default, Debug, Clone, Copy, Serialize, Deserialize)]
561#[serde(crate = "duat_core::cache::serde")]
562pub struct PrintInfo {
563    /// The index of the first [`char`] that should be printed on
564    /// the screen.
565    points: (Point, Option<Point>),
566    /// How shifted the text is to the left.
567    x_shift: u32,
568    /// The last position of the main cursor.
569    prev_main: Point,
570    last_points: Option<(Point, Option<Point>)>,
571}
572
573impl PrintInfo {
574    fn fix(&mut self, text: &Text) {
575        let max = text.len().min(self.points.0);
576        let (_, max_ghost) = text.ghost_max_points_at(max.byte());
577        self.points = (max, self.points.1.min(max_ghost))
578    }
579}
580
581/// Scrolls down until the gap between the main cursor and the
582/// bottom of the widget is equal to `config.scrolloff.y_gap`.
583fn scroll_ver_around(
584    mut info: PrintInfo,
585    width: u32,
586    height: u32,
587    point: Point,
588    text: &Text,
589    cfg: IterCfg,
590) -> PrintInfo {
591    let points = text.ghost_max_points_at(point.byte());
592    let after = text.points_after(points).unwrap_or(text.len_points());
593
594    let cap = cfg.wrap_width(width);
595    let mut iter = rev_print_iter(text.iter_rev(after), cap, cfg)
596        .filter_map(|(caret, item)| caret.wrap.then_some(item.points()));
597
598    let target = match info.prev_main > point {
599        true => cfg.scrolloff().y(),
600        false => height.saturating_sub(cfg.scrolloff().y() + 1),
601    };
602    let first = iter.nth(target as usize).unwrap_or_default();
603
604    if (info.prev_main > point && first <= info.points)
605        || (info.prev_main < point && first >= info.points)
606    {
607        info.points = first;
608    }
609    info
610}
611
612/// Scrolls the file horizontally, usually when no wrapping is
613/// being used.
614fn scroll_hor_around(
615    mut info: PrintInfo,
616    width: u32,
617    point: Point,
618    text: &Text,
619    cfg: IterCfg,
620) -> PrintInfo {
621    let cap = cfg.wrap_width(width);
622
623    let (max_shift, start, end) = {
624        let points = text.ghost_max_points_at(point.byte());
625        let after = text.points_after(points).unwrap_or(text.len_points());
626
627        let mut iter = rev_print_iter(text.iter_rev(after), cap, cfg);
628
629        let (points, start, end, wrap) = iter
630            .find_map(|(Caret { x, len, wrap }, item)| {
631                let points = item.points();
632                item.part.as_char().and(Some((points, x, x + len, wrap)))
633            })
634            .unwrap_or(((Point::default(), None), 0, 0, true));
635
636        let (line_len, align) = {
637            let mut align = Alignment::Left;
638            let (indent, points) = if wrap {
639                (start, points)
640            } else {
641                iter.find_map(|(caret, item)| caret.wrap.then_some((caret.x, item.points())))
642                    .unwrap()
643            };
644
645            let len = print_iter_indented(text.iter_fwd(points), cap, cfg, indent)
646                .inspect(|(_, Item { part, .. })| match part {
647                    Part::AlignLeft => align = Alignment::Left,
648                    Part::AlignCenter => align = Alignment::Center,
649                    Part::AlignRight => align = Alignment::Right,
650                    _ => {}
651                })
652                .take_while(|(caret, item)| !caret.wrap || item.points() == points)
653                .last()
654                .map(|(Caret { x, len, .. }, _)| x + len)
655                .unwrap_or(0);
656
657            (len, align)
658        };
659
660        let diff = match align {
661            Alignment::Left => 0,
662            Alignment::Right => cap - line_len,
663            Alignment::Center => (cap - line_len) / 2,
664        };
665
666        (line_len + diff, start + diff, end + diff)
667    };
668
669    info.x_shift = info
670        .x_shift
671        .min(start.saturating_sub(cfg.scrolloff().x()))
672        .max(if cfg.forced_scrollof() {
673            (end + cfg.scrolloff().x()).saturating_sub(width)
674        } else {
675            (end + cfg.scrolloff().x())
676                .min(max_shift)
677                .saturating_sub(width)
678        });
679    info
680}
681
682fn get_rect(layouts: &[Layout], id: AreaId) -> Option<&Rect> {
683    layouts.iter().find_map(|l| l.get(id))
684}
685
686fn get_layout(layouts: &[Layout], id: AreaId) -> Option<&Layout> {
687    layouts.iter().find(|l| l.get(id).is_some())
688}
689
690fn get_layout_mut(layouts: &mut [Layout], id: AreaId) -> Option<&mut Layout> {
691    layouts.iter_mut().find(|l| l.get(id).is_some())
692}
693
694fn get_layout_pos(layouts: &[Layout], id: AreaId) -> Option<usize> {
695    layouts.iter().position(|l| l.get(id).is_some())
696}
697
698fn transfer_vars_and_recvs(from_p: &mut Printer, to_p: &mut Printer, rect: &Rect) {
699    let (vars, recv) = from_p.take_rect_parts(rect);
700    to_p.insert_rect_parts(vars, recv);
701
702    for (child, _) in rect.children().into_iter().flatten() {
703        transfer_vars_and_recvs(from_p, to_p, child)
704    }
705}