Require space between hash/content in ATX heading (#1140)
While writing some Markdown documentation for Rails, I came across an interesting case where trying to link to an instance method at the start of a line would instead parse as an H1 heading: ```markdown #response_body= ``` Expected: ```html <a href=""><code>#response_body=</code></a> ``` Actual: ```html <h1>response_body=</h1> ``` According to the CommonMark spec: > At least one space or tab is required between the # characters and the > heading’s contents, unless the heading is empty. Note that many > implementations currently do not require the space. However, the space > was required by the original ATX implementation, and it helps prevent > things like the following from being parsed as headings: > > Example 64 So while some implementations do not follow this requirement, I believe RDoc should because it makes it easy to write text similar to Example 64 (which was used in the new test) and it also enables automatically linking to instance methods at the start of a line.
This commit is contained in:
parent
239d54dfbc
commit
d0c17cbd09
@ -1158,7 +1158,7 @@ class RDoc::Markdown
|
|||||||
return _tmp
|
return _tmp
|
||||||
end
|
end
|
||||||
|
|
||||||
# AtxHeading = AtxStart:s @Sp AtxInline+:a (@Sp /#*/ @Sp)? @Newline { RDoc::Markup::Heading.new(s, a.join) }
|
# AtxHeading = AtxStart:s @Spacechar+ AtxInline+:a (@Sp /#*/ @Sp)? @Newline { RDoc::Markup::Heading.new(s, a.join) }
|
||||||
def _AtxHeading
|
def _AtxHeading
|
||||||
|
|
||||||
_save = self.pos
|
_save = self.pos
|
||||||
@ -1169,12 +1169,22 @@ class RDoc::Markdown
|
|||||||
self.pos = _save
|
self.pos = _save
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
_tmp = _Sp()
|
_save1 = self.pos
|
||||||
|
_tmp = _Spacechar()
|
||||||
|
if _tmp
|
||||||
|
while true
|
||||||
|
_tmp = _Spacechar()
|
||||||
|
break unless _tmp
|
||||||
|
end
|
||||||
|
_tmp = true
|
||||||
|
else
|
||||||
|
self.pos = _save1
|
||||||
|
end
|
||||||
unless _tmp
|
unless _tmp
|
||||||
self.pos = _save
|
self.pos = _save
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
_save1 = self.pos
|
_save2 = self.pos
|
||||||
_ary = []
|
_ary = []
|
||||||
_tmp = apply(:_AtxInline)
|
_tmp = apply(:_AtxInline)
|
||||||
if _tmp
|
if _tmp
|
||||||
@ -1187,37 +1197,37 @@ class RDoc::Markdown
|
|||||||
_tmp = true
|
_tmp = true
|
||||||
@result = _ary
|
@result = _ary
|
||||||
else
|
else
|
||||||
self.pos = _save1
|
self.pos = _save2
|
||||||
end
|
end
|
||||||
a = @result
|
a = @result
|
||||||
unless _tmp
|
unless _tmp
|
||||||
self.pos = _save
|
self.pos = _save
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
_save2 = self.pos
|
|
||||||
|
|
||||||
_save3 = self.pos
|
_save3 = self.pos
|
||||||
|
|
||||||
|
_save4 = self.pos
|
||||||
while true # sequence
|
while true # sequence
|
||||||
_tmp = _Sp()
|
_tmp = _Sp()
|
||||||
unless _tmp
|
unless _tmp
|
||||||
self.pos = _save3
|
self.pos = _save4
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
_tmp = scan(/\G(?-mix:#*)/)
|
_tmp = scan(/\G(?-mix:#*)/)
|
||||||
unless _tmp
|
unless _tmp
|
||||||
self.pos = _save3
|
self.pos = _save4
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
_tmp = _Sp()
|
_tmp = _Sp()
|
||||||
unless _tmp
|
unless _tmp
|
||||||
self.pos = _save3
|
self.pos = _save4
|
||||||
end
|
end
|
||||||
break
|
break
|
||||||
end # end sequence
|
end # end sequence
|
||||||
|
|
||||||
unless _tmp
|
unless _tmp
|
||||||
_tmp = true
|
_tmp = true
|
||||||
self.pos = _save2
|
self.pos = _save3
|
||||||
end
|
end
|
||||||
unless _tmp
|
unless _tmp
|
||||||
self.pos = _save
|
self.pos = _save
|
||||||
@ -16539,7 +16549,7 @@ class RDoc::Markdown
|
|||||||
Rules[:_Plain] = rule_info("Plain", "Inlines:a { paragraph a }")
|
Rules[:_Plain] = rule_info("Plain", "Inlines:a { paragraph a }")
|
||||||
Rules[:_AtxInline] = rule_info("AtxInline", "!@Newline !(@Sp /\#*/ @Sp @Newline) Inline")
|
Rules[:_AtxInline] = rule_info("AtxInline", "!@Newline !(@Sp /\#*/ @Sp @Newline) Inline")
|
||||||
Rules[:_AtxStart] = rule_info("AtxStart", "< /\\\#{1,6}/ > { text.length }")
|
Rules[:_AtxStart] = rule_info("AtxStart", "< /\\\#{1,6}/ > { text.length }")
|
||||||
Rules[:_AtxHeading] = rule_info("AtxHeading", "AtxStart:s @Sp AtxInline+:a (@Sp /\#*/ @Sp)? @Newline { RDoc::Markup::Heading.new(s, a.join) }")
|
Rules[:_AtxHeading] = rule_info("AtxHeading", "AtxStart:s @Spacechar+ AtxInline+:a (@Sp /\#*/ @Sp)? @Newline { RDoc::Markup::Heading.new(s, a.join) }")
|
||||||
Rules[:_SetextHeading] = rule_info("SetextHeading", "(SetextHeading1 | SetextHeading2)")
|
Rules[:_SetextHeading] = rule_info("SetextHeading", "(SetextHeading1 | SetextHeading2)")
|
||||||
Rules[:_SetextBottom1] = rule_info("SetextBottom1", "/={1,}/ @Newline")
|
Rules[:_SetextBottom1] = rule_info("SetextBottom1", "/={1,}/ @Newline")
|
||||||
Rules[:_SetextBottom2] = rule_info("SetextBottom2", "/-{1,}/ @Newline")
|
Rules[:_SetextBottom2] = rule_info("SetextBottom2", "/-{1,}/ @Newline")
|
||||||
|
@ -414,10 +414,23 @@ two
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_parse_heading_atx
|
def test_parse_heading_atx
|
||||||
doc = parse "# heading\n"
|
# CommonMark Example 62
|
||||||
|
(1..6).each do |level|
|
||||||
|
doc = parse "#{"#" * level} heading\n"
|
||||||
|
|
||||||
|
expected = @RM::Document.new(
|
||||||
|
@RM::Heading.new(level, "heading"))
|
||||||
|
|
||||||
|
assert_equal expected, doc
|
||||||
|
end
|
||||||
|
|
||||||
|
# CommonMark Example 64
|
||||||
|
doc = parse "#5 bolt\n\n#hashtag\n"
|
||||||
|
|
||||||
expected = @RM::Document.new(
|
expected = @RM::Document.new(
|
||||||
@RM::Heading.new(1, "heading"))
|
para("#5 bolt"),
|
||||||
|
para("#hashtag"),
|
||||||
|
)
|
||||||
|
|
||||||
assert_equal expected, doc
|
assert_equal expected, doc
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user