From 1a704dd312ed3e829baf180974c98f6e7d55dbed Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Fri, 15 Sep 2023 22:44:39 -0400 Subject: [PATCH] Honor the odd startup setting from org-mode files. --- .../sections_and_headings/odd_level_depth.org | 1 + src/compare/diff.rs | 4 +- src/parser/headline.rs | 47 ++++++++++++++----- src/parser/in_buffer_settings.rs | 3 ++ src/types/document.rs | 2 +- 5 files changed, 41 insertions(+), 16 deletions(-) diff --git a/org_mode_samples/sections_and_headings/odd_level_depth.org b/org_mode_samples/sections_and_headings/odd_level_depth.org index 51fecf0..c46b3f1 100644 --- a/org_mode_samples/sections_and_headings/odd_level_depth.org +++ b/org_mode_samples/sections_and_headings/odd_level_depth.org @@ -4,3 +4,4 @@ * Baz *** Lorem * Ipsum +**** Dolar diff --git a/src/compare/diff.rs b/src/compare/diff.rs index f222e1e..1aab578 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -490,11 +490,11 @@ fn compare_heading<'s>( let level = get_property(emacs, ":level")? .ok_or("Level should not be nil")? .as_atom()?; - if rust.stars.to_string() != level { + if rust.level.to_string() != level { this_status = DiffStatus::Bad; message = Some(format!( "Headline level do not match (emacs != rust): {} != {}", - level, rust.stars + level, rust.level )) } diff --git a/src/parser/headline.rs b/src/parser/headline.rs index 8dcee83..b85ae6a 100644 --- a/src/parser/headline.rs +++ b/src/parser/headline.rs @@ -1,4 +1,5 @@ use nom::branch::alt; +use nom::bytes::complete::is_a; use nom::bytes::complete::tag; use nom::character::complete::anychar; use nom::character::complete::space0; @@ -11,7 +12,6 @@ use nom::combinator::recognize; use nom::combinator::verify; use nom::multi::many0; use nom::multi::many1; -use nom::multi::many1_count; use nom::multi::separated_list1; use nom::sequence::tuple; @@ -39,27 +39,27 @@ use crate::types::PriorityCookie; use crate::types::TodoKeywordType; pub(crate) const fn heading( - parent_stars: usize, + parent_level: usize, ) -> impl for<'b, 'g, 'r, 's> Fn( RefContext<'b, 'g, 'r, 's>, OrgSource<'s>, ) -> Res, Heading<'s>> { - move |context, input: OrgSource<'_>| _heading(context, input, parent_stars) + move |context, input: OrgSource<'_>| _heading(context, input, parent_level) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn _heading<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, - parent_stars: usize, + parent_level: usize, ) -> Res, Heading<'s>> { not(|i| context.check_exit_matcher(i))(input)?; let ( remaining, - (star_count, maybe_todo_keyword, maybe_priority, maybe_comment, title, heading_tags), - ) = headline(context, input, parent_stars)?; + (headline_level, maybe_todo_keyword, maybe_priority, maybe_comment, title, heading_tags), + ) = headline(context, input, parent_level)?; let section_matcher = parser_with_context!(section)(context); - let heading_matcher = parser_with_context!(heading(star_count))(context); + let heading_matcher = parser_with_context!(heading(headline_level))(context); let (remaining, maybe_section) = opt(map(section_matcher, DocumentElement::Section))(remaining)?; let (remaining, _ws) = opt(tuple((start_of_line, many0(blank_line))))(remaining)?; @@ -82,7 +82,7 @@ fn _heading<'b, 'g, 'r, 's>( remaining, Heading { source: source.into(), - stars: star_count, + level: headline_level, todo_keyword: maybe_todo_keyword.map(|(todo_keyword_type, todo_keyword)| { (todo_keyword_type, Into::<&str>::into(todo_keyword)) }), @@ -106,7 +106,7 @@ pub(crate) fn detect_headline<'s>(input: OrgSource<'s>) -> Res, () fn headline<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, - parent_stars: usize, + parent_level: usize, ) -> Res< OrgSource<'s>, ( @@ -124,11 +124,12 @@ fn headline<'b, 'g, 'r, 's>( }); let parser_context = context.with_additional_node(&parser_context); - let (remaining, (_, star_count, _)) = tuple(( + let (remaining, (_, (star_count, _), _)) = tuple(( start_of_line, - verify(many1_count(tag("*")), |star_count| { - *star_count > parent_stars - }), + verify( + parser_with_context!(headline_level)(&parser_context), + |(level, _)| *level > parent_level, + ), peek(org_space), ))(input)?; @@ -255,3 +256,23 @@ fn priority_cookie<'s>(input: OrgSource<'s>) -> Res, PriorityCooki })?; Ok((remaining, cookie)) } + +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +fn headline_level<'b, 'g, 'r, 's>( + context: RefContext<'b, 'g, 'r, 's>, + input: OrgSource<'s>, +) -> Res, (usize, OrgSource<'s>)> { + let (remaining, stars) = is_a("*")(input)?; + let count = stars.len(); + let level = match context.get_global_settings().odd_levels_only { + crate::context::HeadlineLevelFilter::Odd => { + if count % 2 == 0 { + (count + 2) / 2 + } else { + (count + 1) / 2 + } + } + crate::context::HeadlineLevelFilter::OddEven => count, + }; + Ok((remaining, (level, stars))) +} diff --git a/src/parser/in_buffer_settings.rs b/src/parser/in_buffer_settings.rs index 626e397..40f8505 100644 --- a/src/parser/in_buffer_settings.rs +++ b/src/parser/in_buffer_settings.rs @@ -81,6 +81,9 @@ pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>( if settings.contains(&"odd") { new_settings.odd_levels_only = HeadlineLevelFilter::Odd; } + if settings.contains(&"oddeven") { + new_settings.odd_levels_only = HeadlineLevelFilter::OddEven; + } } Ok(new_settings) diff --git a/src/types/document.rs b/src/types/document.rs index 142762d..72614f1 100644 --- a/src/types/document.rs +++ b/src/types/document.rs @@ -14,7 +14,7 @@ pub struct Document<'s> { #[derive(Debug)] pub struct Heading<'s> { pub source: &'s str, - pub stars: usize, + pub level: usize, pub todo_keyword: Option<(TodoKeywordType, &'s str)>, pub priority_cookie: Option, pub title: Vec>,