use crate::fence::FenceScanner;
pub fn has_incomplete_code_fence(markdown: &str) -> bool {
let mut scanner = FenceScanner::new();
let bytes = markdown.as_bytes();
let mut line_start = 0usize;
for i in 0..=bytes.len() {
if i == bytes.len() || bytes[i] == b'\n' {
scanner.consume_fence_at_line_start(bytes, line_start);
line_start = i + 1;
}
}
scanner.in_code_block()
}
pub fn has_table(markdown: &str) -> bool {
for line in markdown.split('\n') {
let trimmed = line.trim();
if !trimmed.is_empty() && trimmed.contains('|') && is_table_delimiter(trimmed) {
return true;
}
}
false
}
fn is_table_delimiter(line: &str) -> bool {
let bytes = line.as_bytes();
let mut i = 0;
let len = bytes.len();
if i < len && bytes[i] == b'|' {
i += 1;
}
let mut found_column = false;
loop {
while i < len && (bytes[i] == b' ' || bytes[i] == b'\t') {
i += 1;
}
if i >= len {
break;
}
if bytes[i] == b'|' && !found_column {
return false; }
if bytes[i] == b'|' {
i += 1;
if i >= len {
break;
}
continue;
}
if bytes[i] == b':' {
i += 1;
}
let dash_start = i;
while i < len && bytes[i] == b'-' {
i += 1;
}
if i == dash_start {
return false; }
if i < len && bytes[i] == b':' {
i += 1;
}
while i < len && (bytes[i] == b' ' || bytes[i] == b'\t') {
i += 1;
}
found_column = true;
if i < len && bytes[i] != b'|' {
return false;
}
}
found_column
}
#[cfg(test)]
mod tests {
use super::{has_incomplete_code_fence, has_table};
#[test]
fn detects_incomplete_backtick_fence() {
assert!(has_incomplete_code_fence("```rust\nfn main() {"));
}
#[test]
fn complete_fence_is_not_incomplete() {
assert!(!has_incomplete_code_fence("```rust\nfn main() {}\n```"));
}
#[test]
fn detects_incomplete_tilde_fence() {
assert!(has_incomplete_code_fence("~~~\ncode here"));
}
#[test]
fn closing_fence_must_match_char() {
assert!(has_incomplete_code_fence("```\ncode\n~~~"));
}
#[test]
fn closing_fence_must_be_long_enough() {
assert!(has_incomplete_code_fence("````\ncode\n```"));
}
#[test]
fn closing_fence_equal_length() {
assert!(!has_incomplete_code_fence("```\ncode\n```"));
}
#[test]
fn closing_fence_longer_ok() {
assert!(!has_incomplete_code_fence("```\ncode\n`````"));
}
#[test]
fn no_fence_at_all() {
assert!(!has_incomplete_code_fence("just some text"));
}
#[test]
fn indented_fence() {
assert!(has_incomplete_code_fence(" ```\ncode"));
}
#[test]
fn too_much_indent_is_not_fence() {
assert!(!has_incomplete_code_fence(" ```\ncode"));
}
#[test]
fn no_fence_for_mid_line_backticks() {
assert!(!has_incomplete_code_fence("hello ```\ncode"));
}
#[test]
fn no_fence_for_mid_line_tildes() {
assert!(!has_incomplete_code_fence("hello ~~~\ncode"));
}
#[test]
fn mid_line_fence_on_only_line_is_not_a_fence() {
assert!(!has_incomplete_code_fence("a ```inline fence``` b"));
}
#[test]
fn detects_simple_table() {
assert!(has_table("| a | b |\n| --- | --- |\n| 1 | 2 |"));
}
#[test]
fn detects_aligned_table() {
assert!(has_table("| a | b |\n| :---: | ---: |"));
}
#[test]
fn no_table() {
assert!(!has_table("just some text with | pipes"));
}
#[test]
fn minimal_table_delimiter() {
assert!(has_table("|-|"));
}
}