use super::sink::InlineSink;
use crate::syntax::SyntaxKind;
pub(crate) fn try_parse_substitution(text: &str) -> Option<(usize, usize)> {
let bytes = text.as_bytes();
if bytes.first() != Some(&b'{') || bytes.get(1) != Some(&b'{') {
return None;
}
if bytes.get(2) == Some(&b'<') {
return None;
}
let close = text.find("}}")?;
if close < 2 {
return None;
}
let inner = &text[2..close];
if inner.trim().is_empty() {
return None;
}
Some((close + 2, close - 2))
}
pub(crate) fn emit_substitution(builder: &mut impl InlineSink, text: &str, inner_len: usize) {
builder.start_node(SyntaxKind::MYST_SUBSTITUTION.into());
builder.token(SyntaxKind::TEXT.into(), "{{");
if inner_len > 0 {
builder.token(
SyntaxKind::MYST_SUBSTITUTION_NAME.into(),
&text[2..2 + inner_len],
);
}
builder.token(SyntaxKind::TEXT.into(), "}}");
builder.finish_node();
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic_substitution() {
let (total, inner) = try_parse_substitution("{{ version }}").unwrap();
assert_eq!(total, "{{ version }}".len());
assert_eq!(inner, " version ".len());
}
#[test]
fn substitution_with_filter() {
let src = "{{ name | upper }}";
let (total, inner) = try_parse_substitution(src).unwrap();
assert_eq!(total, src.len());
assert_eq!(&src[2..2 + inner], " name | upper ");
}
#[test]
fn rejects_shortcode_and_empty() {
assert!(try_parse_substitution("{{< meta >}}").is_none());
assert!(try_parse_substitution("{{ }}").is_none());
assert!(try_parse_substitution("{ not }").is_none());
assert!(try_parse_substitution("{{ unterminated").is_none());
}
}