2024-06-03
I've used Hexo for five years and am unhappy with it. Here are some reasons why:
index.md
and other resources.prism.js
and syntect
are slightly better, but still not as good as what an IDE would display. I tried tree-sitter-highlight
to generate HTML and found it also fails to provide good highlighting without LSP information. Code is important for tech blogs and it's worth a better display. Eventually, it turns out that Shiki
is the better option. It can be easily integrated into Zola via CDN or as a hexo plugin.I would show the highlighting result of each library, sorted by preference score (higher the better), in ascending order. Due to the fact that not all themes are shared across libraries, I chosed monokai
, one of the few theme that's available across all libraries, despite that it doesn't look good within the color scheme of this blog.
pub mod graphics;
pub mod theme;
use crate::theme::Theme;
use std::fs;
use std::fs::File;
use std::io::Write;
use toml::{Table, Value};
use tree_sitter_highlight::{Highlight, HighlightConfiguration, Highlighter, HtmlRenderer};
fn main() -> Result<(), std::io::Error> {
let config_str = fs::read_to_string("catppuccin_mocha.toml").expect("should be able to read");
let table = config_str.as_str().parse::<Table>().unwrap();
let theme = Theme::from(Value::Table(table.clone()));
let attrs: Vec<_> = theme
.highlights
.iter()
.map(|v| format!("style=\"{}\"", v.to_string()))
.collect();
let mut highlighter = Highlighter::new();
let lang = tree_sitter_rust::language();
let mut config = HighlightConfiguration::new(
lang,
"rust",
tree_sitter_rust::HIGHLIGHTS_QUERY,
include_str!("../queries/rust/injections.scm"),
include_str!("../queries/rust/locals.scm"),
)
.unwrap();
config.condiv(theme.scopes());
let source = fs::read_to_string("src/main.rs").unwrap();
let src_bytes = source.as_bytes();
let highlights = highlighter
.highlight(&config, src_bytes, None, |_| None)
.unwrap();
let mut renderer = HtmlRenderer::new();
let _ = renderer.render(highlights, src_bytes, &|h: Highlight| attrs[h.0].as_bytes());
let mut file = File::create("out.html")?;
file.write_all(
format!(
"<pre style=\"{} {}\"><code>",
theme.get("ui.background").to_string(),
theme.get("ui.text").to_string()
)
.as_bytes(),
)?;
file.write_all(&renderer.html)?;
file.write_all(b"</code></pre>")?;
Ok(())
}
pub mod graphics;
pub mod theme;
use crate::theme::Theme;
use std::fs;
use std::fs::File;
use std::io::Write;
use toml::{Table, Value};
use tree_sitter_highlight::{Highlight, HighlightConfiguration, Highlighter, HtmlRenderer};
fn main() -> Result<(), std::io::Error> {
let config_str = fs::read_to_string("catppuccin_mocha.toml").expect("should be able to read");
let table = config_str.as_str().parse::<Table>().unwrap();
let theme = Theme::from(Value::Table(table.clone()));
let attrs: Vec<_> = theme
.highlights
.iter()
.map(|v| format!("style=\"{}\"", v.to_string()))
.collect();
let mut highlighter = Highlighter::new();
let lang = tree_sitter_rust::language();
let mut config = HighlightConfiguration::new(
lang,
"rust",
tree_sitter_rust::HIGHLIGHTS_QUERY,
include_str!("../queries/rust/injections.scm"),
include_str!("../queries/rust/locals.scm"),
)
.unwrap();
config.configure(theme.scopes());
let source = fs::read_to_string("src/main.rs").unwrap();
let src_bytes = source.as_bytes();
let highlights = highlighter
.highlight(&config, src_bytes, None, |_| None)
.unwrap();
let mut renderer = HtmlRenderer::new();
let _ = renderer.render(highlights, src_bytes, &|h: Highlight| attrs[h.0].as_bytes());
let mut file = File::create("out.html")?;
file.write_all(
format!(
"<pre style=\"{} {}\"><code>",
theme.get("ui.background").to_string(),
theme.get("ui.text").to_string()
)
.as_bytes(),
)?;
file.write_all(&renderer.html)?;
file.write_all(b"</code></pre>")?;
Ok(())
}
pub mod graphics;
pub mod theme;
use crate::theme::Theme;
use std::fs;
use std::fs::File;
use std::io::Write;
use toml::{Table, Value};
use tree_sitter_highlight::{Highlight, HighlightConfiguration, Highlighter, HtmlRenderer};
fn main() -> Result<(), std::io::Error> {
let config_str = fs::read_to_string("monokai.toml").expect("should be able to read");
let table = config_str.as_str().parse::<Table>().unwrap();
let theme = Theme::from(Value::Table(table.clone()));
let attrs: Vec<_> = theme
.highlights
.iter()
.map(|v| format!("style=\"{}\"", v.to_string()))
.collect();
let mut highlighter = Highlighter::new();
let lang = tree_sitter_rust::language();
let mut config = HighlightConfiguration::new(
lang,
"rust",
tree_sitter_rust::HIGHLIGHTS_QUERY,
include_str!("../queries/rust/injections.scm"),
include_str!("../queries/rust/locals.scm"),
)
.unwrap();
config.configure(theme.scopes());
let source = fs::read_to_string("src/main.rs").unwrap();
let src_bytes = source.as_bytes();
let highlights = highlighter
.highlight(&config, src_bytes, None, |_| None)
.unwrap();
let mut renderer = HtmlRenderer::new();
let _ = renderer.render(highlights, src_bytes, &|h: Highlight| attrs[h.0].as_bytes());
let mut file = File::create("out.html")?;
file.write_all(
format!(
"<pre style=\"{} {}\"><code>",
theme.get("ui.background").to_string(),
theme.get("ui.text").to_string()
)
.as_bytes(),
)?;
file.write_all(&renderer.html)?;
file.write_all(b"</code></pre>")?;
Ok(())
}
pub mod graphics;
pub mod theme;
use crate::theme::Theme;
use std::fs;
use std::fs::File;
use std::io::Write;
use toml::{Table, Value};
use tree_sitter_highlight::{Highlight, HighlightConfiguration, Highlighter, HtmlRenderer};
fn main() -> Result<(), std::io::Error> {
let config_str = fs::read_to_string("monokai.toml").expect("should be able to read");
let table = config_str.as_str().parse::<Table>().unwrap();
let theme = Theme::from(Value::Table(table.clone()));
let attrs: Vec<_> = theme
.highlights
.iter()
.map(|v| format!("style=\"{}\"", v.to_string()))
.collect();
let mut highlighter = Highlighter::new();
let lang = tree_sitter_rust::language();
let mut config = HighlightConfiguration::new(
lang,
"rust",
tree_sitter_rust::HIGHLIGHTS_QUERY,
include_str!("../queries/rust/injections.scm"),
include_str!("../queries/rust/locals.scm"),
)
.unwrap();
config.configure(theme.scopes());
let source = fs::read_to_string("src/main.rs").unwrap();
let src_bytes = source.as_bytes();
let highlights = highlighter
.highlight(&config, src_bytes, None, |_| None)
.unwrap();
let mut renderer = HtmlRenderer::new();
let _ = renderer.render(highlights, src_bytes, &|h: Highlight| attrs[h.0].as_bytes());
let mut file = File::create("out.html")?;
file.write_all(
format!(
"<pre style=\"{} {}\"><code>",
theme.get("ui.background").to_string(),
theme.get("ui.text").to_string()
)
.as_bytes(),
)?;
file.write_all(&renderer.html)?;
file.write_all(b"</code></pre>")?;
Ok(())
}
pub mod graphics;
pub mod theme;
use crate::theme::Theme;
use std::fs;
use std::fs::File;
use std::io::Write;
use toml::{Table, Value};
use tree_sitter_highlight::{Highlight, HighlightConfiguration, Highlighter, HtmlRenderer};
fn main() -> Result<(), std::io::Error> {
let config_str = fs::read_to_string("catppuccin_mocha.toml").expect("should be able to read");
let table = config_str.as_str().parse::<Table>().unwrap();
let theme = Theme::from(Value::Table(table.clone()));
let attrs: Vec<_> = theme
.highlights
.iter()
.map(|v| format!("style=\"{}\"", v.to_string()))
.collect();
let mut highlighter = Highlighter::new();
let lang = tree_sitter_rust::language();
let mut config = HighlightConfiguration::new(
lang,
"rust",
tree_sitter_rust::HIGHLIGHTS_QUERY,
include_str!("../queries/rust/injections.scm"),
include_str!("../queries/rust/locals.scm"),
)
.unwrap();
config.configure(theme.scopes());
let source = fs::read_to_string("src/main.rs").unwrap();
let src_bytes = source.as_bytes();
let highlights = highlighter
.highlight(&config, src_bytes, None, |_| None)
.unwrap();
let mut renderer = HtmlRenderer::new();
let _ = renderer.render(highlights, src_bytes, &|h: Highlight| attrs[h.0].as_bytes());
let mut file = File::create("out.html")?;
file.write_all(
format!(
"<pre style=\"{} {}\"><code>",
theme.get("ui.background").to_string(),
theme.get("ui.text").to_string()
)
.as_bytes(),
)?;
file.write_all(&renderer.html)?;
file.write_all(b"</code></pre>")?;
Ok(())
}
Shiki does the job so well that it's absolutely overkill.