use super::error::Res; use super::util::WORD_CONSTITUENT_CHARACTERS; use super::Context; use crate::parser::element::element; use crate::parser::greater_element::FootnoteDefinition; use crate::parser::parser_context::ChainBehavior; use crate::parser::parser_context::ContextElement; use crate::parser::parser_context::ExitMatcherNode; use crate::parser::parser_with_context::parser_with_context; use crate::parser::util::blank_line; use crate::parser::util::exit_matcher_parser; use crate::parser::util::get_consumed; use crate::parser::util::start_of_line; use crate::parser::util::whitespace_eof; use nom::branch::alt; use nom::bytes::complete::tag; use nom::bytes::complete::tag_no_case; use nom::bytes::complete::take_while; use nom::character::complete::digit1; use nom::character::complete::multispace0; use nom::character::complete::space0; use nom::combinator::recognize; use nom::combinator::verify; use nom::multi::many1; use nom::multi::many_till; use nom::sequence::tuple; #[tracing::instrument(ret, level = "debug")] pub fn footnote_definition<'r, 's>( context: Context<'r, 's>, input: &'s str, ) -> Res<&'s str, FootnoteDefinition<'s>> { start_of_line(context, input)?; // Cannot be indented. let (remaining, (_lead_in, lbl, _lead_out, _ws)) = tuple((tag_no_case("[fn:"), label, tag("]"), space0))(input)?; let parser_context = context .with_additional_node(ContextElement::ConsumeTrailingWhitespace(true)) .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { exit_matcher: ChainBehavior::IgnoreParent(Some(&footnote_definition_end)), })); // TODO: The problem is we are not accounting for trailing whitespace like we do in section. Maybe it would be easier if we passed down whether or not to parse trailing whitespace into the element matcher similar to how tag takes in parameters. let element_matcher = parser_with_context!(element)(&parser_context); let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); let (remaining, (children, _exit_contents)) = many_till(element_matcher, exit_matcher)(remaining)?; let source = get_consumed(input, remaining); Ok(( remaining, FootnoteDefinition { source, label: lbl, children, }, )) } #[tracing::instrument(ret, level = "debug")] fn label<'s>(input: &'s str) -> Res<&'s str, &'s str> { alt(( digit1, take_while(|c| WORD_CONSTITUENT_CHARACTERS.contains(c) || "-_".contains(c)), ))(input) } #[tracing::instrument(ret, level = "debug")] fn footnote_definition_end<'r, 's>( context: Context<'r, 's>, input: &'s str, ) -> Res<&'s str, &'s str> { let start_of_line_matcher = parser_with_context!(start_of_line)(context); let footnote_definition_matcher = parser_with_context!(footnote_definition)(context); alt(( recognize(tuple((multispace0, footnote_definition_matcher))), recognize(tuple(( start_of_line_matcher, verify(many1(blank_line), |lines: &Vec<&str>| lines.len() >= 2), ))), ))(input) }