mirror of
https://github.com/typst/typst
synced 2025-08-15 07:28:32 +08:00
deduplicate cell next pos loop
This commit is contained in:
parent
2d4a81ec24
commit
ad049491b4
@ -1876,32 +1876,13 @@ fn resolve_cell_position(
|
|||||||
// Note that the counter ignores any cells with fixed positions,
|
// Note that the counter ignores any cells with fixed positions,
|
||||||
// but automatically-positioned cells will avoid conflicts by
|
// but automatically-positioned cells will avoid conflicts by
|
||||||
// simply skipping existing cells, headers and footers.
|
// simply skipping existing cells, headers and footers.
|
||||||
let mut resolved_index = *auto_index;
|
let resolved_index = find_next_available_position::<false>(
|
||||||
|
header,
|
||||||
loop {
|
footer,
|
||||||
if let Some(Some(_)) = resolved_cells.get(resolved_index) {
|
resolved_cells,
|
||||||
// Skip any non-absent cell positions (`Some(None)`) to
|
columns,
|
||||||
// determine where this cell will be placed. An out of
|
*auto_index,
|
||||||
// bounds position (thus `None`) is also a valid new
|
)?;
|
||||||
// position (only requires expanding the vector).
|
|
||||||
resolved_index += 1;
|
|
||||||
} else if let Some(header) =
|
|
||||||
header.filter(|header| resolved_index / columns < header.end)
|
|
||||||
{
|
|
||||||
// Skip header
|
|
||||||
resolved_index = header.end * columns;
|
|
||||||
} else if let Some((footer_end, _, _)) =
|
|
||||||
footer.filter(|(end, _, footer)| {
|
|
||||||
resolved_index / columns >= footer.start
|
|
||||||
&& resolved_index / columns < *end
|
|
||||||
})
|
|
||||||
{
|
|
||||||
// Skip footer
|
|
||||||
resolved_index = *footer_end * columns;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the next cell with automatic position will be
|
// Ensure the next cell with automatic position will be
|
||||||
// placed after this one (maybe not immediately after).
|
// placed after this one (maybe not immediately after).
|
||||||
@ -1947,49 +1928,19 @@ fn resolve_cell_position(
|
|||||||
// row / the header or footer's first row (specified through
|
// row / the header or footer's first row (specified through
|
||||||
// 'first_available_row'). Otherwise, start searching at the
|
// 'first_available_row'). Otherwise, start searching at the
|
||||||
// first row.
|
// first row.
|
||||||
let mut resolved_y = first_available_row;
|
let initial_index = cell_index(cell_x, first_available_row)?;
|
||||||
|
|
||||||
// There may be row groups, so we have to not only skip
|
// Try each row until either we reach an absent position at the
|
||||||
// rows where the requested column is occupied to find the
|
// requested column ('Some(None)') or an out of bounds position
|
||||||
// first suitable row, but also skip rows belonging to
|
// ('None'), in which case we'd create a new row to place this
|
||||||
// headers or footers.
|
// cell in.
|
||||||
loop {
|
find_next_available_position::<true>(
|
||||||
if let Some(Some(_)) =
|
header,
|
||||||
resolved_cells.get(cell_index(cell_x, resolved_y)?)
|
footer,
|
||||||
{
|
resolved_cells,
|
||||||
// Try each row until either we reach an absent
|
columns,
|
||||||
// position at the requested column (`Some(None)`)
|
initial_index,
|
||||||
// or an out of bounds position (`None`), in which
|
)
|
||||||
// case we'd create a new row to place this cell
|
|
||||||
// in.
|
|
||||||
//
|
|
||||||
// Therefore, this loop will always finish, even if
|
|
||||||
// the cell has to go all the way to the end to be
|
|
||||||
// at its requested column. However, if the cell is
|
|
||||||
// in a header or footer, that could cause the
|
|
||||||
// header or footer to expand and conflict with
|
|
||||||
// other cells along the way, but that is checked
|
|
||||||
// separately later in the 'expand_row_group'
|
|
||||||
// function.
|
|
||||||
resolved_y += 1;
|
|
||||||
} else if let Some(header) =
|
|
||||||
header.filter(|header| resolved_y < header.end)
|
|
||||||
{
|
|
||||||
// Skip header
|
|
||||||
resolved_y = header.end;
|
|
||||||
} else if let Some((footer_end, _, _)) =
|
|
||||||
footer.filter(|(end, _, footer)| {
|
|
||||||
resolved_y >= footer.start && resolved_y < *end
|
|
||||||
})
|
|
||||||
{
|
|
||||||
// Skip footer
|
|
||||||
resolved_y = *footer_end;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cell_index(cell_x, resolved_y)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Cell has only chosen its row, not its column.
|
// Cell has only chosen its row, not its column.
|
||||||
@ -2027,6 +1978,64 @@ fn resolve_cell_position(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Finds the first available position after the initial index in the resolved
|
||||||
|
/// grid of cells. Skips any non-absent positions (positions which already
|
||||||
|
/// have cells specified by the user) as well as any headers and footers.
|
||||||
|
#[inline]
|
||||||
|
fn find_next_available_position<const SKIP_ROWS: bool>(
|
||||||
|
header: Option<&Header>,
|
||||||
|
footer: Option<&(usize, Span, Footer)>,
|
||||||
|
resolved_cells: &[Option<Entry<'_>>],
|
||||||
|
columns: usize,
|
||||||
|
initial_index: usize,
|
||||||
|
) -> HintedStrResult<usize> {
|
||||||
|
let mut resolved_index = initial_index;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if let Some(Some(_)) = resolved_cells.get(resolved_index) {
|
||||||
|
// Skip any non-absent cell positions (`Some(None)`) to
|
||||||
|
// determine where this cell will be placed. An out of
|
||||||
|
// bounds position (thus `None`) is also a valid new
|
||||||
|
// position (only requires expanding the vector).
|
||||||
|
if SKIP_ROWS {
|
||||||
|
// Skip one row at a time (cell chose its column, so we don't
|
||||||
|
// change it).
|
||||||
|
resolved_index =
|
||||||
|
resolved_index.checked_add(columns).ok_or_else(|| {
|
||||||
|
HintedString::from(eco_format!("cell position too large"))
|
||||||
|
})?;
|
||||||
|
} else {
|
||||||
|
// Ensure we don't run unnecessary checks in the hot path
|
||||||
|
// (for fully automatically-positioned cells). Memory usage
|
||||||
|
// would become impractically large before this overflows.
|
||||||
|
resolved_index += 1;
|
||||||
|
}
|
||||||
|
} else if let Some(header) =
|
||||||
|
header.filter(|header| resolved_index < header.end * columns)
|
||||||
|
{
|
||||||
|
// Skip header (can't place a cell inside it from outside it).
|
||||||
|
resolved_index = header.end * columns;
|
||||||
|
|
||||||
|
if SKIP_ROWS {
|
||||||
|
// Ensure the cell's chosen column is kept after the
|
||||||
|
// header.
|
||||||
|
resolved_index += initial_index % columns;
|
||||||
|
}
|
||||||
|
} else if let Some((footer_end, _, _)) = footer.filter(|(end, _, footer)| {
|
||||||
|
resolved_index >= footer.start * columns && resolved_index < *end * columns
|
||||||
|
}) {
|
||||||
|
// Skip footer, for the same reason.
|
||||||
|
resolved_index = *footer_end * columns;
|
||||||
|
|
||||||
|
if SKIP_ROWS {
|
||||||
|
resolved_index += initial_index % columns;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Ok(resolved_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Computes the `y` of the next available empty row, given the auto index as
|
/// Computes the `y` of the next available empty row, given the auto index as
|
||||||
/// an initial index for search, since we know that there are no empty rows
|
/// an initial index for search, since we know that there are no empty rows
|
||||||
/// before automatically-positioned cells, as they are placed sequentially.
|
/// before automatically-positioned cells, as they are placed sequentially.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user