diff --git a/src/parser/plain_list.rs b/src/parser/plain_list.rs index b0e881e1..b12451c0 100644 --- a/src/parser/plain_list.rs +++ b/src/parser/plain_list.rs @@ -44,6 +44,7 @@ use crate::types::IndentationLevel; use crate::types::Object; use crate::types::PlainList; use crate::types::PlainListItem; +use crate::types::PlainListType; #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub(crate) fn detect_plain_list<'b, 'g, 'r, 's>( @@ -90,6 +91,7 @@ pub(crate) fn plain_list<'b, 'g, 'r, 's>( // children stores tuple of (input string, parsed object) so we can re-parse the final item let mut children = Vec::new(); let mut first_item_indentation: Option = None; + let mut first_item_list_type: Option = None; let mut remaining = input; // The final list item does not consume trailing blank lines (which instead get consumed by the list). We have three options here: @@ -102,8 +104,15 @@ pub(crate) fn plain_list<'b, 'g, 'r, 's>( loop { let list_item = parser_with_context!(plain_list_item)(&parser_context)(remaining); + match (&first_item_list_type, &list_item) { + (None, Ok((_remain, (list_type, _item)))) => { + let _ = first_item_list_type.insert(*list_type); + } + (None, Err(_)) => {} + (Some(_), _) => {} + }; match list_item { - Ok((remain, item)) + Ok((remain, (_list_type, item))) if item.indentation == *first_item_indentation.get_or_insert(item.indentation) => { children.push((remaining, item)); @@ -130,7 +139,7 @@ pub(crate) fn plain_list<'b, 'g, 'r, 's>( }; let final_item_context = ContextElement::ConsumeTrailingWhitespace(false); let final_item_context = parser_context.with_additional_node(&final_item_context); - let (remaining, reparsed_final_item) = + let (remaining, (_, reparsed_final_item)) = parser_with_context!(plain_list_item)(&final_item_context)(final_child_start)?; children.push((final_child_start, reparsed_final_item)); @@ -139,6 +148,7 @@ pub(crate) fn plain_list<'b, 'g, 'r, 's>( remaining, PlainList { source: source.into(), + list_type: PlainListType::Ordered, children: children.into_iter().map(|(_start, item)| item).collect(), }, )) @@ -148,7 +158,7 @@ pub(crate) fn plain_list<'b, 'g, 'r, 's>( fn plain_list_item<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, -) -> Res, PlainListItem<'s>> { +) -> Res, (PlainListType, PlainListItem<'s>)> { start_of_line(input)?; let (remaining, (indent_level, _leading_whitespace)) = indentation_level(context, input)?; let (remaining, (bullet_type, bull)) = verify( @@ -170,6 +180,12 @@ fn plain_list_item<'b, 'g, 'r, 's>( } else { (remaining, None) }; + let list_type = match (&maybe_tag, bullet_type) { + (None, BulletType::Ordered) => PlainListType::Ordered, + (None, BulletType::Unordered) => PlainListType::Unordered, + (Some(_), BulletType::Ordered) => unreachable!(), + (Some(_), BulletType::Unordered) => PlainListType::Descriptive, + }; let exit_matcher = plain_list_item_end(indent_level); let contexts = [ @@ -195,16 +211,19 @@ fn plain_list_item<'b, 'g, 'r, 's>( let source = get_consumed(input, remaining); return Ok(( remaining, - PlainListItem { - source: source.into(), - indentation: indent_level, - bullet: bull.into(), - checkbox: None, - tag: maybe_tag - .map(|(_ws, item_tag)| item_tag) - .unwrap_or(Vec::new()), - children: Vec::new(), - }, + ( + list_type, + PlainListItem { + source: source.into(), + indentation: indent_level, + bullet: bull.into(), + checkbox: None, + tag: maybe_tag + .map(|(_ws, item_tag)| item_tag) + .unwrap_or(Vec::new()), + children: Vec::new(), + }, + ), )); } Err(_) => {} @@ -235,17 +254,21 @@ fn plain_list_item<'b, 'g, 'r, 's>( let source = get_consumed(input, remaining); return Ok(( remaining, - PlainListItem { - source: source.into(), - indentation: indent_level, - bullet: bull.into(), - checkbox: maybe_checkbox - .map(|(_, (checkbox_type, source))| (checkbox_type, Into::<&str>::into(source))), - tag: maybe_tag - .map(|(_ws, item_tag)| item_tag) - .unwrap_or(Vec::new()), - children: children.into_iter().map(|(_start, item)| item).collect(), - }, + ( + list_type, + PlainListItem { + source: source.into(), + indentation: indent_level, + bullet: bull.into(), + checkbox: maybe_checkbox.map(|(_, (checkbox_type, source))| { + (checkbox_type, Into::<&str>::into(source)) + }), + tag: maybe_tag + .map(|(_ws, item_tag)| item_tag) + .unwrap_or(Vec::new()), + children: children.into_iter().map(|(_start, item)| item).collect(), + }, + ), )); } @@ -454,7 +477,7 @@ mod tests { let initial_context = ContextElement::document_context(); let initial_context = Context::new(&global_settings, List::new(&initial_context)); let plain_list_item_matcher = parser_with_context!(plain_list_item)(&initial_context); - let (remaining, result) = plain_list_item_matcher(input).unwrap(); + let (remaining, (_, result)) = plain_list_item_matcher(input).unwrap(); assert_eq!(Into::<&str>::into(remaining), ""); assert_eq!(result.get_standard_properties().get_source(), "1."); } @@ -466,7 +489,7 @@ mod tests { let initial_context = ContextElement::document_context(); let initial_context = Context::new(&global_settings, List::new(&initial_context)); let plain_list_item_matcher = parser_with_context!(plain_list_item)(&initial_context); - let (remaining, result) = plain_list_item_matcher(input).unwrap(); + let (remaining, (_, result)) = plain_list_item_matcher(input).unwrap(); assert_eq!(Into::<&str>::into(remaining), ""); assert_eq!(result.get_standard_properties().get_source(), "1. foo"); } diff --git a/src/types/greater_element.rs b/src/types/greater_element.rs index 828c4b7d..bfe469a9 100644 --- a/src/types/greater_element.rs +++ b/src/types/greater_element.rs @@ -7,9 +7,17 @@ use super::StandardProperties; #[derive(Debug)] pub struct PlainList<'s> { pub source: &'s str, + pub list_type: PlainListType, pub children: Vec>, } +#[derive(Debug, Copy, Clone)] +pub enum PlainListType { + Unordered, + Ordered, + Descriptive, +} + /// The width that something is indented. For example, a single tab character could be a value of 4 or 8. pub type IndentationLevel = u16; diff --git a/src/types/mod.rs b/src/types/mod.rs index 7fa6e809..ca31e249 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -24,6 +24,7 @@ pub use greater_element::IndentationLevel; pub use greater_element::NodeProperty; pub use greater_element::PlainList; pub use greater_element::PlainListItem; +pub use greater_element::PlainListType; pub use greater_element::PropertyDrawer; pub use greater_element::Table; pub use greater_element::TableRow;