Building the plain list item context.

This commit is contained in:
Tom Alexander
2023-03-25 14:10:22 -04:00
parent 4a863e92ff
commit e6752b9d83
7 changed files with 110 additions and 28 deletions

View File

@@ -1,5 +1,22 @@
use nom::branch::alt;
use nom::character::complete::space0;
use nom::combinator::eof;
use nom::combinator::not;
use nom::combinator::recognize;
use nom::combinator::verify;
use nom::sequence::tuple;
use crate::parser::parser_context::ChainBehavior;
use crate::parser::parser_context::ContextElement;
use crate::parser::parser_context::ExitMatcherNode;
use crate::parser::util::start_of_line;
use super::error::CustomError;
use super::error::MyError;
use super::error::Res;
use super::lesser_element::Paragraph;
use super::parser_with_context::parser_with_context;
use super::util::non_whitespace_character;
use super::Context;
#[allow(dead_code)]
@@ -7,5 +24,52 @@ pub fn plain_list_item<'r, 's>(
context: Context<'r, 's>,
input: &'s str,
) -> Res<&'s str, Paragraph<'s>> {
not(|i| context.check_exit_matcher(i))(input)?;
start_of_line(context, input)?;
let (remaining, leading_whitespace) = space0(input)?;
// It is fine that we get the indent level using the number of bytes rather than the number of characters because nom's space0 only matches space and tab (0x20 and 0x09)
let indent_level = leading_whitespace.len();
let list_item_context = context
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
exit_matcher: ChainBehavior::AndParent(Some(&plain_list_item_end)),
}))
.with_additional_node(ContextElement::ListItem(indent_level));
todo!()
}
fn plain_list_item_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> {
let plain_list_item_matcher = parser_with_context!(plain_list_item)(context);
let line_indented_lte_matcher = parser_with_context!(line_indented_lte)(context);
alt((
recognize(plain_list_item_matcher),
line_indented_lte_matcher,
eof,
))(input)
}
fn line_indented_lte<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> {
let current_item_indent_level: &usize =
get_context_item_indent(context).ok_or(nom::Err::Error(CustomError::MyError(MyError(
"Not inside a plain list item",
))))?;
start_of_line(context, input)?;
let matched = recognize(verify(
tuple((space0::<&str, _>, non_whitespace_character)),
// It is fine that we get the indent level using the number of bytes rather than the number of characters because nom's space0 only matches space and tab (0x20 and 0x09)
|(_space0, _anychar)| _space0.len() <= *current_item_indent_level,
))(input)?;
Ok(matched)
}
fn get_context_item_indent<'r, 's>(context: Context<'r, 's>) -> Option<&'r usize> {
for thing in context.iter() {
match thing.get_data() {
ContextElement::ListItem(depth) => return Some(depth),
_ => {}
};
}
None
}