use carta_ast::{Block, Inline, Table, to_plain_text};
use carta_core::Extensions;
use crate::heading_ids::{IdRegistry, IdScheme};
pub(crate) fn assign_header_identifiers(blocks: &mut [Block], ext: Extensions, markdown: bool) {
let mut numbering = HeaderNumbering::new(ext, markdown);
if numbering.scheme.is_none() {
return;
}
walk(blocks, &mut numbering);
}
pub(crate) struct HeaderNumbering {
scheme: Option<IdScheme>,
markdown: bool,
registry: IdRegistry,
}
impl HeaderNumbering {
pub(crate) fn new(ext: Extensions, markdown: bool) -> Self {
Self {
scheme: IdScheme::select(ext, markdown),
markdown,
registry: IdRegistry::default(),
}
}
pub(crate) fn id_for(&mut self, explicit_id: &str, inlines: &[Inline]) -> String {
let Some(scheme) = self.scheme else {
return explicit_id.to_owned();
};
if explicit_id.is_empty() {
let text = to_plain_text(inlines);
if self.markdown {
self.registry.assign_markdown(scheme, &text)
} else {
self.registry.assign(scheme, &text)
}
} else {
if self.markdown {
self.registry.reserve_native(explicit_id);
} else {
self.registry.reserve(scheme, explicit_id);
}
explicit_id.to_owned()
}
}
}
fn walk(blocks: &mut [Block], numbering: &mut HeaderNumbering) {
for block in blocks {
match block {
Block::Header(_, attr, inlines) => {
attr.id = numbering.id_for(&attr.id, inlines);
}
Block::BlockQuote(children)
| Block::Div(_, children)
| Block::Figure(_, _, children) => walk(children, numbering),
Block::BulletList(items) | Block::OrderedList(_, items) => {
for item in items {
walk(item, numbering);
}
}
Block::DefinitionList(items) => {
for (_, definitions) in items {
for definition in definitions {
walk(definition, numbering);
}
}
}
Block::Table(table) => walk_table(table, numbering),
_ => {}
}
}
}
fn walk_table(table: &mut Table, numbering: &mut HeaderNumbering) {
let body_rows = table
.bodies
.iter_mut()
.flat_map(|body| body.head.iter_mut().chain(body.body.iter_mut()));
let rows = table
.head
.rows
.iter_mut()
.chain(body_rows)
.chain(table.foot.rows.iter_mut());
for row in rows {
for cell in &mut row.cells {
walk(&mut cell.content, numbering);
}
}
}