End plain lists when there are two blank lines.

This commit is contained in:
Tom Alexander 2023-04-12 13:09:44 -04:00
parent 7d5eb7c6bb
commit f27965001d
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE
3 changed files with 57 additions and 6 deletions

View File

@ -20,7 +20,7 @@ clean:
.PHONY: test
test:
> cargo test
> cargo test --bin toy
.PHONY: run
run:

View File

@ -38,6 +38,12 @@ impl<'s> Source<'s> for Paragraph<'s> {
}
}
impl<'s> Source<'s> for PlainList<'s> {
fn get_source(&'s self) -> &'s str {
self.source
}
}
#[tracing::instrument(ret, level = "debug")]
pub fn element<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Element<'s>> {
let non_paragraph_matcher = parser_with_context!(non_paragraph_element)(context);

View File

@ -11,6 +11,7 @@ use crate::parser::element::element;
use crate::parser::parser_context::ChainBehavior;
use crate::parser::parser_context::ContextElement;
use crate::parser::parser_context::ExitMatcherNode;
use crate::parser::util::blank_line;
use crate::parser::util::exit_matcher_parser;
use crate::parser::util::get_consumed;
use crate::parser::util::start_of_line;
@ -24,15 +25,20 @@ use nom::character::complete::space1;
use nom::combinator::eof;
use nom::combinator::recognize;
use nom::combinator::verify;
use nom::multi::many1;
use nom::multi::many_till;
use nom::sequence::tuple;
#[tracing::instrument(ret, level = "debug")]
pub fn plain_list<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, PlainList<'s>> {
// TODO: Are we handling 2 blank lines causing the end of all plain lists?
let (mut remaining, first_item) = plain_list_item(context, input)?;
let parser_context =
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
exit_matcher: ChainBehavior::AndParent(Some(&plain_list_end)),
}));
let (mut remaining, first_item) = plain_list_item(&parser_context, input)?;
let first_item_indentation = first_item.indentation;
let plain_list_item_matcher = parser_with_context!(plain_list_item)(context);
let exit_matcher = parser_with_context!(exit_matcher_parser)(context);
let plain_list_item_matcher = parser_with_context!(plain_list_item)(&parser_context);
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
let mut children = Vec::new();
children.push(first_item);
loop {
@ -125,6 +131,15 @@ fn counter<'s>(i: &'s str) -> Res<&'s str, &'s str> {
alt((recognize(one_of("abcdefghijklmnopqrstuvwxyz")), digit1))(i)
}
#[tracing::instrument(ret, level = "debug")]
fn plain_list_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> {
let start_of_line_matcher = parser_with_context!(start_of_line)(context);
recognize(tuple((
start_of_line_matcher,
verify(many1(blank_line), |lines: &Vec<&str>| lines.len() >= 2),
)))(input)
}
#[tracing::instrument(ret, level = "debug")]
fn plain_list_item_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> {
let current_item_indent_level: &usize =
@ -138,7 +153,6 @@ fn plain_list_item_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<
pli.indentation <= *current_item_indent_level
})),
recognize(line_indented_lte_matcher),
eof,
))(input)
}
@ -175,6 +189,7 @@ mod tests {
use crate::parser::parser_context::ContextElement;
use crate::parser::parser_context::ContextTree;
use crate::parser::parser_with_context::parser_with_context;
use crate::parser::Source;
use super::*;
@ -249,4 +264,34 @@ mod tests {
let result = plain_list_matcher(input);
assert!(result.is_ok());
}
#[test]
fn two_blank_lines_ends_list() {
// Plain lists with an asterisk bullet must be indented or else they would be a headline
let input = r#"1. foo
2. bar
baz
3. lorem
ipsum
"#;
let initial_context: ContextTree<'_, '_> = ContextTree::new();
let document_context =
initial_context.with_additional_node(ContextElement::DocumentRoot(input));
let plain_list_matcher = parser_with_context!(plain_list)(&document_context);
let (remaining, result) =
plain_list_matcher(input).expect("Should parse the plain list successfully.");
assert_eq!(remaining, " ipsum\n");
assert_eq!(
result.get_source(),
r#"1. foo
2. bar
baz
3. lorem
"#
);
}
}