block: parse captions
This commit is contained in:
parent
99f10fda4c
commit
8339befe2f
3 changed files with 111 additions and 6 deletions
110
src/block.rs
110
src/block.rs
|
@ -65,6 +65,9 @@ pub enum Leaf {
|
|||
/// Has zero or one inline for the cell contents.
|
||||
TableCell(Alignment),
|
||||
|
||||
/// Span is '^' character.
|
||||
Caption,
|
||||
|
||||
/// Span is the link tag.
|
||||
/// Inlines are lines of the URL.
|
||||
LinkDefinition,
|
||||
|
@ -268,7 +271,25 @@ impl<'s> TreeParser<'s> {
|
|||
self.alignments.clear();
|
||||
self.tree.enter(Node::Container(Table), span);
|
||||
let mut last_row_node = None;
|
||||
for row in lines {
|
||||
let caption_line = lines
|
||||
.iter()
|
||||
.position(|sp| sp.of(self.src).trim_start().starts_with('^'))
|
||||
.map_or(lines.len(), |caption_line| {
|
||||
self.tree.enter(Node::Leaf(Caption), span);
|
||||
lines[caption_line] =
|
||||
lines[caption_line].trim_start(self.src).skip("^ ".len());
|
||||
lines[lines.len() - 1] = lines[lines.len() - 1].trim_end(self.src);
|
||||
for line in &lines[caption_line..] {
|
||||
self.tree.inline(*line);
|
||||
}
|
||||
self.tree.exit(); // caption, will insert inlines later if any
|
||||
caption_line
|
||||
});
|
||||
for row in &lines[..caption_line] {
|
||||
let row = row.trim(self.src);
|
||||
if row.is_empty() {
|
||||
break;
|
||||
}
|
||||
let row_node = self
|
||||
.tree
|
||||
.enter(Node::Container(TableRow { head: false }), row.with_len(1));
|
||||
|
@ -468,6 +489,7 @@ struct BlockParser {
|
|||
kind: Block,
|
||||
span: Span,
|
||||
fence: Option<(char, usize)>,
|
||||
caption: bool,
|
||||
}
|
||||
|
||||
impl BlockParser {
|
||||
|
@ -595,6 +617,7 @@ impl BlockParser {
|
|||
kind,
|
||||
span,
|
||||
fence,
|
||||
caption: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -638,14 +661,26 @@ impl BlockParser {
|
|||
!((&mut c).take(fence_length).all(|c| c == fence)
|
||||
&& c.next().map_or(true, char::is_whitespace))
|
||||
}
|
||||
Block::Container(List { .. } | DescriptionList | TableRow { .. })
|
||||
| Block::Leaf(TableCell(..)) => {
|
||||
panic!()
|
||||
}
|
||||
Block::Container(Table) if self.caption => !line.trim().is_empty(),
|
||||
Block::Container(Table) => {
|
||||
let line = line.trim();
|
||||
let l = line.len();
|
||||
line.as_bytes()[l - 1] == b'|' && line.as_bytes()[l - 2] != b'\\'
|
||||
match l {
|
||||
0 => true,
|
||||
1..=2 => false,
|
||||
_ => {
|
||||
if line.starts_with("^ ") {
|
||||
self.caption = true;
|
||||
true
|
||||
} else {
|
||||
line.as_bytes()[l - 1] == b'|' && line.as_bytes()[l - 2] != b'\\'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Block::Container(List { .. } | DescriptionList | TableRow { .. })
|
||||
| Block::Leaf(TableCell(..) | Caption) => {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1470,6 +1505,69 @@ mod test {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_table_caption() {
|
||||
test_parse!(
|
||||
"|a|\n^ caption",
|
||||
(Enter(Container(Table)), ""),
|
||||
(Enter(Leaf(Caption)), ""),
|
||||
(Inline, "caption"),
|
||||
(Exit(Leaf(Caption)), ""),
|
||||
(Enter(Container(TableRow { head: false })), "|"),
|
||||
(Enter(Leaf(TableCell(Alignment::Unspecified))), "|"),
|
||||
(Inline, "a"),
|
||||
(Exit(Leaf(TableCell(Alignment::Unspecified))), "|"),
|
||||
(Exit(Container(TableRow { head: false })), "|"),
|
||||
(Exit(Container(Table)), ""),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_table_caption_multiline() {
|
||||
test_parse!(
|
||||
concat!(
|
||||
"|a|\n", //
|
||||
"\n", //
|
||||
"^ caption\n", //
|
||||
"continued\n", //
|
||||
"\n", //
|
||||
"para\n", //
|
||||
),
|
||||
(Enter(Container(Table)), ""),
|
||||
(Enter(Leaf(Caption)), ""),
|
||||
(Inline, "caption\n"),
|
||||
(Inline, "continued"),
|
||||
(Exit(Leaf(Caption)), ""),
|
||||
(Enter(Container(TableRow { head: false })), "|"),
|
||||
(Enter(Leaf(TableCell(Alignment::Unspecified))), "|"),
|
||||
(Inline, "a"),
|
||||
(Exit(Leaf(TableCell(Alignment::Unspecified))), "|"),
|
||||
(Exit(Container(TableRow { head: false })), "|"),
|
||||
(Exit(Container(Table)), ""),
|
||||
(Atom(Blankline), "\n"),
|
||||
(Enter(Leaf(Paragraph)), ""),
|
||||
(Inline, "para"),
|
||||
(Exit(Leaf(Paragraph)), ""),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_table_caption_empty() {
|
||||
test_parse!(
|
||||
"|a|\n^ ",
|
||||
(Enter(Container(Table)), ""),
|
||||
(Enter(Container(TableRow { head: false })), "|"),
|
||||
(Enter(Leaf(TableCell(Alignment::Unspecified))), "|"),
|
||||
(Inline, "a"),
|
||||
(Exit(Leaf(TableCell(Alignment::Unspecified))), "|"),
|
||||
(Exit(Container(TableRow { head: false })), "|"),
|
||||
(Exit(Container(Table)), ""),
|
||||
(Enter(Leaf(Paragraph)), ""),
|
||||
(Inline, "^"),
|
||||
(Exit(Leaf(Paragraph)), ""),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_table_sep_row_only() {
|
||||
test_parse!(
|
||||
|
|
|
@ -157,6 +157,7 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<'s, I, W> {
|
|||
Container::Heading { level } => write!(self.out, "<h{}", level)?,
|
||||
Container::TableCell { head: false, .. } => self.out.write_str("<td")?,
|
||||
Container::TableCell { head: true, .. } => self.out.write_str("<th")?,
|
||||
Container::Caption => self.out.write_str("<caption")?,
|
||||
Container::DescriptionTerm => self.out.write_str("<dt")?,
|
||||
Container::CodeBlock { .. } => self.out.write_str("<pre")?,
|
||||
Container::Span | Container::Math { .. } => self.out.write_str("<span")?,
|
||||
|
@ -338,6 +339,7 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<'s, I, W> {
|
|||
Container::Heading { level } => write!(self.out, "</h{}>", level)?,
|
||||
Container::TableCell { head: false, .. } => self.out.write_str("</td>")?,
|
||||
Container::TableCell { head: true, .. } => self.out.write_str("</th>")?,
|
||||
Container::Caption => self.out.write_str("</caption>")?,
|
||||
Container::DescriptionTerm => self.out.write_str("</dt>")?,
|
||||
Container::CodeBlock { .. } => self.out.write_str("</code></pre>")?,
|
||||
Container::Span => self.out.write_str("</span>")?,
|
||||
|
|
|
@ -56,6 +56,8 @@ pub enum Container<'s> {
|
|||
Heading { level: usize },
|
||||
/// A cell element of row within a table.
|
||||
TableCell { alignment: Alignment, head: bool },
|
||||
/// A caption within a table.
|
||||
Caption,
|
||||
/// A term within a description list.
|
||||
DescriptionTerm,
|
||||
/// A block with raw markup for a specific output format.
|
||||
|
@ -111,6 +113,7 @@ impl<'s> Container<'s> {
|
|||
| Self::Paragraph
|
||||
| Self::Heading { .. }
|
||||
| Self::TableCell { .. }
|
||||
| Self::Caption
|
||||
| Self::DescriptionTerm
|
||||
| Self::RawBlock { .. }
|
||||
| Self::CodeBlock { .. } => true,
|
||||
|
@ -148,6 +151,7 @@ impl<'s> Container<'s> {
|
|||
Self::Paragraph
|
||||
| Self::Heading { .. }
|
||||
| Self::TableCell { .. }
|
||||
| Self::Caption
|
||||
| Self::DescriptionTerm
|
||||
| Self::RawBlock { .. }
|
||||
| Self::CodeBlock { .. }
|
||||
|
@ -541,6 +545,7 @@ impl<'s> Parser<'s> {
|
|||
alignment,
|
||||
head: self.table_head_row,
|
||||
},
|
||||
block::Leaf::Caption => Container::Caption,
|
||||
block::Leaf::LinkDefinition => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue