Skip to content

Commit e0b9dec

Browse files
committed
parse: Fix bug in handling of trailing blank lines in nested lists
fixes #5
1 parent d40156b commit e0b9dec

File tree

4 files changed

+135
-35
lines changed

4 files changed

+135
-35
lines changed

commonmark-lib/commonmark/private/parse/block.rkt

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,9 @@
106106
;; By default, `add-block!` also closes any unentered containers, which is
107107
;; generally the right choice, since the start of a new block always closes
108108
;; any unentered containers. However, functions like `close-leaf!` and
109-
;; `close-container!` call `add-block!` to close a previously-opened block,
110-
;; which should always be added to the innermost open container, so those
111-
;; functions set #:close-entered? to #f.
109+
;; `close-container!` call `add-block!` as part of the process of closing
110+
;; unentered containers, so they pass `#:close-unentered? #f` to avoid
111+
;; infinite recursion.
112112
(define (add-block! block #:close-unentered? [close-unentered? #t])
113113
(when close-unentered?
114114
(close-unentered-containers!))
@@ -178,30 +178,29 @@
178178
; If there’s an open leaf, we need to take care to close it /before/ we
179179
; remove the open container.
180180
(close-leaf!)
181-
(define new-block
182-
(match (gvector-remove-last! open-containers)
183-
[(o:blockquote blocks)
184-
(blockquote (reverse blocks))]
185-
[(? o:list? open-list)
186-
; see Note [Transfer `end-blank?` to parent lists]
187-
(when (and (o:list-end-blank? open-list)
188-
(not (zero? (gvector-count open-containers))))
189-
(define last-idx (sub1 (gvector-count open-containers)))
190-
(define parent-container (gvector-ref open-containers last-idx))
191-
(when (o:list? parent-container)
192-
(gvector-set! open-containers
193-
last-idx
194-
(struct-copy o:list parent-container
195-
[end-blank? #t]))))
196-
197-
(itemization (reverse (accumulate-list-blockss open-list))
198-
(o:list-style open-list)
199-
(o:list-start-num open-list))]
200-
[(o:footnote-definition blocks label)
201-
(gvector-add! footnote-defns (footnote-definition (reverse blocks) label))
202-
#f]))
203-
(when new-block
204-
(add-block! new-block #:close-unentered? #f)))
181+
(match (gvector-remove-last! open-containers)
182+
[(o:blockquote blocks)
183+
(add-block! (blockquote (reverse blocks)) #:close-unentered? #f)]
184+
185+
[(? o:list? open-list)
186+
(add-block! (itemization (reverse (accumulate-list-blockss open-list))
187+
(o:list-style open-list)
188+
(o:list-start-num open-list))
189+
#:close-unentered? #f)
190+
191+
; See Note [Transfer `end-blank?` to parent lists].
192+
(when (and (o:list-end-blank? open-list)
193+
(not (zero? (gvector-count open-containers))))
194+
(define last-idx (sub1 (gvector-count open-containers)))
195+
(define parent-container (gvector-ref open-containers last-idx))
196+
(when (o:list? parent-container)
197+
(gvector-set! open-containers
198+
last-idx
199+
(struct-copy o:list parent-container
200+
[end-blank? #t]))))]
201+
202+
[(o:footnote-definition blocks label)
203+
(gvector-add! footnote-defns (footnote-definition (reverse blocks) label))]))
205204

206205
(define (unentered-containers?)
207206
(< entered-containers (gvector-count open-containers)))
@@ -959,6 +958,24 @@ inner list, not the outer one. Therefore, to ensure the outer list is properly
959958
marked loose, we must transfer the value of `end-blank?` to any immediately-
960959
enclosing parent list whenever a list is closed.
961960
961+
Note that it is critical that we perform this transfer /after/ we’ve already
962+
added the child list to the parent. If we transferred `end-blank?` to the parent
963+
/before/ adding the child list, we’d accidentally interpret
964+
965+
* foo⏎
966+
* bar⏎
967+
968+
969+
essentially as if it were
970+
971+
* foo⏎
972+
973+
* bar⏎
974+
975+
which would result in the outer list incorrectly being marked loose with no
976+
trailing newline. (This may seem obvious, but we previously got this wrong; see
977+
GitHub issue #5.)
978+
962979
One might wonder whether it’s safe to only do this transferring to immediately-
963980
enclosing lists, as there could be a blockquote in the way:
964981

commonmark-test/tests/commonmark/parse/footnote.rkt

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,8 @@
33
(require commonmark
44
commonmark/struct
55
racket/match
6-
racket/string
7-
rackunit)
8-
9-
(define (md . strs)
10-
(string->document (string-join strs "\n")))
11-
(define (md* . strs)
12-
(define doc (apply md strs))
13-
(list doc (document->xexprs doc)))
6+
rackunit
7+
"../test-util.rkt")
148

159
(check-equal? (md "A paragraph.[^1]"
1610
""
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#lang racket/base
2+
3+
;; Regression test for <https://github.com/lexi-lambda/racket-commonmark/issues/5>
4+
5+
(require commonmark/struct
6+
rackunit
7+
"../test-util.rkt")
8+
9+
(check-equal? (md "* outer"
10+
" * inner"
11+
"")
12+
(document
13+
(list
14+
(itemization
15+
(list
16+
(list
17+
(paragraph "outer")
18+
(itemization (list (list (paragraph "inner"))) 'tight #f)))
19+
'tight
20+
#f))
21+
null))
22+
23+
(check-equal? (md "* outer"
24+
" * inner"
25+
""
26+
"new para")
27+
(document
28+
(list
29+
(itemization
30+
(list
31+
(list
32+
(paragraph "outer")
33+
(itemization (list (list (paragraph "inner"))) 'tight #f)))
34+
'tight
35+
#f)
36+
(paragraph "new para"))
37+
null))
38+
39+
(check-equal? (md "* outer"
40+
" * inner"
41+
""
42+
"> new blockquote")
43+
(document
44+
(list
45+
(itemization
46+
(list
47+
(list
48+
(paragraph "outer")
49+
(itemization (list (list (paragraph "inner"))) 'tight #f)))
50+
'tight
51+
#f)
52+
(blockquote (list (paragraph "new blockquote"))))
53+
null))
54+
55+
(check-equal? (md "* outer 1"
56+
" * middle"
57+
" * inner"
58+
""
59+
"* outer 2")
60+
(document
61+
(list
62+
(itemization
63+
(list
64+
(list
65+
(paragraph "outer 1")
66+
(itemization
67+
(list
68+
(list
69+
(paragraph "middle")
70+
(itemization (list (list (paragraph "inner"))) 'tight #f)))
71+
'tight
72+
#f))
73+
(list (paragraph "outer 2")))
74+
'loose
75+
#f))
76+
'()))
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#lang racket/base
2+
3+
(require commonmark
4+
racket/string)
5+
6+
(provide md md*)
7+
8+
(define (md . strs)
9+
(string->document (string-join strs "\n" #:after-last "\n")))
10+
11+
(define (md* . strs)
12+
(define doc (apply md strs))
13+
(list doc (document->xexprs doc)))

0 commit comments

Comments
 (0)