Skip to content

Commit c168d42

Browse files
committed
Convert Markdown soft line breaks to <br> tags in HTML output
Replace the gsub in accept_paragraph that collapsed newlines between word characters into spaces. Instead, convert soft line breaks (\n) to <br> tags to produce visible line breaks matching GFM rendering. The RDoc markup parser already converts newlines to spaces at parse time (in RDoc::Markup::Parser#build_paragraph), so only Markdown content has bare \n in paragraph text. Hard breaks (already represented as <br>\n) are preserved as-is via a negative lookbehind in the regex.
1 parent b37955d commit c168d42

File tree

3 files changed

+93
-39
lines changed

3 files changed

+93
-39
lines changed

lib/rdoc/markup/to_html.rb

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -313,10 +313,14 @@ def accept_block_quote(block_quote)
313313
def accept_paragraph(paragraph)
314314
@res << "\n<p>"
315315
text = paragraph.text @hard_break
316-
text = text.gsub(/(#{SPACE_SEPARATED_LETTER_CLASS})?\K\r?\n(?=(?(1)(#{SPACE_SEPARATED_LETTER_CLASS})?))/o) {
317-
defined?($2) && ' '
318-
}
319-
@res << to_html(text)
316+
text = to_html(text)
317+
# Convert soft line breaks (\n) to <br> tags to match GFM rendering.
318+
# The RDoc markup parser already converts newlines to spaces at parse
319+
# time (see RDoc::Markup::Parser#build_paragraph), so only Markdown
320+
# content has bare \n in paragraph text. Hard breaks are already
321+
# represented as <br>\n, so we skip those with a negative lookbehind.
322+
text = text.gsub(/(?<!<br>)\n/, "<br>\n")
323+
@res << text
320324
@res << "</p>\n"
321325
end
322326

test/rdoc/markup/to_html_test.rb

Lines changed: 8 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ def accept_paragraph_br
236236
end
237237

238238
def accept_paragraph_break
239-
assert_equal "\n<p>hello<br> world</p>\n", @to.res.join
239+
assert_equal "\n<p>hello<br>\nworld</p>\n", @to.res.join
240240
end
241241

242242
def accept_paragraph_i
@@ -411,43 +411,17 @@ def test_accept_heading_dedup_resets_on_start_accepting
411411
end
412412

413413
def test_accept_paragraph_newline
414-
hellos = ["hello", "\u{393 3b5 3b9 3ac} \u{3c3 3bf 3c5}"]
415-
worlds = ["world", "\u{3ba 3cc 3c3 3bc 3bf 3c2}"]
416-
ohayo, sekai = %W"\u{304a 306f 3088 3046} \u{4e16 754c}"
417-
418-
hellos.product(worlds) do |hello, world|
419-
@to.start_accepting
420-
@to.accept_paragraph para("#{hello}\n", "#{world}\n")
421-
assert_equal "\n<p>#{hello} #{world}</p>\n", @to.res.join
422-
end
423-
424-
hellos.each do |hello|
425-
@to.start_accepting
426-
@to.accept_paragraph para("#{hello}\n", "#{sekai}\n")
427-
assert_equal "\n<p>#{hello}#{sekai}</p>\n", @to.res.join
428-
end
429-
430-
worlds.each do |world|
431-
@to.start_accepting
432-
@to.accept_paragraph para("#{ohayo}\n", "#{world}\n")
433-
assert_equal "\n<p>#{ohayo}#{world}</p>\n", @to.res.join
434-
end
435-
414+
# Soft line breaks (\n) are converted to <br> tags in HTML output to
415+
# match GFM rendering. The RDoc markup parser already converts newlines
416+
# to spaces at parse time (in parser.rb), so this only affects Markdown
417+
# content where newlines represent GFM soft breaks.
436418
@to.start_accepting
437-
@to.accept_paragraph para("#{ohayo}\n", "#{sekai}\n")
438-
assert_equal "\n<p>#{ohayo}#{sekai}</p>\n", @to.res.join
419+
@to.accept_paragraph para("hello\n", "world\n")
420+
assert_equal "\n<p>hello<br>\nworld<br>\n</p>\n", @to.res.join
439421

440422
@to.start_accepting
441423
@to.accept_paragraph para("+hello+\n", "world\n")
442-
assert_equal "\n<p><code>hello</code> world</p>\n", @to.res.join
443-
444-
@to.start_accepting
445-
@to.accept_paragraph para("hello\n", "+world+\n")
446-
assert_equal "\n<p>hello <code>world</code></p>\n", @to.res.join
447-
448-
@to.start_accepting
449-
@to.accept_paragraph para("+hello+\n", "+world+\n")
450-
assert_equal "\n<p><code>hello</code> <code>world</code></p>\n", @to.res.join
424+
assert_equal "\n<p><code>hello</code><br>\nworld<br>\n</p>\n", @to.res.join
451425
end
452426

453427
def test_accept_heading_output_decoration

test/rdoc/rdoc_markdown_test.rb

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,17 @@ def test_parse_block_quote_continue
7575
assert_equal expected, doc
7676
end
7777

78+
def test_parse_block_quote_continue_html
79+
doc = parse <<-BLOCK_QUOTE
80+
> this is
81+
a block quote
82+
BLOCK_QUOTE
83+
84+
html = doc.accept(RDoc::Markup::ToHtml.new)
85+
86+
assert_include html, "<p>this is<br>\na block quote</p>"
87+
end
88+
7889
def test_parse_block_quote_list
7990
doc = parse <<-BLOCK_QUOTE
8091
> text
@@ -120,12 +131,77 @@ def test_parse_block_quote_separate
120131
expected =
121132
doc(
122133
block(
123-
para("this is\na block quote"),
134+
para("this is\na block quote")),
135+
block(
124136
para("that continues")))
125137

126138
assert_equal expected, doc
127139
end
128140

141+
def test_parse_block_quote_no_lazy_continuation_for_list
142+
doc = parse <<-BLOCK_QUOTE
143+
> foo
144+
- bar
145+
BLOCK_QUOTE
146+
147+
expected =
148+
doc(
149+
block(
150+
para("foo")),
151+
list(:BULLET,
152+
item(nil, para("bar"))))
153+
154+
assert_equal expected, doc
155+
end
156+
157+
def test_parse_block_quote_no_lazy_continuation_for_ordered_list
158+
doc = parse <<-BLOCK_QUOTE
159+
> foo
160+
1. bar
161+
BLOCK_QUOTE
162+
163+
expected =
164+
doc(
165+
block(
166+
para("foo")),
167+
list(:NUMBER,
168+
item(nil, para("bar"))))
169+
170+
assert_equal expected, doc
171+
end
172+
173+
def test_parse_block_quote_no_lazy_continuation_for_heading
174+
doc = parse <<-BLOCK_QUOTE
175+
> foo
176+
# bar
177+
BLOCK_QUOTE
178+
179+
expected =
180+
doc(
181+
block(
182+
para("foo")),
183+
head(1, "bar"))
184+
185+
assert_equal expected, doc
186+
end
187+
188+
def test_parse_block_quote_no_lazy_continuation_for_code_fence
189+
doc = parse <<~BLOCK_QUOTE
190+
> foo
191+
```
192+
code
193+
```
194+
BLOCK_QUOTE
195+
196+
expected =
197+
doc(
198+
block(
199+
para("foo")),
200+
verb("code\n"))
201+
202+
assert_equal expected, doc
203+
end
204+
129205
def test_parse_char_entity
130206
doc = parse '&pi; &nn;'
131207

0 commit comments

Comments
 (0)