From 178894680ba0f050d476f564dd22cf29ed042314 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Mon, 2 Oct 2023 10:48:34 -0400 Subject: [PATCH] Compare footnote section. --- .../footnote_section/simple.org | 3 +++ src/compare/diff.rs | 13 ++++++++++++- src/compare/util.rs | 16 ++++++++++++++++ src/context/global_settings.rs | 6 ++++++ src/parser/headline.rs | 10 ++++++++-- src/types/document.rs | 1 + 6 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 org_mode_samples/sections_and_headings/footnote_section/simple.org diff --git a/org_mode_samples/sections_and_headings/footnote_section/simple.org b/org_mode_samples/sections_and_headings/footnote_section/simple.org new file mode 100644 index 00000000..6bbce6f9 --- /dev/null +++ b/org_mode_samples/sections_and_headings/footnote_section/simple.org @@ -0,0 +1,3 @@ +* Foo +* Footnotes +* Footnotes and stuff diff --git a/src/compare/diff.rs b/src/compare/diff.rs index dbd37661..25cf74da 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -9,6 +9,7 @@ use super::sexp::unquote; use super::sexp::Token; use super::util::compare_standard_properties; use super::util::get_property; +use super::util::get_property_boolean; use super::util::get_property_quoted_string; use super::util::get_property_unquoted_atom; use crate::types::AngleLink; @@ -726,7 +727,17 @@ fn compare_heading<'s>( )); } - // TODO: Compare :pre-blank :footnote-section-p :scheduled :closed + // Compare footnote-section-p + let footnote_section = get_property_boolean(emacs, ":footnote-section-p")?; + if footnote_section != rust.is_footnote_section { + this_status = DiffStatus::Bad; + message = Some(format!( + "footnote section mismatch (emacs != rust) {:?} != {:?}", + footnote_section, rust.is_footnote_section + )); + } + + // TODO: Compare :pre-blank :scheduled :closed // // :scheduled and :closed seem to only appear when the headline has a planning diff --git a/src/compare/util.rs b/src/compare/util.rs index a7068048..c770abd7 100644 --- a/src/compare/util.rs +++ b/src/compare/util.rs @@ -217,3 +217,19 @@ pub(crate) fn get_property_quoted_string<'s, 'x>( .map(unquote) .map_or(Ok(None), |r| r.map(Some))?) } + +/// Get a named property containing a boolean value. +/// +/// This uses the elisp convention of nil == false, non-nil == true. +/// +/// Returns false if key is not found. +pub(crate) fn get_property_boolean<'s, 'x>( + emacs: &'s Token<'s>, + key: &'x str, +) -> Result> { + Ok(get_property(emacs, key)? + .map(Token::as_atom) + .map_or(Ok(None), |r| r.map(Some))? + .unwrap_or("nil") + != "nil") +} diff --git a/src/context/global_settings.rs b/src/context/global_settings.rs index c12bbcf5..f730c582 100644 --- a/src/context/global_settings.rs +++ b/src/context/global_settings.rs @@ -27,6 +27,11 @@ pub struct GlobalSettings<'g, 's> { /// /// Corresponds to org-odd-levels-only elisp variable. pub odd_levels_only: HeadlineLevelFilter, + + /// If a headline title matches this string exactly, then that section will become a "footnote section". + /// + /// Corresponds to org-footnote-section elisp variable. + pub footnote_section: &'g str, } pub const DEFAULT_TAB_WIDTH: IndentationLevel = 8; @@ -43,6 +48,7 @@ impl<'g, 's> GlobalSettings<'g, 's> { list_allow_alphabetical: false, tab_width: DEFAULT_TAB_WIDTH, odd_levels_only: HeadlineLevelFilter::default(), + footnote_section: "Footnotes", } } } diff --git a/src/parser/headline.rs b/src/parser/headline.rs index d6e57bb3..4e94726a 100644 --- a/src/parser/headline.rs +++ b/src/parser/headline.rs @@ -4,6 +4,7 @@ use nom::bytes::complete::tag; use nom::character::complete::anychar; use nom::character::complete::space0; use nom::character::complete::space1; +use nom::combinator::consumed; use nom::combinator::map; use nom::combinator::not; use nom::combinator::opt; @@ -101,6 +102,7 @@ fn _heading<'b, 'g, 'r, 's>( children, is_comment: maybe_comment.is_some(), is_archived, + is_footnote_section: false, // TODO }, )) } @@ -159,7 +161,9 @@ fn headline<'b, 'g, 'r, 's>( let (remaining, maybe_title) = opt(tuple(( space1, - many1(parser_with_context!(standard_set_object)(&parser_context)), + consumed(many1(parser_with_context!(standard_set_object)( + &parser_context, + ))), )))(remaining)?; let (remaining, maybe_tags) = opt(tuple((space0, tags)))(remaining)?; @@ -174,7 +178,9 @@ fn headline<'b, 'g, 'r, 's>( maybe_todo_keyword.map(|(_, todo, _)| todo), maybe_priority, maybe_comment.map(|(_, comment, _)| comment), - maybe_title.map(|(_, title)| title).unwrap_or(Vec::new()), + maybe_title + .map(|(_, (_, title))| title) + .unwrap_or(Vec::new()), maybe_tags .map(|(_ws, tags)| { tags.into_iter() diff --git a/src/types/document.rs b/src/types/document.rs index c271e8d8..60668734 100644 --- a/src/types/document.rs +++ b/src/types/document.rs @@ -28,6 +28,7 @@ pub struct Heading<'s> { pub children: Vec>, pub is_comment: bool, pub is_archived: bool, + pub is_footnote_section: bool, } #[derive(Debug)]