diff --git a/src/cm.rs b/src/cm.rs index d713db21..00223618 100644 --- a/src/cm.rs +++ b/src/cm.rs @@ -8,7 +8,7 @@ use crate::ctype::{isalpha, isdigit, ispunct, ispunct_char, isspace, isspace_cha use crate::nodes::{ ListDelimType, ListType, Node, NodeAlert, NodeBlockDirective, NodeCodeBlock, NodeHeading, NodeHtmlBlock, NodeLink, NodeList, NodeMath, NodeTaskItem, NodeValue, NodeWikiLink, - TableAlignment, + TableAlignment, TableRowKind, }; use crate::parser::options::{Options, Plugins, WikiLinksMode}; #[cfg(feature = "phoenix_heex")] @@ -446,7 +446,7 @@ impl<'a, 'o, 'c, 'w> CommonMarkFormatter<'a, 'o, 'c, 'w> { .next_sibling() .is_none_or(|next| next.data().value.block()); let text_in_cell = node_matches!(node, NodeValue::Text(..)) - && parent_node.is_some_and(|n| node_matches!(n, NodeValue::TableCell)); + && parent_node.is_some_and(|n| node_matches!(n, NodeValue::TableCell(..))); match node.data().value { NodeValue::Document => (), @@ -493,7 +493,7 @@ impl<'a, 'o, 'c, 'w> CommonMarkFormatter<'a, 'o, 'c, 'w> { NodeValue::ShortCode(ref ne) => self.format_shortcode(ne, entering)?, NodeValue::Table(..) => self.format_table(entering), NodeValue::TableRow(..) => self.format_table_row(entering)?, - NodeValue::TableCell => self.format_table_cell(node, entering)?, + NodeValue::TableCell(..) => self.format_table_cell(node, entering)?, NodeValue::FootnoteDefinition(ref nfd) => { self.format_footnote_definition(&nfd.name, entering)? } @@ -1079,12 +1079,12 @@ impl<'a, 'o, 'c, 'w> CommonMarkFormatter<'a, 'o, 'c, 'w> { write!(self, " |")?; let row = &node.parent().unwrap().data().value; - let in_header = match *row { - NodeValue::TableRow(header) => header, + let kind = match *row { + NodeValue::TableRow(trk) => trk, _ => panic!(), }; - if in_header && node.next_sibling().is_none() { + if kind == TableRowKind::Header && node.next_sibling().is_none() { let table = &node.parent().unwrap().parent().unwrap().data().value; let alignments = match table { NodeValue::Table(nt) => &nt.alignments, @@ -1273,7 +1273,7 @@ fn is_autolink(node: Node<'_>, nl: &NodeLink) -> bool { fn table_escape(node: Node<'_>, c: char) -> bool { match node.data().value { - NodeValue::Table(..) | NodeValue::TableRow(..) | NodeValue::TableCell => false, + NodeValue::Table(..) | NodeValue::TableRow(..) | NodeValue::TableCell(..) => false, _ => c == '|', } } diff --git a/src/html.rs b/src/html.rs index 1e27e129..4a7dec3e 100644 --- a/src/html.rs +++ b/src/html.rs @@ -21,7 +21,7 @@ use crate::nodes::NodeShortCode; use crate::nodes::{ ListType, Node, NodeAlert, NodeBlockDirective, NodeCode, NodeCodeBlock, NodeFootnoteDefinition, NodeFootnoteReference, NodeHeading, NodeHtmlBlock, NodeLink, NodeList, NodeMath, NodeTaskItem, - NodeValue, NodeWikiLink, TableAlignment, + NodeValue, NodeWikiLink, TableAlignment, TableRowKind, }; use crate::parser::options::{AlertStyleType, Options, Plugins}; use crate::{node_matches, scanners}; @@ -386,8 +386,8 @@ pub fn format_node_default( NodeValue::Highlight => render_highlight(context, node, entering), NodeValue::Insert => render_insert(context, node, entering), NodeValue::Table(_) => render_table(context, node, entering), - NodeValue::TableCell => render_table_cell(context, node, entering), - NodeValue::TableRow(thead) => render_table_row(context, node, entering, thead), + NodeValue::TableCell(..) => render_table_cell(context, node, entering), + NodeValue::TableRow(trk) => render_table_row(context, node, entering, trk), NodeValue::TaskItem(ref nti) => render_task_item(context, node, entering, nti), // Extensions @@ -1146,8 +1146,8 @@ fn render_table_cell( panic!("rendered a table cell without a containing table row"); }; let row = &row_node.data().value; - let in_header = match *row { - NodeValue::TableRow(header) => header, + let kind = match *row { + NodeValue::TableRow(trk) => trk, _ => panic!("rendered a table cell contained by something other than a table row"), }; @@ -1164,7 +1164,7 @@ fn render_table_cell( if entering { context.cr()?; - if in_header { + if kind == TableRowKind::Header { context.write_str("( } context.write_str(">")?; - } else if in_header { + } else if kind == TableRowKind::Header { context.write_str("")?; } else { context.write_str("")?; @@ -1206,15 +1206,15 @@ fn render_table_row( context: &mut Context, node: Node<'_>, entering: bool, - thead: bool, + kind: TableRowKind, ) -> Result { if entering { context.cr()?; - if thead { + if kind == TableRowKind::Header { context.write_str("")?; context.lf()?; } else if let Some(n) = node.previous_sibling() { - if let NodeValue::TableRow(true) = n.data().value { + if let NodeValue::TableRow(TableRowKind::Header) = n.data().value { context.write_str("")?; context.lf()?; } @@ -1225,7 +1225,7 @@ fn render_table_row( } else { context.cr()?; context.write_str("")?; - if thead { + if kind == TableRowKind::Header { context.cr()?; context.write_str("")?; } diff --git a/src/nodes.rs b/src/nodes.rs index c09bf9e2..b29ad97c 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -123,10 +123,10 @@ pub enum NodeValue { /// **Block**. A table row. The `bool` represents whether the row is the header row or not. /// Contains table cells. - TableRow(bool), + TableRow(TableRowKind), /// **Block**. A table cell. Contains **inlines**. - TableCell, + TableCell(NodeTableCell), /// **Inline**. [Textual content](https://github.github.com/gfm/#textual-content). All text /// in a document will be contained in a `Text` node. @@ -297,6 +297,9 @@ impl TableAlignment { /// The metadata of a table #[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct NodeTable { + /// XXX + pub kind: TableKind, + /// The table alignments pub alignments: Vec, @@ -310,6 +313,44 @@ pub struct NodeTable { pub num_nonempty_cells: usize, } +#[derive(Debug, Default, Clone, PartialEq, Eq, Copy)] +/// XXX +pub enum TableKind { + #[default] + /// XXX + Pipe, + + /// XXX + Grid, +} + +#[derive(Debug, Default, Clone, PartialEq, Eq, Copy)] +/// XXX +pub enum TableRowKind { + /// XXX + Header, + + #[default] + /// XXX + Body, + + /// XXX + Footer, +} + +#[derive(Debug, Default, Clone, PartialEq, Eq, Copy)] +/// XXX +pub struct NodeTableCell { + /// XXX + pub colspan: usize, + + /// XXX + pub rowspan: usize, + + /// XXX + pub grid_column: usize, +} + /// A task list item's contents, and where it was found #[derive(Debug, Clone, PartialEq, Eq, Copy)] pub struct NodeTaskItem { @@ -677,7 +718,7 @@ impl NodeValue { | NodeValue::ThematicBreak | NodeValue::Table(..) | NodeValue::TableRow(..) - | NodeValue::TableCell + | NodeValue::TableCell(..) | NodeValue::TaskItem(..) | NodeValue::MultilineBlockQuote(_) | NodeValue::Alert(_) @@ -695,7 +736,7 @@ impl NodeValue { *self, NodeValue::Paragraph | NodeValue::Heading(..) - | NodeValue::TableCell + | NodeValue::TableCell(..) | NodeValue::Subtext ) } @@ -743,7 +784,7 @@ impl NodeValue { NodeValue::ThematicBreak => "thematic_break", NodeValue::Table(..) => "table", NodeValue::TableRow(..) => "table_row", - NodeValue::TableCell => "table_cell", + NodeValue::TableCell(..) => "table_cell", NodeValue::Text(..) => "text", NodeValue::SoftBreak => "softbreak", NodeValue::LineBreak => "linebreak", @@ -1078,8 +1119,8 @@ impl<'a> arena_tree::Node<'a, RefCell> { | NodeValue::EscapedTag(_) => !child.block(), NodeValue::Table(..) => matches!(*child, NodeValue::TableRow(..)), - NodeValue::TableRow(..) => matches!(*child, NodeValue::TableCell), - NodeValue::TableCell => { + NodeValue::TableRow(..) => matches!(*child, NodeValue::TableCell(..)), + NodeValue::TableCell(..) => { #[cfg(feature = "shortcodes")] if matches!(*child, NodeValue::ShortCode(..)) { return true; @@ -1090,29 +1131,31 @@ impl<'a> arena_tree::Node<'a, RefCell> { return true; } - matches!( - *child, - NodeValue::Text(..) - | NodeValue::Code(..) - | NodeValue::Emph - | NodeValue::Strong - | NodeValue::Link(..) - | NodeValue::Image(..) - | NodeValue::Strikethrough - | NodeValue::Highlight - | NodeValue::Insert - | NodeValue::HtmlInline(..) - | NodeValue::Math(..) - | NodeValue::WikiLink(..) - | NodeValue::FootnoteReference(..) - | NodeValue::Superscript - | NodeValue::SpoileredText - | NodeValue::Underline - | NodeValue::Subscript - | NodeValue::TaskItem(_) - | NodeValue::Escaped - | NodeValue::EscapedTag(_) - ) + // XXX: only true for grid table cells? :thonk: + child.block() || + matches!( + *child, + NodeValue::Text(..) + | NodeValue::Code(..) + | NodeValue::Emph + | NodeValue::Strong + | NodeValue::Link(..) + | NodeValue::Image(..) + | NodeValue::Strikethrough + | NodeValue::Highlight + | NodeValue::Insert + | NodeValue::HtmlInline(..) + | NodeValue::Math(..) + | NodeValue::WikiLink(..) + | NodeValue::FootnoteReference(..) + | NodeValue::Superscript + | NodeValue::SpoileredText + | NodeValue::Underline + | NodeValue::Subscript + | NodeValue::TaskItem(_) + | NodeValue::Escaped + | NodeValue::EscapedTag(_) + ) } NodeValue::MultilineBlockQuote(_) => { child.block() && !matches!(*child, NodeValue::Item(..) | NodeValue::TaskItem(..)) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index bd534fa6..81c41516 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -370,7 +370,7 @@ where } NodeValue::Heading(..) | NodeValue::TableRow(..) - | NodeValue::TableCell + | NodeValue::TableCell(..) | NodeValue::Subtext => { break; } @@ -2614,7 +2614,7 @@ where let parent = node.parent().unwrap(); - if node_matches!(parent, NodeValue::TableCell) { + if node_matches!(parent, NodeValue::TableCell(..)) { if !self.options.parse.tasklist_in_table { return; } diff --git a/src/parser/table.rs b/src/parser/table.rs index 5dd09836..7008b85b 100644 --- a/src/parser/table.rs +++ b/src/parser/table.rs @@ -2,7 +2,10 @@ use std::borrow::Cow; use std::cmp::min; use std::mem; -use crate::nodes::{Ast, LineColumn, Node, NodeTable, NodeValue, TableAlignment}; +use crate::nodes::{ + Ast, LineColumn, Node, NodeTable, NodeTableCell, NodeValue, TableAlignment, TableKind, + TableRowKind, +}; use crate::parser::Parser; use crate::scanners; use crate::strings::{count_newlines, is_line_end_char, newlines_of, trim_cow}; @@ -90,6 +93,7 @@ fn try_opening_header<'a>( let start = container.data().sourcepos.start; let child = Ast::new( NodeValue::Table(Box::new(NodeTable { + kind: TableKind::Pipe, alignments, num_columns: header_row.cells.len(), num_rows: 0, @@ -100,7 +104,11 @@ fn try_opening_header<'a>( let table = parser.arena.alloc(child.into()); container.append(table); - let header = parser.add_child(table, NodeValue::TableRow(true), start.column); + let header = parser.add_child( + table, + NodeValue::TableRow(TableRowKind::Header), + start.column, + ); { let header_ast = &mut header.data_mut(); header_ast.sourcepos.start.line = start.line; @@ -118,7 +126,11 @@ fn try_opening_header<'a>( let cell = &mut header_row.cells[i]; let ast_cell = parser.add_child( header, - NodeValue::TableCell, + NodeValue::TableCell(NodeTableCell { + colspan: 1, + rowspan: 1, + grid_column: i, + }), start.column + cell.start_offset - header_row.paragraph_offset, ); let ast = &mut ast_cell.data_mut(); @@ -164,7 +176,7 @@ fn try_opening_row<'a>( let new_row = parser.add_child( container, - NodeValue::TableRow(false), + NodeValue::TableRow(TableRowKind::Body), sourcepos.start.column, ); new_row.data_mut().sourcepos.end.column = parser.curline_end_col; @@ -176,7 +188,11 @@ fn try_opening_row<'a>( let cell = &mut this_row.cells[i]; let cell_node = parser.add_child( new_row, - NodeValue::TableCell, + NodeValue::TableCell(NodeTableCell { + colspan: 1, + rowspan: 1, + grid_column: i, + }), sourcepos.start.column + cell.start_offset, ); let cell_ast = &mut cell_node.data_mut(); @@ -192,7 +208,15 @@ fn try_opening_row<'a>( } while i < alignments.len() { - let cell_node = parser.add_child(new_row, NodeValue::TableCell, last_column + 1); + let cell_node = parser.add_child( + new_row, + NodeValue::TableCell(NodeTableCell { + colspan: 1, + rowspan: 1, + grid_column: i, + }), + last_column + 1, + ); // for autocompleted (empty) cells, set end column equal to start let cell_ast = &mut cell_node.data_mut(); cell_ast.sourcepos.end.column = last_column + 1; diff --git a/src/xml.rs b/src/xml.rs index 9ec10140..ba663cf5 100644 --- a/src/xml.rs +++ b/src/xml.rs @@ -3,7 +3,7 @@ use std::fmt::{self, Write}; use crate::character_set::character_set; use crate::node_matches; -use crate::nodes::{ListType, NodeCode, NodeMath, NodeValue}; +use crate::nodes::{ListType, NodeCode, NodeMath, NodeValue, TableRowKind}; use crate::nodes::{Node, NodeHtmlBlock}; use crate::parser::options::{Options, Plugins}; @@ -255,13 +255,15 @@ impl<'o, 'c> XmlFormatter<'o, 'c> { NodeValue::TableRow(..) => { // noop } - NodeValue::TableCell => { + NodeValue::TableCell(..) => { let mut ancestors = node.ancestors().skip(1); let header_row = &ancestors.next().unwrap().data().value; let table = &ancestors.next().unwrap().data().value; - if let (NodeValue::TableRow(true), NodeValue::Table(nt)) = (header_row, table) { + if let (NodeValue::TableRow(TableRowKind::Header), NodeValue::Table(nt)) = + (header_row, table) + { let ix = node.preceding_siblings().count() - 1; if let Some(xml_align) = nt.alignments[ix].xml_name() { write!(self.output, " align=\"{}\"", xml_align)?;