aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Titus Wormer <tituswormer@gmail.com>2022-08-22 17:32:40 +0200
committerLibravatar Titus Wormer <tituswormer@gmail.com>2022-08-22 17:32:40 +0200
commit49b6a4e72516e8b2a8768e761a60a4f461802d69 (patch)
tree1baeb4eba94d9d49ccfd8bd15d0fb3fefd45a993
parent8774b207b7251730eaa7fbfe4f144122a472dda0 (diff)
downloadmarkdown-rs-49b6a4e72516e8b2a8768e761a60a4f461802d69.tar.gz
markdown-rs-49b6a4e72516e8b2a8768e761a60a4f461802d69.tar.bz2
markdown-rs-49b6a4e72516e8b2a8768e761a60a4f461802d69.zip
Fix lazy paragraph after definition
-rw-r--r--src/construct/document.rs68
-rw-r--r--src/construct/gfm_task_list_item_check.rs2
-rw-r--r--src/tokenizer.rs6
-rw-r--r--tests/block_quote.rs12
-rw-r--r--tests/gfm_task_list_item.rs6
-rw-r--r--tests/list.rs12
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)"