8b0f128313
Signed-off-by: Gato <cedric@goutailler-olivier.fr>
109 lines
3.5 KiB
Rust
109 lines
3.5 KiB
Rust
use comrak::plugins::syntect::SyntectAdapterBuilder;
|
|
use comrak::{markdown_to_html_with_plugins, Options, Plugins};
|
|
use syntect::highlighting::ThemeSet;
|
|
use syntect::html::{css_for_theme_with_class_style, ClassStyle};
|
|
use syntect::parsing::SyntaxDefinition;
|
|
|
|
use crate::domain::MarkdownRenderer;
|
|
|
|
const TYPESCRIPT_SYNTAX: &str =
|
|
include_str!("../../resources/syntaxes/TypeScript.sublime-syntax");
|
|
|
|
fn build_syntax_set() -> syntect::parsing::SyntaxSet {
|
|
let mut builder = syntect::parsing::SyntaxSet::load_defaults_nonewlines().into_builder();
|
|
if let Ok(def) = SyntaxDefinition::load_from_str(TYPESCRIPT_SYNTAX, true, None) {
|
|
builder.add(def);
|
|
}
|
|
builder.build()
|
|
}
|
|
|
|
pub struct ComrakRenderer;
|
|
pub struct ComrakPreviewRenderer;
|
|
|
|
impl MarkdownRenderer for ComrakRenderer {
|
|
fn render(&self, content: &str) -> String {
|
|
let adapter = SyntectAdapterBuilder::new()
|
|
.syntax_set(build_syntax_set())
|
|
.css()
|
|
.build();
|
|
let mut options = Options::default();
|
|
options.extension.table = true;
|
|
options.extension.strikethrough = true;
|
|
options.extension.autolink = true;
|
|
options.extension.tasklist = true;
|
|
let mut plugins = Plugins::default();
|
|
plugins.render.codefence_syntax_highlighter = Some(&adapter);
|
|
markdown_to_html_with_plugins(content, &options, &plugins)
|
|
}
|
|
}
|
|
|
|
impl MarkdownRenderer for ComrakPreviewRenderer {
|
|
fn render(&self, content: &str) -> String {
|
|
let adapter = SyntectAdapterBuilder::new()
|
|
.syntax_set(build_syntax_set())
|
|
.css()
|
|
.build();
|
|
let options = Options::default();
|
|
let mut plugins = Plugins::default();
|
|
plugins.render.codefence_syntax_highlighter = Some(&adapter);
|
|
markdown_to_html_with_plugins(content, &options, &plugins)
|
|
}
|
|
}
|
|
|
|
pub fn syntax_css_for_theme(syntect_theme_name: &str) -> String {
|
|
let ts = ThemeSet::load_defaults();
|
|
let theme = match ts.themes.get(syntect_theme_name) {
|
|
Some(t) => t,
|
|
None => return String::new(),
|
|
};
|
|
match css_for_theme_with_class_style(theme, ClassStyle::Spaced) {
|
|
Ok(css) => css.replace(".code {", ".syntax-highlighting {"),
|
|
Err(_) => String::new(),
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn render_outputs_syntax_highlighting_class() {
|
|
let r = ComrakRenderer;
|
|
let html = r.render("```rust\nfn main() {}\n```\n");
|
|
assert!(html.contains("syntax-highlighting"));
|
|
}
|
|
|
|
#[test]
|
|
fn render_outputs_classed_spans() {
|
|
let r = ComrakRenderer;
|
|
let html = r.render("```rust\nfn main() {}\n```\n");
|
|
assert!(html.contains("<span class="));
|
|
}
|
|
|
|
#[test]
|
|
fn typescript_syntax_is_highlighted() {
|
|
let r = ComrakRenderer;
|
|
let html = r.render("```typescript\ninterface Foo { id: string; }\n```\n");
|
|
assert!(html.contains("<span class="), "TypeScript should produce highlighted spans");
|
|
}
|
|
|
|
#[test]
|
|
fn syntax_css_known_theme_contains_wrapper() {
|
|
let css = syntax_css_for_theme("base16-ocean.dark");
|
|
assert!(!css.is_empty());
|
|
assert!(css.contains(".syntax-highlighting {"));
|
|
}
|
|
|
|
#[test]
|
|
fn syntax_css_known_theme_contains_token_rules() {
|
|
let css = syntax_css_for_theme("base16-ocean.dark");
|
|
assert!(css.contains("color:") || css.contains("color :"));
|
|
}
|
|
|
|
#[test]
|
|
fn syntax_css_unknown_theme_returns_empty() {
|
|
let css = syntax_css_for_theme("nonexistent-theme-xyz");
|
|
assert!(css.is_empty());
|
|
}
|
|
}
|