Repository: kivikakk/comrak (Rust)

Issue:
Minor inconsistencies when outputting ordered lists to commonmark 
When outputting ordered lists and ordered task lists to `commonmark`, there are a few minor problems with numbering. The outputted commonmark is technically correct, but it might be nice to have the numbering be consecutive rather than simply what the original value was.  Using a task list, the numbering doesn't increase or maintain the original value - it's always the same number.

`comrak -t commonmark`

```
3. one
5. two
```

should yield 

```
3.  one
4.  one
```

instead of

```
3.  one
5.  two
```

notice the numbering isn't consecutive

---

`comrak -t commonmark -e tasklist`

```
3. [ ] one
5. [ ] two
```

should yield

```
3.  [ ] one
4.  [ ] two
```

instead of

```
3.  [ ] one
3.  [ ] two
```

notice the numbering isn't correct




Discussion:

Patch:
diff --git a/src/cm.rs b/src/cm.rs
index e0a51dfa..d251c320 100644
--- a/src/cm.rs
+++ b/src/cm.rs
@@ -1,9 +1,9 @@
 use crate::ctype::{isalpha, isdigit, ispunct, isspace};
-use crate::nodes::TableAlignment;
 use crate::nodes::{
     AstNode, ListDelimType, ListType, NodeCodeBlock, NodeHeading, NodeHtmlBlock, NodeLink,
     NodeMath, NodeTable, NodeValue, NodeWikiLink,
 };
+use crate::nodes::{NodeList, TableAlignment};
 #[cfg(feature = "shortcodes")]
 use crate::parser::shortcodes::NodeShortCode;
 use crate::parser::Options;
@@ -61,6 +61,7 @@ struct CommonMarkFormatter<'a, 'o> {
     in_tight_list_item: bool,
     custom_escape: Option<fn(&'a AstNode<'a>, u8) -> bool>,
     footnote_ix: u32,
+    ol_stack: Vec<usize>,
 }
 
 #[derive(PartialEq, Clone, Copy)]
@@ -98,6 +99,7 @@ impl<'a, 'o> CommonMarkFormatter<'a, 'o> {
             in_tight_list_item: false,
             custom_escape: None,
             footnote_ix: 0,
+            ol_stack: vec![],
         }
     }
 
@@ -420,18 +422,35 @@ impl<'a, 'o> CommonMarkFormatter<'a, 'o> {
     }
 
     fn format_list(&mut self, node: &'a AstNode<'a>, entering: bool) {
-        if !entering
-            && match node.next_sibling() {
+        let ol_start = match node.data.borrow().value {
+            NodeValue::List(NodeList {
+                list_type: ListType::Ordered,
+                start,
+                ..
+            }) => Some(start),
+            _ => None,
+        };
+
+        if entering {
+            if let Some(start) = ol_start {
+                self.ol_stack.push(start);
+            }
+        } else {
+            if ol_start.is_some() {
+                self.ol_stack.pop();
+            }
+
+            if match node.next_sibling() {
                 Some(next_sibling) => matches!(
                     next_sibling.data.borrow().value,
                     NodeValue::CodeBlock(..) | NodeValue::List(..)
                 ),
                 _ => false,
+            } {
+                self.cr();
+                write!(self, "<!-- end list -->").unwrap();
+                self.blankline();
             }
-        {
-            self.cr();
-            write!(self, "<!-- end list -->").unwrap();
-            self.blankline();
         }
     }
 
@@ -446,11 +465,11 @@ impl<'a, 'o> CommonMarkFormatter<'a, 'o> {
         let marker_width = if parent.list_type == ListType::Bullet {
             2
         } else {
-            let list_number = match node.data.borrow().value {
-                NodeValue::Item(ref ni) => ni.start,
-                NodeValue::TaskItem(_) => parent.start,
-                _ => unreachable!(),
-            };
+            let last_stack = self.ol_stack.last_mut().unwrap();
+            let list_number = *last_stack;
+            if entering {
+                *last_stack += 1;
+            }
             let list_delim = parent.delimiter;
             write!(
                 listmarker,


Test patch:
diff --git a/src/tests/commonmark.rs b/src/tests/commonmark.rs
index d2bda4d8..5fb0a268 100644
--- a/src/tests/commonmark.rs
+++ b/src/tests/commonmark.rs
@@ -7,12 +7,9 @@ use ntest::test_case;
 
 #[test]
 fn commonmark_removes_redundant_strong() {
-    let options = Options::default();
-
     let input = "This is **something **even** better**";
     let output = "This is **something even better**\n";
-
-    commonmark(input, output, Some(&options));
+    commonmark(input, output, None);
 }
 
 #[test]
@@ -52,7 +49,7 @@ fn math(markdown: &str, cm: &str) {
     options.extension.math_dollars = true;
     options.extension.math_code = true;
 
-    commonmark(markdown, cm, Some(&options));
+    commonmark(markdown, cm, None);
 }
 
 #[test_case("This [[url]] that", "This [[url|url]] that\n")]
@@ -63,3 +60,22 @@ fn wikilinks(markdown: &str, cm: &str) {
 
     commonmark(markdown, cm, Some(&options));
 }
+#[test]
+fn commonmark_relist() {
+    commonmark(
+        concat!("3. one\n", "5. two\n",),
+        // Note that right now we always include enough room for up to two
+        // digits. TODO: Ideally we determine the maximum digit length before
+        // getting this far.
+        concat!("3.  one\n", "4.  two\n",),
+        None,
+    );
+
+    let mut options = Options::default();
+    options.extension.tasklist = true;
+    commonmark(
+        concat!("3. [ ] one\n", "5. [ ] two\n",),
+        concat!("3.  [ ] one\n", "4.  [ ] two\n",),
+        Some(&options),
+    );
+}