From ae3510abd5fcdf0c4066098da5217326085f3f43 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Thu, 24 Aug 2023 20:10:43 -0400 Subject: [PATCH 1/6] Do not cast lesser block name to lowercase at runtime. This reduced the runtime of my problematic test case from 6.9 seconds to 6 seconds. --- src/parser/lesser_block.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index a039061..9615727 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -265,13 +265,14 @@ fn _lesser_block_end<'r, 's, 'x>( Ok((remaining, source)) } -fn lesser_block_begin( - current_name: &str, +/// Parser for the beginning of a lesser block +/// +/// current_name MUST be lowercase. We do not do the conversion ourselves because it is not allowed in a const fn. +const fn lesser_block_begin( + current_name: &'static str, ) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res, OrgSource<'s>> { - let current_name_lower = current_name.to_lowercase(); - move |context: Context, input: OrgSource<'_>| { - _lesser_block_begin(context, input, current_name_lower.as_str()) - } + // TODO: Since this is a const fn, is there ANY way to "generate" functions at compile time? + move |context: Context, input: OrgSource<'_>| _lesser_block_begin(context, input, current_name) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] From 03faa7257f34a35ba9a718222b9e9807a7449a10 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Thu, 24 Aug 2023 20:37:58 -0400 Subject: [PATCH 2/6] Move the indent level for plain list's exit matcher to const fn instead of grabbing from the context. This made a slight improvement to performance. --- src/parser/parser_context.rs | 3 --- src/parser/plain_list.rs | 52 +++++++++++++++++++++--------------- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/parser/parser_context.rs b/src/parser/parser_context.rs index 1369cd9..4ee2a76 100644 --- a/src/parser/parser_context.rs +++ b/src/parser/parser_context.rs @@ -112,9 +112,6 @@ pub enum ContextElement<'r, 's> { ExitMatcherNode(ExitMatcherNode<'r>), Context(&'r str), - /// Stores the indentation level of the current list item. - ListItem(usize), - /// Stores the name of the greater block. GreaterBlock(&'s str), diff --git a/src/parser/plain_list.rs b/src/parser/plain_list.rs index 12aab97..fb65c5f 100644 --- a/src/parser/plain_list.rs +++ b/src/parser/plain_list.rs @@ -135,12 +135,12 @@ pub fn plain_list_item<'r, 's>( }; let (remaining, _ws) = space1(remaining)?; + let exit_matcher = plain_list_item_end(indent_level); let parser_context = context .with_additional_node(ContextElement::ConsumeTrailingWhitespace(true)) - .with_additional_node(ContextElement::ListItem(indent_level)) .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { class: ExitClass::Beta, - exit_matcher: &plain_list_item_end, + exit_matcher: &exit_matcher, })); let (remaining, (children, _exit_contents)) = verify( @@ -194,47 +194,55 @@ fn plain_list_end<'r, 's>( )))(input) } -#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn plain_list_item_end<'r, 's>( +const fn plain_list_item_end( + indent_level: usize, +) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res, OrgSource<'s>> { + let line_indented_lte_matcher = line_indented_lte(indent_level); + move |context: Context, input: OrgSource<'_>| { + _plain_list_item_end(context, input, &line_indented_lte_matcher) + } +} + +#[cfg_attr( + feature = "tracing", + tracing::instrument(ret, level = "debug", skip(line_indented_lte_matcher)) +)] +fn _plain_list_item_end<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, + line_indented_lte_matcher: impl for<'rr, 'ss> Fn( + Context<'rr, 'ss>, + OrgSource<'ss>, + ) -> Res, OrgSource<'ss>>, ) -> Res, OrgSource<'s>> { start_of_line(input)?; recognize(tuple(( opt(blank_line), - parser_with_context!(line_indented_lte)(context), + parser_with_context!(line_indented_lte_matcher)(context), )))(input) } +const fn line_indented_lte( + indent_level: usize, +) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res, OrgSource<'s>> { + move |context: Context, input: OrgSource<'_>| _line_indented_lte(context, input, indent_level) +} + #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn line_indented_lte<'r, 's>( +fn _line_indented_lte<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, + indent_level: usize, ) -> Res, OrgSource<'s>> { - 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".into(), - ))))?; - let matched = recognize(verify( tuple((space0::, _>, 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, + |(_space0, _anychar)| _space0.len() <= 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 -} - #[cfg(test)] mod tests { use super::*; From 33d7ae03d1c38bba6b3289d5c401570ef0093b39 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Thu, 24 Aug 2023 21:35:34 -0400 Subject: [PATCH 3/6] Add a TODO. --- src/parser/plain_list.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/parser/plain_list.rs b/src/parser/plain_list.rs index fb65c5f..26e300e 100644 --- a/src/parser/plain_list.rs +++ b/src/parser/plain_list.rs @@ -116,6 +116,7 @@ pub fn plain_list_item<'r, 's>( Into::<&str>::into(bull) != "*" || indent_level > 0 })(remaining)?; + // TODO: This isn't taking into account items that immediately line break and then have contents let maybe_contentless_item: Res, OrgSource<'_>> = alt((eof, line_ending))(remaining); match maybe_contentless_item { From 02fe10fba3243dc6760133edacd2bee1fd69356a Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Thu, 24 Aug 2023 23:34:23 -0400 Subject: [PATCH 4/6] Move objects to a lower exit class. Paragraph's exit matcher which detects elements was causing the plain list parser to exit after the first item was parsed which was causing significant amounts of re-parsing. --- src/parser/angle_link.rs | 2 +- src/parser/citation.rs | 4 ++-- src/parser/citation_reference.rs | 4 ++-- src/parser/exiting.rs | 3 +++ src/parser/export_snippet.rs | 2 +- src/parser/inline_babel_call.rs | 6 +++--- src/parser/paragraph.rs | 2 +- src/parser/parser_context.rs | 2 +- src/parser/plain_link.rs | 2 +- src/parser/radio_link.rs | 2 +- src/parser/subscript_and_superscript.rs | 2 +- src/parser/text_markup.rs | 6 +++--- src/parser/timestamp.rs | 16 ++++++++-------- 13 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/parser/angle_link.rs b/src/parser/angle_link.rs index 0e04278..ea10fbd 100644 --- a/src/parser/angle_link.rs +++ b/src/parser/angle_link.rs @@ -44,7 +44,7 @@ fn path_angle<'r, 's>( ) -> Res, OrgSource<'s>> { let parser_context = context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Beta, + class: ExitClass::Gamma, exit_matcher: &path_angle_end, })); diff --git a/src/parser/citation.rs b/src/parser/citation.rs index e19938b..ffce49a 100644 --- a/src/parser/citation.rs +++ b/src/parser/citation.rs @@ -90,7 +90,7 @@ fn global_prefix<'r, 's>( depth: 0, })) .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Beta, + class: ExitClass::Gamma, exit_matcher: &global_prefix_end, })); let (remaining, (children, _exit_contents)) = verify( @@ -151,7 +151,7 @@ fn global_suffix<'r, 's>( depth: 0, })) .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Beta, + class: ExitClass::Gamma, exit_matcher: &global_suffix_end, })); let (remaining, (children, _exit_contents)) = verify( diff --git a/src/parser/citation_reference.rs b/src/parser/citation_reference.rs index ed18a65..b854f6f 100644 --- a/src/parser/citation_reference.rs +++ b/src/parser/citation_reference.rs @@ -76,7 +76,7 @@ fn key_prefix<'r, 's>( depth: 0, })) .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Beta, + class: ExitClass::Gamma, exit_matcher: &key_prefix_end, })); let (remaining, (children, _exit_contents)) = verify( @@ -101,7 +101,7 @@ fn key_suffix<'r, 's>( depth: 0, })) .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Beta, + class: ExitClass::Gamma, exit_matcher: &key_suffix_end, })); let (remaining, (children, _exit_contents)) = verify( diff --git a/src/parser/exiting.rs b/src/parser/exiting.rs index a7e3fa7..6f8c359 100644 --- a/src/parser/exiting.rs +++ b/src/parser/exiting.rs @@ -8,6 +8,9 @@ pub enum ExitClass { /// Elements who cede priority to alpha elements when matching. Beta = 300, + + /// Elements who cede priority to alpha and beta elements when matching. + Gamma = 4000, } impl std::fmt::Display for ExitClass { diff --git a/src/parser/export_snippet.rs b/src/parser/export_snippet.rs index 0725684..9351929 100644 --- a/src/parser/export_snippet.rs +++ b/src/parser/export_snippet.rs @@ -27,7 +27,7 @@ pub fn export_snippet<'r, 's>( let (remaining, backend_name) = backend(context, remaining)?; let parser_context = context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Beta, + class: ExitClass::Gamma, exit_matcher: &export_snippet_end, })); let (remaining, backend_contents) = opt(tuple(( diff --git a/src/parser/inline_babel_call.rs b/src/parser/inline_babel_call.rs index 36f62b6..a1ed4e3 100644 --- a/src/parser/inline_babel_call.rs +++ b/src/parser/inline_babel_call.rs @@ -49,7 +49,7 @@ fn name<'r, 's>( ) -> Res, OrgSource<'s>> { let parser_context = context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Beta, + class: ExitClass::Gamma, exit_matcher: &name_end, })); let (remaining, name) = recognize(many_till( @@ -80,7 +80,7 @@ fn header<'r, 's>( depth: 0, })) .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Beta, + class: ExitClass::Gamma, exit_matcher: &header_end, })); @@ -131,7 +131,7 @@ fn argument<'r, 's>( depth: 0, })) .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Beta, + class: ExitClass::Gamma, exit_matcher: &argument_end, })); diff --git a/src/parser/paragraph.rs b/src/parser/paragraph.rs index 9591706..15b449d 100644 --- a/src/parser/paragraph.rs +++ b/src/parser/paragraph.rs @@ -28,7 +28,7 @@ pub fn paragraph<'r, 's>( ) -> Res, Paragraph<'s>> { let parser_context = context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Beta, + class: ExitClass::Gamma, exit_matcher: ¶graph_end, })); let standard_set_object_matcher = parser_with_context!(standard_set_object)(&parser_context); diff --git a/src/parser/parser_context.rs b/src/parser/parser_context.rs index 4ee2a76..83760ae 100644 --- a/src/parser/parser_context.rs +++ b/src/parser/parser_context.rs @@ -62,7 +62,7 @@ impl<'r, 's> ContextTree<'r, 's> { // exit_matcher: ChainBehavior::IgnoreParent(Some(&always_fail)), // })); - let mut current_class_filter = ExitClass::Beta; + let mut current_class_filter = ExitClass::Gamma; for current_node in self.iter() { let context_element = current_node.get_data(); match context_element { diff --git a/src/parser/plain_link.rs b/src/parser/plain_link.rs index 4aa097c..58617ea 100644 --- a/src/parser/plain_link.rs +++ b/src/parser/plain_link.rs @@ -113,7 +113,7 @@ fn path_plain<'r, 's>( // TODO: "optionally containing parenthesis-wrapped non-whitespace non-bracket substrings up to a depth of two. The string must end with either a non-punctation non-whitespace character, a forwards slash, or a parenthesis-wrapped substring" let parser_context = context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Beta, + class: ExitClass::Gamma, exit_matcher: &path_plain_end, })); diff --git a/src/parser/radio_link.rs b/src/parser/radio_link.rs index 5a62240..6e121dd 100644 --- a/src/parser/radio_link.rs +++ b/src/parser/radio_link.rs @@ -91,7 +91,7 @@ pub fn radio_target<'r, 's>( let (remaining, _opening) = tag("<<<")(input)?; let parser_context = context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Beta, + class: ExitClass::Gamma, exit_matcher: &radio_target_end, })); diff --git a/src/parser/subscript_and_superscript.rs b/src/parser/subscript_and_superscript.rs index cd10167..43978da 100644 --- a/src/parser/subscript_and_superscript.rs +++ b/src/parser/subscript_and_superscript.rs @@ -162,7 +162,7 @@ fn script_with_braces<'r, 's>( }, )) .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Beta, + class: ExitClass::Gamma, exit_matcher: &script_with_braces_end, })); diff --git a/src/parser/text_markup.rs b/src/parser/text_markup.rs index ce8cf96..d198457 100644 --- a/src/parser/text_markup.rs +++ b/src/parser/text_markup.rs @@ -179,7 +179,7 @@ fn _text_markup_object<'r, 's, 'x>( let text_markup_end_specialized = text_markup_end(open.into()); let parser_context = context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Beta, + class: ExitClass::Gamma, exit_matcher: &text_markup_end_specialized, })); @@ -229,7 +229,7 @@ fn _text_markup_string<'r, 's, 'x>( let text_markup_end_specialized = text_markup_end(open.into()); let parser_context = context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Beta, + class: ExitClass::Gamma, exit_matcher: &text_markup_end_specialized, })); @@ -338,7 +338,7 @@ fn _rematch_text_markup_object<'r, 's, 'x>( let text_markup_end_specialized = text_markup_end(open.into()); let parser_context = context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Beta, + class: ExitClass::Gamma, exit_matcher: &text_markup_end_specialized, })); diff --git a/src/parser/timestamp.rs b/src/parser/timestamp.rs index e3dc8df..2814716 100644 --- a/src/parser/timestamp.rs +++ b/src/parser/timestamp.rs @@ -67,7 +67,7 @@ fn sexp<'r, 's>( ) -> Res, OrgSource<'s>> { let parser_context = context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Beta, + class: ExitClass::Gamma, exit_matcher: &sexp_end, })); @@ -99,7 +99,7 @@ fn active_timestamp<'r, 's>( let (remaining, _date) = date(context, remaining)?; let time_context = context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Beta, + class: ExitClass::Gamma, exit_matcher: &active_time_rest_end, })); let (remaining, _time) = @@ -132,7 +132,7 @@ fn inactive_timestamp<'r, 's>( let (remaining, _date) = date(context, remaining)?; let time_context = context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Beta, + class: ExitClass::Gamma, exit_matcher: &inactive_time_rest_end, })); let (remaining, _time) = @@ -186,12 +186,12 @@ fn active_time_range_timestamp<'r, 's>( let (remaining, _date) = date(context, remaining)?; let time_context = context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Beta, + class: ExitClass::Gamma, exit_matcher: &active_time_rest_end, })); let first_time_context = time_context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Beta, + class: ExitClass::Gamma, exit_matcher: &time_range_rest_end, })); let (remaining, _first_time) = @@ -247,12 +247,12 @@ fn inactive_time_range_timestamp<'r, 's>( let (remaining, _date) = date(context, remaining)?; let time_context = context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Beta, + class: ExitClass::Gamma, exit_matcher: &inactive_time_rest_end, })); let first_time_context = time_context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Beta, + class: ExitClass::Gamma, exit_matcher: &time_range_rest_end, })); let (remaining, _first_time) = @@ -303,7 +303,7 @@ fn dayname<'r, 's>( ) -> Res, OrgSource<'s>> { let parser_context = context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Beta, + class: ExitClass::Gamma, exit_matcher: &dayname_end, })); From 0dbc8f0925a127a9f818532f01f4828ecffcc72e Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Thu, 24 Aug 2023 23:55:11 -0400 Subject: [PATCH 5/6] Remove redundant exit matcher checks. --- src/parser/object_parser.rs | 6 ------ src/parser/plain_list.rs | 3 --- 2 files changed, 9 deletions(-) diff --git a/src/parser/object_parser.rs b/src/parser/object_parser.rs index 37973a1..be6e45b 100644 --- a/src/parser/object_parser.rs +++ b/src/parser/object_parser.rs @@ -1,6 +1,5 @@ use nom::branch::alt; use nom::combinator::map; -use nom::combinator::not; use super::org_source::OrgSource; use super::parser_with_context::parser_with_context; @@ -34,8 +33,6 @@ pub fn standard_set_object<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, ) -> Res, Object<'s>> { - not(|i| context.check_exit_matcher(i))(input)?; - alt(( map(parser_with_context!(timestamp)(context), Object::Timestamp), map(parser_with_context!(subscript)(context), Object::Subscript), @@ -93,8 +90,6 @@ pub fn minimal_set_object<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, ) -> Res, Object<'s>> { - not(|i| context.check_exit_matcher(i))(input)?; - alt(( map(parser_with_context!(subscript)(context), Object::Subscript), map( @@ -116,7 +111,6 @@ pub fn any_object_except_plain_text<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, ) -> Res, Object<'s>> { - // Used for exit matchers so this does not check exit matcher condition. alt(( map(parser_with_context!(timestamp)(context), Object::Timestamp), map(parser_with_context!(subscript)(context), Object::Subscript), diff --git a/src/parser/plain_list.rs b/src/parser/plain_list.rs index 26e300e..ed41f0c 100644 --- a/src/parser/plain_list.rs +++ b/src/parser/plain_list.rs @@ -90,9 +90,6 @@ pub fn plain_list<'r, 's>( parser_with_context!(plain_list_item)(&final_item_context)(final_child_start)?; children.push((final_child_start, reparsed_final_item)); - let (remaining, _trailing_ws) = - maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; - let source = get_consumed(input, remaining); Ok(( remaining, From 9c1e6ccc97e53995660e1a18eb7c1d1bf77f9ac9 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Fri, 25 Aug 2023 00:48:34 -0400 Subject: [PATCH 6/6] Add a detect_element function. This is an optimization. When you have something like plain text which ends when it hits the next element, we only need to parse enough to detect that an element is about to occur. For elements like plain lists, this is as simple as parsing a line starting with optional whitespace and then a bullet, which avoids parsing the entire plain list tree. The benefit is most noticeable in deeply nested plain lists. --- src/parser/element_parser.rs | 28 +++++++++++++++++++++++++++- src/parser/paragraph.rs | 4 ++-- src/parser/plain_list.rs | 23 ++++++++++++++++++++++- 3 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/parser/element_parser.rs b/src/parser/element_parser.rs index d781a7a..2c79e46 100644 --- a/src/parser/element_parser.rs +++ b/src/parser/element_parser.rs @@ -21,16 +21,19 @@ use super::lesser_block::src_block; use super::lesser_block::verse_block; use super::org_source::OrgSource; use super::paragraph::paragraph; +use super::plain_list::detect_plain_list; use super::plain_list::plain_list; use super::source::SetSource; use super::util::get_consumed; use super::util::maybe_consume_trailing_whitespace_if_not_exiting; use super::Context; +use crate::error::CustomError; +use crate::error::MyError; use crate::error::Res; use crate::parser::parser_with_context::parser_with_context; use crate::parser::table::org_mode_table; -pub fn element( +pub const fn element( can_be_paragraph: bool, ) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res, Element<'s>> { move |context: Context, input: OrgSource<'_>| _element(context, input, can_be_paragraph) @@ -108,3 +111,26 @@ fn _element<'r, 's>( Ok((remaining, element)) } + +pub const fn detect_element( + can_be_paragraph: bool, +) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res, ()> { + move |context: Context, input: OrgSource<'_>| _detect_element(context, input, can_be_paragraph) +} + +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +fn _detect_element<'r, 's>( + context: Context<'r, 's>, + input: OrgSource<'s>, + can_be_paragraph: bool, +) -> Res, ()> { + if detect_plain_list(context, input).is_ok() { + return Ok((input, ())); + } + if _element(context, input, can_be_paragraph).is_ok() { + return Ok((input, ())); + } + return Err(nom::Err::Error(CustomError::MyError(MyError( + "No element detected.".into(), + )))); +} diff --git a/src/parser/paragraph.rs b/src/parser/paragraph.rs index 15b449d..9cfda4d 100644 --- a/src/parser/paragraph.rs +++ b/src/parser/paragraph.rs @@ -6,13 +6,13 @@ use nom::multi::many1; use nom::multi::many_till; use nom::sequence::tuple; +use super::element_parser::detect_element; use super::lesser_element::Paragraph; use super::org_source::OrgSource; use super::util::blank_line; use super::util::get_consumed; use super::Context; use crate::error::Res; -use crate::parser::element_parser::element; use crate::parser::exiting::ExitClass; use crate::parser::object_parser::standard_set_object; use crate::parser::parser_context::ContextElement; @@ -57,7 +57,7 @@ fn paragraph_end<'r, 's>( context: Context<'r, 's>, input: OrgSource<'s>, ) -> Res, OrgSource<'s>> { - let non_paragraph_element_matcher = parser_with_context!(element(false))(context); + let non_paragraph_element_matcher = parser_with_context!(detect_element(false))(context); alt(( recognize(tuple((start_of_line, many1(blank_line)))), recognize(non_paragraph_element_matcher), diff --git a/src/parser/plain_list.rs b/src/parser/plain_list.rs index ed41f0c..d39b8f7 100644 --- a/src/parser/plain_list.rs +++ b/src/parser/plain_list.rs @@ -32,6 +32,27 @@ use crate::parser::util::get_consumed; use crate::parser::util::maybe_consume_trailing_whitespace_if_not_exiting; use crate::parser::util::start_of_line; +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +pub fn detect_plain_list<'r, 's>( + _context: Context<'r, 's>, + input: OrgSource<'s>, +) -> Res, ()> { + // TODO: Add support for plain list items that do not have content on the first line. + if verify( + tuple((start_of_line, space0, bullet, space1)), + |(_start, indent, bull, _after_whitespace)| { + Into::<&str>::into(bull) != "*" || indent.len() > 0 + }, + )(input) + .is_ok() + { + return Ok((input, ())); + } + return Err(nom::Err::Error(CustomError::MyError(MyError( + "No element detected.".into(), + )))); +} + #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub fn plain_list<'r, 's>( context: Context<'r, 's>, @@ -228,7 +249,7 @@ const fn line_indented_lte( #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn _line_indented_lte<'r, 's>( - context: Context<'r, 's>, + _context: Context<'r, 's>, input: OrgSource<'s>, indent_level: usize, ) -> Res, OrgSource<'s>> {