Code highlighting

This commit is contained in:
Michal Vanko 2024-09-11 13:15:00 +02:00
parent a9ef5c8f93
commit 4305da1d0c
6 changed files with 138 additions and 23 deletions

View File

@ -27,6 +27,16 @@ similis: caput te prodere disceditis: quinque: an et in, accipis divitior talia?
Per deducit ademi, _sub_ qvem orbatura Pindo te manus verbaque **tuorum nati** Per deducit ademi, _sub_ qvem orbatura Pindo te manus verbaque **tuorum nati**
vivere, an me detectique est. Decoram erat mediaque auras. vivere, an me detectique est. Decoram erat mediaque auras.
`sh ./oneliner.sh`
This is a paragraph with a `reference to code` inside.
This is a paragraph with a `reference to code` inside.
This is a paragraph with a `reference to code` inside.
This is a paragraph with a `reference to code` inside.
This is a paragraph with a `reference to code` inside.
This is a paragraph with a `reference to code` inside.
This is a paragraph with a `reference to code` inside.
> Leti ensis mihi torquere fiducia me sunt nec prima caeli quaeras et coma > Leti ensis mihi torquere fiducia me sunt nec prima caeli quaeras et coma
> tinctis sibi; tua fidem aethera. Animosque ferret vultus puellari poteris > tinctis sibi; tua fidem aethera. Animosque ferret vultus puellari poteris
> florilegae ignes crevisse ad pulvere recenti, luce male; neque nec! > florilegae ignes crevisse ad pulvere recenti, luce male; neque nec!
@ -51,7 +61,7 @@ praemia pariter exaestuat fecerat. Haemonio quem: _in_ sibi spectans parmam,
tetenderat filia ait quo calcitrat at vides, cui iuvenem rerum erat. Eminus tetenderat filia ait quo calcitrat at vides, cui iuvenem rerum erat. Eminus
flammas iamque. flammas iamque.
```typescript ```ts
var brouterVisualRecycle = var brouterVisualRecycle =
netmaskExbibyteMac + netmaskExbibyteMac +
download(twitter_serp_yobibyte, backlinkDirectBandwidth, hot) download(twitter_serp_yobibyte, backlinkDirectBandwidth, hot)
@ -73,7 +83,7 @@ Erat Iunonis pennis lugubris, vixque nec quo tua lacrimarum nubila nobiscum.
Ferrum inhaeret ille; operi in Theseus contingere fateri, mirabatur, consequar Ferrum inhaeret ille; operi in Theseus contingere fateri, mirabatur, consequar
ullis, exuit fatemur humani iustis! ullis, exuit fatemur humani iustis!
``` ```js
var mpeg_reader_modifier = jfs; var mpeg_reader_modifier = jfs;
if (318464 >= association_thunderbolt_bar) { if (318464 >= association_thunderbolt_bar) {
copyrightMemoryWep.skinHeaderEmoticon = copyrightMemoryWep.skinHeaderEmoticon =

View File

@ -23,6 +23,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
image = "0.25.2" image = "0.25.2"
anyhow = "1.0.86" anyhow = "1.0.86"
rayon = "1.10.0" rayon = "1.10.0"
syntect = "5.2.0"
[build] [build]
rustflags = ["-Z", "threads=8"] rustflags = ["-Z", "threads=8"]

View File

@ -48,9 +48,6 @@ async fn main() {
axum::serve(listener, app).await.unwrap(); axum::serve(listener, app).await.unwrap();
} }
// TODO Display blog posts
// TODO Markdown notes
// TODO code snippets highlighting
// TODO responsive design // TODO responsive design
// TODO Colors // TODO Colors
// TODO go live pipeline // TODO go live pipeline

View File

@ -4,8 +4,9 @@ use axum::http::StatusCode;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use gray_matter::{engine::YAML, Matter}; use gray_matter::{engine::YAML, Matter};
use image::image_dimensions; use image::image_dimensions;
use pulldown_cmark::{Event, Options, Parser, Tag, TagEnd}; use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag, TagEnd};
use serde::{de::DeserializeOwned, Deserialize, Deserializer}; use serde::{de::DeserializeOwned, Deserialize, Deserializer};
use syntect::{highlighting::ThemeSet, html::highlighted_html_for_string, parsing::SyntaxSet};
use tokio::fs; use tokio::fs;
use tracing::debug; use tracing::debug;
@ -68,6 +69,11 @@ pub async fn parse_post<'de, Metadata: DeserializeOwned>(
}) })
} }
enum TextKind {
Text,
Code(String),
}
pub fn parse_html(markdown: &str, generate_images: bool) -> String { pub fn parse_html(markdown: &str, generate_images: bool) -> String {
let mut options = Options::empty(); let mut options = Options::empty();
options.insert(Options::ENABLE_TABLES); options.insert(Options::ENABLE_TABLES);
@ -77,6 +83,11 @@ pub fn parse_html(markdown: &str, generate_images: bool) -> String {
options.insert(Options::ENABLE_SMART_PUNCTUATION); options.insert(Options::ENABLE_SMART_PUNCTUATION);
options.insert(Options::ENABLE_HEADING_ATTRIBUTES); options.insert(Options::ENABLE_HEADING_ATTRIBUTES);
let mut text_kind = TextKind::Text;
let syntax_set = SyntaxSet::load_defaults_newlines();
let theme_set = ThemeSet::load_defaults();
let theme = theme_set.themes.get("InspiredGitHub").unwrap();
let parser = Parser::new_ext(markdown, options).map(|event| match event { let parser = Parser::new_ext(markdown, options).map(|event| match event {
/* /*
Parsing images considers `alt` attribute as inner `Text` event Parsing images considers `alt` attribute as inner `Text` event
@ -123,16 +134,6 @@ pub fn parse_html(markdown: &str, generate_images: bool) -> String {
alt = title, alt = title,
src = dest_url, src = dest_url,
)); ));
// let picture_markup = format!(
// r#"
// <img
// alt="{alt}"
// src="{src}"
// />"#,
// alt = title,
// src = dest_url,
// );
debug!( debug!(
"Image link_type: {:?} url: {} title: {} id: {}", "Image link_type: {:?} url: {} title: {} id: {}",
link_type, dest_url, title, id link_type, dest_url, title, id
@ -147,8 +148,37 @@ pub fn parse_html(markdown: &str, generate_images: bool) -> String {
.into(), .into(),
) )
} }
Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(lang))) => {
text_kind = TextKind::Code(lang.to_string());
Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(lang)))
}
Event::Text(text) => match &text_kind {
TextKind::Code(lang) => {
// TODO Check https://github.com/trishume/syntect/pull/535 for typescript support
let lang = if ["ts".to_string(), "typescript".to_string()].contains(lang) {
"javascript"
} else {
lang
};
let syntax_reference = syntax_set
.find_syntax_by_token(lang)
.unwrap_or(syntax_set.find_syntax_plain_text());
syntax_set.syntaxes().iter().for_each(|sr| {
debug!("{}", sr.name);
});
let highlighted =
highlighted_html_for_string(&text, &syntax_set, syntax_reference, theme)
.unwrap();
Event::Html(highlighted.into())
}
_ => Event::Text(text),
},
Event::Start(_) => event, Event::Start(_) => event,
Event::End(TagEnd::Image) => Event::Html("</figcaption></figure>".into()), Event::End(TagEnd::Image) => Event::Html("</figcaption></figure>".into()),
Event::End(TagEnd::CodeBlock) => {
text_kind = TextKind::Text;
Event::End(TagEnd::CodeBlock)
}
_ => event, _ => event,
}); });

View File

@ -35,7 +35,7 @@ a {
} }
table { table {
@apply text-sm mx-auto max-w-image table-auto border-collapse border-spacing-12 border border-gray-200 rounded md:text-base md:my-4 lg:text-xl lg:my-8; @apply text-sm mx-auto my-4 max-w-image table-auto border-collapse border-spacing-12 border border-gray-200 rounded md:text-base lg:text-xl lg:my-8;
} }
thead { thead {
@ -62,6 +62,14 @@ a {
@apply my-2 md:my-4 text-slate-600 max-w-note; @apply my-2 md:my-4 text-slate-600 max-w-note;
} }
} }
:not(pre) code {
@apply text-pink-950 rounded border border-blue-300 px-1 py-0.5 bg-blue-100 text-sm md:text-base lg:text-xl;
}
pre code pre {
@apply mx-2 rounded lg:mx-auto lg:text-lg shadow-sm lg:max-w-note;
}
} }
.video-embed { .video-embed {

View File

@ -1228,6 +1228,10 @@ a {
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
} }
table {
margin-top: 1rem;
margin-bottom: 1rem;
}
table { table {
max-width: 70rem; max-width: 70rem;
} }
@ -1256,12 +1260,6 @@ a {
font-size: 0.875rem; font-size: 0.875rem;
line-height: 1.25rem; line-height: 1.25rem;
} }
@media (min-width: 768px) {
table {
margin-top: 1rem;
margin-bottom: 1rem;
}
}
@media (min-width: 768px) { @media (min-width: 768px) {
table { table {
font-size: 1rem; font-size: 1rem;
@ -1371,6 +1369,77 @@ a {
} }
} }
} }
:not(pre) code {
border-radius: 0.25rem;
}
:not(pre) code {
border-width: 1px;
}
:not(pre) code {
--tw-border-opacity: 1;
border-color: rgb(147 197 253 / var(--tw-border-opacity));
}
:not(pre) code {
--tw-bg-opacity: 1;
background-color: rgb(219 234 254 / var(--tw-bg-opacity));
}
:not(pre) code {
padding-left: 0.25rem;
padding-right: 0.25rem;
}
:not(pre) code {
padding-top: 0.125rem;
padding-bottom: 0.125rem;
}
:not(pre) code {
font-size: 0.875rem;
line-height: 1.25rem;
}
:not(pre) code {
--tw-text-opacity: 1;
color: rgb(80 7 36 / var(--tw-text-opacity));
}
@media (min-width: 768px) {
:not(pre) code {
font-size: 1rem;
line-height: 1.5rem;
}
}
@media (min-width: 1024px) {
:not(pre) code {
font-size: 1.25rem;
line-height: 1.75rem;
}
}
pre code pre {
margin-left: 0.5rem;
margin-right: 0.5rem;
}
pre code pre {
border-radius: 0.25rem;
}
pre code pre {
--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
@media (min-width: 1024px) {
pre code pre {
margin-left: auto;
margin-right: auto;
}
}
@media (min-width: 1024px) {
pre code pre {
max-width: 60rem;
}
}
@media (min-width: 1024px) {
pre code pre {
font-size: 1.125rem;
line-height: 1.75rem;
}
}
} }
.video-embed { .video-embed {