diff --git a/org_mode_samples/planning/simple.org b/org_mode_samples/planning/simple.org new file mode 100644 index 0000000..6d955b1 --- /dev/null +++ b/org_mode_samples/planning/simple.org @@ -0,0 +1,2 @@ +* foo +SCHEDULED: <2023-04-20 Thu> CLOSED: <2023-04-21 Fri> diff --git a/src/compare/diff.rs b/src/compare/diff.rs index 0a48752..fb6a51b 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -18,6 +18,7 @@ use crate::parser::Heading; use crate::parser::Paragraph; use crate::parser::PlainList; use crate::parser::PlainListItem; +use crate::parser::Planning; use crate::parser::PropertyDrawer; use crate::parser::Section; use crate::parser::SrcBlock; @@ -217,6 +218,7 @@ fn compare_element<'s>( Element::SrcBlock(obj) => compare_src_block(source, emacs, obj), Element::Clock(obj) => compare_clock(source, emacs, obj), Element::DiarySexp(obj) => compare_diary_sexp(source, emacs, obj), + Element::Planning(obj) => compare_planning(source, emacs, obj), } } @@ -727,3 +729,26 @@ fn compare_diary_sexp<'s>( children: Vec::new(), }) } + +fn compare_planning<'s>( + source: &'s str, + emacs: &'s Token<'s>, + rust: &'s Planning<'s>, +) -> Result> { + let mut this_status = DiffStatus::Good; + let emacs_name = "planning"; + if assert_name(emacs, emacs_name).is_err() { + this_status = DiffStatus::Bad; + } + + if assert_bounds(source, emacs, rust).is_err() { + this_status = DiffStatus::Bad; + } + + Ok(DiffResult { + status: this_status, + name: emacs_name.to_owned(), + message: None, + children: Vec::new(), + }) +} diff --git a/src/parser/document.rs b/src/parser/document.rs index 28b4157..9020903 100644 --- a/src/parser/document.rs +++ b/src/parser/document.rs @@ -6,6 +6,7 @@ use crate::parser::object_parser::standard_set_object; use crate::parser::parser_context::ContextElement; use crate::parser::parser_context::ContextTree; use crate::parser::parser_context::ExitMatcherNode; +use crate::parser::planning::planning; use crate::parser::property_drawer::property_drawer; use crate::parser::util::blank_line; use crate::parser::util::maybe_consume_trailing_whitespace_if_not_exiting; @@ -168,20 +169,27 @@ fn section<'r, 's>(context: Context<'r, 's>, mut input: &'s str) -> Res<&'s str, let element_matcher = parser_with_context!(element)(&parser_context); let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); // TODO: Match whatever a planning is. - let (mut remaining, property_drawer_element) = - opt(parser_with_context!(property_drawer)(&parser_context))(input)?; - if property_drawer_element.is_none() { + let (mut remaining, (planning_element, property_drawer_element)) = tuple(( + opt(parser_with_context!(planning)(&parser_context)), + opt(parser_with_context!(property_drawer)(&parser_context)), + ))(input)?; + if planning_element.is_none() && property_drawer_element.is_none() { let (remain, _ws) = many0(blank_line)(remaining)?; remaining = remain; input = remain; } let (remaining, (mut children, _exit_contents)) = verify( many_till(element_matcher, exit_matcher), - |(children, _exit_contents)| !children.is_empty() || property_drawer_element.is_some(), + |(children, _exit_contents)| { + !children.is_empty() || property_drawer_element.is_some() || planning_element.is_some() + }, )(remaining)?; property_drawer_element .map(Element::PropertyDrawer) .map(|ele| children.insert(0, ele)); + planning_element + .map(Element::Planning) + .map(|ele| children.insert(0, ele)); let (remaining, _trailing_ws) = maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; diff --git a/src/parser/element.rs b/src/parser/element.rs index bfc19d1..e051dd8 100644 --- a/src/parser/element.rs +++ b/src/parser/element.rs @@ -11,6 +11,7 @@ use super::lesser_element::DiarySexp; use super::lesser_element::ExampleBlock; use super::lesser_element::ExportBlock; use super::lesser_element::Paragraph; +use super::lesser_element::Planning; use super::lesser_element::SrcBlock; use super::lesser_element::VerseBlock; use super::source::Source; @@ -34,6 +35,7 @@ pub enum Element<'s> { SrcBlock(SrcBlock<'s>), Clock(Clock<'s>), DiarySexp(DiarySexp<'s>), + Planning(Planning<'s>), } impl<'s> Source<'s> for Element<'s> { @@ -55,6 +57,7 @@ impl<'s> Source<'s> for Element<'s> { Element::SrcBlock(obj) => obj.source, Element::Clock(obj) => obj.source, Element::DiarySexp(obj) => obj.source, + Element::Planning(obj) => obj.source, } } } diff --git a/src/parser/lesser_element.rs b/src/parser/lesser_element.rs index d210b62..55664c9 100644 --- a/src/parser/lesser_element.rs +++ b/src/parser/lesser_element.rs @@ -69,6 +69,11 @@ pub struct DiarySexp<'s> { pub source: &'s str, } +#[derive(Debug)] +pub struct Planning<'s> { + pub source: &'s str, +} + impl<'s> Paragraph<'s> { pub fn of_text(input: &'s str) -> Self { let mut objects = Vec::with_capacity(1); @@ -135,3 +140,9 @@ impl<'s> Source<'s> for DiarySexp<'s> { self.source } } + +impl<'s> Source<'s> for Planning<'s> { + fn get_source(&'s self) -> &'s str { + self.source + } +} diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 76ef01b..e1b5f2b 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -20,6 +20,7 @@ mod parser_context; mod parser_with_context; mod plain_list; mod plain_text; +mod planning; mod property_drawer; pub mod sexp; mod source; @@ -47,6 +48,7 @@ pub use lesser_element::DiarySexp; pub use lesser_element::ExampleBlock; pub use lesser_element::ExportBlock; pub use lesser_element::Paragraph; +pub use lesser_element::Planning; pub use lesser_element::SrcBlock; pub use lesser_element::TableCell; pub use lesser_element::VerseBlock; diff --git a/src/parser/planning.rs b/src/parser/planning.rs new file mode 100644 index 0000000..7f0b0bf --- /dev/null +++ b/src/parser/planning.rs @@ -0,0 +1,42 @@ +use nom::branch::alt; +use nom::bytes::complete::is_not; +use nom::bytes::complete::tag; +use nom::bytes::complete::tag_no_case; +use nom::character::complete::line_ending; +use nom::character::complete::space0; +use nom::character::complete::space1; +use nom::combinator::eof; +use nom::multi::separated_list1; +use nom::sequence::tuple; + +use super::Context; +use crate::error::Res; +use crate::parser::lesser_element::Planning; +use crate::parser::util::get_consumed; +use crate::parser::util::start_of_line; + +#[tracing::instrument(ret, level = "debug")] +pub fn planning<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Planning<'s>> { + start_of_line(context, input)?; + let (remaining, _leading_whitespace) = space0(input)?; + let (remaining, _planning_parameters) = separated_list1(space1, planning_parameter)(remaining)?; + let (remaining, _trailing_ws) = tuple((space0, alt((line_ending, eof))))(remaining)?; + + let source = get_consumed(input, remaining); + + Ok((remaining, Planning { source })) +} + +#[tracing::instrument(ret, level = "debug")] +fn planning_parameter<'s>(input: &'s str) -> Res<&'s str, &'s str> { + let (remaining, _planning_type) = alt(( + tag_no_case("DEADLINE"), + tag_no_case("SCHEDULED"), + tag_no_case("CLOSED"), + ))(input)?; + let (remaining, _gap) = tuple((tag(":"), space1))(remaining)?; + // TODO: Make this invoke the real timestamp parser. + let (remaining, _timestamp) = tuple((tag("<"), is_not("\r\n>"), tag(">")))(remaining)?; + let source = get_consumed(input, remaining); + Ok((remaining, source)) +}