use child_range instead of child_start/end

This commit is contained in:
PgBiel 2025-02-21 02:40:08 -03:00
parent 4103be5138
commit 838bdc0b89

View File

@ -1,4 +1,5 @@
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
use std::ops::Range;
use std::sync::Arc; use std::sync::Arc;
use ecow::eco_format; use ecow::eco_format;
@ -778,11 +779,16 @@ impl<'a> CellGrid<'a> {
// instead, and use a separate counter outside them. // instead, and use a separate counter outside them.
let mut local_auto_index = auto_index; let mut local_auto_index = auto_index;
// The index of the first cell within this child in the grid. Note // The range of rows of cells inside this child in the grid. The
// that cells outside headers and footers are children with a // first and last rows are guaranteed to have cells (an exception
// single cell inside, in which case 'child_start == child_end'. // is made when there is gutter, in which case the child range may
let mut child_start = usize::MAX; // be expanded to include an additional gutter row when there is a
let mut child_end = 0; // repeatable header or footer).
//
// Note that cells outside headers and footers are children
// with a single cell inside, in which case the range has a single
// row.
let mut child_range: Option<Range<usize>> = None;
let mut child_span = Span::detached(); let mut child_span = Span::detached();
// The first row in which this table child can fit. // The first row in which this table child can fit.
@ -1036,8 +1042,10 @@ impl<'a> CellGrid<'a> {
// Ensure each cell in a header or footer is fully // Ensure each cell in a header or footer is fully
// contained within it by expanding the header or footer // contained within it by expanding the header or footer
// towards this new cell. // towards this new cell.
let new_child_start = child_start.min(y); let (new_child_start, new_child_end) =
let new_child_end = child_end.max(y + rowspan); child_range.clone().map_or((y, y + rowspan), |r| {
(r.start.min(y), r.end.max(y + rowspan))
});
// TODO: Maybe remove this check and just keep the loop, // TODO: Maybe remove this check and just keep the loop,
// we will loop in the "good case" anyway // we will loop in the "good case" anyway
@ -1079,8 +1087,15 @@ impl<'a> CellGrid<'a> {
// Of course, we also need to check for downward expansion // Of course, we also need to check for downward expansion
// as there could be a non-empty row below the header, but // as there could be a non-empty row below the header, but
// the upward case is highlighted due to its differences. // the upward case is highlighted due to its differences.
let new_rows = (new_child_start..child_start.min(new_child_end)) let new_rows = child_range.clone().map_or(
.chain(child_end.max(new_child_start + 1)..new_child_end); (new_child_start..new_child_end).chain(0..0),
|r| {
// NOTE: To keep types the same, we have to always
// return (range).chain(range), which justifies
// chaining an empty range above.
(new_child_start..r.start).chain(r.end..new_child_end)
},
);
// Note that simply checking for non-empty rows like below // Note that simply checking for non-empty rows like below
// not only prevents conflicts with top-level cells // not only prevents conflicts with top-level cells
@ -1089,9 +1104,9 @@ impl<'a> CellGrid<'a> {
// an invariant that even empty headers and footers must // an invariant that even empty headers and footers must
// contain at least one 'Some(...)' position in // contain at least one 'Some(...)' position in
// 'resolved_cells'. More precisely, each header and footer // 'resolved_cells'. More precisely, each header and footer
// has at least one 'Some(...)' cell at 'child_start' and // has at least one 'Some(...)' cell at 'child_range.start'
// at 'child_end - 1' - non-empty headers and footers don't // and at 'child_range.end - 1' - non-empty headers and
// span any unnecessary rows. // footers don't span any unnecessary rows.
for new_y in new_rows { for new_y in new_rows {
if let Some(new_row @ [_non_empty, ..]) = resolved_cells if let Some(new_row @ [_non_empty, ..]) = resolved_cells
.get(new_y * c..) .get(new_y * c..)
@ -1104,7 +1119,7 @@ impl<'a> CellGrid<'a> {
// - Detect when header or footer collided with // - Detect when header or footer collided with
// another header or footer and provide a // another header or footer and provide a
// better error message if so. // better error message if so.
if new_y < child_start.min(new_child_end) { if child_range.map_or(true, |r| new_y < r.start) {
bail!( bail!(
cell_span, cell_span,
"cell would cause header or footer to expand to non-empty row {new_y}"; "cell would cause header or footer to expand to non-empty row {new_y}";
@ -1132,8 +1147,7 @@ impl<'a> CellGrid<'a> {
} }
} }
child_start = new_child_start; child_range = Some(new_child_start..new_child_end);
child_end = new_child_end;
} else { } else {
// TODO: Perhaps do this during cell resolving? // TODO: Perhaps do this during cell resolving?
// This is unnecessary for non-row-positioned cells, as // This is unnecessary for non-row-positioned cells, as
@ -1234,12 +1248,16 @@ impl<'a> CellGrid<'a> {
} }
} }
if is_row_group && child_start == usize::MAX { if is_row_group {
let child_range = match child_range {
Some(child_range) => child_range,
None => {
// Empty header/footer: consider the header/footer to be // Empty header/footer: consider the header/footer to be
// at the next empty row after the latest auto index. // at the next empty row after the latest auto index.
local_auto_index = first_available_row * c; local_auto_index = first_available_row * c;
child_start = first_available_row; let child_start = first_available_row;
child_end = child_start + 1; let child_end = child_start + 1;
if resolved_cells.len() <= c * child_start { if resolved_cells.len() <= c * child_start {
// Ensure the automatically chosen row actually exists. // Ensure the automatically chosen row actually exists.
@ -1268,10 +1286,13 @@ impl<'a> CellGrid<'a> {
locator.next(&()), locator.next(&()),
styles, styles,
))); )));
child_start..child_end
} }
};
if is_header { if is_header {
if child_start != 0 { if child_range.start != 0 {
bail!( bail!(
child_span, child_span,
"header must start at the first row"; "header must start at the first row";
@ -1284,7 +1305,7 @@ impl<'a> CellGrid<'a> {
// is gutter. But only once all cells have been analyzed // is gutter. But only once all cells have been analyzed
// and the header has fully expanded in the fixup loop // and the header has fully expanded in the fixup loop
// below. // below.
end: child_end, end: child_range.end,
}); });
} }
@ -1292,7 +1313,7 @@ impl<'a> CellGrid<'a> {
// Only check if the footer is at the end later, once we know // Only check if the footer is at the end later, once we know
// the final amount of rows. // the final amount of rows.
footer = Some(( footer = Some((
child_end, child_range.end,
child_span, child_span,
Footer { Footer {
// Later on, we have to correct this number in case there // Later on, we have to correct this number in case there
@ -1301,12 +1322,11 @@ impl<'a> CellGrid<'a> {
// known. That is because the gutter row immediately // known. That is because the gutter row immediately
// before the footer might not be included as part of // before the footer might not be included as part of
// the footer if it is contained within the header. // the footer if it is contained within the header.
start: child_start, start: child_range.start,
}, },
)); ));
} }
} else {
if !is_row_group {
// The child was a single cell outside headers or footers. // The child was a single cell outside headers or footers.
// Therefore, 'local_auto_index' for this table child was // Therefore, 'local_auto_index' for this table child was
// simply an alias for 'auto_index', so we update it as needed. // simply an alias for 'auto_index', so we update it as needed.