tabled/settings/span/
row.rs1use std::cmp::{self, Ordering};
2
3use crate::{
4 grid::{
5 config::{ColoredConfig, Entity, Position, SpannedConfig},
6 records::{ExactRecords, PeekableRecords, Records, RecordsMut},
7 },
8 settings::CellOption,
9};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
13pub struct RowSpan {
14 size: isize,
15}
16
17impl RowSpan {
18 pub const fn new(size: isize) -> Self {
20 Self { size }
21 }
22
23 pub const fn max() -> Self {
25 Self::new(isize::MAX)
26 }
27
28 pub fn min() -> Self {
30 Self::new(isize::MIN)
31 }
32
33 pub fn spread() -> Self {
35 Self::new(0)
36 }
37}
38
39impl<R> CellOption<R, ColoredConfig> for RowSpan
40where
41 R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
42{
43 fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
44 let count_rows = records.count_rows();
45 let count_cols = records.count_columns();
46 let shape = (count_rows, count_cols).into();
47
48 for pos in entity.iter(count_rows, count_cols) {
49 set_span(records, cfg, self.size, pos, shape);
50 }
51
52 remove_false_spans(cfg);
53 }
54}
55
56fn set_span<R>(recs: &mut R, cfg: &mut SpannedConfig, span: isize, pos: Position, shape: Position)
57where
58 R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
59{
60 if !shape.has_coverage(pos) {
61 return;
62 }
63
64 match span.cmp(&0) {
65 Ordering::Less => {
66 let span = span.unsigned_abs();
72 let (row, span) = if span > pos.row {
73 (0, pos.row)
74 } else {
75 (pos.row - span, span)
76 };
77
78 let content = recs.get_text(pos).to_string();
79
80 for i in row..span + 1 {
81 recs.set(Position::new(i, pos.col), String::new());
82 }
83
84 recs.set(Position::new(row, pos.col), content);
85 cfg.set_row_span(Position::new(row, pos.col), span + 1);
86 }
87 Ordering::Equal => {
88 let content = recs.get_text(pos).to_string();
89 let span = recs.count_rows();
90
91 for i in 0..recs.count_rows() {
92 recs.set(Position::new(i, pos.col), String::new());
93 }
94
95 recs.set(Position::new(0, pos.col), content);
96 cfg.set_row_span(Position::new(0, pos.col), span);
97 }
98 Ordering::Greater => {
99 let span = cmp::min(span as usize, shape.row - pos.row);
100 if span_has_intersections(cfg, pos, span) {
101 return;
102 }
103
104 set_span_row(cfg, pos, span);
105 }
106 }
107}
108
109fn set_span_row(cfg: &mut SpannedConfig, p: Position, span: usize) {
110 if span == 0 {
111 if p.col == 0 {
112 return;
113 }
114
115 if let Some(nearcol) = closest_visible(cfg, p - (0, 1)) {
116 let span = p.col + 1 - nearcol;
117 cfg.set_row_span((p.row, nearcol).into(), span);
118 }
119 }
120
121 cfg.set_row_span(p, span);
122}
123
124fn closest_visible(cfg: &SpannedConfig, mut pos: Position) -> Option<usize> {
125 loop {
126 if cfg.is_cell_visible(pos) {
127 return Some(pos.row);
128 }
129
130 if pos.row == 0 {
131 return None;
133 }
134
135 pos -= (1, 0);
136 }
137}
138
139fn span_has_intersections(cfg: &SpannedConfig, p: Position, span: usize) -> bool {
140 for row in p.row..p.row + span {
141 if !cfg.is_cell_visible((row, p.col).into()) {
142 return true;
143 }
144 }
145
146 false
147}
148
149fn remove_false_spans(cfg: &mut SpannedConfig) {
150 for (pos, _) in cfg.get_column_spans() {
151 if cfg.is_cell_visible(pos) {
152 continue;
153 }
154
155 cfg.set_row_span(pos, 1);
156 cfg.set_column_span(pos, 1);
157 }
158
159 for (pos, _) in cfg.get_row_spans() {
160 if cfg.is_cell_visible(pos) {
161 continue;
162 }
163
164 cfg.set_row_span(pos, 1);
165 cfg.set_column_span(pos, 1);
166 }
167}