From b06424cb1779d229eb19ddf926f3f492e9742565 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 22 Feb 2025 15:09:00 -0500 Subject: [PATCH 01/19] Initial highlighting code. The dust auto-escaping is causing this naive approach to fail so I will have to create a distinction between highlighted code and not-highlighted code. --- Cargo.lock | 138 ++++++++++++++++++++++++++++++++-- Cargo.toml | 2 + src/intermediate/src_block.rs | 68 +++++++++++++++++ 3 files changed, 201 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c1bd77d..a1a742c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,15 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "anstream" version = "0.6.15" @@ -123,6 +132,15 @@ version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +[[package]] +name = "cc" +version = "1.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af" +dependencies = [ + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -445,6 +463,8 @@ dependencies = [ "serde_json", "tokio", "toml", + "tree-sitter-highlight", + "tree-sitter-nix", "url", ] @@ -517,9 +537,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "proc-macro2" -version = "1.0.88" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -539,6 +559,35 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -582,10 +631,11 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.130" +version = "1.0.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "610f75ff4a8e3cb29b85da56eabdd1bff5b06739059a4b8e2967fef32e5d9944" +checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" dependencies = [ + "indexmap", "itoa", "memchr", "ryu", @@ -601,6 +651,12 @@ dependencies = [ "serde", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "slab" version = "0.4.9" @@ -617,10 +673,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] -name = "syn" -version = "2.0.79" +name = "streaming-iterator" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520" + +[[package]] +name = "syn" +version = "2.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -633,6 +695,26 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "thiserror" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tinyvec" version = "1.8.0" @@ -693,6 +775,48 @@ dependencies = [ "winnow", ] +[[package]] +name = "tree-sitter" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5168a515fe492af54c5cc8800ff8c840be09fa5168de45838afaecd3e008bce4" +dependencies = [ + "cc", + "regex", + "regex-syntax", + "serde_json", + "streaming-iterator", + "tree-sitter-language", +] + +[[package]] +name = "tree-sitter-highlight" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "457164f56e8dbbd0dc620c239bd7e2eb6025b76e4d1593a690bd4d9ed37bf168" +dependencies = [ + "regex", + "streaming-iterator", + "thiserror", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-language" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4013970217383f67b18aef68f6fb2e8d409bc5755227092d32efb0422ba24b8" + +[[package]] +name = "tree-sitter-nix" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8d4f8705d377d63242a075331d2d8c1dcc9828fd74aa13d7145185b3d9c004" +dependencies = [ + "cc", + "tree-sitter-language", +] + [[package]] name = "unicode-bidi" version = "0.3.17" diff --git a/Cargo.toml b/Cargo.toml index 47d1263..5b71650 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,8 @@ serde = { version = "1.0.189", default-features = false, features = ["std", "der serde_json = "1.0.107" tokio = { version = "1.30.0", default-features = false, features = ["rt", "rt-multi-thread", "fs", "io-util"] } toml = "0.8.2" +tree-sitter-highlight = "0.25.2" +tree-sitter-nix = "0.0.2" url = "2.5.0" # Optimized build for any sort of release. diff --git a/src/intermediate/src_block.rs b/src/intermediate/src_block.rs index b0d948b..0055d46 100644 --- a/src/intermediate/src_block.rs +++ b/src/intermediate/src_block.rs @@ -1,6 +1,12 @@ +use std::borrow::Cow; + use super::macros::intermediate; use crate::error::CustomError; use organic::types::StandardProperties; +use tree_sitter_highlight::Highlight; +use tree_sitter_highlight::HighlightConfiguration; +use tree_sitter_highlight::HighlightEvent; +use tree_sitter_highlight::Highlighter; #[derive(Debug, Clone)] pub(crate) struct ISrcBlock { @@ -59,6 +65,14 @@ intermediate!( }) .collect(); let language = original.language.map(str::to_owned); + + let lines = match language.as_ref().map(String::as_str) { + Some("nix") => { + // foo + highlight_nix(lines)? + } + _ => lines, + }; Ok(ISrcBlock { lines, language, @@ -76,3 +90,57 @@ fn ascii_whitespace_value(c: char) -> usize { _ => unreachable!("Only ascii whitespace can reach this code."), } } + +fn highlight_nix(lines: Vec) -> Result, CustomError> { + let highlight_names = ["comment", "keyword"]; + // Need 1 highlighter per thread + let mut highlighter = Highlighter::new(); + let language = tree_sitter_nix::LANGUAGE.into(); + let mut config = + HighlightConfiguration::new(language, "nix", tree_sitter_nix::HIGHLIGHTS_QUERY, "", "") + .unwrap(); + config.configure(&highlight_names); + + let combined_text = lines.join(""); + + let highlights = highlighter + .highlight(&config, combined_text.as_bytes(), None, |_| None) + .unwrap(); + + let mut highlighted_text = Vec::new(); + for event in highlights { + match event.unwrap() { + HighlightEvent::Source { start, end } => { + highlighted_text.push(Cow::Borrowed(&combined_text[start..end])); + } + HighlightEvent::HighlightStart(s) => { + let class_name = format!("srchl_{}", highlight_names[s.0]); + highlighted_text.push(Cow::Owned(format!(r#""#, class_name))); + } + HighlightEvent::HighlightEnd => { + highlighted_text.push(Cow::Borrowed(r#""#)); + } + } + } + + let highlighted_text = highlighted_text.join(""); + let lines = highlighted_text + .split_inclusive('\n') + .map(str::to_owned) + .collect(); + Ok(lines) +} + +// use tree_sitter::Parser; +// fn dump_nix(body: B) -> Result<(), CustomError> +// where +// B: AsRef, +// { +// let mut parser = Parser::new(); +// parser +// .set_language(&tree_sitter_nix::LANGUAGE.into()) +// .expect("Error loading Nix grammar"); +// let mut tree = parser.parse(body.as_ref(), None).unwrap(); +// println!("{}", tree.root_node()); +// Ok(()) +// } From c067ca9cc898b9080cd542331a5f3d73b84fbe5b Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 22 Feb 2025 15:22:35 -0500 Subject: [PATCH 02/19] Introduce an enum for a separate highlighted src block type. --- src/context/src_block.rs | 31 +++++++++++++++++++++++++------ src/intermediate/src_block.rs | 35 +++++++++++++++++++++++++++-------- 2 files changed, 52 insertions(+), 14 deletions(-) diff --git a/src/context/src_block.rs b/src/context/src_block.rs index 2f6298d..f6af667 100644 --- a/src/context/src_block.rs +++ b/src/context/src_block.rs @@ -6,19 +6,38 @@ use crate::intermediate::ISrcBlock; use super::macros::render; +#[derive(Debug, Serialize)] +#[serde(untagged)] +pub(crate) enum RenderSrcBlock { + Plain(RenderPlainSrcBlock), + Highlighted(RenderHighlightedSrcBlock), +} + #[derive(Debug, Serialize)] #[serde(tag = "type")] #[serde(rename = "src_block")] -pub(crate) struct RenderSrcBlock { +pub(crate) struct RenderPlainSrcBlock { + lines: Vec, + language: Option, + post_blank: organic::types::PostBlank, +} + +#[derive(Debug, Serialize)] +#[serde(tag = "type")] +#[serde(rename = "highlighted_src_block")] +pub(crate) struct RenderHighlightedSrcBlock { lines: Vec, language: Option, post_blank: organic::types::PostBlank, } render!(RenderSrcBlock, ISrcBlock, original, _render_context, { - Ok(RenderSrcBlock { - lines: original.lines.clone(), - language: original.language.clone(), - post_blank: original.post_blank, - }) + match original { + ISrcBlock::Plain(plain_src_block) => Ok(RenderSrcBlock::Plain(RenderPlainSrcBlock { + lines: plain_src_block.lines.clone(), + language: plain_src_block.language.clone(), + post_blank: plain_src_block.post_blank, + })), + ISrcBlock::Highlighted(_) => todo!(), + } }); diff --git a/src/intermediate/src_block.rs b/src/intermediate/src_block.rs index 0055d46..5566cbf 100644 --- a/src/intermediate/src_block.rs +++ b/src/intermediate/src_block.rs @@ -1,3 +1,4 @@ +use std::borrow::Borrow; use std::borrow::Cow; use super::macros::intermediate; @@ -9,7 +10,20 @@ use tree_sitter_highlight::HighlightEvent; use tree_sitter_highlight::Highlighter; #[derive(Debug, Clone)] -pub(crate) struct ISrcBlock { +pub(crate) enum ISrcBlock { + Plain(PlainSrcBlock), + Highlighted(HighlightedSrcBlock), +} + +#[derive(Debug, Clone)] +pub(crate) struct PlainSrcBlock { + pub(crate) lines: Vec, + pub(crate) language: Option, + pub(crate) post_blank: organic::types::PostBlank, +} + +#[derive(Debug, Clone)] +pub(crate) struct HighlightedSrcBlock { pub(crate) lines: Vec, pub(crate) language: Option, pub(crate) post_blank: organic::types::PostBlank, @@ -66,18 +80,20 @@ intermediate!( .collect(); let language = original.language.map(str::to_owned); - let lines = match language.as_ref().map(String::as_str) { + match language.as_ref().map(String::as_str) { Some("nix") => { - // foo - highlight_nix(lines)? + let highlighted = highlight_nix(&lines); + if let Ok(highlighted) = highlighted { + // return the other type + } } - _ => lines, + _ => {} }; - Ok(ISrcBlock { + Ok(ISrcBlock::Plain(PlainSrcBlock { lines, language, post_blank: original.get_post_blank(), - }) + })) } ); @@ -91,7 +107,10 @@ fn ascii_whitespace_value(c: char) -> usize { } } -fn highlight_nix(lines: Vec) -> Result, CustomError> { +fn highlight_nix(lines: &[L]) -> Result, CustomError> +where + L: Borrow, +{ let highlight_names = ["comment", "keyword"]; // Need 1 highlighter per thread let mut highlighter = Highlighter::new(); From e34e2ef75f0976e8d09e26fcd89e4b0d4e5f65f7 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 22 Feb 2025 15:44:45 -0500 Subject: [PATCH 03/19] Implement the highlighted src intermediate format. I am going to remove the enum because I realized plain src blocks can just be highlighted src blocks with only RawText entries. --- src/intermediate/src_block.rs | 61 +++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/src/intermediate/src_block.rs b/src/intermediate/src_block.rs index 5566cbf..ca88164 100644 --- a/src/intermediate/src_block.rs +++ b/src/intermediate/src_block.rs @@ -24,11 +24,23 @@ pub(crate) struct PlainSrcBlock { #[derive(Debug, Clone)] pub(crate) struct HighlightedSrcBlock { - pub(crate) lines: Vec, + pub(crate) lines: Vec, pub(crate) language: Option, pub(crate) post_blank: organic::types::PostBlank, } +#[derive(Debug, Clone)] +pub(crate) struct HighlightedSrcLine { + pub(crate) children: Vec, +} + +#[derive(Debug, Clone)] +pub(crate) enum HighlightedSrcSegment { + RawText(String), + HighlightStart { name: String }, + HighlightEnd, +} + intermediate!( ISrcBlock, &'orig organic::types::SrcBlock<'parse>, @@ -97,6 +109,14 @@ intermediate!( } ); +impl HighlightedSrcLine { + pub(crate) fn new() -> HighlightedSrcLine { + HighlightedSrcLine { + children: Vec::new(), + } + } +} + fn ascii_whitespace_value(c: char) -> usize { match c { ' ' => 1, @@ -107,7 +127,7 @@ fn ascii_whitespace_value(c: char) -> usize { } } -fn highlight_nix(lines: &[L]) -> Result, CustomError> +fn highlight_nix(lines: &[L]) -> Result, CustomError> where L: Borrow, { @@ -126,28 +146,43 @@ where .highlight(&config, combined_text.as_bytes(), None, |_| None) .unwrap(); - let mut highlighted_text = Vec::new(); + let mut highlighted_text: Vec = Vec::with_capacity(lines.len()); + let mut current_line = HighlightedSrcLine::new(); for event in highlights { match event.unwrap() { HighlightEvent::Source { start, end } => { - highlighted_text.push(Cow::Borrowed(&combined_text[start..end])); + let mut span = &combined_text[start..end]; + while let Some(line_break_index) = span.find('\n') { + let first_line = &span[..(line_break_index + 1)]; + current_line + .children + .push(HighlightedSrcSegment::RawText(first_line.to_owned())); + highlighted_text.push(current_line); + current_line = HighlightedSrcLine::new(); + span = &span[(line_break_index + 1)..]; + } + if !span.is_empty() { + current_line + .children + .push(HighlightedSrcSegment::RawText(span.to_owned())); + } } HighlightEvent::HighlightStart(s) => { - let class_name = format!("srchl_{}", highlight_names[s.0]); - highlighted_text.push(Cow::Owned(format!(r#""#, class_name))); + current_line + .children + .push(HighlightedSrcSegment::HighlightStart { + name: highlight_names[s.0].to_owned(), + }); } HighlightEvent::HighlightEnd => { - highlighted_text.push(Cow::Borrowed(r#""#)); + current_line + .children + .push(HighlightedSrcSegment::HighlightEnd); } } } - let highlighted_text = highlighted_text.join(""); - let lines = highlighted_text - .split_inclusive('\n') - .map(str::to_owned) - .collect(); - Ok(lines) + Ok(highlighted_text) } // use tree_sitter::Parser; From 7e934cd360132cb05223c0fa11fcb6ce916758a7 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 22 Feb 2025 15:55:16 -0500 Subject: [PATCH 04/19] Removed the enum and made plain src blocks just highlighted src blocks with only plain text in them. --- src/context/src_block.rs | 32 ++++++-------------------- src/intermediate/src_block.rs | 42 ++++++++++++++++++++--------------- 2 files changed, 31 insertions(+), 43 deletions(-) diff --git a/src/context/src_block.rs b/src/context/src_block.rs index f6af667..4602a45 100644 --- a/src/context/src_block.rs +++ b/src/context/src_block.rs @@ -6,38 +6,20 @@ use crate::intermediate::ISrcBlock; use super::macros::render; -#[derive(Debug, Serialize)] -#[serde(untagged)] -pub(crate) enum RenderSrcBlock { - Plain(RenderPlainSrcBlock), - Highlighted(RenderHighlightedSrcBlock), -} - #[derive(Debug, Serialize)] #[serde(tag = "type")] #[serde(rename = "src_block")] -pub(crate) struct RenderPlainSrcBlock { - lines: Vec, - language: Option, - post_blank: organic::types::PostBlank, -} - -#[derive(Debug, Serialize)] -#[serde(tag = "type")] -#[serde(rename = "highlighted_src_block")] -pub(crate) struct RenderHighlightedSrcBlock { +pub(crate) struct RenderSrcBlock { lines: Vec, language: Option, post_blank: organic::types::PostBlank, } render!(RenderSrcBlock, ISrcBlock, original, _render_context, { - match original { - ISrcBlock::Plain(plain_src_block) => Ok(RenderSrcBlock::Plain(RenderPlainSrcBlock { - lines: plain_src_block.lines.clone(), - language: plain_src_block.language.clone(), - post_blank: plain_src_block.post_blank, - })), - ISrcBlock::Highlighted(_) => todo!(), - } + Ok(RenderSrcBlock { + // lines: original.lines.clone(), + lines: Vec::new(), + language: original.language.clone(), + post_blank: original.post_blank, + }) }); diff --git a/src/intermediate/src_block.rs b/src/intermediate/src_block.rs index ca88164..64cda52 100644 --- a/src/intermediate/src_block.rs +++ b/src/intermediate/src_block.rs @@ -10,20 +10,7 @@ use tree_sitter_highlight::HighlightEvent; use tree_sitter_highlight::Highlighter; #[derive(Debug, Clone)] -pub(crate) enum ISrcBlock { - Plain(PlainSrcBlock), - Highlighted(HighlightedSrcBlock), -} - -#[derive(Debug, Clone)] -pub(crate) struct PlainSrcBlock { - pub(crate) lines: Vec, - pub(crate) language: Option, - pub(crate) post_blank: organic::types::PostBlank, -} - -#[derive(Debug, Clone)] -pub(crate) struct HighlightedSrcBlock { +pub(crate) struct ISrcBlock { pub(crate) lines: Vec, pub(crate) language: Option, pub(crate) post_blank: organic::types::PostBlank, @@ -96,16 +83,21 @@ intermediate!( Some("nix") => { let highlighted = highlight_nix(&lines); if let Ok(highlighted) = highlighted { - // return the other type + return Ok(ISrcBlock { + lines: highlighted, + language, + post_blank: original.get_post_blank(), + }); } } _ => {} }; - Ok(ISrcBlock::Plain(PlainSrcBlock { - lines, + let highlighted = highlight_plain(&lines)?; + Ok(ISrcBlock { + lines: highlighted, language, post_blank: original.get_post_blank(), - })) + }) } ); @@ -127,6 +119,20 @@ fn ascii_whitespace_value(c: char) -> usize { } } +fn highlight_plain(lines: &[L]) -> Result, CustomError> +where + std::string::String: for<'a> From<&'a L>, +{ + Ok(lines + .into_iter() + .map(|l| { + let mut line = HighlightedSrcLine::new(); + line.children.push(HighlightedSrcSegment::RawText(l.into())); + line + }) + .collect()) +} + fn highlight_nix(lines: &[L]) -> Result, CustomError> where L: Borrow, From 4cc04bda461087cd9ad963f36210ac2dc4972e4a Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 22 Feb 2025 16:04:47 -0500 Subject: [PATCH 05/19] Update the render context to use the new src block format. --- src/context/src_block.rs | 43 +++++++++++++++++++++++++++++++--- src/intermediate/mod.rs | 1 + src/intermediate/src_block.rs | 44 ++++++++++++++++------------------- 3 files changed, 61 insertions(+), 27 deletions(-) diff --git a/src/context/src_block.rs b/src/context/src_block.rs index 4602a45..1d03eef 100644 --- a/src/context/src_block.rs +++ b/src/context/src_block.rs @@ -3,6 +3,7 @@ use serde::Serialize; use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::ISrcBlock; +use crate::intermediate::ISrcSegment; use super::macros::render; @@ -10,15 +11,51 @@ use super::macros::render; #[serde(tag = "type")] #[serde(rename = "src_block")] pub(crate) struct RenderSrcBlock { - lines: Vec, + lines: Vec, language: Option, post_blank: organic::types::PostBlank, } +#[derive(Debug, Serialize)] +#[serde(tag = "type")] +#[serde(rename = "src_block")] +pub(crate) struct RenderSrcLine { + children: Vec, +} + +#[derive(Debug, Serialize)] +pub(crate) enum RenderSrcSegment { + #[serde(rename = "raw_text")] + RawText(String), + + #[serde(rename = "highlight_start")] + HighlightStart { name: String }, + + #[serde(rename = "highlight_end")] + HighlightEnd, +} + render!(RenderSrcBlock, ISrcBlock, original, _render_context, { + let lines = original + .lines + .iter() + .map(|original_line| { + let children = original_line + .children + .iter() + .map(|original_segment| match original_segment { + ISrcSegment::RawText(body) => RenderSrcSegment::RawText(body.to_owned()), + ISrcSegment::HighlightStart { name } => RenderSrcSegment::HighlightStart { + name: name.to_owned(), + }, + ISrcSegment::HighlightEnd => RenderSrcSegment::HighlightEnd, + }) + .collect(); + RenderSrcLine { children } + }) + .collect(); Ok(RenderSrcBlock { - // lines: original.lines.clone(), - lines: Vec::new(), + lines, language: original.language.clone(), post_blank: original.post_blank, }) diff --git a/src/intermediate/mod.rs b/src/intermediate/mod.rs index 44c257f..41950f1 100644 --- a/src/intermediate/mod.rs +++ b/src/intermediate/mod.rs @@ -127,6 +127,7 @@ pub(crate) use regular_link::LinkTarget; pub(crate) use section::ISection; pub(crate) use special_block::ISpecialBlock; pub(crate) use src_block::ISrcBlock; +pub(crate) use src_block::ISrcSegment; pub(crate) use statistics_cookie::IStatisticsCookie; pub(crate) use strike_through::IStrikeThrough; pub(crate) use subscript::ISubscript; diff --git a/src/intermediate/src_block.rs b/src/intermediate/src_block.rs index 64cda52..ad18df7 100644 --- a/src/intermediate/src_block.rs +++ b/src/intermediate/src_block.rs @@ -11,18 +11,18 @@ use tree_sitter_highlight::Highlighter; #[derive(Debug, Clone)] pub(crate) struct ISrcBlock { - pub(crate) lines: Vec, + pub(crate) lines: Vec, pub(crate) language: Option, pub(crate) post_blank: organic::types::PostBlank, } #[derive(Debug, Clone)] -pub(crate) struct HighlightedSrcLine { - pub(crate) children: Vec, +pub(crate) struct ISrcLine { + pub(crate) children: Vec, } #[derive(Debug, Clone)] -pub(crate) enum HighlightedSrcSegment { +pub(crate) enum ISrcSegment { RawText(String), HighlightStart { name: String }, HighlightEnd, @@ -101,9 +101,9 @@ intermediate!( } ); -impl HighlightedSrcLine { - pub(crate) fn new() -> HighlightedSrcLine { - HighlightedSrcLine { +impl ISrcLine { + pub(crate) fn new() -> ISrcLine { + ISrcLine { children: Vec::new(), } } @@ -119,21 +119,21 @@ fn ascii_whitespace_value(c: char) -> usize { } } -fn highlight_plain(lines: &[L]) -> Result, CustomError> +fn highlight_plain(lines: &[L]) -> Result, CustomError> where std::string::String: for<'a> From<&'a L>, { Ok(lines .into_iter() .map(|l| { - let mut line = HighlightedSrcLine::new(); - line.children.push(HighlightedSrcSegment::RawText(l.into())); + let mut line = ISrcLine::new(); + line.children.push(ISrcSegment::RawText(l.into())); line }) .collect()) } -fn highlight_nix(lines: &[L]) -> Result, CustomError> +fn highlight_nix(lines: &[L]) -> Result, CustomError> where L: Borrow, { @@ -152,8 +152,8 @@ where .highlight(&config, combined_text.as_bytes(), None, |_| None) .unwrap(); - let mut highlighted_text: Vec = Vec::with_capacity(lines.len()); - let mut current_line = HighlightedSrcLine::new(); + let mut highlighted_text: Vec = Vec::with_capacity(lines.len()); + let mut current_line = ISrcLine::new(); for event in highlights { match event.unwrap() { HighlightEvent::Source { start, end } => { @@ -162,28 +162,24 @@ where let first_line = &span[..(line_break_index + 1)]; current_line .children - .push(HighlightedSrcSegment::RawText(first_line.to_owned())); + .push(ISrcSegment::RawText(first_line.to_owned())); highlighted_text.push(current_line); - current_line = HighlightedSrcLine::new(); + current_line = ISrcLine::new(); span = &span[(line_break_index + 1)..]; } if !span.is_empty() { current_line .children - .push(HighlightedSrcSegment::RawText(span.to_owned())); + .push(ISrcSegment::RawText(span.to_owned())); } } HighlightEvent::HighlightStart(s) => { - current_line - .children - .push(HighlightedSrcSegment::HighlightStart { - name: highlight_names[s.0].to_owned(), - }); + current_line.children.push(ISrcSegment::HighlightStart { + name: highlight_names[s.0].to_owned(), + }); } HighlightEvent::HighlightEnd => { - current_line - .children - .push(HighlightedSrcSegment::HighlightEnd); + current_line.children.push(ISrcSegment::HighlightEnd); } } } From 4ea1a4670501a548b1c016f1b68da995bd287e52 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 22 Feb 2025 16:13:23 -0500 Subject: [PATCH 06/19] Update dust templates to support the new source code block format. --- default_environment/templates/html/src_block.dust | 7 ++++++- src/context/src_block.rs | 9 +++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/default_environment/templates/html/src_block.dust b/default_environment/templates/html/src_block.dust index f0a6357..608f90d 100644 --- a/default_environment/templates/html/src_block.dust +++ b/default_environment/templates/html/src_block.dust @@ -4,7 +4,12 @@ {#.lines} - {.} + {#.children}{@select key=.type} + {@eq value="raw_text"}{.content}{/eq} + {@eq value="highlight_start"}{/eq} + {@eq value="highlight_end"}{/eq} + {@none}{!TODO: make this panic!}ERROR: Unrecognized type {.type}.{/none} + {/select}{/.children} {/.lines} diff --git a/src/context/src_block.rs b/src/context/src_block.rs index 1d03eef..1780914 100644 --- a/src/context/src_block.rs +++ b/src/context/src_block.rs @@ -17,16 +17,15 @@ pub(crate) struct RenderSrcBlock { } #[derive(Debug, Serialize)] -#[serde(tag = "type")] -#[serde(rename = "src_block")] pub(crate) struct RenderSrcLine { children: Vec, } #[derive(Debug, Serialize)] +#[serde(tag = "type")] pub(crate) enum RenderSrcSegment { #[serde(rename = "raw_text")] - RawText(String), + RawText { content: String }, #[serde(rename = "highlight_start")] HighlightStart { name: String }, @@ -44,7 +43,9 @@ render!(RenderSrcBlock, ISrcBlock, original, _render_context, { .children .iter() .map(|original_segment| match original_segment { - ISrcSegment::RawText(body) => RenderSrcSegment::RawText(body.to_owned()), + ISrcSegment::RawText(body) => RenderSrcSegment::RawText { + content: body.to_owned(), + }, ISrcSegment::HighlightStart { name } => RenderSrcSegment::HighlightStart { name: name.to_owned(), }, From c601c8697a47cd7262310826193fadbd70966cf3 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 22 Feb 2025 16:25:36 -0500 Subject: [PATCH 07/19] Start a language-specific css file for highlight colors. --- default_environment/stylesheet/language_nix.css | 9 +++++++++ default_environment/templates/html/src_block.dust | 2 +- src/context/blog_post_page.rs | 6 ++++++ src/context/blog_stream.rs | 6 ++++++ src/context/page.rs | 6 ++++++ 5 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 default_environment/stylesheet/language_nix.css diff --git a/default_environment/stylesheet/language_nix.css b/default_environment/stylesheet/language_nix.css new file mode 100644 index 0000000..afc2665 --- /dev/null +++ b/default_environment/stylesheet/language_nix.css @@ -0,0 +1,9 @@ +.main_content { + .src_block { + &.srclg_nix { + .srchl_keyword { + color: green; + } + } + } +} diff --git a/default_environment/templates/html/src_block.dust b/default_environment/templates/html/src_block.dust index 608f90d..b7fac67 100644 --- a/default_environment/templates/html/src_block.dust +++ b/default_environment/templates/html/src_block.dust @@ -1,4 +1,4 @@ -
+
{?.language}
{.language}
{/.language} diff --git a/src/context/blog_post_page.rs b/src/context/blog_post_page.rs index 9dce558..f1d566b 100644 --- a/src/context/blog_post_page.rs +++ b/src/context/blog_post_page.rs @@ -64,6 +64,12 @@ render!( render_context.output_file, "stylesheet/main.css", )?, + get_web_path( + render_context.config, + render_context.output_root_directory, + render_context.output_file, + "stylesheet/language_nix.css", + )?, ]; let js_files = vec![get_web_path( render_context.config, diff --git a/src/context/blog_stream.rs b/src/context/blog_stream.rs index fac3606..9182226 100644 --- a/src/context/blog_stream.rs +++ b/src/context/blog_stream.rs @@ -62,6 +62,12 @@ render!( render_context.output_file, "stylesheet/main.css", )?, + get_web_path( + render_context.config, + render_context.output_root_directory, + render_context.output_file, + "stylesheet/language_nix.css", + )?, ]; let js_files = vec![get_web_path( render_context.config, diff --git a/src/context/page.rs b/src/context/page.rs index cd8e5f0..5515189 100644 --- a/src/context/page.rs +++ b/src/context/page.rs @@ -43,6 +43,12 @@ render!(RenderPage, IPage, original, render_context, { render_context.output_file, "stylesheet/main.css", )?, + get_web_path( + render_context.config, + render_context.output_root_directory, + render_context.output_file, + "stylesheet/language_nix.css", + )?, ]; let js_files = vec![get_web_path( render_context.config, From 57eb1b81ec47f80529b973a43ec8d44c6924fbc8 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 22 Feb 2025 16:35:05 -0500 Subject: [PATCH 08/19] Start assigning colors. --- default_environment/stylesheet/language_nix.css | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/default_environment/stylesheet/language_nix.css b/default_environment/stylesheet/language_nix.css index afc2665..6ef1993 100644 --- a/default_environment/stylesheet/language_nix.css +++ b/default_environment/stylesheet/language_nix.css @@ -1,8 +1,23 @@ +:root { + --srclg-nix-srchl-keyword-color: #1a936f; + --srclg-nix-srchl-comment-color: #048a81; +} + +@media (prefers-color-scheme: light) { + :root { + --srclg-nix-srchl-keyword-color: green; + --srclg-nix-srchl-comment-color: #bfbccb; + } +} + .main_content { .src_block { &.srclg_nix { .srchl_keyword { - color: green; + color: var(--srclg-nix-srchl-keyword-color); + } + .srchl_comment { + color: var(--srclg-nix-srchl-comment-color); } } } From 3245e830d2dc4213ef83c2442e0e1b02a9dc80b9 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 22 Feb 2025 16:46:47 -0500 Subject: [PATCH 09/19] Assign more colors. --- default_environment/stylesheet/language_nix.css | 14 ++++++++++++-- src/intermediate/src_block.rs | 7 ++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/default_environment/stylesheet/language_nix.css b/default_environment/stylesheet/language_nix.css index 6ef1993..ccbe45e 100644 --- a/default_environment/stylesheet/language_nix.css +++ b/default_environment/stylesheet/language_nix.css @@ -1,12 +1,16 @@ :root { --srclg-nix-srchl-keyword-color: #1a936f; --srclg-nix-srchl-comment-color: #048a81; + --srclg-nix-srchl-property-color: #bfbccb; + --srclg-nix-srchl-string-color: #ecc30b; } @media (prefers-color-scheme: light) { :root { - --srclg-nix-srchl-keyword-color: green; - --srclg-nix-srchl-comment-color: #bfbccb; + --srclg-nix-srchl-keyword-color: #e56c90; + --srclg-nix-srchl-comment-color: #fb757e; + --srclg-nix-srchl-property-color: #404334; + --srclg-nix-srchl-string-color: #133cf4; } } @@ -19,6 +23,12 @@ .srchl_comment { color: var(--srclg-nix-srchl-comment-color); } + .srchl_property { + color: var(--srclg-nix-srchl-property-color); + } + .srchl_string { + color: var(--srclg-nix-srchl-string-color); + } } } } diff --git a/src/intermediate/src_block.rs b/src/intermediate/src_block.rs index ad18df7..87f0552 100644 --- a/src/intermediate/src_block.rs +++ b/src/intermediate/src_block.rs @@ -137,7 +137,12 @@ fn highlight_nix(lines: &[L]) -> Result, CustomError> where L: Borrow, { - let highlight_names = ["comment", "keyword"]; + let highlight_names = [ + "comment", "keyword", "property", + "string", + // "string.special.path", + // "string.special.uri", + ]; // Need 1 highlighter per thread let mut highlighter = Highlighter::new(); let language = tree_sitter_nix::LANGUAGE.into(); From c4cf814f8df0e506fb7528625e7b6ced889cf997 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 22 Feb 2025 16:55:53 -0500 Subject: [PATCH 10/19] Also add highlighting for paths. --- default_environment/stylesheet/language_nix.css | 5 +++++ src/context/src_block.rs | 10 +++++++++- src/intermediate/src_block.rs | 6 ++++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/default_environment/stylesheet/language_nix.css b/default_environment/stylesheet/language_nix.css index ccbe45e..d0f1ea0 100644 --- a/default_environment/stylesheet/language_nix.css +++ b/default_environment/stylesheet/language_nix.css @@ -3,6 +3,7 @@ --srclg-nix-srchl-comment-color: #048a81; --srclg-nix-srchl-property-color: #bfbccb; --srclg-nix-srchl-string-color: #ecc30b; + --srclg-nix-srchl-string-special-path-color: #067bc2; } @media (prefers-color-scheme: light) { @@ -11,6 +12,7 @@ --srclg-nix-srchl-comment-color: #fb757e; --srclg-nix-srchl-property-color: #404334; --srclg-nix-srchl-string-color: #133cf4; + --srclg-nix-srchl-string-special-path-color: #f9843d; } } @@ -29,6 +31,9 @@ .srchl_string { color: var(--srclg-nix-srchl-string-color); } + .srchl_string_special_path { + color: var(--srclg-nix-srchl-string-special-path-color); + } } } } diff --git a/src/context/src_block.rs b/src/context/src_block.rs index 1780914..34c32e3 100644 --- a/src/context/src_block.rs +++ b/src/context/src_block.rs @@ -47,7 +47,7 @@ render!(RenderSrcBlock, ISrcBlock, original, _render_context, { content: body.to_owned(), }, ISrcSegment::HighlightStart { name } => RenderSrcSegment::HighlightStart { - name: name.to_owned(), + name: css_safe_name(name), }, ISrcSegment::HighlightEnd => RenderSrcSegment::HighlightEnd, }) @@ -61,3 +61,11 @@ render!(RenderSrcBlock, ISrcBlock, original, _render_context, { post_blank: original.post_blank, }) }); + +fn css_safe_name(inp: S) -> String +where + std::string::String: From, +{ + let inp: String = inp.into(); + inp.replace(".", "_") +} diff --git a/src/intermediate/src_block.rs b/src/intermediate/src_block.rs index 87f0552..3287a2f 100644 --- a/src/intermediate/src_block.rs +++ b/src/intermediate/src_block.rs @@ -138,9 +138,11 @@ where L: Borrow, { let highlight_names = [ - "comment", "keyword", "property", + "comment", + "keyword", + "property", "string", - // "string.special.path", + "string.special.path", // "string.special.uri", ]; // Need 1 highlighter per thread From 749f6d7a55db083b22e1d874d8d5fd9c1ccd10a8 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 22 Feb 2025 17:20:08 -0500 Subject: [PATCH 11/19] Dynamically register which CSS files are needed. --- src/context/blog_post_page.rs | 70 ++++++++++++++++------------ src/context/blog_stream.rs | 76 ++++++++++++++++++------------- src/context/dependency.rs | 6 +++ src/context/dependency_manager.rs | 16 +++++++ src/context/page.rs | 68 ++++++++++++++++----------- src/context/src_block.rs | 12 ++++- 6 files changed, 161 insertions(+), 87 deletions(-) diff --git a/src/context/blog_post_page.rs b/src/context/blog_post_page.rs index f1d566b..eb81bd9 100644 --- a/src/context/blog_post_page.rs +++ b/src/context/blog_post_page.rs @@ -1,3 +1,5 @@ +use std::collections::HashSet; + use serde::Serialize; use super::render_context::RenderContext; @@ -51,34 +53,6 @@ render!( render_context, { push_file!(render_context, &original.page.src, { - let css_files = vec![ - get_web_path( - render_context.config, - render_context.output_root_directory, - render_context.output_file, - "stylesheet/reset.css", - )?, - get_web_path( - render_context.config, - render_context.output_root_directory, - render_context.output_file, - "stylesheet/main.css", - )?, - get_web_path( - render_context.config, - render_context.output_root_directory, - render_context.output_file, - "stylesheet/language_nix.css", - )?, - ]; - let js_files = vec![get_web_path( - render_context.config, - render_context.output_root_directory, - render_context.output_file, - "blog_post.js", - )?]; - let global_settings = - GlobalSettings::new(original.page.title.clone(), css_files, js_files); let page_header = PageHeader::new( render_context.config.get_site_title().map(str::to_string), Some(get_web_path( @@ -120,6 +94,46 @@ render!( ret }; + let mut css_files = vec![ + get_web_path( + render_context.config, + render_context.output_root_directory, + render_context.output_file, + "stylesheet/reset.css", + )?, + get_web_path( + render_context.config, + render_context.output_root_directory, + render_context.output_file, + "stylesheet/main.css", + )?, + ]; + let additional_css_files = render_context + .dependency_manager + .lock() + .unwrap() + .list_css()? + .map(|css_name| { + get_web_path( + render_context.config, + render_context.output_root_directory, + render_context.output_file, + format!("stylesheet/{}", css_name), + ) + }) + .collect::, _>>()?; + css_files.extend(additional_css_files.into_iter()); + + let js_files = vec![get_web_path( + render_context.config, + render_context.output_root_directory, + render_context.output_file, + "blog_post.js", + )?]; + + let global_settings = + GlobalSettings::new(original.page.title.clone(), css_files, js_files); + let ret = RenderBlogPostPage { global_settings, page_header: Some(page_header), diff --git a/src/context/blog_stream.rs b/src/context/blog_stream.rs index 9182226..635575e 100644 --- a/src/context/blog_stream.rs +++ b/src/context/blog_stream.rs @@ -1,3 +1,5 @@ +use std::collections::HashSet; + use serde::Serialize; use super::macros::render; @@ -49,37 +51,6 @@ render!( original, render_context, { - let css_files = vec![ - get_web_path( - render_context.config, - render_context.output_root_directory, - render_context.output_file, - "stylesheet/reset.css", - )?, - get_web_path( - render_context.config, - render_context.output_root_directory, - render_context.output_file, - "stylesheet/main.css", - )?, - get_web_path( - render_context.config, - render_context.output_root_directory, - render_context.output_file, - "stylesheet/language_nix.css", - )?, - ]; - let js_files = vec![get_web_path( - render_context.config, - render_context.output_root_directory, - render_context.output_file, - "blog_post.js", - )?]; - let global_settings = GlobalSettings::new( - render_context.config.get_site_title().map(str::to_string), - css_files, - js_files, - ); let page_header = PageHeader::new( render_context.config.get_site_title().map(str::to_string), Some(get_web_path( @@ -111,6 +82,49 @@ render!( None }; + let mut css_files = vec![ + get_web_path( + render_context.config, + render_context.output_root_directory, + render_context.output_file, + "stylesheet/reset.css", + )?, + get_web_path( + render_context.config, + render_context.output_root_directory, + render_context.output_file, + "stylesheet/main.css", + )?, + ]; + let additional_css_files = render_context + .dependency_manager + .lock() + .unwrap() + .list_css()? + .map(|css_name| { + get_web_path( + render_context.config, + render_context.output_root_directory, + render_context.output_file, + format!("stylesheet/{}", css_name), + ) + }) + .collect::, _>>()?; + css_files.extend(additional_css_files.into_iter()); + + let js_files = vec![get_web_path( + render_context.config, + render_context.output_root_directory, + render_context.output_file, + "blog_post.js", + )?]; + + let global_settings = GlobalSettings::new( + render_context.config.get_site_title().map(str::to_string), + css_files, + js_files, + ); + Ok(RenderBlogStream { global_settings, page_header: Some(page_header), diff --git a/src/context/dependency.rs b/src/context/dependency.rs index e79063f..6e537f5 100644 --- a/src/context/dependency.rs +++ b/src/context/dependency.rs @@ -7,6 +7,7 @@ use super::RenderContext; #[derive(Debug)] pub(crate) enum Dependency { StaticFile { absolute_path: PathBuf }, + CssFile { name: String }, } impl Dependency { @@ -40,6 +41,11 @@ impl Dependency { } Ok(()) } + Dependency::CssFile { name } => { + // We don't do anything because the CSS files are already copied into the output from natter's default environment. + // TODO: When we add support for CSS outside the default environment, we should add support for dynamically picking which ones to copy here. + Ok(()) + } } } } diff --git a/src/context/dependency_manager.rs b/src/context/dependency_manager.rs index a5355b6..7667c47 100644 --- a/src/context/dependency_manager.rs +++ b/src/context/dependency_manager.rs @@ -65,4 +65,20 @@ impl DependencyManager { std::mem::swap(&mut self.dependencies, &mut dependencies); dependencies } + + pub(crate) fn include_css(&mut self, name: N) -> Result<(), CustomError> + where + std::string::String: From, + { + self.dependencies + .push(Dependency::CssFile { name: name.into() }); + Ok(()) + } + + pub(crate) fn list_css(&self) -> Result, CustomError> { + Ok(self.dependencies.iter().filter_map(|dep| match dep { + Dependency::CssFile { name } => Some(name), + _ => None, + })) + } } diff --git a/src/context/page.rs b/src/context/page.rs index 5515189..be4108d 100644 --- a/src/context/page.rs +++ b/src/context/page.rs @@ -1,3 +1,5 @@ +use std::collections::HashSet; + use super::footnote_definition::RenderRealFootnoteDefinition; use super::macros::render; use super::render_context::RenderContext; @@ -30,33 +32,6 @@ pub(crate) struct RenderPage { render!(RenderPage, IPage, original, render_context, { push_file!(render_context, &original.src, { - let css_files = vec![ - get_web_path( - render_context.config, - render_context.output_root_directory, - render_context.output_file, - "stylesheet/reset.css", - )?, - get_web_path( - render_context.config, - render_context.output_root_directory, - render_context.output_file, - "stylesheet/main.css", - )?, - get_web_path( - render_context.config, - render_context.output_root_directory, - render_context.output_file, - "stylesheet/language_nix.css", - )?, - ]; - let js_files = vec![get_web_path( - render_context.config, - render_context.output_root_directory, - render_context.output_file, - "blog_post.js", - )?]; - let global_settings = GlobalSettings::new(original.title.clone(), css_files, js_files); let page_header = PageHeader::new( render_context.config.get_site_title().map(str::to_string), Some(get_web_path( @@ -98,6 +73,45 @@ render!(RenderPage, IPage, original, render_context, { ret }; + let mut css_files = vec![ + get_web_path( + render_context.config, + render_context.output_root_directory, + render_context.output_file, + "stylesheet/reset.css", + )?, + get_web_path( + render_context.config, + render_context.output_root_directory, + render_context.output_file, + "stylesheet/main.css", + )?, + ]; + let additional_css_files = render_context + .dependency_manager + .lock() + .unwrap() + .list_css()? + .map(|css_name| { + get_web_path( + render_context.config, + render_context.output_root_directory, + render_context.output_file, + format!("stylesheet/{}", css_name), + ) + }) + .collect::, _>>()?; + css_files.extend(additional_css_files.into_iter()); + + let js_files = vec![get_web_path( + render_context.config, + render_context.output_root_directory, + render_context.output_file, + "blog_post.js", + )?]; + + let global_settings = GlobalSettings::new(original.title.clone(), css_files, js_files); + let ret = RenderPage { global_settings, page_header: Some(page_header), diff --git a/src/context/src_block.rs b/src/context/src_block.rs index 34c32e3..e8af3ac 100644 --- a/src/context/src_block.rs +++ b/src/context/src_block.rs @@ -34,7 +34,7 @@ pub(crate) enum RenderSrcSegment { HighlightEnd, } -render!(RenderSrcBlock, ISrcBlock, original, _render_context, { +render!(RenderSrcBlock, ISrcBlock, original, render_context, { let lines = original .lines .iter() @@ -55,6 +55,16 @@ render!(RenderSrcBlock, ISrcBlock, original, _render_context, { RenderSrcLine { children } }) .collect(); + match original.language.as_ref().map(String::as_str) { + Some("nix") => { + render_context + .dependency_manager + .lock() + .unwrap() + .include_css("language_nix.css")?; + } + _ => {} + }; Ok(RenderSrcBlock { lines, language: original.language.clone(), From 04952895cff09b4e92e44327f2d8f2d92e17dc18 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 22 Feb 2025 17:56:56 -0500 Subject: [PATCH 12/19] Add support for highlighting python based on the nix highlighter. --- Cargo.lock | 11 +++ Cargo.toml | 1 + .../stylesheet/language_python.css | 39 +++++++++ src/context/src_block.rs | 7 ++ src/intermediate/src_block.rs | 82 +++++++++++++++---- 5 files changed, 122 insertions(+), 18 deletions(-) create mode 100644 default_environment/stylesheet/language_python.css diff --git a/Cargo.lock b/Cargo.lock index a1a742c..ec733b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -465,6 +465,7 @@ dependencies = [ "toml", "tree-sitter-highlight", "tree-sitter-nix", + "tree-sitter-python", "url", ] @@ -817,6 +818,16 @@ dependencies = [ "tree-sitter-language", ] +[[package]] +name = "tree-sitter-python" +version = "0.23.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d065aaa27f3aaceaf60c1f0e0ac09e1cb9eb8ed28e7bcdaa52129cffc7f4b04" +dependencies = [ + "cc", + "tree-sitter-language", +] + [[package]] name = "unicode-bidi" version = "0.3.17" diff --git a/Cargo.toml b/Cargo.toml index 5b71650..b5ad31d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ tokio = { version = "1.30.0", default-features = false, features = ["rt", "rt-mu toml = "0.8.2" tree-sitter-highlight = "0.25.2" tree-sitter-nix = "0.0.2" +tree-sitter-python = "0.23.6" url = "2.5.0" # Optimized build for any sort of release. diff --git a/default_environment/stylesheet/language_python.css b/default_environment/stylesheet/language_python.css new file mode 100644 index 0000000..e620691 --- /dev/null +++ b/default_environment/stylesheet/language_python.css @@ -0,0 +1,39 @@ +:root { + --srclg-python-srchl-keyword-color: #1a936f; + --srclg-python-srchl-comment-color: #048a81; + --srclg-python-srchl-property-color: #bfbccb; + --srclg-python-srchl-string-color: #ecc30b; + --srclg-python-srchl-string-special-path-color: #067bc2; +} + +@media (prefers-color-scheme: light) { + :root { + --srclg-python-srchl-keyword-color: #e56c90; + --srclg-python-srchl-comment-color: #fb757e; + --srclg-python-srchl-property-color: #404334; + --srclg-python-srchl-string-color: #133cf4; + --srclg-python-srchl-string-special-path-color: #f9843d; + } +} + +.main_content { + .src_block { + &.srclg_python { + .srchl_keyword { + color: var(--srclg-python-srchl-keyword-color); + } + .srchl_comment { + color: var(--srclg-python-srchl-comment-color); + } + .srchl_property { + color: var(--srclg-python-srchl-property-color); + } + .srchl_string { + color: var(--srclg-python-srchl-string-color); + } + .srchl_string_special_path { + color: var(--srclg-python-srchl-string-special-path-color); + } + } + } +} diff --git a/src/context/src_block.rs b/src/context/src_block.rs index e8af3ac..6ca36a9 100644 --- a/src/context/src_block.rs +++ b/src/context/src_block.rs @@ -63,6 +63,13 @@ render!(RenderSrcBlock, ISrcBlock, original, render_context, { .unwrap() .include_css("language_nix.css")?; } + Some("python") => { + render_context + .dependency_manager + .lock() + .unwrap() + .include_css("language_python.css")?; + } _ => {} }; Ok(RenderSrcBlock { diff --git a/src/intermediate/src_block.rs b/src/intermediate/src_block.rs index 3287a2f..6238dc5 100644 --- a/src/intermediate/src_block.rs +++ b/src/intermediate/src_block.rs @@ -80,7 +80,7 @@ intermediate!( let language = original.language.map(str::to_owned); match language.as_ref().map(String::as_str) { - Some("nix") => { + Some(lang @ "nix") => { let highlighted = highlight_nix(&lines); if let Ok(highlighted) = highlighted { return Ok(ISrcBlock { @@ -88,8 +88,25 @@ intermediate!( language, post_blank: original.get_post_blank(), }); + } else { + println!("Warning: Failed to highlight {} source.", lang); } } + Some(lang @ "python") => { + let highlighted = highlight_python(&lines); + if let Ok(highlighted) = highlighted { + return Ok(ISrcBlock { + lines: highlighted, + language, + post_blank: original.get_post_blank(), + }); + } else { + println!("Warning: Failed to highlight {} source.", lang); + } + } + Some(lang) => { + println!("Warning: No highlighting for language: {}", lang); + } _ => {} }; let highlighted = highlight_plain(&lines)?; @@ -133,27 +150,18 @@ where .collect()) } -fn highlight_nix(lines: &[L]) -> Result, CustomError> +fn highlight_tree_sitter( + config: HighlightConfiguration, + highlight_names: &[&str], + lines: &[L], +) -> Result, CustomError> where L: Borrow, { - let highlight_names = [ - "comment", - "keyword", - "property", - "string", - "string.special.path", - // "string.special.uri", - ]; + let combined_text = lines.join(""); + // Need 1 highlighter per thread let mut highlighter = Highlighter::new(); - let language = tree_sitter_nix::LANGUAGE.into(); - let mut config = - HighlightConfiguration::new(language, "nix", tree_sitter_nix::HIGHLIGHTS_QUERY, "", "") - .unwrap(); - config.configure(&highlight_names); - - let combined_text = lines.join(""); let highlights = highlighter .highlight(&config, combined_text.as_bytes(), None, |_| None) @@ -182,7 +190,7 @@ where } HighlightEvent::HighlightStart(s) => { current_line.children.push(ISrcSegment::HighlightStart { - name: highlight_names[s.0].to_owned(), + name: highlight_names[s.0].into(), }); } HighlightEvent::HighlightEnd => { @@ -194,6 +202,44 @@ where Ok(highlighted_text) } +fn highlight_nix(lines: &[L]) -> Result, CustomError> +where + L: Borrow, +{ + let highlight_names = [ + "comment", + "keyword", + "property", + "string", + "string.special.path", + // "string.special.uri", + ]; + let language = tree_sitter_nix::LANGUAGE.into(); + let mut config = + HighlightConfiguration::new(language, "nix", tree_sitter_nix::HIGHLIGHTS_QUERY, "", "") + .unwrap(); + config.configure(&highlight_names); + highlight_tree_sitter(config, &highlight_names, lines) +} + +fn highlight_python(lines: &[L]) -> Result, CustomError> +where + L: Borrow, +{ + let highlight_names = ["comment"]; + let language = tree_sitter_python::LANGUAGE.into(); + let mut config = HighlightConfiguration::new( + language, + "python", + tree_sitter_python::HIGHLIGHTS_QUERY, + "", + "", + ) + .unwrap(); + config.configure(&highlight_names); + highlight_tree_sitter(config, &highlight_names, lines) +} + // use tree_sitter::Parser; // fn dump_nix(body: B) -> Result<(), CustomError> // where From c67eb32774e00a5bb51add10e0f11e9a235835fb Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 22 Feb 2025 18:26:33 -0500 Subject: [PATCH 13/19] Add more colors to python. --- TODO.org | 17 ++++++++++ .../stylesheet/language_python.css | 34 +++++++++++++------ default_environment/stylesheet/main.css | 1 + src/intermediate/src_block.rs | 10 +++++- 4 files changed, 50 insertions(+), 12 deletions(-) create mode 100644 TODO.org diff --git a/TODO.org b/TODO.org new file mode 100644 index 0000000..46cb300 --- /dev/null +++ b/TODO.org @@ -0,0 +1,17 @@ +* Things to do [3/14] +** DONE If the paragraph only contains an image, text-align center +** DONE Syntax highlighting for code blocks +** TODO Render gnuplot +** TODO Pretty-print the timestamps +** TODO Support Table of Contents +** TODO Support line numbers in code blocks +** TODO Support references to code block lines +** TODO Only include text up to first heading on homepage and include a "read more" link +** DONE Make loading language-specific CSS files conditional on the presence of src blocks using those languages +** TODO Set up tracing so I can use warning and such +** TODO Make copying of language-specific CSS files conditional on the presence of src blocks using those languages +** TODO Switch to an entirely lazily-evaluated output tree +** TODO Add highlighting for languages [0/2] +*** TODO bash +*** TODO gnuplot +** TODO Bug: carry over highlight starts when breaking lines diff --git a/default_environment/stylesheet/language_python.css b/default_environment/stylesheet/language_python.css index e620691..266d5e1 100644 --- a/default_environment/stylesheet/language_python.css +++ b/default_environment/stylesheet/language_python.css @@ -1,38 +1,50 @@ +/* ea912c */ +/* e95a62 */ :root { - --srclg-python-srchl-keyword-color: #1a936f; --srclg-python-srchl-comment-color: #048a81; - --srclg-python-srchl-property-color: #bfbccb; + --srclg-python-srchl-function-builtin-color: #e95a62; + --srclg-python-srchl-keyword-color: #1a936f; + --srclg-python-srchl-property-color: inherit; --srclg-python-srchl-string-color: #ecc30b; - --srclg-python-srchl-string-special-path-color: #067bc2; + --srclg-python-srchl-type-color: #067bc2; + --srclg-python-srchl-variable-color: #ea912c; } @media (prefers-color-scheme: light) { :root { - --srclg-python-srchl-keyword-color: #e56c90; --srclg-python-srchl-comment-color: #fb757e; - --srclg-python-srchl-property-color: #404334; + --srclg-python-srchl-function-builtin-color: #16a59d; + --srclg-python-srchl-keyword-color: #e56c90; + --srclg-python-srchl-property-color: inherit; --srclg-python-srchl-string-color: #133cf4; - --srclg-python-srchl-string-special-path-color: #f9843d; + --srclg-python-srchl-type-color: #f9843d; + --srclg-python-srchl-variable-color: #156ed3; } } .main_content { .src_block { &.srclg_python { - .srchl_keyword { - color: var(--srclg-python-srchl-keyword-color); - } .srchl_comment { color: var(--srclg-python-srchl-comment-color); } + .srchl_function_builtin { + color: var(--srclg-python-srchl-function-builtin-color); + } + .srchl_keyword { + color: var(--srclg-python-srchl-keyword-color); + } .srchl_property { color: var(--srclg-python-srchl-property-color); } .srchl_string { color: var(--srclg-python-srchl-string-color); } - .srchl_string_special_path { - color: var(--srclg-python-srchl-string-special-path-color); + .srchl_type { + color: var(--srclg-python-srchl-type-color); + } + .srchl_variable { + color: var(--srclg-python-srchl-variable-color); } } } diff --git a/default_environment/stylesheet/main.css b/default_environment/stylesheet/main.css index 5c5ef00..46aae3f 100644 --- a/default_environment/stylesheet/main.css +++ b/default_environment/stylesheet/main.css @@ -161,6 +161,7 @@ body { font-size: 1rem; font-family: var(--src-font-family); margin: 1rem 0; + font-variant-ligatures: none; .src_language { display: inline-block; diff --git a/src/intermediate/src_block.rs b/src/intermediate/src_block.rs index 6238dc5..7b357ab 100644 --- a/src/intermediate/src_block.rs +++ b/src/intermediate/src_block.rs @@ -226,7 +226,15 @@ fn highlight_python(lines: &[L]) -> Result, CustomError> where L: Borrow, { - let highlight_names = ["comment"]; + let highlight_names = [ + "comment", + "function.builtin", + "keyword", + "property", + "string", + "type", + "variable", + ]; let language = tree_sitter_python::LANGUAGE.into(); let mut config = HighlightConfiguration::new( language, From 75a763569b753f7dc505b6e1a452e11e9d011711 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 22 Feb 2025 18:27:45 -0500 Subject: [PATCH 14/19] Disable ligatures in all code areas. --- default_environment/stylesheet/main.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/default_environment/stylesheet/main.css b/default_environment/stylesheet/main.css index 46aae3f..68562bb 100644 --- a/default_environment/stylesheet/main.css +++ b/default_environment/stylesheet/main.css @@ -186,11 +186,13 @@ body { .inline_source_block { font-family: var(--src-font-family); font-size: 1.2rem; + font-variant-ligatures: none; } .code, .verbatim { font-family: var(--src-font-family); + font-variant-ligatures: none; } .quote_block { From 41927764fc6f79240295f4a75f0c6cf3654af59a Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 22 Feb 2025 18:39:41 -0500 Subject: [PATCH 15/19] Continue highlights across code block lines. --- TODO.org | 4 ++-- src/intermediate/src_block.rs | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/TODO.org b/TODO.org index 46cb300..cb2226c 100644 --- a/TODO.org +++ b/TODO.org @@ -1,4 +1,4 @@ -* Things to do [3/14] +* Things to do [4/14] ** DONE If the paragraph only contains an image, text-align center ** DONE Syntax highlighting for code blocks ** TODO Render gnuplot @@ -14,4 +14,4 @@ ** TODO Add highlighting for languages [0/2] *** TODO bash *** TODO gnuplot -** TODO Bug: carry over highlight starts when breaking lines +** DONE Bug: carry over highlight starts when breaking lines diff --git a/src/intermediate/src_block.rs b/src/intermediate/src_block.rs index 7b357ab..4588256 100644 --- a/src/intermediate/src_block.rs +++ b/src/intermediate/src_block.rs @@ -169,6 +169,7 @@ where let mut highlighted_text: Vec = Vec::with_capacity(lines.len()); let mut current_line = ISrcLine::new(); + let mut highlight_stack: Vec<&str> = Vec::new(); for event in highlights { match event.unwrap() { HighlightEvent::Source { start, end } => { @@ -178,8 +179,22 @@ where current_line .children .push(ISrcSegment::RawText(first_line.to_owned())); + current_line.children.extend( + highlight_stack + .iter() + .map(|_name| ISrcSegment::HighlightEnd), + ); highlighted_text.push(current_line); current_line = ISrcLine::new(); + current_line + .children + .extend( + highlight_stack + .iter() + .map(|name| ISrcSegment::HighlightStart { + name: (*name).into(), + }), + ); span = &span[(line_break_index + 1)..]; } if !span.is_empty() { @@ -189,16 +204,20 @@ where } } HighlightEvent::HighlightStart(s) => { + highlight_stack.push(highlight_names[s.0]); current_line.children.push(ISrcSegment::HighlightStart { name: highlight_names[s.0].into(), }); } HighlightEvent::HighlightEnd => { + highlight_stack.pop(); current_line.children.push(ISrcSegment::HighlightEnd); } } } + debug_assert!(highlight_stack.is_empty()); + Ok(highlighted_text) } From c501f7cedc8d57ba3eceb8f20a42d79626c0b121 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 22 Feb 2025 18:57:42 -0500 Subject: [PATCH 16/19] Add syntax highlighting for bash. --- Cargo.lock | 11 ++++++ Cargo.toml | 1 + TODO.org | 5 ++- .../stylesheet/language_bash.css | 39 +++++++++++++++++++ src/context/src_block.rs | 7 ++++ src/intermediate/src_block.rs | 25 ++++++++++++ 6 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 default_environment/stylesheet/language_bash.css diff --git a/Cargo.lock b/Cargo.lock index ec733b8..9808599 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -463,6 +463,7 @@ dependencies = [ "serde_json", "tokio", "toml", + "tree-sitter-bash", "tree-sitter-highlight", "tree-sitter-nix", "tree-sitter-python", @@ -790,6 +791,16 @@ dependencies = [ "tree-sitter-language", ] +[[package]] +name = "tree-sitter-bash" +version = "0.23.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "329a4d48623ac337d42b1df84e81a1c9dbb2946907c102ca72db158c1964a52e" +dependencies = [ + "cc", + "tree-sitter-language", +] + [[package]] name = "tree-sitter-highlight" version = "0.25.2" diff --git a/Cargo.toml b/Cargo.toml index b5ad31d..b9a5a57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ serde = { version = "1.0.189", default-features = false, features = ["std", "der serde_json = "1.0.107" tokio = { version = "1.30.0", default-features = false, features = ["rt", "rt-multi-thread", "fs", "io-util"] } toml = "0.8.2" +tree-sitter-bash = "0.23.3" tree-sitter-highlight = "0.25.2" tree-sitter-nix = "0.0.2" tree-sitter-python = "0.23.6" diff --git a/TODO.org b/TODO.org index cb2226c..3f4c277 100644 --- a/TODO.org +++ b/TODO.org @@ -11,7 +11,8 @@ ** TODO Set up tracing so I can use warning and such ** TODO Make copying of language-specific CSS files conditional on the presence of src blocks using those languages ** TODO Switch to an entirely lazily-evaluated output tree -** TODO Add highlighting for languages [0/2] -*** TODO bash +** TODO Add highlighting for languages [1/2] +*** DONE bash *** TODO gnuplot +https://github.com/dpezto/tree-sitter-gnuplot is not on crates.io so I'd have to add a git dependency to use it. This would prevent publishing this crate to crates.io. ** DONE Bug: carry over highlight starts when breaking lines diff --git a/default_environment/stylesheet/language_bash.css b/default_environment/stylesheet/language_bash.css new file mode 100644 index 0000000..43bbac6 --- /dev/null +++ b/default_environment/stylesheet/language_bash.css @@ -0,0 +1,39 @@ +:root { + --srclg-bash-srchl-comment-color: #048a81; + --srclg-bash-srchl-function-color: #e95a62; + --srclg-bash-srchl-keyword-color: #1a936f; + --srclg-bash-srchl-property-color: inherit; + --srclg-bash-srchl-string-color: #ecc30b; +} + +@media (prefers-color-scheme: light) { + :root { + --srclg-bash-srchl-comment-color: #fb757e; + --srclg-bash-srchl-function-color: #16a59d; + --srclg-bash-srchl-keyword-color: #e56c90; + --srclg-bash-srchl-property-color: inherit; + --srclg-bash-srchl-string-color: #133cf4; + } +} + +.main_content { + .src_block { + &.srclg_bash { + .srchl_comment { + color: var(--srclg-bash-srchl-comment-color); + } + .srchl_function { + color: var(--srclg-bash-srchl-function-color); + } + .srchl_keyword { + color: var(--srclg-bash-srchl-keyword-color); + } + .srchl_property { + color: var(--srclg-bash-srchl-property-color); + } + .srchl_string { + color: var(--srclg-bash-srchl-string-color); + } + } + } +} diff --git a/src/context/src_block.rs b/src/context/src_block.rs index 6ca36a9..58c06fd 100644 --- a/src/context/src_block.rs +++ b/src/context/src_block.rs @@ -56,6 +56,13 @@ render!(RenderSrcBlock, ISrcBlock, original, render_context, { }) .collect(); match original.language.as_ref().map(String::as_str) { + Some("bash") => { + render_context + .dependency_manager + .lock() + .unwrap() + .include_css("language_bash.css")?; + } Some("nix") => { render_context .dependency_manager diff --git a/src/intermediate/src_block.rs b/src/intermediate/src_block.rs index 4588256..cb4085f 100644 --- a/src/intermediate/src_block.rs +++ b/src/intermediate/src_block.rs @@ -80,6 +80,18 @@ intermediate!( let language = original.language.map(str::to_owned); match language.as_ref().map(String::as_str) { + Some(lang @ "bash") => { + let highlighted = highlight_bash(&lines); + if let Ok(highlighted) = highlighted { + return Ok(ISrcBlock { + lines: highlighted, + language, + post_blank: original.get_post_blank(), + }); + } else { + println!("Warning: Failed to highlight {} source.", lang); + } + } Some(lang @ "nix") => { let highlighted = highlight_nix(&lines); if let Ok(highlighted) = highlighted { @@ -221,6 +233,19 @@ where Ok(highlighted_text) } +fn highlight_bash(lines: &[L]) -> Result, CustomError> +where + L: Borrow, +{ + let highlight_names = ["comment", "function", "keyword", "property", "string"]; + let language = tree_sitter_bash::LANGUAGE.into(); + let mut config = + HighlightConfiguration::new(language, "bash", tree_sitter_bash::HIGHLIGHT_QUERY, "", "") + .unwrap(); + config.configure(&highlight_names); + highlight_tree_sitter(config, &highlight_names, lines) +} + fn highlight_nix(lines: &[L]) -> Result, CustomError> where L: Borrow, From 7d73a3c9488474ef54ee716394401cf012a60b20 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 22 Feb 2025 19:24:05 -0500 Subject: [PATCH 17/19] Clean up. --- src/context/dependency.rs | 2 +- src/intermediate/paragraph.rs | 6 ++---- src/intermediate/src_block.rs | 2 -- src/intermediate/timestamp.rs | 1 - 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/context/dependency.rs b/src/context/dependency.rs index 6e537f5..9b42aaf 100644 --- a/src/context/dependency.rs +++ b/src/context/dependency.rs @@ -41,7 +41,7 @@ impl Dependency { } Ok(()) } - Dependency::CssFile { name } => { + Dependency::CssFile { name: _ } => { // We don't do anything because the CSS files are already copied into the output from natter's default environment. // TODO: When we add support for CSS outside the default environment, we should add support for dynamically picking which ones to copy here. Ok(()) diff --git a/src/intermediate/paragraph.rs b/src/intermediate/paragraph.rs index b9e134c..bf57a4a 100644 --- a/src/intermediate/paragraph.rs +++ b/src/intermediate/paragraph.rs @@ -1,5 +1,3 @@ -use std::any::Any; - use super::macros::intermediate; use super::IObject; use crate::error::CustomError; @@ -53,7 +51,7 @@ impl IParagraph { .iter() .filter(|c| match c { IObject::RegularLink(iregular_link) => match &iregular_link.target { - super::LinkTarget::Image { src, alt } => true, + super::LinkTarget::Image { src: _, alt: _ } => true, _ => false, }, _ => false, @@ -62,7 +60,7 @@ impl IParagraph { num_images == 1 && self.children.iter().all(|c| match c { IObject::RegularLink(iregular_link) => match &iregular_link.target { - super::LinkTarget::Image { src, alt } => true, + super::LinkTarget::Image { src: _, alt: _ } => true, _ => false, }, IObject::PlainText(iplain_text) => { diff --git a/src/intermediate/src_block.rs b/src/intermediate/src_block.rs index cb4085f..39c018d 100644 --- a/src/intermediate/src_block.rs +++ b/src/intermediate/src_block.rs @@ -1,10 +1,8 @@ use std::borrow::Borrow; -use std::borrow::Cow; use super::macros::intermediate; use crate::error::CustomError; use organic::types::StandardProperties; -use tree_sitter_highlight::Highlight; use tree_sitter_highlight::HighlightConfiguration; use tree_sitter_highlight::HighlightEvent; use tree_sitter_highlight::Highlighter; diff --git a/src/intermediate/timestamp.rs b/src/intermediate/timestamp.rs index 8df50f4..8e2ca2a 100644 --- a/src/intermediate/timestamp.rs +++ b/src/intermediate/timestamp.rs @@ -1,4 +1,3 @@ -use super::macros::inoop; use super::macros::intermediate; use super::util::coalesce_whitespace; use crate::error::CustomError; From 8d85d5ef79e603ac27659a370ad398b4c57b72a0 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 22 Feb 2025 19:42:24 -0500 Subject: [PATCH 18/19] Fix clippy. --- src/context/src_block.rs | 2 +- src/intermediate/footnote_definition.rs | 1 + src/intermediate/macros.rs | 1 + src/intermediate/paragraph.rs | 17 +++++++++-------- src/intermediate/registry.rs | 1 + src/intermediate/regular_link.rs | 9 ++++----- src/intermediate/src_block.rs | 4 ++-- 7 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/context/src_block.rs b/src/context/src_block.rs index 58c06fd..cfc98dc 100644 --- a/src/context/src_block.rs +++ b/src/context/src_block.rs @@ -55,7 +55,7 @@ render!(RenderSrcBlock, ISrcBlock, original, render_context, { RenderSrcLine { children } }) .collect(); - match original.language.as_ref().map(String::as_str) { + match original.language.as_deref() { Some("bash") => { render_context .dependency_manager diff --git a/src/intermediate/footnote_definition.rs b/src/intermediate/footnote_definition.rs index 38085bf..f82753e 100644 --- a/src/intermediate/footnote_definition.rs +++ b/src/intermediate/footnote_definition.rs @@ -32,6 +32,7 @@ pub(crate) struct IRealFootnoteDefinition { } impl IRealFootnoteDefinition { + #[allow(clippy::needless_lifetimes)] pub(crate) async fn new<'orig, 'parse>( _intermediate_context: IntermediateContext<'orig, 'parse>, footnote_id: usize, diff --git a/src/intermediate/macros.rs b/src/intermediate/macros.rs index 54f877e..203355d 100644 --- a/src/intermediate/macros.rs +++ b/src/intermediate/macros.rs @@ -9,6 +9,7 @@ macro_rules! inoop { } impl $istruct { + #[allow(clippy::extra_unused_lifetimes)] pub(crate) async fn new<'reg, 'orig, 'parse>( _intermediate_context: crate::intermediate::IntermediateContext<'orig, 'parse>, original: &'orig organic::types::$pstruct<'parse>, diff --git a/src/intermediate/paragraph.rs b/src/intermediate/paragraph.rs index bf57a4a..6b022b6 100644 --- a/src/intermediate/paragraph.rs +++ b/src/intermediate/paragraph.rs @@ -31,6 +31,7 @@ intermediate!( ); impl IParagraph { + #[allow(clippy::needless_lifetimes)] pub(crate) async fn artificial<'orig, 'parse>( _intermediate_context: crate::intermediate::IntermediateContext<'orig, 'parse>, children: Vec, @@ -50,19 +51,19 @@ impl IParagraph { .children .iter() .filter(|c| match c { - IObject::RegularLink(iregular_link) => match &iregular_link.target { - super::LinkTarget::Image { src: _, alt: _ } => true, - _ => false, - }, + IObject::RegularLink(iregular_link) => matches!( + &iregular_link.target, + super::LinkTarget::Image { src: _, alt: _ } + ), _ => false, }) .count(); num_images == 1 && self.children.iter().all(|c| match c { - IObject::RegularLink(iregular_link) => match &iregular_link.target { - super::LinkTarget::Image { src: _, alt: _ } => true, - _ => false, - }, + IObject::RegularLink(iregular_link) => matches!( + &iregular_link.target, + super::LinkTarget::Image { src: _, alt: _ } + ), IObject::PlainText(iplain_text) => { iplain_text.source.chars().all(|c| c.is_ascii_whitespace()) } diff --git a/src/intermediate/registry.rs b/src/intermediate/registry.rs index f9335b7..30d5830 100644 --- a/src/intermediate/registry.rs +++ b/src/intermediate/registry.rs @@ -180,6 +180,7 @@ async fn convert_definition_contents<'orig, 'parse>( } /// Take a footnote definition that has not yet received a reference and move it into the active footnotes. +#[allow(clippy::needless_lifetimes)] pub(crate) async fn promote_footnote_definition<'orig, 'parse>( intermediate_context: IntermediateContext<'orig, 'parse>, label: &'parse str, diff --git a/src/intermediate/regular_link.rs b/src/intermediate/regular_link.rs index 63544e0..8f384b6 100644 --- a/src/intermediate/regular_link.rs +++ b/src/intermediate/regular_link.rs @@ -186,7 +186,7 @@ impl LinkTarget { if path.is_absolute() { return Ok(format!("file://{}", input)); } - return Ok(input.into_owned()); + Ok(input.into_owned()) } /// Get file name from the last segment of an image path. @@ -199,7 +199,7 @@ impl LinkTarget { let path = Path::new(input.as_ref()); match path .components() - .last() + .next_back() .ok_or("Images should have at least one component in their path.")? { std::path::Component::Prefix(_) => { @@ -209,9 +209,7 @@ impl LinkTarget { std::path::Component::RootDir | std::path::Component::CurDir | std::path::Component::ParentDir => { - return Err( - "Final component of an image path should be a normal component.".into(), - ); + Err("Final component of an image path should be a normal component.".into()) } std::path::Component::Normal(file_name) => Ok(file_name .to_str() @@ -236,6 +234,7 @@ mod tests { let registry = Registry::new(); let registry = Arc::new(Mutex::new(registry)); let intermediate_context = IntermediateContext::new(registry)?; + #[allow(clippy::single_element_loop)] for (inp, typ) in [( "https://test.example/foo", LinkType::Protocol(Cow::from("https")), diff --git a/src/intermediate/src_block.rs b/src/intermediate/src_block.rs index 39c018d..e13c518 100644 --- a/src/intermediate/src_block.rs +++ b/src/intermediate/src_block.rs @@ -77,7 +77,7 @@ intermediate!( .collect(); let language = original.language.map(str::to_owned); - match language.as_ref().map(String::as_str) { + match language.as_deref() { Some(lang @ "bash") => { let highlighted = highlight_bash(&lines); if let Ok(highlighted) = highlighted { @@ -151,7 +151,7 @@ where std::string::String: for<'a> From<&'a L>, { Ok(lines - .into_iter() + .iter() .map(|l| { let mut line = ISrcLine::new(); line.children.push(ISrcSegment::RawText(l.into())); From 4e34ebc29eda323c34b357d263b48624a1c774b8 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 22 Feb 2025 19:45:04 -0500 Subject: [PATCH 19/19] Reformat css. --- default_environment/stylesheet/main.css | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/default_environment/stylesheet/main.css b/default_environment/stylesheet/main.css index 68562bb..77e69ec 100644 --- a/default_environment/stylesheet/main.css +++ b/default_environment/stylesheet/main.css @@ -9,9 +9,8 @@ --blog-post-background-color: #0a0a0a; - --src-font-family: - ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas, - "DejaVu Sans Mono", monospace; + --src-font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, + Consolas, "DejaVu Sans Mono", monospace; --src-block-background-color: #141414; --src-block-border-color: #84828f; @@ -56,9 +55,8 @@ body { color: var(--site-text-color); background-color: var(--site-background-color); - font-family: - source-sans-pro, Seravek, "Gill Sans Nova", Ubuntu, Calibri, "DejaVu Sans", - sans-serif; + font-family: source-sans-pro, Seravek, "Gill Sans Nova", Ubuntu, Calibri, + "DejaVu Sans", sans-serif; a:link, a:visited {