diff --git a/src/main.rs b/src/main.rs index e5a19dc..fc6d4dd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -111,11 +111,13 @@ fn run_parse_on_file>(org_path: P) -> Result<(), Box( final_settings.extend(setup_file_settings); } final_settings.extend(document_settings); - let new_settings = apply_in_buffer_settings(final_settings, context.get_global_settings()).map_err(|_err| { - nom::Err::Error(CustomError::MyError(MyError( - "TODO: make this take an owned string so I can dump err.to_string() into it." - .into(), - ))) - })?; + let new_settings = apply_in_buffer_settings(final_settings, context.get_global_settings()) + .map_err(|_err| { + nom::Err::Error(CustomError::MyError(MyError( + "TODO: make this take an owned string so I can dump err.to_string() into it." + .into(), + ))) + })?; let new_context = context.with_global_settings(&new_settings); let context = &new_context; @@ -382,7 +383,6 @@ fn headline<'b, 'g, 'r, 's>( exit_matcher: &headline_title_end, }); let parser_context = context.with_additional_node(&parser_context); - let standard_set_object_matcher = parser_with_context!(standard_set_object)(&parser_context); let ( remaining, @@ -393,8 +393,11 @@ fn headline<'b, 'g, 'r, 's>( *star_count > parent_stars }), space1, - opt(tuple((heading_keyword, space1))), - many1(standard_set_object_matcher), + opt(tuple(( + parser_with_context!(heading_keyword)(&parser_context), + space1, + ))), + many1(parser_with_context!(standard_set_object)(&parser_context)), opt(tuple((space0, tags))), space0, alt((line_ending, eof)), @@ -443,9 +446,34 @@ fn single_tag<'r, 's>(input: OrgSource<'s>) -> Res, OrgSource<'s>> } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn heading_keyword<'s>(input: OrgSource<'s>) -> Res, OrgSource<'s>> { - // TODO: This should take into account the value of "#+TODO:" ref https://orgmode.org/manual/Per_002dfile-keywords.html and possibly the configurable variable org-todo-keywords ref https://orgmode.org/manual/Workflow-states.html. Case is significant. - alt((tag("TODO"), tag("DONE")))(input) +fn heading_keyword<'b, 'g, 'r, 's>( + context: RefContext<'b, 'g, 'r, 's>, + input: OrgSource<'s>, +) -> Res, OrgSource<'s>> { + let global_settings = context.get_global_settings(); + if global_settings.in_progress_todo_keywords.is_empty() + && global_settings.complete_todo_keywords.is_empty() + { + alt((tag("TODO"), tag("DONE")))(input) + } else { + for todo_keyword in global_settings + .in_progress_todo_keywords + .iter() + .chain(global_settings.complete_todo_keywords.iter()) + .map(String::as_str) + { + let result = tag::<_, _, CustomError<_>>(todo_keyword)(input); + match result { + Ok((remaining, ent)) => { + return Ok((remaining, ent)); + } + Err(_) => {} + } + } + Err(nom::Err::Error(CustomError::MyError(MyError( + "NoTodoKeyword".into(), + )))) + } } impl<'s> Document<'s> { diff --git a/src/parser/in_buffer_settings.rs b/src/parser/in_buffer_settings.rs index f2ff0e7..59f1143 100644 --- a/src/parser/in_buffer_settings.rs +++ b/src/parser/in_buffer_settings.rs @@ -6,7 +6,7 @@ use nom::multi::many0; use nom::multi::many_till; use super::keyword::filtered_keyword; -use super::keyword_todo::keyword_todo; +use super::keyword_todo::todo_keywords; use super::OrgSource; use crate::error::Res; use crate::types::Keyword; @@ -56,7 +56,7 @@ pub fn apply_in_buffer_settings<'g, 's, 'sf>( || kw.key.eq_ignore_ascii_case("typ_todo") }) { let (_, (in_progress_words, complete_words)) = - keyword_todo(kw.value).map_err(|err| err.to_string())?; + todo_keywords(kw.value).map_err(|err| err.to_string())?; new_settings .in_progress_todo_keywords .extend(in_progress_words.into_iter().map(str::to_string)); diff --git a/src/parser/keyword_todo.rs b/src/parser/keyword_todo.rs index 44facbb..821aa8e 100644 --- a/src/parser/keyword_todo.rs +++ b/src/parser/keyword_todo.rs @@ -12,14 +12,18 @@ use nom::sequence::tuple; use crate::error::Res; +// ref https://orgmode.org/manual/Per_002dfile-keywords.html +// ref https://orgmode.org/manual/Workflow-states.html +// Case is significant. + /// 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)?; +pub fn todo_keywords<'s>(input: &'s str) -> Res<&'s str, (Vec<&'s str>, Vec<&'s str>)> { + let (remaining, mut before_pipe_words) = separated_list0(space1, todo_keyword_word)(input)?; let (remaining, after_pipe_words) = opt(tuple(( tuple((space0, tag("|"), space0)), - separated_list0(space1, keyword_word), + separated_list0(space1, todo_keyword_word), )))(remaining)?; let (remaining, _eol) = alt((line_ending, eof))(remaining)?; if let Some((_pipe, after_pipe_words)) = after_pipe_words { @@ -39,7 +43,7 @@ pub fn keyword_todo<'s>(input: &'s str) -> Res<&'s str, (Vec<&'s str>, Vec<&'s s } } -fn keyword_word<'s>(input: &'s str) -> Res<&'s str, &'s str> { +fn todo_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) @@ -51,7 +55,7 @@ mod tests { #[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)?; + let (remaining, (before_pipe_words, after_pipe_words)) = todo_keywords(input)?; assert_eq!(remaining, ""); assert_eq!(before_pipe_words, vec!["foo", "bar", "baz"]); assert_eq!(after_pipe_words, vec!["lorem", "ipsum"]); @@ -61,7 +65,7 @@ mod tests { #[test] fn no_pipe() -> Result<(), Box> { let input = "foo bar baz"; - let (remaining, (before_pipe_words, after_pipe_words)) = keyword_todo(input)?; + let (remaining, (before_pipe_words, after_pipe_words)) = todo_keywords(input)?; assert_eq!(remaining, ""); assert_eq!(before_pipe_words, vec!["foo", "bar"]); assert_eq!(after_pipe_words, vec!["baz"]); @@ -71,7 +75,7 @@ mod tests { #[test] fn early_pipe() -> Result<(), Box> { let input = "| foo bar baz"; - let (remaining, (before_pipe_words, after_pipe_words)) = keyword_todo(input)?; + let (remaining, (before_pipe_words, after_pipe_words)) = todo_keywords(input)?; assert_eq!(remaining, ""); assert_eq!(before_pipe_words, Vec::<&str>::new()); assert_eq!(after_pipe_words, vec!["foo", "bar", "baz"]); @@ -81,7 +85,7 @@ mod tests { #[test] fn late_pipe() -> Result<(), Box> { let input = "foo bar baz |"; - let (remaining, (before_pipe_words, after_pipe_words)) = keyword_todo(input)?; + let (remaining, (before_pipe_words, after_pipe_words)) = todo_keywords(input)?; assert_eq!(remaining, ""); assert_eq!(before_pipe_words, vec!["foo", "bar", "baz"]); assert_eq!(after_pipe_words, Vec::<&str>::new());