diff --git a/src/parser/plain_list.rs b/src/parser/plain_list.rs index 50ee254..b0a0a60 100644 --- a/src/parser/plain_list.rs +++ b/src/parser/plain_list.rs @@ -59,7 +59,8 @@ pub fn plain_list_item<'r, 's>( let element_matcher = parser_with_context!(element)(&parser_context); let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); - let (remaining, bull) = bullet(remaining)?; + let (remaining, bull) = + verify(bullet, |bull: &str| bull != "*" || indent_level > 0)(remaining)?; let maybe_contentless_item: Res<&str, &str> = alt((eof, line_ending))(remaining); match maybe_contentless_item { Ok((rem, _ws)) => { @@ -94,7 +95,6 @@ pub fn plain_list_item<'r, 's>( #[tracing::instrument(ret, level = "debug")] fn bullet<'s>(i: &'s str) -> Res<&'s str, &'s str> { - // TODO: If asterisk, it cannot be at start of line or it would be a headline alt(( tag("*"), tag("-"), @@ -208,4 +208,28 @@ mod tests { assert_eq!(remaining, ""); assert_eq!(result.source, "1. foo"); } + + #[test] + fn plain_list_cant_start_line_with_asterisk() { + // Plain lists with an asterisk bullet must be indented or else they would be a headline + let input = "* foo"; + let initial_context: ContextTree<'_, '_> = ContextTree::new(); + let document_context = + initial_context.with_additional_node(ContextElement::DocumentRoot(input)); + let plain_list_matcher = parser_with_context!(plain_list)(&document_context); + let result = plain_list_matcher(input); + assert!(result.is_err()); + } + + #[test] + fn indented_can_start_line_with_asterisk() { + // Plain lists with an asterisk bullet must be indented or else they would be a headline + let input = " * foo"; + let initial_context: ContextTree<'_, '_> = ContextTree::new(); + let document_context = + initial_context.with_additional_node(ContextElement::DocumentRoot(input)); + let plain_list_matcher = parser_with_context!(plain_list)(&document_context); + let result = plain_list_matcher(input); + assert!(result.is_ok()); + } }