///| Fenced and indented code-block parsing.
///|
/// Try to parse fenced code block
fn BlockParser::try_parse_fenced_code(
self : BlockParser,
start : Int,
indent : Int,
) -> Block? {
if indent > 3 {
return None
}
let leading = self.read_leading_trivia()
let fence_char = match self.scanner.peek() {
Some(c) if c == '`' || c == '~' => c
_ => return None
}
let fence_marker = if fence_char == '`' {
FenceMarker::Backtick
} else {
FenceMarker::Tilde
}
let fence_length = self.scanner.count_char(fence_char)
if fence_length < 3 {
return None
}
self.scanner.advance(fence_length)
// Read info string (language + optional filename)
let _ = self.scanner.skip_spaces()
let info = self.scanner.read_line().trim_end(chars=" \t").to_owned()
// Skip newline
if char_is(self.scanner.peek(), '\n') {
self.scanner.advance(1)
}
// Read code content until closing fence
let code_buf = StringBuilder::new()
while !self.scanner.is_eof() {
let line_indent = self.scanner.count_leading_spaces()
// Check for closing fence
if line_indent <= 3 {
let saved = self.scanner.pos
let _ = self.scanner.skip_spaces()
let closing_count = self.scanner.count_char(fence_char)
if closing_count >= fence_length {
// Found closing fence
self.scanner.advance(closing_count)
let _ = self.scanner.skip_spaces()
if char_is(self.scanner.peek(), '\n') || self.scanner.is_eof() {
if char_is(self.scanner.peek(), '\n') {
self.scanner.advance(1)
}
break
}
}
self.scanner.restore(saved)
}
// Read code line (remove up to `indent` leading spaces)
self.scanner.append_line_stripped_indent_to(code_buf, indent)
if char_is(self.scanner.peek(), '\n') {
code_buf.write_char('\n')
self.scanner.advance(1)
}
}
let trailing = Trivia::empty()
Some(
Block::FencedCode(
fence_marker~,
fence_length~,
info~,
code=code_buf.to_string(),
indent~,
span=Span::new(start, self.scanner.pos),
leading_trivia=leading,
trailing_trivia=trailing,
),
)
}
///|
/// Try to parse indented code block
fn BlockParser::try_parse_indented_code(
self : BlockParser,
start : Int,
) -> Block? {
let leading = Trivia::empty()
let code_buf = StringBuilder::new()
while !self.scanner.is_eof() {
let line_start = self.scanner.pos
// Check if line starts with 4+ spaces
let indent = self.scanner.count_leading_spaces()
if indent < 4 && !self.scanner.is_blank_line() {
self.scanner.restore(line_start)
break
}
if self.scanner.is_blank_line() {
code_buf.write_char('\n')
self.scanner.skip_line()
} else {
// Skip 4 spaces
self.scanner.advance(4)
self.scanner.append_line_to(code_buf)
if char_is(self.scanner.peek(), '\n') {
code_buf.write_char('\n')
self.scanner.advance(1)
}
}
}
let code = code_buf.to_string().trim_end(chars="\n").to_owned()
if code.is_empty() {
return None
}
Some(
Block::IndentedCode(
code~,
span=Span::new(start, self.scanner.pos),
leading_trivia=leading,
trailing_trivia=Trivia::empty(),
),
)
}