From bdba495f690869a8dbcaf953d48c70201b41d033 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Mon, 4 Sep 2023 22:39:24 -0400 Subject: [PATCH] Add a parser for the todo keyword's value. --- src/parser/in_buffer_settings.rs | 16 +++++- src/parser/keyword_todo.rs | 90 ++++++++++++++++++++++++++++++++ src/parser/mod.rs | 1 + 3 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 src/parser/keyword_todo.rs diff --git a/src/parser/in_buffer_settings.rs b/src/parser/in_buffer_settings.rs index f21dc1a..3c533ac 100644 --- a/src/parser/in_buffer_settings.rs +++ b/src/parser/in_buffer_settings.rs @@ -25,5 +25,19 @@ pub fn scan_for_in_buffer_settings<'s>( #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn in_buffer_settings_key<'s>(input: OrgSource<'s>) -> Res, OrgSource<'s>> { - alt((tag_no_case("todo"), tag_no_case("setupfile")))(input) + alt(( + tag_no_case("archive"), + tag_no_case("category"), + tag_no_case("columns"), + tag_no_case("filetags"), + tag_no_case("link"), + tag_no_case("priorities"), + tag_no_case("property"), + tag_no_case("seq_todo"), + tag_no_case("setupfile"), + tag_no_case("startup"), + tag_no_case("tags"), + tag_no_case("todo"), + tag_no_case("typ_todo"), + ))(input) } diff --git a/src/parser/keyword_todo.rs b/src/parser/keyword_todo.rs new file mode 100644 index 0000000..44facbb --- /dev/null +++ b/src/parser/keyword_todo.rs @@ -0,0 +1,90 @@ +use nom::branch::alt; +use nom::bytes::complete::tag; +use nom::bytes::complete::take_till; +use nom::character::complete::line_ending; +use nom::character::complete::space0; +use nom::character::complete::space1; +use nom::combinator::eof; +use nom::combinator::opt; +use nom::combinator::verify; +use nom::multi::separated_list0; +use nom::sequence::tuple; + +use crate::error::Res; + +/// Parses the text in the value of a #+TODO keyword. +/// +/// Example input: "foo bar baz | lorem ipsum" +pub fn keyword_todo<'s>(input: &'s str) -> Res<&'s str, (Vec<&'s str>, Vec<&'s str>)> { + let (remaining, mut before_pipe_words) = separated_list0(space1, keyword_word)(input)?; + let (remaining, after_pipe_words) = opt(tuple(( + tuple((space0, tag("|"), space0)), + separated_list0(space1, keyword_word), + )))(remaining)?; + let (remaining, _eol) = alt((line_ending, eof))(remaining)?; + if let Some((_pipe, after_pipe_words)) = after_pipe_words { + Ok((remaining, (before_pipe_words, after_pipe_words))) + } else if !before_pipe_words.is_empty() { + // If there was no pipe, then the last word becomes a completion state instead. + let mut after_pipe_words = Vec::with_capacity(1); + after_pipe_words.push( + before_pipe_words + .pop() + .expect("If-statement proves this is Some."), + ); + Ok((remaining, (before_pipe_words, after_pipe_words))) + } else { + // No words founds + Ok((remaining, (Vec::new(), Vec::new()))) + } +} + +fn keyword_word<'s>(input: &'s str) -> Res<&'s str, &'s str> { + verify(take_till(|c| " \t\r\n|".contains(c)), |result: &str| { + !result.is_empty() + })(input) +} +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn before_and_after() -> Result<(), Box> { + let input = "foo bar baz | lorem ipsum"; + let (remaining, (before_pipe_words, after_pipe_words)) = keyword_todo(input)?; + assert_eq!(remaining, ""); + assert_eq!(before_pipe_words, vec!["foo", "bar", "baz"]); + assert_eq!(after_pipe_words, vec!["lorem", "ipsum"]); + Ok(()) + } + + #[test] + fn no_pipe() -> Result<(), Box> { + let input = "foo bar baz"; + let (remaining, (before_pipe_words, after_pipe_words)) = keyword_todo(input)?; + assert_eq!(remaining, ""); + assert_eq!(before_pipe_words, vec!["foo", "bar"]); + assert_eq!(after_pipe_words, vec!["baz"]); + Ok(()) + } + + #[test] + fn early_pipe() -> Result<(), Box> { + let input = "| foo bar baz"; + let (remaining, (before_pipe_words, after_pipe_words)) = keyword_todo(input)?; + assert_eq!(remaining, ""); + assert_eq!(before_pipe_words, Vec::<&str>::new()); + assert_eq!(after_pipe_words, vec!["foo", "bar", "baz"]); + Ok(()) + } + + #[test] + fn late_pipe() -> Result<(), Box> { + let input = "foo bar baz |"; + let (remaining, (before_pipe_words, after_pipe_words)) = keyword_todo(input)?; + assert_eq!(remaining, ""); + assert_eq!(before_pipe_words, vec!["foo", "bar", "baz"]); + assert_eq!(after_pipe_words, Vec::<&str>::new()); + Ok(()) + } +} diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 72a350c..0b95974 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -19,6 +19,7 @@ mod in_buffer_settings; mod inline_babel_call; mod inline_source_block; mod keyword; +mod keyword_todo; mod latex_environment; mod latex_fragment; mod lesser_block;