use std::cell::RefCell;
use comrak::nodes::{Ast, NodeValue};
use comrak::{parse_document, Arena, Options};
use crate::prelude::*;
pub struct Markdown {}
impl Markdown {
pub fn new<'a>(cx: &'a mut Context, document: &str) -> Handle<'a, Self> {
Self {}
.build(cx, |cx| {
let arena = Arena::new();
let mut options = Options::default();
options.extension.strikethrough = true;
let root = parse_document(&arena, document, &options);
for node in root.children() {
parse_node(cx, node, 0);
}
})
.height(Auto)
}
}
impl View for Markdown {
fn element(&self) -> Option<&'static str> {
Some("markdown")
}
}
fn parse_node<'a>(
cx: &mut Context,
node: &'a comrak::arena_tree::Node<'a, RefCell<Ast>>,
list_level: usize,
) {
match &node.data.borrow().value {
NodeValue::Paragraph => {
Label::rich(cx, "", |cx| {
for child in node.children() {
parse_node(cx, child, list_level);
}
})
.class("p");
}
NodeValue::Heading(heading) => {
Label::rich(cx, "", |cx| {
for child in node.children() {
parse_node(cx, child, list_level);
}
})
.class(match heading.level {
1 => "h1",
2 => "h2",
3 => "h3",
4 => "h4",
5 => "h5",
6 => "h6",
_ => "h6",
});
}
NodeValue::Text(text) => {
TextSpan::new(cx, text, |_| {}).class("span");
}
NodeValue::Emph => {
TextSpan::new(cx, "", |cx| {
for child in node.children() {
parse_node(cx, child, list_level);
}
})
.class("emph");
}
NodeValue::Strong => {
TextSpan::new(cx, "", |cx| {
for child in node.children() {
parse_node(cx, child, list_level);
}
})
.class("strong");
}
NodeValue::Strikethrough => {
TextSpan::new(cx, "", |cx| {
for child in node.children() {
parse_node(cx, child, list_level);
}
})
.class("strikethrough");
}
NodeValue::List(_list) => {
VStack::new(cx, |cx| {
for child in node.children() {
parse_node(cx, child, list_level);
}
})
.height(Auto)
.left(Pixels(20.0));
}
NodeValue::Item(_list) => {
HStack::new(cx, |cx| {
Label::new(cx, "\u{2022} ").width(Auto);
VStack::new(cx, |cx| {
for child in node.children() {
parse_node(cx, child, list_level + 1);
}
})
.height(Auto);
})
.class("li")
.height(Auto);
}
NodeValue::Code(code) => {
TextSpan::new(cx, &code.literal.to_owned(), |_| {}).class("code");
}
NodeValue::CodeBlock(code_block) => {
println!("{:?}", code_block);
let mut code = code_block.literal.to_owned();
code.pop().unwrap();
ScrollView::new(cx, 0.0, 0.0, true, false, |cx| {
Label::new(cx, code).class("code");
})
.height(Auto)
.width(Stretch(1.0));
}
NodeValue::Link(link) => {
let url = link.url.clone();
TextSpan::new(cx, "", |cx| {
for child in node.children() {
parse_node(cx, child, list_level);
}
})
.cursor(CursorIcon::Hand)
.pointer_events(PointerEvents::Auto)
.on_press(move |_| {
open::that(url.as_str()).unwrap();
})
.class("link");
}
NodeValue::SoftBreak => {
TextSpan::new(cx, "\n", |cx| {
for child in node.children() {
parse_node(cx, child, list_level);
}
});
}
_ => {}
}
}
pub struct TextSpan {}
impl TextSpan {
pub fn new<'a>(
cx: &'a mut Context,
text: &str,
children: impl Fn(&mut Context),
) -> Handle<'a, Self> {
Self {}
.build(cx, |cx| {
cx.style.text_span.insert(cx.current(), true);
children(cx);
})
.text(text)
.display(Display::None)
.pointer_events(PointerEvents::None)
}
}
impl View for TextSpan {
fn element(&self) -> Option<&'static str> {
Some("text-span")
}
}