diff --git a/Cargo.lock b/Cargo.lock index 296f15e7..336ea394 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1765,6 +1765,7 @@ dependencies = [ "tempfile", "tiny-skia", "toml", + "two-face", "twox-hash", "usvg", "wgpu", @@ -3932,6 +3933,16 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44dcf002ae3b32cd25400d6df128c5babec3927cd1eb7ce813cfff20eb6c3746" +[[package]] +name = "two-face" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "655eebf0a106a5b97728bfdc108e76a7f36f069625b328ddf02627ce3d5e9452" +dependencies = [ + "serde", + "syntect", +] + [[package]] name = "twox-hash" version = "1.6.3" diff --git a/Cargo.toml b/Cargo.toml index 7fd2ca3d..5986fe44 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,11 @@ twox-hash = "1.6.3" taffy = { git = "https://github.com/DioxusLabs/taffy", rev = "d338f3731da519d182bbc074de46382984ab7c4a" } syntect = "5.0.0" +[dependencies.two-face] +version = "0.1.1" +default-features = false +features = ["extra-syntax"] + [dependencies.glyphon] version = "0.2" git = "https://github.com/trimental/glyphon" diff --git a/src/color.rs b/src/color.rs index 0bbc654c..38015dd4 100644 --- a/src/color.rs +++ b/src/color.rs @@ -107,6 +107,9 @@ impl PartialEq for Theme { } } +// TODO: the error message here degraded when switching this to allow for custom themes. It'd be +// good to still list all the default themes when passing in something else. Add a test for +// the error message #[derive(Deserialize, Clone, Debug, PartialEq, Eq)] #[serde(untagged)] pub enum SyntaxTheme { diff --git a/src/interpreter/snapshots/inlyne__interpreter__tests__handles_comma_in_info_str.snap b/src/interpreter/snapshots/inlyne__interpreter__tests__handles_comma_in_info_str.snap new file mode 100644 index 00000000..8662f026 --- /dev/null +++ b/src/interpreter/snapshots/inlyne__interpreter__tests__handles_comma_in_info_str.snap @@ -0,0 +1,56 @@ +--- +source: src/interpreter/tests.rs +description: " --- md\n\n```rust,ignore\nlet v = 1;\n```\n\n\n --- html\n\n
let v = 1;\n
\n" +expression: interpret_md(text) +--- +[ + TextBox( + TextBox { + background_color: Some(Color { r: 0.92, g: 0.94, b: 0.96 }), + is_code_block: true, + texts: [ + Text { + text: "let", + font_family: Monospace, + color: Some(Color { r: 0.39, g: 0.01, b: 0.11 }), + style: BOLD , + .. + }, + Text { + text: " v ", + font_family: Monospace, + color: Some(Color { r: 0.03, g: 0.03, b: 0.03 }), + .. + }, + Text { + text: "= ", + font_family: Monospace, + color: Some(Color { r: 0.39, g: 0.01, b: 0.11 }), + style: BOLD , + .. + }, + Text { + text: "1", + font_family: Monospace, + color: Some(Color { r: 0.00, g: 0.24, b: 0.45 }), + .. + }, + Text { + text: ";", + font_family: Monospace, + color: Some(Color { r: 0.03, g: 0.03, b: 0.03 }), + .. + }, + Text { + text: "\n", + default_color: Color(BLACK), + .. + }, + ], + .. + }, + ), + Spacer( + InvisibleSpacer(5), + ), +] diff --git a/src/interpreter/snapshots/inlyne__interpreter__tests__toml_gets_highlighted.snap b/src/interpreter/snapshots/inlyne__interpreter__tests__toml_gets_highlighted.snap new file mode 100644 index 00000000..fa1f34fc --- /dev/null +++ b/src/interpreter/snapshots/inlyne__interpreter__tests__toml_gets_highlighted.snap @@ -0,0 +1,42 @@ +--- +source: src/interpreter/tests.rs +description: " --- md\n\n```toml\nkey = 123\n```\n\n\n --- html\n\n
key = 123\n
\n" +expression: interpret_md(text) +--- +[ + TextBox( + TextBox { + background_color: Some(Color { r: 0.92, g: 0.94, b: 0.96 }), + is_code_block: true, + texts: [ + Text { + text: "key ", + font_family: Monospace, + color: Some(Color { r: 0.12, g: 0.37, b: 0.11 }), + .. + }, + Text { + text: "= ", + font_family: Monospace, + color: Some(Color { r: 0.03, g: 0.03, b: 0.03 }), + .. + }, + Text { + text: "123", + font_family: Monospace, + color: Some(Color { r: 0.00, g: 0.24, b: 0.45 }), + .. + }, + Text { + text: "\n", + default_color: Color(BLACK), + .. + }, + ], + .. + }, + ), + Spacer( + InvisibleSpacer(5), + ), +] diff --git a/src/interpreter/tests.rs b/src/interpreter/tests.rs index 73e6e468..002f9360 100644 --- a/src/interpreter/tests.rs +++ b/src/interpreter/tests.rs @@ -155,11 +155,25 @@ In a paragraph https://example.org - In a list https://example.org "; +const TOML_GETS_HIGHLIGHTED: &str = "\ +```toml +key = 123 +``` +"; + +const HANDLES_COMMA_IN_INFO_STR: &str = "\ +```rust,ignore +let v = 1; +``` +"; + snapshot_interpreted_elements!( (footnotes_list_prefix, FOOTNOTES_LIST_PREFIX), (checklist_has_no_text_prefix, CHECKLIST_HAS_NO_TEXT_PREFIX), (code_block_bg_color, CODE_BLOCK_BG_COLOR), (bare_link_gets_autolinked, BARE_LINK_GETS_AUTOLINKED), + (toml_gets_highlighted, TOML_GETS_HIGHLIGHTED), + (handles_comma_in_info_str, HANDLES_COMMA_IN_INFO_STR), ); /// Spin up a server, so we can test network requests without external services diff --git a/src/utils.rs b/src/utils.rs index 4969bc91..bb24c94a 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,10 +1,14 @@ use std::{ collections::HashMap, + io, sync::{Arc, Mutex}, }; use comrak::{ - markdown_to_html_with_plugins, plugins::syntect::SyntectAdapterBuilder, ComrakOptions, + adapters::SyntaxHighlighterAdapter, + markdown_to_html_with_plugins, + plugins::syntect::{SyntectAdapter, SyntectAdapterBuilder}, + ComrakOptions, }; use indexmap::IndexMap; use serde::Deserialize; @@ -77,6 +81,39 @@ impl From for HoverInfo { } } +// TODO(cosmic): Remove after `comrak` supports code block info strings that have a comma +// (like ```rust,ignore) +// https://github.com/kivikakk/comrak/issues/246 +struct CustomSyntectAdapter(SyntectAdapter); + +impl SyntaxHighlighterAdapter for CustomSyntectAdapter { + fn write_highlighted( + &self, + output: &mut dyn io::Write, + lang: Option<&str>, + code: &str, + ) -> io::Result<()> { + let norm_lang = lang.map(|l| l.split_once(',').map(|(lang, _)| lang).unwrap_or(l)); + self.0.write_highlighted(output, norm_lang, code) + } + + fn write_pre_tag( + &self, + output: &mut dyn io::Write, + attributes: HashMap, + ) -> io::Result<()> { + self.0.write_pre_tag(output, attributes) + } + + fn write_code_tag( + &self, + output: &mut dyn io::Write, + attributes: HashMap, + ) -> io::Result<()> { + self.0.write_code_tag(output, attributes) + } +} + pub fn markdown_to_html(md: &str, syntax_theme: SyntectTheme) -> String { let mut options = ComrakOptions::default(); options.extension.autolink = true; @@ -95,14 +132,16 @@ pub fn markdown_to_html(md: &str, syntax_theme: SyntectTheme) -> String { theme_set .themes .insert(String::from(dummy_name), syntax_theme); + let syn_set = two_face::syntax::extra(); let adapter = SyntectAdapterBuilder::new() + .syntax_set(syn_set) .theme_set(theme_set) - // .theme(syntax_theme.as_syntect_name()) .theme(dummy_name) .build(); let mut plugins = comrak::ComrakPlugins::default(); - plugins.render.codefence_syntax_highlighter = Some(&adapter); + let custom = CustomSyntectAdapter(adapter); + plugins.render.codefence_syntax_highlighter = Some(&custom); let htmlified = markdown_to_html_with_plugins(md, &options, &plugins);