use crate::note_editor::rich_text::RichText;
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
pub enum HeadingLevel {
H1 = 1,
H2,
H3,
H4,
H5,
H6,
}
impl From<pulldown_cmark::HeadingLevel> for HeadingLevel {
fn from(value: pulldown_cmark::HeadingLevel) -> Self {
match value {
pulldown_cmark::HeadingLevel::H1 => HeadingLevel::H1,
pulldown_cmark::HeadingLevel::H2 => HeadingLevel::H2,
pulldown_cmark::HeadingLevel::H3 => HeadingLevel::H3,
pulldown_cmark::HeadingLevel::H4 => HeadingLevel::H4,
pulldown_cmark::HeadingLevel::H5 => HeadingLevel::H5,
pulldown_cmark::HeadingLevel::H6 => HeadingLevel::H6,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum BlockQuoteKind {
Note,
Tip,
Important,
Warning,
Caution,
}
impl From<pulldown_cmark::BlockQuoteKind> for BlockQuoteKind {
fn from(value: pulldown_cmark::BlockQuoteKind) -> Self {
match value {
pulldown_cmark::BlockQuoteKind::Tip => BlockQuoteKind::Tip,
pulldown_cmark::BlockQuoteKind::Note => BlockQuoteKind::Note,
pulldown_cmark::BlockQuoteKind::Warning => BlockQuoteKind::Warning,
pulldown_cmark::BlockQuoteKind::Caution => BlockQuoteKind::Caution,
pulldown_cmark::BlockQuoteKind::Important => BlockQuoteKind::Important,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum ItemKind {
Ordered(u64),
Unordered,
}
#[derive(Clone, Debug, PartialEq)]
pub enum TaskKind {
Checked,
Unchecked,
LooselyChecked,
}
pub type SourceRange<Idx> = std::ops::Range<Idx>;
#[derive(Clone, Debug, PartialEq)]
pub enum Node {
Heading {
level: HeadingLevel,
text: RichText,
source_range: SourceRange<usize>,
},
Paragraph {
text: RichText,
source_range: SourceRange<usize>,
},
CodeBlock {
lang: Option<String>,
text: RichText,
source_range: SourceRange<usize>,
},
BlockQuote {
kind: Option<BlockQuoteKind>,
nodes: Vec<Node>,
source_range: SourceRange<usize>,
},
List {
nodes: Vec<Node>,
source_range: SourceRange<usize>,
},
Item {
kind: ItemKind,
nodes: Vec<Node>,
source_range: SourceRange<usize>,
},
Task {
kind: TaskKind,
nodes: Vec<Node>,
source_range: SourceRange<usize>,
},
}
impl Node {
pub fn source_range(&self) -> &SourceRange<usize> {
match self {
Self::Heading { source_range, .. }
| Self::CodeBlock { source_range, .. }
| Self::Paragraph { source_range, .. }
| Self::List { source_range, .. }
| Self::BlockQuote { source_range, .. }
| Self::Item { source_range, .. }
| Self::Task { source_range, .. } => source_range,
}
}
pub fn set_source_range(&mut self, new_range: SourceRange<usize>) {
match self {
Self::Heading { source_range, .. }
| Self::CodeBlock { source_range, .. }
| Self::Paragraph { source_range, .. }
| Self::List { source_range, .. }
| Self::BlockQuote { source_range, .. }
| Self::Item { source_range, .. }
| Self::Task { source_range, .. } => *source_range = new_range,
}
}
pub fn rich_text(&self) -> Option<RichText> {
match self {
Self::Heading { text, .. }
| Self::Paragraph { text, .. }
| Self::CodeBlock { text, .. } => Some(text.clone()),
_ => None,
}
}
}
pub fn nodes_to_sexp(nodes: &[Node]) -> String {
nodes
.iter()
.map(node_to_sexp)
.collect::<Vec<_>>()
.join("\n")
}
pub fn node_to_sexp(node: &Node) -> String {
match node {
Node::Heading {
level,
text,
source_range,
} => {
format!(
"(heading {:?} @{:?}\n {})",
level,
source_range,
rich_text_to_sexp(text)
)
}
Node::Paragraph { text, source_range } => {
format!(
"(paragraph @{:?}\n {})",
source_range,
rich_text_to_sexp(text)
)
}
Node::BlockQuote {
kind,
nodes,
source_range,
} => {
format!(
"(blockquote {:?} @{:?}\n {})",
kind,
source_range,
nodes_to_sexp(nodes)
)
}
Node::CodeBlock {
lang,
text,
source_range,
} => {
format!(
"(codeblock {} @{:?}\n {})",
lang.clone().unwrap_or_default(),
source_range,
rich_text_to_sexp(text)
)
}
Node::List {
nodes,
source_range,
} => {
format!("(list @{:?}\n {})", source_range, nodes_to_sexp(nodes))
}
Node::Item {
kind,
nodes,
source_range,
} => {
format!(
"(item {:?} @{:?}\n {})",
kind,
source_range,
nodes_to_sexp(nodes)
)
}
Node::Task {
kind,
nodes,
source_range,
} => {
format!(
"(item {:?} @{:?}\n {})",
kind,
source_range,
nodes_to_sexp(nodes)
)
}
}
}
pub fn rich_text_to_sexp(rich_text: &RichText) -> String {
rich_text
.segments()
.iter()
.map(|segment| match &segment.style {
Some(style) => format!("({} \"{}\")", style, segment),
None => format!("\"{}\"", segment),
})
.collect::<Vec<_>>()
.join("\n ")
}