Skip to content

Commit e665803

Browse files
wooormtriaeiou
andcommitted
Add improved spread algorithm for list items
Closes GH-75. Co-authored-by: TRIAEIOU <[email protected]>
1 parent ae8239e commit e665803

File tree

8 files changed

+82
-9
lines changed

8 files changed

+82
-9
lines changed

lib/handlers/li.js

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
* @typedef {import('../types.js').Handle} Handle
33
* @typedef {import('../types.js').Element} Element
44
* @typedef {import('../types.js').ElementChild} ElementChild
5-
* @typedef {import('../types.js').MdastNode} MdastNode
65
*/
76

87
import {convertElement} from 'hast-util-is-element'
8+
import {phrasing} from 'hast-util-phrasing'
99
import {wrapChildren} from '../util/wrap-children.js'
1010

1111
const p = convertElement('p')
@@ -45,7 +45,52 @@ export function li(h, node) {
4545
}
4646
}
4747

48-
const content = wrapChildren(h, clone || node)
48+
if (!clone) clone = node
4949

50-
return h(node, 'listItem', {spread: content.length > 1, checked}, content)
50+
const spread = spreadout(clone)
51+
const content = wrapChildren(h, clone)
52+
53+
return h(node, 'listItem', {spread, checked}, content)
54+
}
55+
56+
/**
57+
* Check if an element should spread out.
58+
* The reason to spread out a markdown list item is primarily whether writing
59+
* the equivalent in markdown, would yield a spread out item.
60+
*
61+
* A spread out item results in `<p>` and `</p>` tags.
62+
* Otherwise, the phrasing would be output directly.
63+
* We can check for that: if there’s a `<p>` element, spread it out.
64+
*
65+
* But what if there are no paragraphs?
66+
* In that case, we can also assume that if two “block” things were written in
67+
* an item, that it is spread out, because blocks are typically joined by blank
68+
* lines, which also means a spread item.
69+
*
70+
* Lastly, because in HTML things can be wrapped in a `<div>` or similar, we
71+
* delve into non-phrasing elements here to figure out if they themselves
72+
* contain paragraphs or 2 or more flow non-phrasing elements.
73+
*
74+
* @param {Element} node
75+
* @returns {boolean}
76+
*/
77+
function spreadout(node) {
78+
let index = -1
79+
let seenFlow = false
80+
81+
while (++index < node.children.length) {
82+
const child = node.children[index]
83+
84+
if (child.type === 'element') {
85+
if (phrasing(child)) continue
86+
87+
if (child.tagName === 'p' || seenFlow || spreadout(child)) {
88+
return true
89+
}
90+
91+
seenFlow = true
92+
}
93+
}
94+
95+
return false
5196
}

test/fixtures/dl/index.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
{
2+
"": "spread can’t be losslessly translated between HTML <> markdown",
3+
"tree": false,
24
"fragment": true
35
}

test/fixtures/dl/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ Charlie.
2222
* Firefox
2323

2424
* A web browser.
25+
2526
* A Red Panda.
27+
2628
* ```js
2729
charlie();
2830
```

test/fixtures/ol/index.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
{
2+
"": "spread can’t be losslessly translated between HTML <> markdown",
3+
"tree": false,
24
"fragment": true
35
}

test/fixtures/ol/index.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
2. Alpha
2+
23
3. Bravo
4+
35
4. ```js
46
charlie();
57
```
@@ -27,23 +29,31 @@ Bar.
2729
Baz.
2830

2931
1.
32+
3033
2. Something else
3134

3235
Qux.
3336

3437
1. Something else
38+
3539
2.
3640

3741
Quux.
3842

3943
1. Something else
44+
4045
2.
4146

4247
Quuux.
4348

4449
1. [x] Bravo
50+
4551
2. [ ] Charlie
52+
4653
3. [x] Delta
54+
4755
4. [ ] Echo
56+
4857
5. [ ] **Foxtrot**
58+
4959
6. [ ] **Golf**

test/fixtures/ul/index.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
{
2+
"": "spread can’t be losslessly translated between HTML <> markdown",
3+
"tree": false,
24
"fragment": true
35
}

test/fixtures/ul/index.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
* Alpha
2+
23
* Bravo
4+
35
* ```js
46
charlie();
57
```
@@ -27,23 +29,31 @@ Bar.
2729
Baz.
2830

2931
*
32+
3033
* Something else
3134

3235
Qux.
3336

3437
* Something else
38+
3539
*
3640

3741
Quux.
3842

3943
* Something else
44+
4045
*
4146

4247
Quuux.
4348

4449
* [x] Bravo
50+
4551
* [ ] Charlie
52+
4653
* [x] Delta
54+
4755
* [ ] Echo
56+
4857
* [ ] **Foxtrot**
58+
4959
* [ ] **Golf**

test/index.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -166,13 +166,13 @@ test('fixtures', (t) => {
166166

167167
const fromHtml = unified()
168168
.use(rehypeStringify)
169-
// @ts-expect-error: turn into different tree..
170-
.use(() => {
171-
return transformer
172-
function transformer(/** @type {Node} */ tree) {
173-
return toMdast(tree, config)
169+
.use(
170+
/** @type {import('unified').Plugin<void[], import('hast').Root, import('mdast').Root>} */
171+
() => {
172+
// @ts-expect-error: root in -> root out.
173+
return (tree) => toMdast(tree, config)
174174
}
175-
})
175+
)
176176
.use(remarkStringify)
177177

178178
const tree = removePosition(fromHtml.runSync(fromHtml.parse(input)), true)

0 commit comments

Comments
 (0)