From 9bcfb2f1daab10833184e96848cceaaeca4e35bf Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Wed, 20 Sep 2023 03:22:25 -0400 Subject: [PATCH] Decide headline nesting by star count, not headline level. It is possible to have two headlines that have the same level but different star counts when set to Odd because of rounding. Deciding nesting by star count instead of headline level avoids this issue. --- .../foreign_document_test_entrypoint.sh | 14 ++++---- .../sections_and_headings/odd_level_depth.org | 1 + src/parser/headline.rs | 35 ++++++++++++------- src/types/document.rs | 3 +- src/types/mod.rs | 1 + 5 files changed, 35 insertions(+), 19 deletions(-) diff --git a/docker/organic_test/foreign_document_test_entrypoint.sh b/docker/organic_test/foreign_document_test_entrypoint.sh index 429554a4..3a51ce94 100644 --- a/docker/organic_test/foreign_document_test_entrypoint.sh +++ b/docker/organic_test/foreign_document_test_entrypoint.sh @@ -41,9 +41,9 @@ function main { set -e if [ "$all_status" -ne 0 ]; then - echo "$(red_text "Some tests failed.")" + red_text "Some tests failed." else - echo "$(green_text "All tests passed.")" + green_text "All tests passed." fi return "$all_status" } @@ -64,8 +64,9 @@ function indent { local depth="$1" local scaled_depth=$((depth * 2)) shift 1 - local prefix=$(printf -- "%${scaled_depth}s") - while read l; do + local prefix + prefix=$(printf -- "%${scaled_depth}s") + while read -r l; do (IFS=' '; printf -- '%s%s\n' "$prefix" "$l") done } @@ -93,12 +94,13 @@ function compare_all_org_document { local target_document local all_status=0 while read target_document; do - local relative_path=$($REALPATH --relative-to "$root_dir" "$target_document") + local relative_path + relative_path=$($REALPATH --relative-to "$root_dir" "$target_document") set +e (run_compare "$relative_path" "$target_document") if [ "$?" -ne 0 ]; then all_status=1; fi set -e - done<<<$(find "$root_dir" -type f -iname '*.org') + done<<<"$(find "$root_dir" -type f -iname '*.org' | sort)" return "$all_status" } 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 c46b3f10..f92f920b 100644 --- a/org_mode_samples/sections_and_headings/odd_level_depth.org +++ b/org_mode_samples/sections_and_headings/odd_level_depth.org @@ -5,3 +5,4 @@ *** Lorem * Ipsum **** Dolar +***** Cat diff --git a/src/parser/headline.rs b/src/parser/headline.rs index b85ae6a3..d6e57bb3 100644 --- a/src/parser/headline.rs +++ b/src/parser/headline.rs @@ -34,12 +34,13 @@ use crate::parser::object_parser::standard_set_object; use crate::parser::util::blank_line; use crate::types::DocumentElement; use crate::types::Heading; +use crate::types::HeadlineLevel; use crate::types::Object; use crate::types::PriorityCookie; use crate::types::TodoKeywordType; pub(crate) const fn heading( - parent_level: usize, + parent_level: HeadlineLevel, ) -> impl for<'b, 'g, 'r, 's> Fn( RefContext<'b, 'g, 'r, 's>, OrgSource<'s>, @@ -51,15 +52,23 @@ pub(crate) const fn heading( fn _heading<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, - parent_level: usize, + parent_star_count: HeadlineLevel, ) -> Res, Heading<'s>> { not(|i| context.check_exit_matcher(i))(input)?; let ( remaining, - (headline_level, maybe_todo_keyword, maybe_priority, maybe_comment, title, heading_tags), - ) = headline(context, input, parent_level)?; + ( + headline_level, + star_count, + maybe_todo_keyword, + maybe_priority, + maybe_comment, + title, + heading_tags, + ), + ) = headline(context, input, parent_star_count)?; let section_matcher = parser_with_context!(section)(context); - let heading_matcher = parser_with_context!(heading(headline_level))(context); + let heading_matcher = parser_with_context!(heading(star_count))(context); let (remaining, maybe_section) = opt(map(section_matcher, DocumentElement::Section))(remaining)?; let (remaining, _ws) = opt(tuple((start_of_line, many0(blank_line))))(remaining)?; @@ -106,11 +115,12 @@ 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_level: usize, + parent_star_count: HeadlineLevel, ) -> Res< OrgSource<'s>, ( - usize, + HeadlineLevel, + HeadlineLevel, Option<(TodoKeywordType, OrgSource<'s>)>, Option<(OrgSource<'s>, PriorityCookie)>, Option>, @@ -124,11 +134,11 @@ fn headline<'b, 'g, 'r, 's>( }); let parser_context = context.with_additional_node(&parser_context); - let (remaining, (_, (star_count, _), _)) = tuple(( + let (remaining, (_, (headline_level, star_count, _), _)) = tuple(( start_of_line, verify( parser_with_context!(headline_level)(&parser_context), - |(level, _)| *level > parent_level, + |(_, count, _)| *count > parent_star_count, ), peek(org_space), ))(input)?; @@ -159,6 +169,7 @@ fn headline<'b, 'g, 'r, 's>( Ok(( remaining, ( + headline_level, star_count, maybe_todo_keyword.map(|(_, todo, _)| todo), maybe_priority, @@ -261,9 +272,9 @@ fn priority_cookie<'s>(input: OrgSource<'s>) -> Res, PriorityCooki fn headline_level<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, -) -> Res, (usize, OrgSource<'s>)> { +) -> Res, (HeadlineLevel, HeadlineLevel, OrgSource<'s>)> { let (remaining, stars) = is_a("*")(input)?; - let count = stars.len(); + let count = stars.len().try_into().unwrap(); let level = match context.get_global_settings().odd_levels_only { crate::context::HeadlineLevelFilter::Odd => { if count % 2 == 0 { @@ -274,5 +285,5 @@ fn headline_level<'b, 'g, 'r, 's>( } crate::context::HeadlineLevelFilter::OddEven => count, }; - Ok((remaining, (level, stars))) + Ok((remaining, (level, count, stars))) } diff --git a/src/types/document.rs b/src/types/document.rs index 72614f1f..1acc4688 100644 --- a/src/types/document.rs +++ b/src/types/document.rs @@ -3,6 +3,7 @@ use super::Object; use super::Source; pub type PriorityCookie = u8; +pub type HeadlineLevel = u16; #[derive(Debug)] pub struct Document<'s> { @@ -14,7 +15,7 @@ pub struct Document<'s> { #[derive(Debug)] pub struct Heading<'s> { pub source: &'s str, - pub level: usize, + pub level: HeadlineLevel, pub todo_keyword: Option<(TodoKeywordType, &'s str)>, pub priority_cookie: Option, pub title: Vec>, diff --git a/src/types/mod.rs b/src/types/mod.rs index 83666107..af286cdc 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -7,6 +7,7 @@ mod source; pub use document::Document; pub use document::DocumentElement; pub use document::Heading; +pub use document::HeadlineLevel; pub use document::PriorityCookie; pub use document::Section; pub use document::TodoKeywordType;