From 422535fbe4457d275ea1ea4635937401c2d34259 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 25 Mar 2023 14:23:52 -0400 Subject: [PATCH] Wrote a plain list item parser. --- .../plain_lists/empty_list_item.org | 3 ++ src/parser/plain_list.rs | 40 +++++++++++++++++-- 2 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 org_mode_samples/plain_lists/empty_list_item.org diff --git a/org_mode_samples/plain_lists/empty_list_item.org b/org_mode_samples/plain_lists/empty_list_item.org new file mode 100644 index 00000000..0fe3a9ab --- /dev/null +++ b/org_mode_samples/plain_lists/empty_list_item.org @@ -0,0 +1,3 @@ +1. +2. +3. diff --git a/src/parser/plain_list.rs b/src/parser/plain_list.rs index 13e5b696..085e1523 100644 --- a/src/parser/plain_list.rs +++ b/src/parser/plain_list.rs @@ -1,20 +1,26 @@ use nom::branch::alt; +use nom::bytes::complete::tag; +use nom::character::complete::digit1; +use nom::character::complete::one_of; use nom::character::complete::space0; use nom::combinator::eof; use nom::combinator::not; use nom::combinator::recognize; use nom::combinator::verify; +use nom::multi::many0; use nom::sequence::tuple; +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::get_consumed; 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::greater_element::PlainListItem; use super::parser_with_context::parser_with_context; use super::util::non_whitespace_character; use super::Context; @@ -23,18 +29,44 @@ use super::Context; pub fn plain_list_item<'r, 's>( context: Context<'r, 's>, input: &'s str, -) -> Res<&'s str, Paragraph<'s>> { +) -> Res<&'s str, PlainListItem<'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 + let parser_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!() + + let element_matcher = parser_with_context!(element)(&parser_context); + let (remaining, (bull, _ws)) = tuple((bullet, space0))(remaining)?; + let (remaining, contents) = many0(element_matcher)(remaining)?; + let source = get_consumed(input, remaining); + + Ok(( + remaining, + PlainListItem { + source, + bullet: bull, + contents, + }, + )) +} + +fn bullet<'s>(i: &'s str) -> Res<&'s str, &'s str> { + alt(( + tag("*"), + tag("-"), + tag("+"), + recognize(tuple((counter, alt((tag("."), tag(")")))))), + ))(i) +} + +fn counter<'s>(i: &'s str) -> Res<&'s str, &'s str> { + alt((recognize(one_of("abcdefghijklmnopqrstuvwxyz")), digit1))(i) } fn plain_list_item_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> {