From d4e469508f9bb9fdd8c349c53ab4fd0ef5f3e87e Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 11 Jan 2020 02:17:19 +0000 Subject: refactor(cheddar): Extract code block highlighting into function Since I am going down the path of adding additional Markdown extensions it makes sense to avoid letting `format_markdown` turn into a giant beast of a function. Therefore this commit extracts the logic for rendering code blocks via syntect and changes the innards of `format_markdown` to instead provide arbitrary AST value replacements. --- tools/cheddar/src/main.rs | 84 ++++++++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 37 deletions(-) (limited to 'tools/cheddar/src') diff --git a/tools/cheddar/src/main.rs b/tools/cheddar/src/main.rs index d0115d391c..3eb6fc605e 100644 --- a/tools/cheddar/src/main.rs +++ b/tools/cheddar/src/main.rs @@ -1,4 +1,4 @@ -use comrak::nodes::{AstNode, NodeValue, NodeHtmlBlock}; +use comrak::nodes::{Ast, AstNode, NodeValue, NodeCodeBlock, NodeHtmlBlock}; use comrak::{Arena, parse_document, format_html, ComrakOptions}; use lazy_static::lazy_static; use std::env; @@ -80,9 +80,48 @@ fn iter_nodes<'a, F>(node: &'a AstNode<'a>, f: &F) where F : Fn(&'a AstNode<'a>) // Instead, try finding a syntax match by comparing case insensitively (for // ASCII characters, anyways). fn find_syntax_case_insensitive(info: &str) -> Option<&'static SyntaxReference> { + // TODO(tazjin): memoize this lookup SYNTAXES.syntaxes().iter().rev().find(|&s| info.eq_ignore_ascii_case(&s.name)) } +// Replaces code-block inside of a Markdown AST with HTML blocks rendered by +// syntect. This enables static (i.e. no JavaScript) syntax highlighting, even +// of complex languages. +fn highlight_code_block(code_block: &NodeCodeBlock) -> NodeValue { + let theme = &THEMES.themes["InspiredGitHub"]; + let info = String::from_utf8_lossy(&code_block.info); + + let syntax = find_syntax_case_insensitive(&info) + .or_else(|| SYNTAXES.find_syntax_by_extension(&info)) + .unwrap_or_else(|| SYNTAXES.find_syntax_plain_text()); + + let code = String::from_utf8_lossy(&code_block.literal); + + let rendered = { + // Write the block preamble manually to get exactly the + // desired layout: + let mut hl = HighlightLines::new(syntax, theme); + let mut buf = BLOCK_PRE.to_string(); + + for line in LinesWithEndings::from(&code) { + let regions = hl.highlight(line, &SYNTAXES); + append_highlighted_html_for_styled_line( + ®ions[..], IncludeBackground::No, &mut buf, + ); + } + + buf.push_str(""); + buf + }; + + let block = NodeHtmlBlock { + block_type: 1, // It's unclear what behaviour is toggled by this + literal: rendered.into_bytes(), + }; + + NodeValue::HtmlBlock(block) +} + fn format_markdown() { let document = { let mut buffer = String::new(); @@ -99,43 +138,14 @@ fn format_markdown() { // replacing all code blocks with HTML blocks rendered by syntect. iter_nodes(root, &|node| { let mut ast = node.data.borrow_mut(); - match &ast.value { - NodeValue::CodeBlock(code_block) => { - let theme = &THEMES.themes["InspiredGitHub"]; - let info = String::from_utf8_lossy(&code_block.info); - - let syntax = find_syntax_case_insensitive(&info) - .or_else(|| SYNTAXES.find_syntax_by_extension(&info)) - .unwrap_or_else(|| SYNTAXES.find_syntax_plain_text()); - - let code = String::from_utf8_lossy(&code_block.literal); - - let rendered = { - // Write the block preamble manually to get exactly the - // desired layout: - let mut hl = HighlightLines::new(syntax, theme); - let mut buf = BLOCK_PRE.to_string(); - - for line in LinesWithEndings::from(&code) { - let regions = hl.highlight(line, &SYNTAXES); - append_highlighted_html_for_styled_line( - ®ions[..], IncludeBackground::No, &mut buf, - ); - } - - buf.push_str(""); - buf - }; - - let block = NodeHtmlBlock { - block_type: 1, // It's unclear what behaviour is toggled by this - literal: rendered.into_bytes(), - }; - - ast.value = NodeValue::HtmlBlock(block); - }, - _ => (), + let new = match &ast.value { + NodeValue::CodeBlock(code) => Some(highlight_code_block(code)), + _ => None, }; + + if let Some(new_value) = new { + ast.value = new_value + } }); format_html(root, &MD_OPTS, &mut io::stdout()) -- cgit 1.4.1