use nom::branch::alt; use nom::bytes::complete::tag; use nom::character::complete::anychar; use nom::character::complete::digit1; use nom::character::complete::line_ending; use nom::character::complete::one_of; use nom::character::complete::space0; use nom::combinator::consumed; use nom::combinator::not; use nom::combinator::opt; use nom::combinator::peek; use nom::combinator::recognize; use nom::multi::many0_count; use nom::multi::many1; use nom::sequence::tuple; use super::combinator::context_many_till; use super::error::Res; use super::paragraph::paragraph_end; use super::parser_context::ContextElement; use super::parser_with_context::parser_with_context; use super::text::space; use super::text::text_element; use super::token::ListItem; use super::token::PlainList; use super::token::Token; use super::Context; pub fn plain_list<'r, 's>(context: Context<'r, 's>, i: &'s str) -> Res<&'s str, PlainList<'s>> { // todo todo!() } pub fn item<'r, 's>(context: Context<'r, 's>, i: &'s str) -> Res<&'s str, ListItem<'s>> { let (remaining, leading_whitespace) = space0(i)?; let indent_level = leading_whitespace.len(); let list_item_context = context.with_additional_node(ContextElement::ListItem(indent_level)); let (remaining, (bul, countset, check, tg, sp, (contents, end))) = tuple(( bullet, opt(tuple((space, counter_set))), opt(tuple((space, check_box))), opt(tuple((space, item_tag))), space, // TODO: This context should probably be something involving the item context_many_till(&list_item_context, text_element, item_end), ))(remaining)?; let elements = contents .into_iter() .filter_map(|token| match token { Token::TextElement(text_element) => Some(text_element), Token::Paragraph(_) => panic!("There should only be text elements in items."), }) .collect(); let source = { let offset = remaining.as_ptr() as usize - i.as_ptr() as usize; &i[..offset] }; let ret = ListItem { source, leading_whitespace, bullet: bul, counter_set: countset.map(|(_spc, count)| count), check_box: check.map(|(_spc, check)| check), item_tag: tg.map(|(_spc, tg)| tg), contents: elements, }; Ok((remaining, ret)) } fn counter<'s>(i: &'s str) -> Res<&'s str, &'s str> { alt((recognize(one_of("abcdefghijklmnopqrstuvwxyz")), digit1))(i) } fn bullet<'s>(i: &'s str) -> Res<&'s str, &'s str> { alt(( tag("*"), tag("-"), tag("+"), recognize(tuple((counter, alt((tag("."), tag(")")))))), ))(i) } fn counter_set<'s>(i: &'s str) -> Res<&'s str, &'s str> { recognize(tuple((tag("[@"), counter, tag("]"))))(i) } fn check_box<'s>(i: &'s str) -> Res<&'s str, &'s str> { recognize(alt((tag("[ ]"), tag("[X]"), tag("[-]"))))(i) } fn item_tag<'s>(i: &'s str) -> Res<&'s str, &'s str> { recognize(tuple((tag_text, tag_separator)))(i) } fn tag_text<'s>(i: &'s str) -> Res<&'s str, &'s str> { recognize(many1(tag_text_character))(i) } fn tag_text_character<'s>(i: &'s str) -> Res<&'s str, &'s str> { not(alt((tag_separator, line_ending)))(i)?; recognize(anychar)(i) } fn tag_separator<'s>(i: &'s str) -> Res<&'s str, &'s str> { tag(" :: ")(i) } pub fn item_end<'r, 's>(context: Context<'r, 's>, i: &'s str) -> Res<&'s str, &'s str> { let item_matcher = parser_with_context!(item)(&context); alt(( paragraph_end, recognize(tuple((line_ending, peek(item_matcher)))), ))(i) }