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()); } }