use crate::{MarkdownIt, Node};
use crate::parser::extset::{InlineRootExt, MarkdownItExt};
use crate::parser::inline::{InlineRule, InlineState, Text};
#[derive(Debug, Default)]
struct CodePairCache<const MARKER: char> {
scanned: bool,
max: Vec<usize>,
}
impl<const MARKER: char> InlineRootExt for CodePairCache<MARKER> {}
#[derive(Debug)]
struct CodePairConfig<const MARKER: char>(fn (usize) -> Node);
impl<const MARKER: char> MarkdownItExt for CodePairConfig<MARKER> {}
pub fn add_with<const MARKER: char>(md: &mut MarkdownIt, f: fn (length: usize) -> Node) {
md.ext.insert(CodePairConfig::<MARKER>(f));
md.inline.add_rule::<CodePairScanner<MARKER>>();
}
#[doc(hidden)]
pub struct CodePairScanner<const MARKER: char>;
impl<const MARKER: char> InlineRule for CodePairScanner<MARKER> {
const MARKER: char = MARKER;
fn run(state: &mut InlineState) -> Option<(Node, usize)> {
let mut chars = state.src[state.pos..state.pos_max].chars();
if chars.next().unwrap() != MARKER { return None; }
if state.trailing_text_get().ends_with(MARKER) { return None; }
let mut pos = state.pos + 1;
while Some(MARKER) == chars.next() {
pos += 1;
}
let backticks = state.inline_ext.get_or_insert_default::<CodePairCache<MARKER>>();
let opener_len = pos - state.pos;
if backticks.scanned && backticks.max[opener_len] <= state.pos {
return None;
}
let mut match_start;
let mut match_end = pos;
while let Some(p) = state.src[match_end..state.pos_max].find(MARKER) {
match_start = match_end + p;
match_end = match_start + 1;
chars = state.src[match_end..state.pos_max].chars();
while Some(MARKER) == chars.next() {
match_end += 1;
}
let closer_len = match_end - match_start;
if closer_len == opener_len {
let mut content = state.src[pos..match_start].to_owned().replace('\n', " ");
if content.starts_with(' ') && content.ends_with(' ') && content.len() > 2 {
content = content[1..content.len() - 1].to_owned();
pos += 1;
match_start -= 1;
}
let f = state.md.ext.get::<CodePairConfig<MARKER>>().unwrap().0;
let mut node = f(opener_len);
let mut inner_node = Node::new(Text { content });
inner_node.srcmap = state.get_map(pos, match_start);
node.children.push(inner_node);
return Some((node, match_end - state.pos));
}
let backticks = state.inline_ext.get_mut::<CodePairCache<MARKER>>().unwrap();
while backticks.max.len() <= closer_len { backticks.max.push(0); }
backticks.max[closer_len] = match_start;
}
let mut backticks = state.inline_ext.get_mut::<CodePairCache<MARKER>>().unwrap();
backticks.scanned = true;
None
}
}