Implement check_exit_matcher.
This commit is contained in:
@@ -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.");
|
||||
|
||||
Reference in New Issue
Block a user