From d43173fee7390c05f4e7faf89b1d0dc8fc680d6e Mon Sep 17 00:00:00 2001
From: Andrew Birchall <AndrewABirchall@gmail.com>
Date: Sun, 2 Aug 2020 01:33:16 -0700
Subject: [PATCH] Omit paragraph wrappers from list item text

This updates the style of `list_item` to match the format from https://www.markdownguide.org/basic-syntax/#lists-1
Namely, that `list_item` contents do not have vertical margin due to wrapped `paragraph` tokens.

If folks do want this behavior, they can override the style of `*_list_icon` or `*_list_content`.

This change has the added benefit of eliminating the need for platform specific `*_list_icon` margins and line heights. Horray!

iOS before:
<img width="433" alt="list_ios_before" src="https://user-images.githubusercontent.com/456610/89119457-90897600-d463-11ea-80a7-05ea6e86f4a5.png">

iOS after:
<img width="441" alt="list_ios_after" src="https://user-images.githubusercontent.com/456610/89119465-94b59380-d463-11ea-8f4c-161fbb43e6a6.png">

Android before:
<img width="356" alt="list_android_before" src="https://user-images.githubusercontent.com/456610/89119470-98491a80-d463-11ea-8415-31374584603d.png">

Android after:
<img width="357" alt="list_android_after" src="https://user-images.githubusercontent.com/456610/89119474-9c753800-d463-11ea-98d9-07b4e1585b44.png">
---
 src/lib/parser.js                     |  2 ++
 src/lib/styles.js                     | 41 ---------------------------
 src/lib/util/omitListItemParagraph.js | 29 +++++++++++++++++++
 3 files changed, 31 insertions(+), 41 deletions(-)
 create mode 100644 src/lib/util/omitListItemParagraph.js

diff --git a/src/lib/parser.js b/src/lib/parser.js
index d0507a1..4e89793 100644
--- a/src/lib/parser.js
+++ b/src/lib/parser.js
@@ -2,6 +2,7 @@ import tokensToAST from './util/tokensToAST';
 import {stringToTokens} from './util/stringToTokens';
 import {cleanupTokens} from './util/cleanupTokens';
 import groupTextTokens from './util/groupTextTokens';
+import omitListItemParagraph from './util/omitListItemParagraph';
 
 /**
  *
@@ -18,6 +19,7 @@ export default function parser(source, renderer, markdownIt) {
   let tokens = stringToTokens(source, markdownIt);
   tokens = cleanupTokens(tokens);
   tokens = groupTextTokens(tokens);
+  tokens = omitListItemParagraph(tokens);
 
   const astTree = tokensToAST(tokens);
 
diff --git a/src/lib/styles.js b/src/lib/styles.js
index e2d945a..eccc7f9 100644
--- a/src/lib/styles.js
+++ b/src/lib/styles.js
@@ -66,28 +66,6 @@ export const styles = {
   bullet_list_icon: {
     marginLeft: 10,
     marginRight: 10,
-    ...Platform.select({
-      android: {
-        marginTop: 5,
-      },
-      ios: {
-        marginTop: 0,
-      },
-      default: {
-        marginTop: 0,
-      },
-    }),
-    ...Platform.select({
-      ios: {
-        lineHeight: 36,
-      },
-      android: {
-        lineHeight: 30,
-      },
-      default: {
-        lineHeight: 36,
-      },
-    }),
   },
   // @pseudo class, does not have a unique render rule
   bullet_list_content: {
@@ -98,25 +76,6 @@ export const styles = {
   ordered_list_icon: {
     marginLeft: 10,
     marginRight: 10,
-    ...Platform.select({
-      android: {
-        marginTop: 4,
-      },
-      default: {
-        marginTop: 0,
-      },
-    }),
-    ...Platform.select({
-      ios: {
-        lineHeight: 36,
-      },
-      android: {
-        lineHeight: 30,
-      },
-      default: {
-        lineHeight: 36,
-      },
-    }),
   },
   // @pseudo class, does not have a unique render rule
   ordered_list_content: {
diff --git a/src/lib/util/omitListItemParagraph.js b/src/lib/util/omitListItemParagraph.js
new file mode 100644
index 0000000..8efc08a
--- /dev/null
+++ b/src/lib/util/omitListItemParagraph.js
@@ -0,0 +1,29 @@
+export default function omitListItemParagraph(tokens) {
+  // used to ensure that we remove the correct ending paragraph token
+  let depth = null;
+  return tokens.filter((token, index) => {
+    // update depth if we've already removed a starting paragraph token
+    if (depth !== null) {
+      depth = depth + token.nesting;
+    }
+
+    // check for a list_item token followed by paragraph token (to remove)
+    if (token.type === 'list_item' && token.nesting === 1 && depth === null) {
+      const next = index + 1 in tokens ? tokens[index + 1] : null;
+      if (next && next.type === 'paragraph' && next.nesting === 1) {
+        depth = 0;
+        return true;
+      }
+    } else if (token.type === 'paragraph') {
+      if (token.nesting === 1 && depth === 1) {
+        // remove the paragraph token immediately after the list_item token
+        return false;
+      } else if (token.nesting === -1 && depth === 0) {
+        // remove the ending paragraph token; reset depth
+        depth = null;
+        return false;
+      }
+    }
+    return true;
+  });
+}