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**
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
> tinctis sibi; tua fidem aethera. Animosque ferret vultus puellari poteris
> 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
flammas iamque.
```typescript
```ts
var brouterVisualRecycle =
netmaskExbibyteMac +
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
ullis, exuit fatemur humani iustis!
```
```js
var mpeg_reader_modifier = jfs;
if (318464 >= association_thunderbolt_bar) {
copyrightMemoryWep.skinHeaderEmoticon =

View File

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

View File

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

View File

@ -4,8 +4,9 @@ use axum::http::StatusCode;
use chrono::{DateTime, Utc};
use gray_matter::{engine::YAML, Matter};
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 syntect::{highlighting::ThemeSet, html::highlighted_html_for_string, parsing::SyntaxSet};
use tokio::fs;
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 {
let mut options = Options::empty();
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_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 {
/*
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,
src = dest_url,
));
// let picture_markup = format!(
// r#"
// <img
// alt="{alt}"
// src="{src}"
// />"#,
// alt = title,
// src = dest_url,
// );
debug!(
"Image link_type: {:?} url: {} title: {} id: {}",
link_type, dest_url, title, id
@ -147,8 +148,37 @@ pub fn parse_html(markdown: &str, generate_images: bool) -> String {
.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::End(TagEnd::Image) => Event::Html("</figcaption></figure>".into()),
Event::End(TagEnd::CodeBlock) => {
text_kind = TextKind::Text;
Event::End(TagEnd::CodeBlock)
}
_ => event,
});

View File

@ -35,7 +35,7 @@ a {
}
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 {
@ -62,6 +62,14 @@ a {
@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 {

View File

@ -1228,6 +1228,10 @@ a {
margin-left: auto;
margin-right: auto;
}
table {
margin-top: 1rem;
margin-bottom: 1rem;
}
table {
max-width: 70rem;
}
@ -1256,12 +1260,6 @@ a {
font-size: 0.875rem;
line-height: 1.25rem;
}
@media (min-width: 768px) {
table {
margin-top: 1rem;
margin-bottom: 1rem;
}
}
@media (min-width: 768px) {
table {
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 {