diff --git a/_test/extra.txt b/_test/extra.txt
index 5e2386f..f202b13 100644
--- a/_test/extra.txt
+++ b/_test/extra.txt
@@ -639,3 +639,13 @@ b
//= = = = = = = = = = = = = = = = = = = = = = = =//
+
+
+50: Spaces before a visible hard linebreak should be preserved
+//- - - - - - - - -//
+a \
+b
+//- - - - - - - - -//
+
a
+b
+//= = = = = = = = = = = = = = = = = = = = = = = =//
diff --git a/parser/parser.go b/parser/parser.go
index 0276bee..bac0704 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -1115,6 +1115,12 @@ func (p *parser) walkBlock(block ast.Node, cb func(node ast.Node)) {
cb(block)
}
+const (
+ lineBreakHard uint8 = 1 << iota
+ lineBreakSoft
+ lineBreakVisible
+)
+
func (p *parser) parseBlock(block text.BlockReader, parent ast.Node, pc Context) {
if parent.IsRaw() {
return
@@ -1129,27 +1135,25 @@ func (p *parser) parseBlock(block text.BlockReader, parent ast.Node, pc Context)
break
}
lineLength := len(line)
- softLinebreak := false
- hardlineBreak := false
+ var lineBreakFlags uint8 = 0
hasNewLine := line[lineLength-1] == '\n'
if ((lineLength >= 3 && line[lineLength-2] == '\\' && line[lineLength-3] != '\\') || (lineLength == 2 && line[lineLength-2] == '\\')) && hasNewLine { // ends with \\n
lineLength -= 2
- hardlineBreak = true
-
+ lineBreakFlags |= lineBreakHard | lineBreakVisible
} else if ((lineLength >= 4 && line[lineLength-3] == '\\' && line[lineLength-2] == '\r' && line[lineLength-4] != '\\') || (lineLength == 3 && line[lineLength-3] == '\\' && line[lineLength-2] == '\r')) && hasNewLine { // ends with \\r\n
lineLength -= 3
- hardlineBreak = true
+ lineBreakFlags |= lineBreakHard | lineBreakVisible
} else if lineLength >= 3 && line[lineLength-3] == ' ' && line[lineLength-2] == ' ' && hasNewLine { // ends with [space][space]\n
lineLength -= 3
- hardlineBreak = true
+ lineBreakFlags |= lineBreakHard
} else if lineLength >= 4 && line[lineLength-4] == ' ' && line[lineLength-3] == ' ' && line[lineLength-2] == '\r' && hasNewLine { // ends with [space][space]\r\n
lineLength -= 4
- hardlineBreak = true
+ lineBreakFlags |= lineBreakHard
} else if hasNewLine {
// If the line ends with a newline character, but it is not a hardlineBreak, then it is a softLinebreak
// If the line ends with a hardlineBreak, then it cannot end with a softLinebreak
// See https://spec.commonmark.org/0.30/#soft-line-breaks
- softLinebreak = true
+ lineBreakFlags |= lineBreakSoft
}
l, startPosition := block.Position()
@@ -1213,11 +1217,14 @@ func (p *parser) parseBlock(block text.BlockReader, parent ast.Node, pc Context)
continue
}
diff := startPosition.Between(currentPosition)
- stop := diff.Stop
- rest := diff.WithStop(stop)
- text := ast.NewTextSegment(rest.TrimRightSpace(source))
- text.SetSoftLineBreak(softLinebreak)
- text.SetHardLineBreak(hardlineBreak)
+ var text *ast.Text
+ if lineBreakFlags&(lineBreakHard|lineBreakVisible) == lineBreakHard|lineBreakVisible {
+ text = ast.NewTextSegment(diff)
+ } else {
+ text = ast.NewTextSegment(diff.TrimRightSpace(source))
+ }
+ text.SetSoftLineBreak(lineBreakFlags&lineBreakSoft != 0)
+ text.SetHardLineBreak(lineBreakFlags&lineBreakHard != 0)
parent.AppendChild(parent, text)
block.AdvanceLine()
}