Implement check_exit_matcher.

This commit is contained in:
Tom Alexander
2023-09-03 00:05:47 -04:00
parent 8502a8830d
commit 15e8d1ab77
5 changed files with 191 additions and 73 deletions

View File

@@ -16,21 +16,26 @@ use nom::multi::many1;
use nom::multi::many_till;
use nom::sequence::tuple;
use super::greater_element::PlainList;
use super::greater_element::PlainListItem;
use super::element_parser::element;
use super::object_parser::standard_set_object;
use super::org_source::OrgSource;
use super::util::non_whitespace_character;
use super::Context;
use super::Object;
use crate::context::parser_with_context;
use crate::context::ContextElement;
use crate::context::ExitClass;
use crate::context::ExitMatcherNode;
use crate::context::RefContext;
use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res;
use crate::parser::element_parser::element;
use crate::parser::util::blank_line;
use crate::parser::util::exit_matcher_parser;
use crate::parser::util::get_consumed;
use crate::parser::util::maybe_consume_trailing_whitespace_if_not_exiting;
use crate::parser::util::start_of_line;
use crate::types::Object;
use crate::types::PlainList;
use crate::types::PlainListItem;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn detect_plain_list<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
@@ -59,13 +64,19 @@ pub fn plain_list<'r, 's>(
context: RefContext<'_, 'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, PlainList<'s>> {
let parser_context = context
.with_additional_node(ContextElement::Context("plain list"))
.with_additional_node(ContextElement::ConsumeTrailingWhitespace(true))
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
let contexts = [
ContextElement::Context("plain list"),
ContextElement::ConsumeTrailingWhitespace(true),
ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Beta,
exit_matcher: &plain_list_end,
}));
}),
];
let parser_context = context
.with_additional_node(&contexts[0])
.with_additional_node(&contexts[1])
.with_additional_node(&contexts[2]);
// children stores tuple of (input string, parsed object) so we can re-parse the final item
let mut children = Vec::new();
let mut first_item_indentation: Option<usize> = None;
@@ -107,8 +118,8 @@ pub fn plain_list<'r, 's>(
))));
}
};
let final_item_context =
parser_context.with_additional_node(ContextElement::ConsumeTrailingWhitespace(false));
let final_item_context = ContextElement::ConsumeTrailingWhitespace(false);
let final_item_context = parser_context.with_additional_node(&final_item_context);
let (remaining, reparsed_final_item) =
parser_with_context!(plain_list_item)(&final_item_context)(final_child_start)?;
children.push((final_child_start, reparsed_final_item));
@@ -164,12 +175,16 @@ pub fn plain_list_item<'r, 's>(
};
let (remaining, _ws) = item_tag_post_gap(context, remaining)?;
let exit_matcher = plain_list_item_end(indent_level);
let parser_context = context
.with_additional_node(ContextElement::ConsumeTrailingWhitespace(true))
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
let contexts = [
ContextElement::ConsumeTrailingWhitespace(true),
ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Beta,
exit_matcher: &exit_matcher,
}));
}),
];
let parser_context = context
.with_additional_node(&contexts[0])
.with_additional_node(&contexts[1]);
let (mut remaining, (mut children, _exit_contents)) = many_till(
include_input(parser_with_context!(element(true))(&parser_context)),
@@ -177,8 +192,8 @@ pub fn plain_list_item<'r, 's>(
)(remaining)?;
if !children.is_empty() && !context.should_consume_trailing_whitespace() {
let final_item_context =
parser_context.with_additional_node(ContextElement::ConsumeTrailingWhitespace(false));
let final_item_context = ContextElement::ConsumeTrailingWhitespace(false);
let final_item_context = parser_context.with_additional_node(&final_item_context);
let (final_child_start, _original_final_child) = children
.pop()
.expect("if-statement already checked that children was non-empty.");
@@ -249,9 +264,10 @@ fn plain_list_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>, OrgSource<'s>> {
) -> impl for<'b, 'r, 's> Fn(RefContext<'b, 'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>>
{
let line_indented_lte_matcher = line_indented_lte(indent_level);
move |context: Context, input: OrgSource<'_>| {
move |context, input: OrgSource<'_>| {
_plain_list_item_end(context, input, &line_indented_lte_matcher)
}
}
@@ -263,8 +279,8 @@ const fn plain_list_item_end(
fn _plain_list_item_end<'r, 's>(
context: RefContext<'_, 'r, 's>,
input: OrgSource<'s>,
line_indented_lte_matcher: impl for<'rr, 'ss> Fn(
Context<'rr, 'ss>,
line_indented_lte_matcher: impl for<'bb, 'rr, 'ss> Fn(
RefContext<'bb, 'rr, 'ss>,
OrgSource<'ss>,
) -> Res<OrgSource<'ss>, OrgSource<'ss>>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
@@ -277,8 +293,9 @@ fn _plain_list_item_end<'r, 's>(
const fn line_indented_lte(
indent_level: usize,
) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
move |context: Context, input: OrgSource<'_>| _line_indented_lte(context, input, indent_level)
) -> impl for<'b, 'r, 's> Fn(RefContext<'b, 'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>>
{
move |context, input: OrgSource<'_>| _line_indented_lte(context, input, indent_level)
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
@@ -301,11 +318,11 @@ fn item_tag<'r, 's>(
context: RefContext<'_, 'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
let parser_context =
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Gamma,
exit_matcher: &item_tag_end,
}));
let parser_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Gamma,
exit_matcher: &item_tag_end,
});
let parser_context = context.with_additional_node(&parser_context);
let (remaining, (children, _exit_contents)) = verify(
many_till(
// TODO: Should this be using a different set like the minimal set?
@@ -353,14 +370,16 @@ fn item_tag_post_gap<'r, 's>(
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::parser_context::ContextTree;
use crate::parser::parser_with_context::parser_with_context;
use crate::parser::Source;
use crate::context::Context;
use crate::context::GlobalSettings;
use crate::context::List;
#[test]
fn plain_list_item_empty() {
let input = OrgSource::new("1.");
let initial_context: ContextTree<'_, '_> = ContextTree::new();
let global_settings = GlobalSettings::default();
let initial_context = ContextElement::document_context();
let initial_context = Context::new(&global_settings, List::new(&initial_context));
let plain_list_item_matcher = parser_with_context!(plain_list_item)(&initial_context);
let (remaining, result) = plain_list_item_matcher(input).unwrap();
assert_eq!(Into::<&str>::into(remaining), "");
@@ -370,7 +389,9 @@ mod tests {
#[test]
fn plain_list_item_simple() {
let input = OrgSource::new("1. foo");
let initial_context: ContextTree<'_, '_> = ContextTree::new();
let global_settings = GlobalSettings::default();
let initial_context = ContextElement::document_context();
let initial_context = Context::new(&global_settings, List::new(&initial_context));
let plain_list_item_matcher = parser_with_context!(plain_list_item)(&initial_context);
let (remaining, result) = plain_list_item_matcher(input).unwrap();
assert_eq!(Into::<&str>::into(remaining), "");
@@ -380,7 +401,9 @@ mod tests {
#[test]
fn plain_list_empty() {
let input = OrgSource::new("1.");
let initial_context: ContextTree<'_, '_> = ContextTree::new();
let global_settings = GlobalSettings::default();
let initial_context = ContextElement::document_context();
let initial_context = Context::new(&global_settings, List::new(&initial_context));
let plain_list_matcher = parser_with_context!(plain_list)(&initial_context);
let (remaining, result) = plain_list_matcher(input).unwrap();
assert_eq!(Into::<&str>::into(remaining), "");
@@ -390,7 +413,9 @@ mod tests {
#[test]
fn plain_list_simple() {
let input = OrgSource::new("1. foo");
let initial_context: ContextTree<'_, '_> = ContextTree::new();
let global_settings = GlobalSettings::default();
let initial_context = ContextElement::document_context();
let initial_context = Context::new(&global_settings, List::new(&initial_context));
let plain_list_matcher = parser_with_context!(plain_list)(&initial_context);
let (remaining, result) = plain_list_matcher(input).unwrap();
assert_eq!(Into::<&str>::into(remaining), "");
@@ -401,7 +426,9 @@ mod tests {
fn plain_list_cant_start_line_with_asterisk() {
// Plain lists with an asterisk bullet must be indented or else they would be a headline
let input = OrgSource::new("* foo");
let initial_context: ContextTree<'_, '_> = ContextTree::new();
let global_settings = GlobalSettings::default();
let initial_context = ContextElement::document_context();
let initial_context = Context::new(&global_settings, List::new(&initial_context));
let plain_list_matcher = parser_with_context!(plain_list)(&initial_context);
let result = plain_list_matcher(input);
assert!(result.is_err());
@@ -411,7 +438,9 @@ mod tests {
fn indented_can_start_line_with_asterisk() {
// Plain lists with an asterisk bullet must be indented or else they would be a headline
let input = OrgSource::new(" * foo");
let initial_context: ContextTree<'_, '_> = ContextTree::new();
let global_settings = GlobalSettings::default();
let initial_context = ContextElement::document_context();
let initial_context = Context::new(&global_settings, List::new(&initial_context));
let plain_list_matcher = parser_with_context!(plain_list)(&initial_context);
let result = plain_list_matcher(input);
assert!(result.is_ok());
@@ -429,7 +458,9 @@ mod tests {
ipsum
"#,
);
let initial_context: ContextTree<'_, '_> = ContextTree::new();
let global_settings = GlobalSettings::default();
let initial_context = ContextElement::document_context();
let initial_context = Context::new(&global_settings, List::new(&initial_context));
let plain_list_matcher = parser_with_context!(element(true))(&initial_context);
let (remaining, result) =
plain_list_matcher(input).expect("Should parse the plain list successfully.");
@@ -455,7 +486,9 @@ mod tests {
baz"#,
);
let initial_context: ContextTree<'_, '_> = ContextTree::new();
let global_settings = GlobalSettings::default();
let initial_context = ContextElement::document_context();
let initial_context = Context::new(&global_settings, List::new(&initial_context));
let plain_list_matcher = parser_with_context!(element(true))(&initial_context);
let (remaining, result) =
plain_list_matcher(input).expect("Should parse the plain list successfully.");
@@ -486,7 +519,9 @@ baz"#,
dolar"#,
);
let initial_context: ContextTree<'_, '_> = ContextTree::new();
let global_settings = GlobalSettings::default();
let initial_context = ContextElement::document_context();
let initial_context = Context::new(&global_settings, List::new(&initial_context));
let plain_list_matcher = parser_with_context!(element(true))(&initial_context);
let (remaining, result) =
plain_list_matcher(input).expect("Should parse the plain list successfully.");