Parse out the plain list type.

This commit is contained in:
Tom Alexander 2023-09-29 12:46:01 -04:00
parent 1b7326eafe
commit a4b1d462c3
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE
3 changed files with 58 additions and 26 deletions

View File

@ -44,6 +44,7 @@ use crate::types::IndentationLevel;
use crate::types::Object; use crate::types::Object;
use crate::types::PlainList; use crate::types::PlainList;
use crate::types::PlainListItem; use crate::types::PlainListItem;
use crate::types::PlainListType;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub(crate) fn detect_plain_list<'b, 'g, 'r, 's>( 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 // children stores tuple of (input string, parsed object) so we can re-parse the final item
let mut children = Vec::new(); let mut children = Vec::new();
let mut first_item_indentation: Option<IndentationLevel> = None; let mut first_item_indentation: Option<IndentationLevel> = None;
let mut first_item_list_type: Option<PlainListType> = None;
let mut remaining = input; 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: // 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 { loop {
let list_item = parser_with_context!(plain_list_item)(&parser_context)(remaining); 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 { match list_item {
Ok((remain, item)) Ok((remain, (_list_type, item)))
if item.indentation == *first_item_indentation.get_or_insert(item.indentation) => if item.indentation == *first_item_indentation.get_or_insert(item.indentation) =>
{ {
children.push((remaining, item)); 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 = ContextElement::ConsumeTrailingWhitespace(false);
let final_item_context = parser_context.with_additional_node(&final_item_context); 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)?; parser_with_context!(plain_list_item)(&final_item_context)(final_child_start)?;
children.push((final_child_start, reparsed_final_item)); children.push((final_child_start, reparsed_final_item));
@ -139,6 +148,7 @@ pub(crate) fn plain_list<'b, 'g, 'r, 's>(
remaining, remaining,
PlainList { PlainList {
source: source.into(), source: source.into(),
list_type: PlainListType::Ordered,
children: children.into_iter().map(|(_start, item)| item).collect(), 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>( fn plain_list_item<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, PlainListItem<'s>> { ) -> Res<OrgSource<'s>, (PlainListType, PlainListItem<'s>)> {
start_of_line(input)?; start_of_line(input)?;
let (remaining, (indent_level, _leading_whitespace)) = indentation_level(context, input)?; let (remaining, (indent_level, _leading_whitespace)) = indentation_level(context, input)?;
let (remaining, (bullet_type, bull)) = verify( let (remaining, (bullet_type, bull)) = verify(
@ -170,6 +180,12 @@ fn plain_list_item<'b, 'g, 'r, 's>(
} else { } else {
(remaining, None) (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 exit_matcher = plain_list_item_end(indent_level);
let contexts = [ let contexts = [
@ -195,6 +211,8 @@ fn plain_list_item<'b, 'g, 'r, 's>(
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
return Ok(( return Ok((
remaining, remaining,
(
list_type,
PlainListItem { PlainListItem {
source: source.into(), source: source.into(),
indentation: indent_level, indentation: indent_level,
@ -205,6 +223,7 @@ fn plain_list_item<'b, 'g, 'r, 's>(
.unwrap_or(Vec::new()), .unwrap_or(Vec::new()),
children: Vec::new(), children: Vec::new(),
}, },
),
)); ));
} }
Err(_) => {} Err(_) => {}
@ -235,17 +254,21 @@ fn plain_list_item<'b, 'g, 'r, 's>(
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
return Ok(( return Ok((
remaining, remaining,
(
list_type,
PlainListItem { PlainListItem {
source: source.into(), source: source.into(),
indentation: indent_level, indentation: indent_level,
bullet: bull.into(), bullet: bull.into(),
checkbox: maybe_checkbox checkbox: maybe_checkbox.map(|(_, (checkbox_type, source))| {
.map(|(_, (checkbox_type, source))| (checkbox_type, Into::<&str>::into(source))), (checkbox_type, Into::<&str>::into(source))
}),
tag: maybe_tag tag: maybe_tag
.map(|(_ws, item_tag)| item_tag) .map(|(_ws, item_tag)| item_tag)
.unwrap_or(Vec::new()), .unwrap_or(Vec::new()),
children: children.into_iter().map(|(_start, item)| item).collect(), children: children.into_iter().map(|(_start, item)| item).collect(),
}, },
),
)); ));
} }
@ -454,7 +477,7 @@ mod tests {
let initial_context = ContextElement::document_context(); let initial_context = ContextElement::document_context();
let initial_context = Context::new(&global_settings, List::new(&initial_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 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!(Into::<&str>::into(remaining), "");
assert_eq!(result.get_standard_properties().get_source(), "1."); assert_eq!(result.get_standard_properties().get_source(), "1.");
} }
@ -466,7 +489,7 @@ mod tests {
let initial_context = ContextElement::document_context(); let initial_context = ContextElement::document_context();
let initial_context = Context::new(&global_settings, List::new(&initial_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 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!(Into::<&str>::into(remaining), "");
assert_eq!(result.get_standard_properties().get_source(), "1. foo"); assert_eq!(result.get_standard_properties().get_source(), "1. foo");
} }

View File

@ -7,9 +7,17 @@ use super::StandardProperties;
#[derive(Debug)] #[derive(Debug)]
pub struct PlainList<'s> { pub struct PlainList<'s> {
pub source: &'s str, pub source: &'s str,
pub list_type: PlainListType,
pub children: Vec<PlainListItem<'s>>, pub children: Vec<PlainListItem<'s>>,
} }
#[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. /// The width that something is indented. For example, a single tab character could be a value of 4 or 8.
pub type IndentationLevel = u16; pub type IndentationLevel = u16;

View File

@ -24,6 +24,7 @@ pub use greater_element::IndentationLevel;
pub use greater_element::NodeProperty; pub use greater_element::NodeProperty;
pub use greater_element::PlainList; pub use greater_element::PlainList;
pub use greater_element::PlainListItem; pub use greater_element::PlainListItem;
pub use greater_element::PlainListType;
pub use greater_element::PropertyDrawer; pub use greater_element::PropertyDrawer;
pub use greater_element::Table; pub use greater_element::Table;
pub use greater_element::TableRow; pub use greater_element::TableRow;