From 03faa7257f34a35ba9a718222b9e9807a7449a10 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Thu, 24 Aug 2023 20:37:58 -0400 Subject: [PATCH] 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 1369cd95..4ee2a769 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 12aab971..fb65c5f8 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::*;