diff --git a/pkg/md/md.go b/pkg/md/md.go index 69ba340c..2ca215e6 100644 --- a/pkg/md/md.go +++ b/pkg/md/md.go @@ -136,6 +136,12 @@ func (p *blockParser) render() { continue } } + if len(newContainers) == 0 && len(p.paragraph) == 0 && + (p.lastContainerIs(bulletItem) || p.lastContainerIs(orderedItem)) { + if p.containers[len(p.containers)-1].blankFirst { + p.popLastContainer() + } + } p.popParagraph(len(p.containers)) } else if thematicBreakRegexp.MatchString(line) { p.popParagraph(matchedContainers) @@ -209,13 +215,18 @@ func matchContinuationMarkers(line string, containers []container) (string, int) return line, len(containers) } -var containerStartingMarkerRegexp = regexp.MustCompile( - // Capture groups: - // 1. blockquote marker - // 2. bullet item punctuation - // 3. ordered item start index - // 4. ordered item punctuation - `^ {0,3}(?:(> ?)|([-+*]) {1,4}|([0-9]{1,9})([.)]) {1,4})`) +var ( + containerStartingMarkerRegexp = regexp.MustCompile( + // Capture groups: + // 1. blockquote marker + // 2. bullet item punctuation + // 3. ordered item start index + // 4. ordered item punctuation + `^ {0,3}(?:(> ?)|([-+*]) {1,4}|([0-9]{1,9})([.)]) {1,4})`) + itemStartingMarkerBlankLineRegexp = regexp.MustCompile( + // Capture groups are the same, with group 1 always empty. + `^ {0,3}(?:()([-+*])|([0-9]{1,9})([.)]))[ \t]*$`) +) // Parses starting markers of container blocks. Returns the line after removing // all starting markers and new containers to create. @@ -224,6 +235,11 @@ func parseStartingMarkers(line string, newParagraph bool) (string, []container) // Don't parse thematic breaks like "- - - " as three bullets. for !thematicBreakRegexp.MatchString(line) { m := containerStartingMarkerRegexp.FindStringSubmatch(line) + blankFirst := false + if m == nil && newParagraph { + m = itemStartingMarkerBlankLineRegexp.FindStringSubmatch(line) + blankFirst = true + } if m == nil { break } @@ -237,6 +253,7 @@ func parseStartingMarkers(line string, newParagraph bool) (string, []container) indent = len(strings.TrimRight(marker, " \t")) + 1 } c.indent = strings.Repeat(" ", indent) + c.blankFirst = blankFirst if bulletPunct != "" { c.typ = bulletItem c.punct = bulletPunct[0] @@ -436,10 +453,11 @@ func (s *lineSplitter) backup() { } type container struct { - typ containerType - punct byte - start int - indent string + typ containerType + punct byte + start int + indent string + blankFirst bool } type containerType uint8 diff --git a/pkg/md/md_test.go b/pkg/md/md_test.go index 43ff59f9..372340bd 100644 --- a/pkg/md/md_test.go +++ b/pkg/md/md_test.go @@ -67,6 +67,14 @@ var additionalCases = []testCase{ `, Name: "Code fence supplemental/Empty line in list item", }, + { + Name: "List items supplemental/Two leading empty lines with spaces", + Markdown: "- \n \na", HTML: `
a
+`, + }, } func init() { @@ -159,7 +167,6 @@ func combineHTMLTagPairs(p TagPair, more ...TagPair) TagPair { var ( linkRef = regexp.MustCompile(`(^|\n) {0,3}\[([^\\\[\]]|\\[\\\[\]])+\]:`) indentedCodeBlock = regexp.MustCompile("(^|\n)[ >]*( )") - emptyListItem = regexp.MustCompile(`(^|\n)([-+*]|[0-9]{1,9}[.)])(\n|$)`) ) func TestRender(t *testing.T) { @@ -181,9 +188,6 @@ func TestRender(t *testing.T) { if linkRef.MatchString(tc.Markdown) { t.Skipf("Link reference not supported") } - if emptyListItem.MatchString(tc.Markdown) { - t.Skipf("Empty list item not supported") - } got := Render(tc.Markdown, htmlSyntax) want := loosifyLists(tc.HTML)