From 33372429dd3b2563f9e51e22ad47dd83be6639cf Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Thu, 14 Sep 2023 00:27:54 -0400 Subject: [PATCH] Add a config option for org-list-allow-alphabetical. This fixes an issue where lines in a paragraph were incorrectly getting identified as lists because I had defaulted to assuming alphabetical bullets were allowed. --- src/context/global_settings.rs | 5 +++ src/parser/element_parser.rs | 2 +- src/parser/plain_list.rs | 70 ++++++++++++++++++++++++++-------- 3 files changed, 60 insertions(+), 17 deletions(-) diff --git a/src/context/global_settings.rs b/src/context/global_settings.rs index b0c93059..cb32e6d8 100644 --- a/src/context/global_settings.rs +++ b/src/context/global_settings.rs @@ -12,6 +12,10 @@ pub struct GlobalSettings<'g, 's> { pub file_access: &'g dyn FileAccessInterface, pub in_progress_todo_keywords: BTreeSet, pub complete_todo_keywords: BTreeSet, + /// Set to true to allow for plain lists using single letters as the bullet in the same way that numbers are used. + /// + /// Corresponds to the org-list-allow-alphabetical elisp variable. + pub org_list_allow_alphabetical: bool, } impl<'g, 's> GlobalSettings<'g, 's> { @@ -23,6 +27,7 @@ impl<'g, 's> GlobalSettings<'g, 's> { }, in_progress_todo_keywords: BTreeSet::new(), complete_todo_keywords: BTreeSet::new(), + org_list_allow_alphabetical: false, } } } diff --git a/src/parser/element_parser.rs b/src/parser/element_parser.rs index e210384c..92da53c3 100644 --- a/src/parser/element_parser.rs +++ b/src/parser/element_parser.rs @@ -141,7 +141,7 @@ fn _detect_element<'b, 'g, 'r, 's>( can_be_paragraph: bool, ) -> Res, ()> { if alt(( - detect_plain_list, + parser_with_context!(detect_plain_list)(context), detect_footnote_definition, detect_diary_sexp, detect_comment, diff --git a/src/parser/plain_list.rs b/src/parser/plain_list.rs index 389c5040..83cf83e0 100644 --- a/src/parser/plain_list.rs +++ b/src/parser/plain_list.rs @@ -41,12 +41,15 @@ use crate::types::PlainList; use crate::types::PlainListItem; #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -pub(crate) fn detect_plain_list<'s>(input: OrgSource<'s>) -> Res, ()> { +pub(crate) fn detect_plain_list<'b, 'g, 'r, 's>( + context: RefContext<'b, 'g, 'r, 's>, + input: OrgSource<'s>, +) -> Res, ()> { if verify( tuple(( start_of_line, space0, - bullet, + parser_with_context!(bullet)(context), alt((space1, line_ending, eof)), )), |(_start, indent, bull, _after_whitespace)| { @@ -145,12 +148,17 @@ fn plain_list_item<'b, 'g, 'r, 's>( let (remaining, leading_whitespace) = space0(input)?; // It is fine that we get the indent level using the number of bytes rather than the number of characters because nom's space0 only matches space and tab (0x20 and 0x09) let indent_level = leading_whitespace.len(); - let (remaining, bull) = verify(bullet, |bull: &OrgSource<'_>| { - Into::<&str>::into(bull) != "*" || indent_level > 0 - })(remaining)?; + let (remaining, bull) = verify( + parser_with_context!(bullet)(context), + |bull: &OrgSource<'_>| Into::<&str>::into(bull) != "*" || indent_level > 0, + )(remaining)?; - let (remaining, _maybe_counter_set) = - opt(tuple((space1, tag("[@"), counter, tag("]"))))(remaining)?; + let (remaining, _maybe_counter_set) = opt(tuple(( + space1, + tag("[@"), + parser_with_context!(counter)(context), + tag("]"), + )))(remaining)?; // TODO: parse checkbox @@ -228,18 +236,36 @@ fn plain_list_item<'b, 'g, 'r, 's>( } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn bullet<'s>(i: OrgSource<'s>) -> Res, OrgSource<'s>> { +fn bullet<'b, 'g, 'r, 's>( + context: RefContext<'b, 'g, 'r, 's>, + input: OrgSource<'s>, +) -> Res, OrgSource<'s>> { alt(( tag("*"), tag("-"), tag("+"), - recognize(tuple((counter, alt((tag("."), tag(")")))))), - ))(i) + recognize(tuple(( + parser_with_context!(counter)(context), + alt((tag("."), tag(")"))), + ))), + ))(input) } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] -fn counter<'s>(i: OrgSource<'s>) -> Res, OrgSource<'s>> { - alt((recognize(one_of("abcdefghijklmnopqrstuvwxyz")), digit1))(i) +fn counter<'b, 'g, 'r, 's>( + context: RefContext<'b, 'g, 'r, 's>, + input: OrgSource<'s>, +) -> Res, OrgSource<'s>> { + if context.get_global_settings().org_list_allow_alphabetical { + alt(( + recognize(one_of( + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", + )), + digit1, + ))(input) + } else { + digit1(input) + } } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] @@ -558,21 +584,30 @@ dolar"#, r#"+ "#, ); - let result = detect_plain_list(input); + let global_settings = GlobalSettings::default(); + let initial_context = ContextElement::document_context(); + let initial_context = Context::new(&global_settings, List::new(&initial_context)); + let result = detect_plain_list(&initial_context, input); assert!(result.is_ok()); } #[test] fn detect_eof() { let input = OrgSource::new(r#"+"#); - let result = detect_plain_list(input); + let global_settings = GlobalSettings::default(); + let initial_context = ContextElement::document_context(); + let initial_context = Context::new(&global_settings, List::new(&initial_context)); + let result = detect_plain_list(&initial_context, input); assert!(result.is_ok()); } #[test] fn detect_no_gap() { let input = OrgSource::new(r#"+foo"#); - let result = detect_plain_list(input); + let global_settings = GlobalSettings::default(); + let initial_context = ContextElement::document_context(); + let initial_context = Context::new(&global_settings, List::new(&initial_context)); + let result = detect_plain_list(&initial_context, input); // Since there is no whitespace after the '+' this is a paragraph, not a plain list. assert!(result.is_err()); } @@ -580,7 +615,10 @@ dolar"#, #[test] fn detect_with_gap() { let input = OrgSource::new(r#"+ foo"#); - let result = detect_plain_list(input); + let global_settings = GlobalSettings::default(); + let initial_context = ContextElement::document_context(); + let initial_context = Context::new(&global_settings, List::new(&initial_context)); + let result = detect_plain_list(&initial_context, input); assert!(result.is_ok()); } }