use super::{Chunk, Ctx, Formatter, NL, Out, SPACE, UL};
use crate::ast::Node;
impl Formatter<'_> {
pub(super) fn table(&mut self, node: &Node<'_>, ctx: Ctx, no: Ctx, out: &mut Out) {
let table = self.collect_children(node, no.increment(2));
if ctx.parent_is_table_tag {
advanced(&table, ctx.indent, out);
} else {
pipe(&table, out);
}
}
pub(super) fn thead(&mut self, node: &Node<'_>, no: Ctx, out: &mut Out) {
match self.collect_children(node, no).chunks.into_iter().next() {
Some(Chunk::Text(text)) if text.is_empty() => out.row(Vec::new()),
Some(chunk) => out.chunks.push(chunk),
None => out.row(Vec::new()),
}
}
pub(super) fn tr(&mut self, node: &Node<'_>, no: Ctx, out: &mut Out) {
let cells = self
.collect_children(node, no)
.chunks
.into_iter()
.map(|chunk| match chunk {
Chunk::Text(text) => text,
Chunk::Row(cells) => cells.join(","),
})
.collect();
out.row(cells);
}
pub(super) fn cell(&mut self, node: &Node<'_>, no: Ctx, out: &mut Out) {
let mut inner = self.collect_children(node, no);
self.annotations(node, &mut inner);
out.text(inner.joined().trim().to_owned());
}
}
fn advanced(table: &Out, indent: usize, out: &mut Out) {
let indent = SPACE.repeat(indent);
for (index, chunk) in table.chunks.iter().enumerate() {
match chunk {
Chunk::Text(text) => {
if !text.trim().is_empty() {
out.text(NL);
out.text(text.clone());
}
}
Chunk::Row(cells) => {
if index != 0 {
out.text(NL);
out.text(format!("{indent}---"));
}
for cell in cells {
out.text(format!("{NL}{indent}{UL} {cell}"));
}
}
}
}
out.text(NL);
}
fn pipe(table: &Out, out: &mut Out) {
let rows: Vec<&Vec<String>> = table
.chunks
.iter()
.filter_map(|chunk| match chunk {
Chunk::Row(cells) => Some(cells),
Chunk::Text(_) => None,
})
.collect();
let Some((head, body)) = rows.split_first() else {
return;
};
let mut widths: Vec<usize> = Vec::new();
for row in &rows {
for (column, cell) in row.iter().enumerate() {
let width = super::utf16_len(cell);
if let Some(existing) = widths.get_mut(column) {
*existing = (*existing).max(width);
} else {
widths.push(width);
}
}
}
out.text(NL);
out.text(row(&pad(head, &widths)));
out.text(NL);
out.text(row(&rule(head, &widths)));
out.text(NL);
for cells in body {
out.text(row(&pad(cells, &widths)));
out.text(NL);
}
}
fn row(cells: &[String]) -> String {
format!("| {} |", cells.join(" | "))
}
fn pad(cells: &[String], widths: &[usize]) -> Vec<String> {
cells
.iter()
.enumerate()
.map(|(column, cell)| {
let width = widths.get(column).copied().unwrap_or_default();
let padding = width.saturating_sub(super::utf16_len(cell));
format!("{cell}{}", SPACE.repeat(padding))
})
.collect()
}
fn rule(cells: &[String], widths: &[usize]) -> Vec<String> {
cells
.iter()
.enumerate()
.map(|(column, _)| "-".repeat(widths.get(column).copied().unwrap_or_default()))
.collect()
}