Prevent nesting of text markup of the same type.

This greatly reduces the amount of detect element calls that are occurring.
This commit is contained in:
Tom Alexander 2023-09-07 02:01:34 -04:00
parent 6676012eb1
commit 6b82b46e09
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE
3 changed files with 47 additions and 11 deletions

View File

@ -21,6 +21,9 @@ pub enum ContextElement<'r, 's> {
/// Stores the name of the current element to prevent directly nesting elements of the same type.
Context(&'r str),
/// Stores the name of the current object to prevent directly nesting elements of the same type.
ContextObject(&'r str),
/// Indicates if elements should consume the whitespace after them.
ConsumeTrailingWhitespace(bool),

View File

@ -18,6 +18,7 @@ use tracing::span;
use super::object_parser::standard_set_object;
use super::org_source::OrgSource;
use super::radio_link::RematchObject;
use super::util::in_object_section;
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
use crate::context::parser_with_context;
use crate::context::ContextElement;
@ -177,16 +178,26 @@ fn _text_markup_object<'b, 'g, 'r, 's, 'c>(
input: OrgSource<'s>,
marker_symbol: &'c str,
) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
if in_object_section(context, marker_symbol) {
return Err(nom::Err::Error(CustomError::MyError(MyError(
"Cannot nest objects of the same type".into(),
))));
}
let (remaining, _) = pre(context, input)?;
let (remaining, open) = tag(marker_symbol)(remaining)?;
let (remaining, _peek_not_whitespace) =
peek(verify(anychar, |c| !c.is_whitespace() && *c != '\u{200B}'))(remaining)?;
let text_markup_end_specialized = text_markup_end(open.into());
let parser_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Gamma,
exit_matcher: &text_markup_end_specialized,
});
let parser_context = context.with_additional_node(&parser_context);
let contexts = [
ContextElement::ContextObject(marker_symbol),
ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Gamma,
exit_matcher: &text_markup_end_specialized,
}),
];
let parser_context = context.with_additional_node(&contexts[0]);
let parser_context = parser_context.with_additional_node(&contexts[1]);
let (remaining, (children, _exit_contents)) = verify(
many_till(
@ -230,16 +241,25 @@ fn _text_markup_string<'b, 'g, 'r, 's, 'c>(
input: OrgSource<'s>,
marker_symbol: &'c str,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
if in_object_section(context, marker_symbol) {
return Err(nom::Err::Error(CustomError::MyError(MyError(
"Cannot nest objects of the same type".into(),
))));
}
let (remaining, _) = pre(context, input)?;
let (remaining, open) = tag(marker_symbol)(remaining)?;
let (remaining, _peek_not_whitespace) =
peek(verify(anychar, |c| !c.is_whitespace() && *c != '\u{200B}'))(remaining)?;
let text_markup_end_specialized = text_markup_end(open.into());
let parser_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Gamma,
exit_matcher: &text_markup_end_specialized,
});
let parser_context = context.with_additional_node(&parser_context);
let contexts = [
ContextElement::ContextObject(marker_symbol),
ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Gamma,
exit_matcher: &text_markup_end_specialized,
}),
];
let parser_context = context.with_additional_node(&contexts[0]);
let parser_context = parser_context.with_additional_node(&contexts[1]);
let (remaining, contents) = recognize(verify(
many_till(

View File

@ -24,7 +24,6 @@ pub const WORD_CONSTITUENT_CHARACTERS: &str =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
/// Check if we are below a section of the given section type regardless of depth
#[allow(dead_code)]
pub fn in_section<'b, 'g, 'r, 's, 'x>(
context: RefContext<'b, 'g, 'r, 's>,
section_name: &'x str,
@ -53,6 +52,20 @@ pub fn immediate_in_section<'b, 'g, 'r, 's, 'x>(
false
}
/// Check if we are below a section of the given section type regardless of depth
pub fn in_object_section<'b, 'g, 'r, 's, 'x>(
context: RefContext<'b, 'g, 'r, 's>,
section_name: &'x str,
) -> bool {
for thing in context.iter() {
match thing {
ContextElement::ContextObject(name) if *name == section_name => return true,
_ => {}
}
}
false
}
/// Get a slice of the string that was consumed in a parser using the original input to the parser and the remaining input after the parser.
pub fn get_consumed<'s>(input: OrgSource<'s>, remaining: OrgSource<'s>) -> OrgSource<'s> {
input.get_until(remaining)