Moving to zola

2024-06-03

I've used Hexo for five years and am unhappy with it. Here are some reasons why:

Syntax highlighting comparisons

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.

Highlight.js

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(())
}

Syntect

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(())
}

Tree-sitter

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(())
}

Prism.js

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(())
}

Shiki

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(())
}

Conclusion

Shiki does the job so well that it's absolutely overkill.