use super::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Shortcut {
Heading(u8),
Bullet,
Ordered,
Task(bool),
Quote,
Code,
Rule,
}
impl Shortcut {
pub fn apply(self, text: Text) -> BlockKind {
match self {
Self::Heading(level) => BlockKind::Heading { level, text },
Self::Bullet => BlockKind::Bullet(text),
Self::Ordered => BlockKind::Ordered { number: 1, text },
Self::Task(checked) => BlockKind::Task { checked, text },
Self::Quote => BlockKind::Quote { kind: None, text },
Self::Code => BlockKind::Code {
language: None,
code: Text::plain(text.text),
},
Self::Rule => BlockKind::Rule,
}
}
}
pub fn shortcut(text: &str) -> Option<(Shortcut, usize)> {
const PREFIXES: &[(&str, Shortcut)] = &[
("- [ ] ", Shortcut::Task(false)),
("- [x] ", Shortcut::Task(true)),
("###### ", Shortcut::Heading(6)),
("##### ", Shortcut::Heading(5)),
("#### ", Shortcut::Heading(4)),
("### ", Shortcut::Heading(3)),
("## ", Shortcut::Heading(2)),
("# ", Shortcut::Heading(1)),
("- ", Shortcut::Bullet),
("* ", Shortcut::Bullet),
("+ ", Shortcut::Bullet),
("1. ", Shortcut::Ordered),
("> ", Shortcut::Quote),
("```", Shortcut::Code),
("---", Shortcut::Rule),
];
PREFIXES
.iter()
.find(|(prefix, _)| text.starts_with(prefix))
.map(|(prefix, shortcut)| (*shortcut, prefix.len()))
}
pub fn inline_rule(text: &str, caret: usize) -> Option<(Range<usize>, Range<usize>, Mark)> {
let head = text.get(..caret)?;
for (delimiter, mark) in [
("**", Mark::Bold),
("__", Mark::Bold),
("~~", Mark::Strike),
("`", Mark::Code),
("_", Mark::Italic),
("*", Mark::Italic),
] {
let Some(closes) = head.strip_suffix(delimiter) else {
continue;
};
let Some(open) = closes.rfind(delimiter) else {
continue;
};
if matches!(delimiter, "*" | "_") && text[..open].ends_with(delimiter) {
continue;
}
let inner = open + delimiter.len()..closes.len();
let Some(body) = text.get(inner.clone()).filter(|body| !body.is_empty()) else {
continue;
};
if body.starts_with(char::is_whitespace) || body.ends_with(char::is_whitespace) {
continue;
}
if delimiter.starts_with('_')
&& text[..open]
.chars()
.next_back()
.is_some_and(char::is_alphanumeric)
{
continue;
}
return Some((open..open + delimiter.len(), inner, mark));
}
None
}