From 68a220aa1c09ff18a7cae07a2feade3c94eea0e0 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Fri, 15 Dec 2023 10:16:33 -0500 Subject: [PATCH] Implement the new fields for babel call. --- .../lesser_element/babel_call/simple.org | 2 + src/parser/babel_call.rs | 99 +++++++++---------- src/types/lesser_element.rs | 9 +- 3 files changed, 58 insertions(+), 52 deletions(-) diff --git a/org_mode_samples/lesser_element/babel_call/simple.org b/org_mode_samples/lesser_element/babel_call/simple.org index bafcb01..27d102c 100644 --- a/org_mode_samples/lesser_element/babel_call/simple.org +++ b/org_mode_samples/lesser_element/babel_call/simple.org @@ -5,3 +5,5 @@ #+call: dolar cat(dog) #+call: (bat) + +#+call: diff --git a/src/parser/babel_call.rs b/src/parser/babel_call.rs index fdfdac5..22b3925 100644 --- a/src/parser/babel_call.rs +++ b/src/parser/babel_call.rs @@ -4,7 +4,6 @@ use nom::bytes::complete::tag_no_case; use nom::character::complete::anychar; use nom::character::complete::one_of; use nom::character::complete::space0; -use nom::combinator::consumed; use nom::combinator::opt; use nom::combinator::peek; use nom::combinator::recognize; @@ -43,32 +42,10 @@ where start_of_line(remaining)?; let (remaining, _) = tuple((space0, tag("#+"), tag_no_case("call"), tag(":")))(remaining)?; - if let Ok((remaining, (_, line_break))) = tuple((space0, org_line_ending))(remaining) { - let (remaining, _trailing_ws) = - maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; - let source = get_consumed(input, remaining); - return Ok(( - remaining, - BabelCall { - source: Into::<&str>::into(source), - affiliated_keywords: parse_affiliated_keywords( - context.get_global_settings(), - affiliated_keywords, - ), - value: Into::<&str>::into(line_break.take(0)), - call: None, - inside_header: None, - arguments: None, - end_header: None, - }, - )); - } - let (remaining, _ws) = space0(remaining)?; - let (remaining, (value, babel_call_value)) = consumed(babel_call_value)(remaining)?; - let (remaining, _ws) = tuple((space0, org_line_ending))(remaining)?; + let (remaining, babel_call_value) = babel_call_value(remaining)?; - let (remaining, _trailing_ws) = + let (remaining, post_blank) = maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; let source = get_consumed(input, remaining); @@ -80,17 +57,22 @@ where context.get_global_settings(), affiliated_keywords, ), - value: Into::<&str>::into(value).trim_end(), + value: Into::<&str>::into(babel_call_value.value), call: babel_call_value.call.map(Into::<&str>::into), inside_header: babel_call_value.inside_header.map(Into::<&str>::into), arguments: babel_call_value.arguments.map(Into::<&str>::into), end_header: babel_call_value.end_header.map(Into::<&str>::into), + post_blank: post_blank.map(Into::<&str>::into), }, )) } #[derive(Debug)] struct BabelCallValue<'s> { + /// The entire string to the right of "#+call: " without the trailing line break. + value: OrgSource<'s>, + + /// The function name which may contain a line break if there are no headers/arguments. call: Option>, inside_header: Option>, arguments: Option>, @@ -99,13 +81,45 @@ struct BabelCallValue<'s> { #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn babel_call_value<'s>(input: OrgSource<'s>) -> Res, BabelCallValue<'s>> { - let (remaining, call) = opt(babel_call_call)(input)?; - let (remaining, inside_header) = opt(inside_header)(remaining)?; - let (remaining, arguments) = opt(arguments)(remaining)?; - let (remaining, end_header) = opt(end_header)(remaining)?; + alt(( + babel_call_value_without_headers, + babel_call_value_with_headers, + ))(input) +} + +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +fn babel_call_value_without_headers<'s>( + input: OrgSource<'s>, +) -> Res, BabelCallValue<'s>> { + let (remaining, value) = babel_call_call_with_headers(input)?; + let (remaining, _ws) = tuple((space0, org_line_ending))(remaining)?; + let call = get_consumed(input, remaining); Ok(( remaining, BabelCallValue { + value, + call: Some(call), + inside_header: None, + arguments: None, + end_header: None, + }, + )) +} + +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +fn babel_call_value_with_headers<'s>( + input: OrgSource<'s>, +) -> Res, BabelCallValue<'s>> { + let (remaining, call) = opt(babel_call_call_with_headers)(input)?; + let (remaining, inside_header) = opt(inside_header)(remaining)?; + let (remaining, arguments) = opt(arguments)(remaining)?; + let (remaining, end_header) = opt(end_header)(remaining)?; + let value = get_consumed(input, remaining); + let (remaining, _ws) = tuple((space0, org_line_ending))(remaining)?; + Ok(( + remaining, + BabelCallValue { + value, call, inside_header, arguments: arguments.flatten(), @@ -115,14 +129,15 @@ fn babel_call_value<'s>(input: OrgSource<'s>) -> Res, BabelCallVal } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn babel_call_call<'s>(input: OrgSource<'s>) -> Res, OrgSource<'s>> { +fn babel_call_call_with_headers<'s>(input: OrgSource<'s>) -> Res, OrgSource<'s>> { + // When babel call contains no arguments or headers (for example: "#+call: lorem ipsum\n") then the trailing line break is part of the call. Otherwise, it is not. verify( recognize(many_till( anychar, - alt(( - peek(recognize(one_of("[("))), + peek(alt(( + recognize(one_of("[(")), recognize(tuple((space0, org_line_ending))), - )), + ))), )), |s| s.len() > 0, )(input) @@ -232,19 +247,3 @@ fn impl_balanced_bracket< }; Ok((remaining, contents)) } - -#[cfg(test)] -mod tests { - use nom::combinator::opt; - - use super::*; - - #[test] - fn simple_call() -> Result<(), Box> { - let input = OrgSource::new("()"); - let (remaining, call) = opt(babel_call_call)(input)?; - assert_eq!(Into::<&str>::into(remaining), "()"); - assert!(call.is_none()); - Ok(()) - } -} diff --git a/src/types/lesser_element.rs b/src/types/lesser_element.rs index 596d056..004f938 100644 --- a/src/types/lesser_element.rs +++ b/src/types/lesser_element.rs @@ -172,6 +172,7 @@ pub struct BabelCall<'s> { pub inside_header: Option<&'s str>, pub arguments: Option<&'s str>, pub end_header: Option<&'s str>, + pub post_blank: Option<&'s str>, } #[derive(Debug)] @@ -438,11 +439,15 @@ impl<'s> StandardProperties<'s> for BabelCall<'s> { } fn get_contents<'b>(&'b self) -> Option<&'s str> { - todo!() + None } fn get_post_blank(&self) -> PostBlank { - todo!() + self.post_blank + .map(|text| text.lines().count()) + .unwrap_or(0) + .try_into() + .expect("Too much post-blank to fit into a PostBlank.") } }