From 386ad5091d3463dd1673c2dac00edb4199342a2d Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Thu, 5 Oct 2023 01:55:33 -0400 Subject: [PATCH] End switches at the first unrecognized word in src blocks. --- .../example/parameters_without_colon.org | 3 + src/parser/lesser_block.rs | 60 ++++++++++++------- 2 files changed, 40 insertions(+), 23 deletions(-) create mode 100644 org_mode_samples/lesser_element/lesser_block/example/parameters_without_colon.org diff --git a/org_mode_samples/lesser_element/lesser_block/example/parameters_without_colon.org b/org_mode_samples/lesser_element/lesser_block/example/parameters_without_colon.org new file mode 100644 index 00000000..8780c857 --- /dev/null +++ b/org_mode_samples/lesser_element/lesser_block/example/parameters_without_colon.org @@ -0,0 +1,3 @@ +#+begin_example foo -n bar -k baz + +#+end_example diff --git a/src/parser/lesser_block.rs b/src/parser/lesser_block.rs index 5289c6b8..f4cf7923 100644 --- a/src/parser/lesser_block.rs +++ b/src/parser/lesser_block.rs @@ -9,14 +9,12 @@ use nom::character::complete::space1; use nom::combinator::consumed; use nom::combinator::eof; use nom::combinator::map; -use nom::combinator::not; use nom::combinator::opt; use nom::combinator::peek; use nom::combinator::recognize; use nom::combinator::verify; use nom::multi::many0; use nom::multi::many_till; -use nom::multi::separated_list1; use nom::sequence::tuple; use super::keyword::affiliated_keyword; @@ -27,8 +25,9 @@ use crate::context::ContextElement; use crate::context::ContextMatcher; use crate::context::ExitClass; use crate::context::ExitMatcherNode; -use crate::context::Matcher; use crate::context::RefContext; +use crate::error::CustomError; +use crate::error::MyError; use crate::error::Res; use crate::parser::object_parser::standard_set_object; use crate::parser::util::blank_line; @@ -251,9 +250,8 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>( let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?; let (remaining, _) = lesser_block_begin("src")(context, input)?; // https://orgmode.org/worg/org-syntax.html#Blocks claims that data is mandatory and must follow the LANGUAGE SWITCHES ARGUMENTS pattern but testing has shown that no data and incorrect data here will still parse to a src block. - let (remaining, language) = opt(map(tuple((space1, switch_word(true))), |(_, language)| { - language - }))(remaining)?; + let (remaining, language) = + opt(map(tuple((space1, switch_word)), |(_, language)| language))(remaining)?; let (remaining, switches) = opt(src_switches)(remaining)?; let (remaining, parameters) = opt(map(tuple((space1, src_parameters)), |(_, parameters)| { parameters @@ -430,14 +428,21 @@ fn _example_src_switches<'s>( let mut use_labels = true; let mut label_format = None; let mut saw_r = false; - - let (remaining, words) = separated_list1(space1, switch_word(stop_at_parameters))(input)?; - let (remaining, _post_spaces) = opt(tuple((space0, peek(line_ending))))(remaining)?; - let source = input.get_until(remaining); - + let mut matched_a_word = false; + let mut remaining = input; + let mut last_match_remaining = input; let mut state = SwitchState::Normal; - for word in words { + 'outer: loop { + let (remain, word) = opt(switch_word)(remaining)?; + let word = match word { + Some(word) => word, + None => { + break; + } + }; + let normalized_word = Into::<&str>::into(word); + loop { match (&state, normalized_word) { (SwitchState::Normal, "-n") => { @@ -510,11 +515,30 @@ fn _example_src_switches<'s>( label_format = Some(normalized_word); state = SwitchState::Normal; } + (SwitchState::Normal, _) if stop_at_parameters => { + break 'outer; + } (SwitchState::Normal, _) => {} }; + matched_a_word = true; + remaining = remain; + last_match_remaining = remain; + + let (remain, divider) = opt(space1)(remaining)?; + if divider.is_none() { + break 'outer; + } + remaining = remain; break; } } + if !matched_a_word { + return Err(nom::Err::Error(CustomError::MyError(MyError("No words.")))); + } + let remaining = last_match_remaining; + + let (remaining, _post_spaces) = opt(tuple((space0, peek(line_ending))))(remaining)?; + let source = input.get_until(remaining); // Handle state that didn't get processed because we ran out of words. match state { @@ -546,18 +570,8 @@ fn _example_src_switches<'s>( )) } -fn switch_word<'s>(stop_at_parameters: bool) -> impl Matcher { - move |input| _switch_word(input, stop_at_parameters) -} - #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn _switch_word<'s>( - input: OrgSource<'s>, - stop_at_parameters: bool, -) -> Res, OrgSource<'s>> { - if stop_at_parameters { - not(tag(":"))(input)?; - } +fn switch_word<'s>(input: OrgSource<'s>) -> Res, OrgSource<'s>> { alt(( map( tuple((tag(r#"""#), is_not("\"\r\n"), tag(r#"""#))),