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 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 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 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 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 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#[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
707fn 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
757fn scroll_hor_around(info: &mut PrintInfo, width: u32, p: Point, text: &Text, cfg: PrintCfg) {
760 let cap = cfg.wrap_width(width);
761 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}