diff options
author | Titus Wormer <tituswormer@gmail.com> | 2022-08-22 17:32:40 +0200 |
---|---|---|
committer | Titus Wormer <tituswormer@gmail.com> | 2022-08-22 17:32:40 +0200 |
commit | 49b6a4e72516e8b2a8768e761a60a4f461802d69 (patch) | |
tree | 1baeb4eba94d9d49ccfd8bd15d0fb3fefd45a993 | |
parent | 8774b207b7251730eaa7fbfe4f144122a472dda0 (diff) | |
download | markdown-rs-49b6a4e72516e8b2a8768e761a60a4f461802d69.tar.gz markdown-rs-49b6a4e72516e8b2a8768e761a60a4f461802d69.tar.bz2 markdown-rs-49b6a4e72516e8b2a8768e761a60a4f461802d69.zip |
Fix lazy paragraph after definition
-rw-r--r-- | src/construct/document.rs | 68 | ||||
-rw-r--r-- | src/construct/gfm_task_list_item_check.rs | 2 | ||||
-rw-r--r-- | src/tokenizer.rs | 6 | ||||
-rw-r--r-- | tests/block_quote.rs | 12 | ||||
-rw-r--r-- | tests/gfm_task_list_item.rs | 6 | ||||
-rw-r--r-- | tests/list.rs | 12 |
6 files changed, 93 insertions, 13 deletions
diff --git a/src/construct/document.rs b/src/construct/document.rs index 4ef6acc..b438808 100644 --- a/src/construct/document.rs +++ b/src/construct/document.rs @@ -337,16 +337,65 @@ pub fn flow_end(tokenizer: &mut Tokenizer) -> State { state, ); - let paragraph = matches!(state, State::Next(StateName::ParagraphInside)) - || (!child.events.is_empty() - && child.events - [skip::opt_back(&child.events, child.events.len() - 1, &[Name::LineEnding])] - .name - == Name::Paragraph); - tokenizer.tokenize_state.document_child_state = Some(state); - if child.lazy && paragraph && tokenizer.tokenize_state.document_paragraph_before { + // If we’re in a lazy line, and the previous (lazy or not) line is something + // that can be lazy, and this line is that too, allow it. + // + // Accept: + // + // ```markdown + // | * a + // > | b + // ^ + // | ``` + // ``` + // + // Do not accept: + // + // ```markdown + // | * # a + // > | b + // ^ + // | ``` + // ``` + // + // Do not accept: + // + // ```markdown + // | * a + // > | # b + // ^ + // | ``` + // ``` + let mut document_lazy_continuation_current = false; + let mut stack_index = child.stack.len(); + + // Use two algo’s: one for when we’re suspended or in multiline things + // like definitions, another (b) for when we fed the line ending and closed + // a) + while !document_lazy_continuation_current && stack_index > 0 { + stack_index -= 1; + let name = &child.stack[stack_index]; + if name == &Name::Paragraph || name == &Name::Definition { + document_lazy_continuation_current = true; + } + } + + // …another because we parse each “rest” line as a paragraph, and we passed + // a EOL already. + if !document_lazy_continuation_current && !child.events.is_empty() { + let before = skip::opt_back(&child.events, child.events.len() - 1, &[Name::LineEnding]); + let name = &child.events[before].name; + if name == &Name::Paragraph { + document_lazy_continuation_current = true; + } + } + + if child.lazy + && tokenizer.tokenize_state.document_lazy_accepting_before + && document_lazy_continuation_current + { tokenizer.tokenize_state.document_continued = tokenizer.tokenize_state.document_container_stack.len(); } @@ -366,7 +415,8 @@ pub fn flow_end(tokenizer: &mut Tokenizer) -> State { } Some(_) => { tokenizer.tokenize_state.document_continued = 0; - tokenizer.tokenize_state.document_paragraph_before = paragraph; + tokenizer.tokenize_state.document_lazy_accepting_before = + document_lazy_continuation_current; // Containers would only be interrupting if we’ve continued. tokenizer.interrupt = false; State::Retry(StateName::DocumentContainerExistingBefore) diff --git a/src/construct/gfm_task_list_item_check.rs b/src/construct/gfm_task_list_item_check.rs index 62ff8aa..b30659a 100644 --- a/src/construct/gfm_task_list_item_check.rs +++ b/src/construct/gfm_task_list_item_check.rs @@ -81,7 +81,7 @@ pub fn start(tokenizer: &mut Tokenizer) -> State { /// ``` pub fn inside(tokenizer: &mut Tokenizer) -> State { match tokenizer.current { - Some(b'\t' | b' ') => { + Some(b'\t' | b'\n' | b' ') => { tokenizer.enter(Name::GfmTaskListItemValueUnchecked); tokenizer.consume(); tokenizer.exit(Name::GfmTaskListItemValueUnchecked); diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 731b829..83514cb 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -142,8 +142,8 @@ pub struct TokenizeState<'a> { pub document_data_index: Option<usize>, /// Container exits by line number. pub document_exits: Vec<Option<Vec<Event>>>, - /// Whether the previous flow was a paragraph. - pub document_paragraph_before: bool, + /// Whether the previous flow was a paragraph or a definition. + pub document_lazy_accepting_before: bool, /// Whether this is the first paragraph (potentially after definitions) in /// a list item. /// Used for GFM task list items. @@ -282,7 +282,7 @@ impl<'a> Tokenizer<'a> { document_container_stack: vec![], document_exits: vec![], document_continued: 0, - document_paragraph_before: false, + document_lazy_accepting_before: false, document_data_index: None, document_child_state: None, document_child: None, diff --git a/tests/block_quote.rs b/tests/block_quote.rs index 5c72fb9..be9da40 100644 --- a/tests/block_quote.rs +++ b/tests/block_quote.rs @@ -89,6 +89,18 @@ fn block_quote() { ); assert_eq!( + micromark("> [\na"), + "<blockquote>\n<p>[\na</p>\n</blockquote>", + "should support lazy, definition-like lines" + ); + + assert_eq!( + micromark("> [a]: b\nc"), + "<blockquote>\n<p>c</p>\n</blockquote>", + "should support a definition, followed by a lazy paragraph" + ); + + assert_eq!( micromark(">"), "<blockquote>\n</blockquote>", "should support empty block quotes (1)" diff --git a/tests/gfm_task_list_item.rs b/tests/gfm_task_list_item.rs index 5db5d1b..66b646f 100644 --- a/tests/gfm_task_list_item.rs +++ b/tests/gfm_task_list_item.rs @@ -70,6 +70,9 @@ fn gfm_task_list_item() { + [X] With an upper case `x` +* [ +] In a lazy line + - [ ] With two spaces + [x] Two spaces indent @@ -163,6 +166,9 @@ EOL after: <li><input type="checkbox" disabled="" checked="" /> With an upper case <code>x</code></li> </ul> <ul> +<li><input type="checkbox" disabled="" /> In a lazy line</li> +</ul> +<ul> <li>[ ] With two spaces</li> </ul> <ul> diff --git a/tests/list.rs b/tests/list.rs index fc416cc..bbba7cd 100644 --- a/tests/list.rs +++ b/tests/list.rs @@ -262,6 +262,18 @@ fn list() { ); assert_eq!( + micromark("- [\na"), + "<ul>\n<li>[\na</li>\n</ul>", + "should support lazy, definition-like lines" + ); + + assert_eq!( + micromark("- [a]: b\nc"), + "<ul>\n<li>c</li>\n</ul>", + "should support a definition, followed by a lazy paragraph" + ); + + assert_eq!( micromark("- foo\n - bar\n - baz\n - boo"), "<ul>\n<li>foo\n<ul>\n<li>bar\n<ul>\n<li>baz\n<ul>\n<li>boo</li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>", "should support sublists w/ enough spaces (1)" |