End switches at the first unrecognized word in src blocks.

This commit is contained in:
Tom Alexander 2023-10-05 01:55:33 -04:00
parent 5f84cd974d
commit 386ad5091d
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE
2 changed files with 40 additions and 23 deletions

View File

@ -0,0 +1,3 @@
#+begin_example foo -n bar -k baz
#+end_example

View File

@ -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>, OrgSource<'s>> {
if stop_at_parameters {
not(tag(":"))(input)?;
}
fn switch_word<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
alt((
map(
tuple((tag(r#"""#), is_not("\"\r\n"), tag(r#"""#))),