Description
Similar to #422, it'd be nice if NodeCodeBlock provided an indication of whether a fenced code block has a closing fence:
```rust
fn this_has_a_closing_fence() {}
```
vs
```rust
fn this_does_not() {}
Motivation
I'm working on a markdown editor for a note taking app that lets you edit the rendered markdown similar to Bear or Obsidian. When the user clicks the code block and starts editing, I use sourcepos information to figure out what part of the file needs to change, but I need the sourcepos information for the editable part of the code block (i.e. the code itself) rather than the whole thing. That means I have to read the last line in the reported source position and check if it's a closing fence to know if it's editable or not:
let info_line_idx = sourcepos.start.line;
let mut code_line_idx = info_line_idx + 1;
// "If the end of the containing block (or document) is reached and
// no closing code fence has been found, the code block contains all
// of the lines after the opening code fence until the end of the
// containing block (or document)."
// https://github.github.com/gfm/#fenced-code-blocks
let last_line_idx = sourcepos.end.line;
let last_line_range = self.bounds.source_lines[last_line_idx];
let last_line = &self.buffer[last_line_range];
let last_line_indentation_spaces = last_line.chars().take_while(|&c| c == ' ').count();
let code_block_closed = is_closing_fence(last_line, fence_char, fence_length);
let last_code_line_idx =
if code_block_closed { last_line_idx - 1 } else { last_line_idx };
while code_line_idx <= last_code_line_idx {
let code_line_range = self.bounds.source_lines[code_line_idx];
let code_line_text = &self.buffer[code_line_range];
/* code for drawing the code line omitted */
code_line_idx += 1;
}
Here's how I do that:
// "The closing code fence may be indented up to three spaces, and may be
5D59
// followed only by spaces, which are ignored."
// https://github.github.com/gfm/#fenced-code-blocks
fn is_closing_fence(line: &str, fence_char: &u8, fence_length: usize) -> bool {
let ch = *fence_char as char;
let s = line.trim_end(); // Remove trailing spaces
let mut chars = s.chars();
// Skip up to 3 leading spaces
for _ in 0..3 {
if chars.clone().next() == Some(' ') {
chars.next();
} else {
break;
}
}
// Count matching fence characters
let mut count = 0;
for c in chars {
if c == ch {
count += 1;
if count >= fence_length {
return true;
}
} else {
break;
}
}
false
}
I understand comrak doesn't aim to provide a CST, and I wouldn't mind having this kind of code around my codebase except that I'm not sure it's correct. If there's a situation where comrak considers a line to be a closing code fence and my implementation doesn't or vice versa, my app will probably crash.
I'm open to contributing if this is a change you'll accept. Is this a good first issue?