///| ATX and setext heading parsing (split from block_parser.mbt).
///|
/// `#` to `######` followed by a space, a tab or the end of the line.
fn BlockParser::try_atx_heading(self : BlockParser, container : Node) -> Node? {
let mut i = self.next_nonspace
let mut level = 0
while i < self.line_len && self.line.unsafe_get(i) == '#' && level < 7 {
level = level + 1
i = i + 1
}
guard level >= 1 && level <= 6 else { return None }
guard i >= self.line_len || is_space_or_tab(self.line.unsafe_get(i)) else {
return None
}
let node = self.add_child(
container,
NodeKind::HeadingNode,
self.line_start + self.next_nonspace,
)
node.level = level
node.style = HeadingStyle::Atx
node.end = self.line_end
self.advance_offset(self.next_nonspace + level - self.offset, false)
Some(node)
}
///|
/// Drop the optional closing run of `#`, which has to be preceded by
/// whitespace and followed by nothing but whitespace.
fn chop_trailing_hashes(text : String) -> (String, Int) {
let mut end = text.length()
while end > 0 && is_space_or_tab(text.unsafe_get(end - 1)) {
end = end - 1
}
let mut hash_start = end
while hash_start > 0 && text.unsafe_get(hash_start - 1) == '#' {
hash_start = hash_start - 1
}
let hashes = end - hash_start
if hashes > 0 &&
(hash_start == 0 || is_space_or_tab(text.unsafe_get(hash_start - 1))) {
let mut content_end = hash_start
while content_end > 0 && is_space_or_tab(text.unsafe_get(content_end - 1)) {
content_end = content_end - 1
}
return (text.unsafe_substring(start=0, end=content_end), hashes)
}
(text.unsafe_substring(start=0, end~), 0)
}
///|
/// A line of `=` or `-` under a paragraph turns it into a heading.
fn BlockParser::try_setext_heading(
self : BlockParser,
container : Node,
) -> Bool {
let c = self.peek_line(self.next_nonspace)
guard c == '=' || c == '-' else { return false }
let mut i = self.next_nonspace
while i < self.line_len && self.line.unsafe_get(i) == c {
i = i + 1
}
guard i > self.next_nonspace else { return false }
while i < self.line_len && is_space_or_tab(self.line.unsafe_get(i)) {
i = i + 1
}
guard i >= self.line_len else { return false }
// A paragraph holding nothing but link reference definitions cannot be
// turned into a heading.
guard !is_blank_text(self.strip_link_definitions_dry(container)) else {
return false
}
container.kind = NodeKind::HeadingNode
container.level = if c == '=' { 1 } else { 2 }
container.style = HeadingStyle::Setext
container.end = self.line_end
true
}