use memchr::memchr;
use ox_content_allocator::Vec;
use ox_content_ast::{AlignKind, Node, Span, Table, TableCell, TableRow};
use super::Parser;
use crate::error::ParseResult;
#[allow(unused_imports)]
use crate::profile_span;
impl<'a> Parser<'a> {
pub(super) fn try_parse_table(&self) -> bool {
let bytes = self.source.as_bytes();
let p0 = self.position;
let nl0 = match memchr(b'\n', &bytes[p0..]) {
Some(off) => p0 + off,
None => return false,
};
let p1 = nl0 + 1;
if p1 >= bytes.len() {
return false;
}
let nl1 = memchr(b'\n', &bytes[p1..]).map_or(bytes.len(), |off| p1 + off);
let first_line = self.source[p0..nl0].trim();
if memchr(b'|', first_line.as_bytes()).is_none() {
return false;
}
let second_line = self.source[p1..nl1].trim();
if memchr(b'|', second_line.as_bytes()).is_none()
|| memchr(b'-', second_line.as_bytes()).is_none()
{
return false;
}
let is_delimiter = second_line.split('|').filter(|s| !s.is_empty()).all(|cell| {
let trimmed = cell.trim();
if trimmed.is_empty() {
return true;
}
trimmed.chars().all(|c| c == '-' || c == ':')
});
is_delimiter
}
pub(super) fn parse_table(&mut self, start: usize) -> ParseResult<Option<Node<'a>>> {
profile_span!("parser::parse_table");
let mut align: Vec<'a, AlignKind> = self.allocator.new_vec();
let header_line = self.consume_line();
let delimiter_line = self.consume_line();
for cell in delimiter_line.split('|').filter(|s| !s.trim().is_empty()) {
let cell = cell.trim();
let starts_colon = cell.starts_with(':');
let ends_colon = cell.ends_with(':');
let alignment = match (starts_colon, ends_colon) {
(true, true) => AlignKind::Center,
(true, false) => AlignKind::Left,
(false, true) => AlignKind::Right,
(false, false) => AlignKind::None,
};
align.push(alignment);
}
let mut children: Vec<'a, TableRow<'a>> = self.allocator.new_vec();
children.push(self.parse_table_row(header_line)?);
loop {
if self.is_at_end() {
break;
}
let line_start = self.position;
self.skip_whitespace();
if self.peek() == Some('\n') || self.is_at_end() {
self.position = line_start;
break;
}
let remaining = self.remaining();
let line = remaining.lines().next().unwrap_or("");
if !line.contains('|') {
self.position = line_start;
break;
}
self.position = line_start;
let row_line = self.consume_line();
children.push(self.parse_table_row(row_line)?);
}
let span = Span::new(start as u32, self.position as u32);
Ok(Some(Node::Table(Table { align, children, span })))
}
pub(super) fn parse_table_row(&self, line: &'a str) -> ParseResult<TableRow<'a>> {
let mut cells: Vec<'a, TableCell<'a>> = self.allocator.new_vec();
for cell_content in Self::table_row_cells(line) {
let cell_children = self.parse_inline(cell_content, 0)?;
cells.push(TableCell { children: cell_children, span: Span::new(0, 0) });
}
Ok(TableRow { children: cells, span: Span::new(0, 0) })
}
pub(super) fn table_row_cells(line: &'a str) -> impl Iterator<Item = &'a str> {
let trimmed = line.trim();
let trimmed = trimmed.strip_prefix('|').unwrap_or(trimmed);
let trimmed = trimmed.strip_suffix('|').unwrap_or(trimmed);
trimmed.split('|').map(str::trim)
}
}