This commit is contained in:
thek4n 2026-07-13 17:07:29 +03:00
parent 4895160309
commit 48daaa3748
2 changed files with 112 additions and 12 deletions

View File

@ -1,15 +1,21 @@
use syntect::highlighting::ThemeSet; use syntect::highlighting::ThemeSet;
use syntect::parsing::SyntaxSet; use syntect::parsing::SyntaxSet;
use pulldown_cmark::{Event, Options, Parser, Tag, TagEnd, TextMergeStream}; use pulldown_cmark::{CodeBlockKind, BlockQuoteKind, Event, Options, Parser, Tag, TagEnd, TextMergeStream, CowStr, LinkType};
pub fn markdown_to_html(markdown: &str, ss: &SyntaxSet, ts: &ThemeSet, _file_path: &str) -> String { pub fn markdown_to_html(markdown: &str, ss: &SyntaxSet, ts: &ThemeSet, _file_path: &str) -> String {
let mut options = Options::empty(); let mut options = Options::empty();
options.insert(Options::ENABLE_TASKLISTS);
options.insert(Options::ENABLE_TABLES); options.insert(Options::ENABLE_TABLES);
options.insert(Options::ENABLE_DEFINITION_LIST); options.insert(Options::ENABLE_FOOTNOTES);
options.insert(Options::ENABLE_HEADING_ATTRIBUTES);
options.insert(Options::ENABLE_STRIKETHROUGH); options.insert(Options::ENABLE_STRIKETHROUGH);
options.insert(Options::ENABLE_TASKLISTS);
options.insert(Options::ENABLE_HEADING_ATTRIBUTES);
options.insert(Options::ENABLE_DEFINITION_LIST);
options.insert(Options::ENABLE_MATH);
options.insert(Options::ENABLE_GFM);
let mut in_code_block = false;
let mut code_content = String::new();
let mut html_output = String::new(); let mut html_output = String::new();
@ -18,29 +24,48 @@ pub fn markdown_to_html(markdown: &str, ss: &SyntaxSet, ts: &ThemeSet, _file_pat
for event in iterator { for event in iterator {
match event { match event {
Event::Start(tag) => { Event::Start(tag) => {
if let Tag::CodeBlock(_) = tag {
in_code_block = true;
code_content.clear();
}
html_output.push_str(render_start_tag(tag).as_str()); html_output.push_str(render_start_tag(tag).as_str());
}, },
Event::End(tag) => { Event::End(tag) => {
if let TagEnd::CodeBlock = tag {
in_code_block = false;
html_output.push_str(escape_html(code_content.as_str()).as_str());
}
html_output.push_str(render_end_tag(tag).as_str()); html_output.push_str(render_end_tag(tag).as_str());
}, },
Event::Text(text) => { Event::Text(text) => {
html_output.push_str(text.to_string().as_str()); if in_code_block {
code_content.push_str(&text);
} else {
html_output.push_str(&escape_html(&text));
}
}, },
Event::TaskListMarker(checked) => { Event::TaskListMarker(checked) => {
if checked { if checked {
html_output.push_str("<input type=\"checkbox\" checked></input>"); html_output.push_str("<input type=\"checkbox\" checked/>");
} else { } else {
html_output.push_str("<input type=\"checkbox\"></input>"); html_output.push_str("<input type=\"checkbox\"/>");
} }
}, },
Event::Code(code) => { Event::Code(code) => {
html_output.push_str(format!("<code>{}</code>", code.to_string()).as_str()); html_output.push_str(format!("<code>{}</code>", escape_html(&code)).as_str());
}, },
Event::SoftBreak => html_output.push_str("<wbr>"),
Event::Rule => html_output.push_str("<hr>"),
Event::InlineHtml(_) => (),
Event::Html(_) => (),
other => println!("[*] Event {:?}", other), other => println!("[*] Event {:?}", other),
} }
} }
println!("{}", html_output);
html_output html_output
} }
@ -56,7 +81,19 @@ fn render_start_tag(tag: Tag<'_>) -> String {
Tag::List(Some(_)) => output.push_str("<ol>"), Tag::List(Some(_)) => output.push_str("<ol>"),
Tag::List(None) => output.push_str("<ul>"), Tag::List(None) => output.push_str("<ul>"),
Tag::Item => output.push_str("<li>"), Tag::Item => output.push_str("<li>"),
_ => println!("* [Tag] {:?}", tag), Tag::Paragraph => output.push_str("<p>"),
Tag::CodeBlock(kind) => output.push_str(render_codeblock(kind).as_str()),
Tag::Emphasis => output.push_str("<em>"),
Tag::Strong => output.push_str("<strong>"),
Tag::BlockQuote(kind) => output.push_str(render_blockquote(kind).as_str()),
Tag::Link{link_type, dest_url, title, ..} => output.push_str(render_link(link_type, dest_url, title).as_str()),
Tag::Table(alignments) => output.push_str("<table>"),
Tag::TableHead => output.push_str("<thead>"),
Tag::TableCell => output.push_str("<td>"),
Tag::TableRow => output.push_str("<tr>"),
other => println!("* [Tag] {:?}", other),
} }
output output
@ -74,9 +111,68 @@ fn render_end_tag(tag: TagEnd) -> String {
TagEnd::List(true) => output.push_str("</ol>"), TagEnd::List(true) => output.push_str("</ol>"),
TagEnd::List(false) => output.push_str("</ul>"), TagEnd::List(false) => output.push_str("</ul>"),
TagEnd::Item => output.push_str("</li>"), TagEnd::Item => output.push_str("</li>"),
_ => println!("* [TagEnd] {:?}", tag), TagEnd::Paragraph => output.push_str("</p>"),
TagEnd::CodeBlock => output.push_str("</code></pre>"),
TagEnd::Emphasis => output.push_str("</em>"),
TagEnd::Strong => output.push_str("</strong>"),
TagEnd::BlockQuote(_) => output.push_str("</blockquote>"),
TagEnd::Link => output.push_str("</a>"),
TagEnd::Table => output.push_str("</table>"),
TagEnd::TableHead => output.push_str("</thead>"),
TagEnd::TableCell => output.push_str("</td>"),
TagEnd::TableRow => output.push_str("</tr>"),
other => println!("* [TagEnd] {:?}", other),
} }
println!("* [TagEnd] {:?}", tag);
output output
} }
fn render_codeblock(block_kind: CodeBlockKind<'_>) -> String {
match block_kind {
CodeBlockKind::Indented => "<pre><code>".to_string(),
CodeBlockKind::Fenced(code_type) => format!("<pre><code class=\"language-{}\">", code_type),
}
}
fn render_blockquote(quote_kind: Option<BlockQuoteKind>) -> String {
match quote_kind {
None => {
"<blockquote>".to_string()
},
Some(kind) => {
match kind {
BlockQuoteKind::Note => "<blockquote><div class=\"markdown-alert markdown-alert-note\"><p class=\"markdown-alert-title\">Note</p>".to_string(),
BlockQuoteKind::Tip => "<blockquote><div class=\"markdown-alert markdown-alert-tip\"><p class=\"markdown-alert-title\">Tip</p>".to_string(),
BlockQuoteKind::Important =>"<blockquote><div class=\"markdown-alert markdown-alert-important\"><p class=\"markdown-alert-title\">Important</p>".to_string(),
BlockQuoteKind::Warning => "<blockquote><div class=\"markdown-alert markdown-alert-warning\"><p class=\"markdown-alert-title\">Warning</p>".to_string(),
BlockQuoteKind::Caution => "<blockquote><div class=\"markdown-alert markdown-alert-caution\"><p class=\"markdown-alert-title\">Caution</p>".to_string(),
}
},
}
}
fn render_link(link_type: LinkType, dest_url: CowStr<'_>, title: CowStr<'_>) -> String {
match link_type {
LinkType::Inline => format!("<a href=\"{}\" title=\"{}\">", dest_url, title),
LinkType::Reference => format!("<a href=\"{}\" title=\"{}\">", dest_url, title),
LinkType::ReferenceUnknown => format!("<a href=\"{}\" title=\"{}\">", dest_url, title),
LinkType::Collapsed => format!("<a href=\"{}\" title=\"{}\">", dest_url, title),
LinkType::CollapsedUnknown => format!("<a href=\"{}\" title=\"{}\">", dest_url, title),
LinkType::Shortcut => format!("<a href=\"{}\" title=\"{}\">", dest_url, title),
LinkType::ShortcutUnknown => format!("<a href=\"{}\" title=\"{}\">", dest_url, title),
LinkType::Autolink => format!("<a href=\"{}\" title=\"{}\">", dest_url, title),
LinkType::Email => format!("<a href=\"{}\" title=\"{}\">", dest_url, title),
LinkType::WikiLink{has_pothole: _} => { // TODO
format!("<a href=\"{}\" title=\"{}\">", dest_url, title)
},
}
}
fn escape_html(text: &str) -> String {
text.replace('&', "&amp;")
.replace('<', "&lt;")
.replace('>', "&gt;")
.replace('"', "&quot;")
.replace('\'', "&#39;")
}

View File

@ -5,6 +5,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Note{% endblock %}</title> <title>{% block title %}Note{% endblock %}</title>
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<style> <style>
body { body {
background-color: #121212; background-color: #121212;
@ -209,6 +211,8 @@
theme: 'dark', theme: 'dark',
securityLevel: 'loose', securityLevel: 'loose',
}); });
hljs.highlightAll();
</script> </script>
</body> </body>
</html> </html>