From 3ff39a3f9cdbac4e9300b9bb50b4fe2a19e93151 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 18 Jan 2026 04:30:53 +0000 Subject: [PATCH] refactor: update paxhtml to use bump allocator API - Update paxhtml dependency to latest version (a5342664) which uses bumpalo bump allocator for efficient memory management - Update all html! macro calls to use new `in bump;` syntax - Update all function signatures to accept bump allocator reference - Update Builder, Document, Element, and Attribute usage with new API - Update syntax highlighter to use bump allocator for Element creation --- Cargo.lock | 26 ++- src/main.rs | 508 ++++++++++++++++++++++++++++---------------------- src/syntax.rs | 11 +- 3 files changed, 316 insertions(+), 229 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ffba312..8e20488 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,6 +46,12 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +[[package]] +name = "bumpalo" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" + [[package]] name = "cc" version = "1.2.45" @@ -233,16 +239,30 @@ source = "git+https://github.com/philpax/parse-wiki-text-2.git?branch=wasm-suppo [[package]] name = "paxhtml" version = "0.1.0" -source = "git+https://github.com/philpax/paxhtml.git#26d0785acbb290ab96a70f7b39cef2ad7a18c388" +source = "git+https://github.com/philpax/paxhtml.git#a5342664a071bdf31f1b6b5432f325c03c9df6a0" dependencies = [ + "bumpalo", "html-escape", "paxhtml_macro", + "paxhtml_parser", ] [[package]] name = "paxhtml_macro" version = "0.1.0" -source = "git+https://github.com/philpax/paxhtml.git#26d0785acbb290ab96a70f7b39cef2ad7a18c388" +source = "git+https://github.com/philpax/paxhtml.git#a5342664a071bdf31f1b6b5432f325c03c9df6a0" +dependencies = [ + "convert_case", + "paxhtml_parser", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "paxhtml_parser" +version = "0.1.0" +source = "git+https://github.com/philpax/paxhtml.git#a5342664a071bdf31f1b6b5432f325c03c9df6a0" dependencies = [ "convert_case", "proc-macro2", @@ -253,7 +273,7 @@ dependencies = [ [[package]] name = "paxhtml_tailwind" version = "0.1.0" -source = "git+https://github.com/philpax/paxhtml.git#26d0785acbb290ab96a70f7b39cef2ad7a18c388" +source = "git+https://github.com/philpax/paxhtml.git#a5342664a071bdf31f1b6b5432f325c03c9df6a0" [[package]] name = "pkg-config" diff --git a/src/main.rs b/src/main.rs index 259ceb6..d35dc16 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,8 @@ use std::{ sync::OnceLock, }; +use paxhtml::bumpalo::{self, Bump}; + use serde::{Deserialize, Serialize}; use wikitext_simplified::{Span, Spanned, WikitextSimplifiedNode}; use wikitext_simplified_template_eval::{ @@ -52,7 +54,12 @@ fn main() -> anyhow::Result<()> { fs::write(output_dir.join("style/tailwind.css"), tailwind_css)?; // Generate wiki - generate_wiki(Path::new(WIKI_DIRECTORY), &output_dir.join(WIKI_DIRECTORY))?; + let bump = Bump::new(); + generate_wiki( + &bump, + Path::new(WIKI_DIRECTORY), + &output_dir.join(WIKI_DIRECTORY), + )?; Ok(()) } @@ -74,7 +81,11 @@ fn copy_files_recursively(src: &Path, dst: &Path) -> std::io::Result<()> { Ok(()) } -fn generate_missing_index_pages(dst_root: &Path, generated: &GeneratedPages) -> anyhow::Result<()> { +fn generate_missing_index_pages( + bump: &Bump, + dst_root: &Path, + generated: &GeneratedPages, +) -> anyhow::Result<()> { // Collect all directory paths that need index pages let mut dirs_needing_index = BTreeSet::new(); @@ -116,13 +127,14 @@ fn generate_missing_index_pages(dst_root: &Path, generated: &GeneratedPages) -> parts[..parts.len() - 1].join("/") }; - generate_index_page(dst_root, &parent_path, dir_name, generated)?; + generate_index_page(bump, dst_root, &parent_path, dir_name, generated)?; } Ok(()) } fn generate_index_page( + bump: &Bump, dst_root: &Path, parent_path: &str, dir_name: &str, @@ -173,7 +185,7 @@ fn generate_index_page( for child in all_children { let display_name = child.replace('_', " "); let link_path = format!("{}/{}", full_path, child); - items.push(paxhtml::html! { + items.push(paxhtml::html! { in bump;
  • {display_name} @@ -182,11 +194,11 @@ fn generate_index_page( }); } - let content = paxhtml::html! { + let content = paxhtml::html! { in bump;
      #{items}
    }; - let document = layout(&title, content); + let document = layout(bump, &title, content); // Write the document let route_path = page_title_to_route_path(&full_path); @@ -194,7 +206,7 @@ fn generate_index_page( // Also create a redirect from full_path/index.html to full_path.html // This allows both /category and /category/ to work - let redirect_doc = redirect(&route_path.url_path()); + let redirect_doc = redirect(bump, &route_path.url_path()); let redirect_route = paxhtml::RoutePath::new( route_path .url_path() @@ -208,7 +220,7 @@ fn generate_index_page( Ok(()) } -fn generate_wiki(src: &Path, dst: &Path) -> anyhow::Result<()> { +fn generate_wiki(bump: &Bump, src: &Path, dst: &Path) -> anyhow::Result<()> { fs::create_dir_all(dst)?; let context = PageTemplateContext::new(src)?; @@ -224,22 +236,33 @@ fn generate_wiki(src: &Path, dst: &Path) -> anyhow::Result<()> { fs::write(output_dir.join("style/syntax.css"), syntax_css)?; let mut generated = GeneratedPages::default(); - generate_wiki_folder(&context, &mut evaluator, src, dst, dst, "", &mut generated)?; + generate_wiki_folder( + bump, + &context, + &mut evaluator, + src, + dst, + dst, + "", + &mut generated, + )?; // Generate missing index pages - generate_missing_index_pages(output_dir, &generated)?; + generate_missing_index_pages(bump, output_dir, &generated)?; // Write search index let search_index_json = serde_json::to_string(&generated.search_index)?; fs::write(output_dir.join("search-index.json"), search_index_json)?; - redirect(&page_title_to_route_path("Main_Page").url_path()) + redirect(bump, &page_title_to_route_path("Main_Page").url_path()) .write_to_route(dst, paxhtml::RoutePath::new([], "index.html".to_string()))?; Ok(()) } +#[allow(clippy::too_many_arguments)] fn generate_wiki_folder( + bump: &Bump, context: &PageTemplateContext, evaluator: &mut TemplateEvaluator<'_>, src: &Path, @@ -263,6 +286,7 @@ fn generate_wiki_folder( format!("{}/{}", relative_path, dir_name) }; generate_wiki_folder( + bump, context, evaluator, &path, @@ -302,98 +326,101 @@ fn generate_wiki_folder( .map(|s| s.to_string()), ); - let document = - if let [node] = simplified.as_slice() - && let WikitextSimplifiedNode::Redirect { target } = &node.value - { - redirect(&page_title_to_route_path(target).url_path()) - } else { - let sub_page_name = path + let document = if let [node] = simplified.as_slice() + && let WikitextSimplifiedNode::Redirect { target } = &node.value + { + redirect(bump, &page_title_to_route_path(target).url_path()) + } else { + let sub_page_name = path + .with_extension("") + .file_name() + .unwrap() + .to_string_lossy() + .to_string(); + + // Update the template context with the current page's sub_page_name + context.set_sub_page_name(&sub_page_name); + + let page_context = PageContext { + input_path: path.clone(), + title: output_html_rel .with_extension("") - .file_name() + .to_str() + .map(|s| s.to_string()) .unwrap() - .to_string_lossy() - .to_string(); - - // Update the template context with the current page's sub_page_name - context.set_sub_page_name(&sub_page_name); - - let page_context = PageContext { - input_path: path.clone(), - title: output_html_rel - .with_extension("") - .to_str() - .map(|s| s.to_string()) - .unwrap() - .replace("\\", "/") - .replace("_", " "), - route_path: route_path.clone(), - }; + .replace("\\", "/") + .replace("_", " "), + route_path: route_path.clone(), + }; - // Extract search data from the page - let mut all_text = String::new(); - let mut all_headings = Vec::new(); - for node in &simplified { - let (text, headings) = extract_search_data(&node.value); - all_text.push_str(&text); - all_text.push(' '); - all_headings.extend(headings); - } + // Extract search data from the page + let mut all_text = String::new(); + let mut all_headings = Vec::new(); + for node in &simplified { + let (text, headings) = extract_search_data(&node.value); + all_text.push_str(&text); + all_text.push(' '); + all_headings.extend(headings); + } - // Write search text to file - let search_text_path = output_html.with_extension("txt"); - fs::write(&search_text_path, all_text.trim())?; + // Write search text to file + let search_text_path = output_html.with_extension("txt"); + fs::write(&search_text_path, all_text.trim())?; - // Add page title to search index - let page_idx = generated.search_index.pages.len(); - generated - .search_index - .pages - .push(page_context.title.clone()); + // Add page title to search index + let page_idx = generated.search_index.pages.len(); + generated + .search_index + .pages + .push(page_context.title.clone()); - // Build word weight map for this page - let mut word_weights: BTreeMap = BTreeMap::new(); + // Build word weight map for this page + let mut word_weights: BTreeMap = BTreeMap::new(); - // Index words from content (weight 1) - for word in tokenize_text(&all_text) { - word_weights.entry(word).or_insert(1); - } - - // Index words from headings (weight 3, higher priority) - for heading in &all_headings { - for word in tokenize_text(heading) { - word_weights - .entry(word) - .and_modify(|w| *w = (*w).max(3)) - .or_insert(3); - } - } + // Index words from content (weight 1) + for word in tokenize_text(&all_text) { + word_weights.entry(word).or_insert(1); + } - // Index words from title (weight 5, highest priority) - for word in tokenize_text(&page_context.title) { + // Index words from headings (weight 3, higher priority) + for heading in &all_headings { + for word in tokenize_text(heading) { word_weights .entry(word) - .and_modify(|w| *w = (*w).max(5)) - .or_insert(5); + .and_modify(|w| *w = (*w).max(3)) + .or_insert(3); } + } - // Add to inverted index with weights - for (word, weight) in word_weights { - generated - .search_index - .words - .entry(word) - .or_default() - .push((page_idx, weight)); - } + // Index words from title (weight 5, highest priority) + for word in tokenize_text(&page_context.title) { + word_weights + .entry(word) + .and_modify(|w| *w = (*w).max(5)) + .or_insert(5); + } - layout( - &page_context.title, - paxhtml::Element::from_iter(simplified.iter().map(|node| { - convert_wikitext_to_html(evaluator, &node.value, &page_context) - })), - ) - }; + // Add to inverted index with weights + for (word, weight) in word_weights { + generated + .search_index + .words + .entry(word) + .or_default() + .push((page_idx, weight)); + } + + layout( + bump, + &page_context.title, + paxhtml::Element::from_iter( + bump, + simplified.iter().map(|node| { + convert_wikitext_to_html(bump, evaluator, &node.value, &page_context) + }), + ), + ) + }; document.write_to_route(dst_root, route_path)?; @@ -414,7 +441,13 @@ fn generate_wiki_folder( Ok(()) } -fn layout(title: &str, inner: paxhtml::Element) -> paxhtml::Document { +fn layout<'bump>( + bump: &'bump Bump, + title: &str, + inner: paxhtml::Element<'bump>, +) -> paxhtml::Document<'bump> { + let b = paxhtml::builder::Builder::new(bump); + let mut links = vec![( "Home", paxhtml::RoutePath::new( @@ -438,78 +471,82 @@ fn layout(title: &str, inner: paxhtml::Element) -> paxhtml::Document { let mut breadcrumbs = vec![]; for (idx, (component, route_path)) in links.into_iter().enumerate() { if idx > 0 { - breadcrumbs.push(paxhtml::html! { " / " }); + breadcrumbs.push(paxhtml::html! { in bump; " / " }); } - breadcrumbs.push(paxhtml::html! {
    {component} }); + breadcrumbs.push(paxhtml::html! { in bump; {component} }); } - paxhtml::Document::new([ - paxhtml::builder::doctype(["html".into()]), - paxhtml::html! { - - - - - {format!("JC2-MP Documentation - {title}")} - - - - - -
    -
    -

    #{breadcrumbs}

    -
    - {inner} + +
    +
    +

    #{breadcrumbs}

    +
    + {inner} +
    -
    - - - - }, - ]) + + + + }, + ], + ) } -fn convert_wikitext_to_html( +fn convert_wikitext_to_html<'bump>( + bump: &'bump Bump, evaluator: &mut TemplateEvaluator<'_>, node: &WikitextSimplifiedNode, page_context: &PageContext, -) -> paxhtml::Element { +) -> paxhtml::Element<'bump> { use WikitextSimplifiedNode as WSN; - use paxhtml::html; - fn parse_attributes_from_wsn( + fn parse_attributes_from_wsn<'b>( + bump: &'b Bump, evaluator: &mut TemplateEvaluator<'_>, attributes_context: &str, attributes: &[WSN], page_context: &PageContext, - ) -> Vec { + ) -> bumpalo::collections::Vec<'b, paxhtml::Attribute<'b>> { if attributes.is_empty() { - return vec![]; + return bumpalo::collections::Vec::new_in(bump); } // Instantiate the attributes before extracting the text let attributes = pollster::block_on( @@ -551,31 +588,43 @@ fn convert_wikitext_to_html( ); } - paxhtml::Attribute::parse_from_str(&merged_text).unwrap() + paxhtml::Attribute::parse_from_str(bump, &merged_text).unwrap() } - fn parse_optional_attributes_from_wsn( + fn parse_optional_attributes_from_wsn<'b>( + bump: &'b Bump, evaluator: &mut TemplateEvaluator<'_>, attributes_context: &str, attributes: &Option>>, page_context: &PageContext, - ) -> Vec { + ) -> bumpalo::collections::Vec<'b, paxhtml::Attribute<'b>> { attributes .as_ref() .map(|attributes| { let unwrapped: Vec = attributes.iter().map(|s| s.value.clone()).collect(); - parse_attributes_from_wsn(evaluator, attributes_context, &unwrapped, page_context) + parse_attributes_from_wsn( + bump, + evaluator, + attributes_context, + &unwrapped, + page_context, + ) }) - .unwrap_or_default() + .unwrap_or_else(|| bumpalo::collections::Vec::new_in(bump)) } + let b = paxhtml::builder::Builder::new(bump); + let convert_children = |evaluator: &mut TemplateEvaluator<'_>, children: &[Spanned]| { paxhtml::Element::from_iter( + bump, children .iter() .skip_while(|node| matches!(node.value, WSN::ParagraphBreak | WSN::Newline)) - .map(|node| convert_wikitext_to_html(evaluator, &node.value, page_context)), + .map(|node| { + convert_wikitext_to_html(bump, evaluator, &node.value, page_context) + }), ) }; @@ -585,10 +634,10 @@ fn convert_wikitext_to_html( let template = pollster::block_on( evaluator.instantiate(TemplateToInstantiate::Name(name), parameters), ); - convert_wikitext_to_html(evaluator, &template, page_context) + convert_wikitext_to_html(bump, evaluator, &template, page_context) } tpu @ WSN::TemplateParameterUse { .. } => { - html! { <>{tpu.to_wikitext()} } + paxhtml::html! { in bump; <>{tpu.to_wikitext()} } } WSN::Heading { level, children } => { let class = match level { @@ -597,46 +646,47 @@ fn convert_wikitext_to_html( 4 => "text-lg font-semibold mt-4 mb-2", _ => "font-semibold mt-4 mb-2", }; - paxhtml::builder::tag( - format!("h{level}"), - paxhtml::Attribute::parse_from_str(&format!("class=\"{}\"", class)).unwrap(), + let tag_name = format!("h{level}"); + b.tag( + &tag_name, + paxhtml::Attribute::parse_from_str(bump, &format!("class=\"{}\"", class)).unwrap(), false, )(convert_children(evaluator, children)) } WSN::Link { text, title } => { - html! { + paxhtml::html! { in bump; - {paxhtml::Element::Raw { html: text.to_string() }} + {b.raw(text)} } } WSN::ExtLink { link, text } => { - html! { + paxhtml::html! { in bump; - {paxhtml::Element::Raw { html: text.as_ref().unwrap_or(link).to_string() }} + {b.raw(text.as_ref().unwrap_or(link))} } } WSN::Bold { children } => { - html! { {convert_children(evaluator, children)} } + paxhtml::html! { in bump; {convert_children(evaluator, children)} } } WSN::Italic { children } => { - html! { {convert_children(evaluator, children)} } + paxhtml::html! { in bump; {convert_children(evaluator, children)} } } WSN::Blockquote { children } => { - html! {
    {convert_children(evaluator, children)}
    } + paxhtml::html! { in bump;
    {convert_children(evaluator, children)}
    } } WSN::Superscript { children } => { - html! { {convert_children(evaluator, children)} } + paxhtml::html! { in bump; {convert_children(evaluator, children)} } } WSN::Subscript { children } => { - html! { {convert_children(evaluator, children)} } + paxhtml::html! { in bump; {convert_children(evaluator, children)} } } WSN::Small { children } => { - html! { {convert_children(evaluator, children)} } + paxhtml::html! { in bump; {convert_children(evaluator, children)} } } WSN::Preformatted { children } => { - html! {
    {convert_children(evaluator, children)}
    } + paxhtml::html! { in bump;
    {convert_children(evaluator, children)}
    } } WSN::Tag { name, @@ -668,39 +718,41 @@ fn convert_wikitext_to_html( text.trim() } else { // If not simple text, fall back to plain rendering - let parsed_attributes = paxhtml::Attribute::parse_from_str(attrs_str).unwrap(); - return html! {
    {convert_children(evaluator, children)}
    }; + let parsed_attributes = + paxhtml::Attribute::parse_from_str(bump, attrs_str).unwrap(); + return paxhtml::html! { in bump;
    {convert_children(evaluator, children)}
    }; }; // Use syntax highlighter if let Some(highlighter) = SYNTAX_HIGHLIGHTER.get() { - match highlighter.highlight_code(lang, code) { + match highlighter.highlight_code(bump, lang, code) { Ok(highlighted) => { - html! {
    {highlighted}
    } + paxhtml::html! { in bump;
    {highlighted}
    } } Err(_) => { // Fallback to plain text if highlighting fails let parsed_attributes = - paxhtml::Attribute::parse_from_str(attrs_str).unwrap(); - html! {
    {code}
    } + paxhtml::Attribute::parse_from_str(bump, attrs_str).unwrap(); + paxhtml::html! { in bump;
    {code}
    } } } } else { // Fallback if highlighter not initialized - let parsed_attributes = paxhtml::Attribute::parse_from_str(attrs_str).unwrap(); - html! {
    {code}
    } + let parsed_attributes = + paxhtml::Attribute::parse_from_str(bump, attrs_str).unwrap(); + paxhtml::html! { in bump;
    {code}
    } } } else { - let parsed_attributes = - paxhtml::Attribute::parse_from_str(attributes.as_deref().unwrap_or_default()) - .unwrap(); + let parsed_attributes = paxhtml::Attribute::parse_from_str( + bump, + attributes.as_deref().unwrap_or_default(), + ) + .unwrap(); let children = convert_children(evaluator, children); - paxhtml::builder::tag(name.to_string(), parsed_attributes, false)(children) + b.tag(name, parsed_attributes, false)(children) } } - WSN::Text { text } => paxhtml::Element::Raw { - html: text.to_string(), - }, + WSN::Text { text } => b.raw(text), WSN::Table { attributes, captions, @@ -750,9 +802,14 @@ fn convert_wikitext_to_html( .iter() .map(|s| s.value.clone()) .collect(); - let attributes = - parse_attributes_from_wsn(evaluator, "main", &unwrapped_attributes, page_context); - html! { + let attributes = parse_attributes_from_wsn( + bump, + evaluator, + "main", + &unwrapped_attributes, + page_context, + ); + paxhtml::html! { in bump; @@ -760,12 +817,13 @@ fn convert_wikitext_to_html( .iter() .map(|caption| { let attributes = parse_optional_attributes_from_wsn( + bump, evaluator, "caption", &caption.attributes, page_context, ); - html! { + paxhtml::html! { in bump; @@ -781,24 +839,26 @@ fn convert_wikitext_to_html( .map(|(idx, row)| { let unwrapped_row_attrs: Vec = row.attributes.iter().map(|s| s.value.clone()).collect(); let attributes = parse_attributes_from_wsn( + bump, evaluator, "row", &unwrapped_row_attrs, page_context, ); let row_class = if idx % 2 == 0 { "bg-white" } else { "bg-gray-50" }; - html! { + paxhtml::html! { in bump; #{row.cells .iter() .map(|cell| { let attributes = parse_optional_attributes_from_wsn( + bump, evaluator, "cell", &cell.attributes, page_context, ); - html! { + paxhtml::html! { in bump; @@ -814,24 +874,24 @@ fn convert_wikitext_to_html( } } WSN::OrderedList { items } => { - html! { + paxhtml::html! { in bump;
      #{items .iter() .map(|i| { - html! {
    1. {convert_children(evaluator, &i.content)}
    2. } + paxhtml::html! { in bump;
    3. {convert_children(evaluator, &i.content)}
    4. } }) }
    } } WSN::UnorderedList { items } => { - html! { + paxhtml::html! { in bump;
      #{items .iter() .map(|i| { - html! {
    • {convert_children(evaluator, &i.content)}
    • } + paxhtml::html! { in bump;
    • {convert_children(evaluator, &i.content)}
    • } }) }
    @@ -839,26 +899,28 @@ fn convert_wikitext_to_html( } WSN::DefinitionList { items } => { use wikitext_simplified::DefinitionListItemType; - html! { + paxhtml::html! { in bump;
    #{items.iter().map(|i| { let children = convert_children(evaluator, &i.content); match i.type_ { - DefinitionListItemType::Term => html! {
    {children}
    }, - DefinitionListItemType::Details => html! {
    {children}
    }, + DefinitionListItemType::Term => paxhtml::html! { in bump;
    {children}
    }, + DefinitionListItemType::Details => paxhtml::html! { in bump;
    {children}
    }, } })}
    } } - WSN::Redirect { target } => html! { + WSN::Redirect { target } => paxhtml::html! { in bump; "REDIRECT: "{target} }, - WSN::HorizontalDivider => html! {
    }, - WSN::ParagraphBreak => html! {
    }, - WSN::Newline => html! {
    }, + WSN::HorizontalDivider => { + paxhtml::html! { in bump;
    } + } + WSN::ParagraphBreak => paxhtml::html! { in bump;
    }, + WSN::Newline => paxhtml::html! { in bump;
    }, } } @@ -873,30 +935,34 @@ fn page_title_to_route_path(title: &str) -> paxhtml::RoutePath { ) } -fn redirect(to_url: &str) -> paxhtml::Document { - paxhtml::Document::new([ - paxhtml::builder::doctype(["html".into()]), - paxhtml::html! { - - - "Redirecting..." - - - - - - - - - }, - ]) +fn redirect<'bump>(bump: &'bump Bump, to_url: &str) -> paxhtml::Document<'bump> { + let b = paxhtml::builder::Builder::new(bump); + paxhtml::Document::new( + bump, + [ + b.doctype([b.attr("html")]), + paxhtml::html! { in bump; + + + "Redirecting..." + + + + + + + + + }, + ], + ) } /// Tokenize text into searchable words diff --git a/src/syntax.rs b/src/syntax.rs index 095af0e..cec5b4c 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -1,3 +1,4 @@ +use paxhtml::bumpalo::Bump; use syntect::{ highlighting::ThemeSet, html::{ClassStyle, ClassedHTMLGenerator, css_for_theme_with_class_style}, @@ -40,11 +41,12 @@ impl SyntaxHighlighter { }) } - pub fn highlight_code( + pub fn highlight_code<'bump>( &self, + bump: &'bump Bump, language: Option<&str>, code: &str, - ) -> Result { + ) -> Result, syntect::Error> { let syntax = self.lookup_language(language); let mut html_generator = ClassedHTMLGenerator::new_with_class_style( syntax, @@ -54,8 +56,7 @@ impl SyntaxHighlighter { for line in LinesWithEndings::from(code) { html_generator.parse_html_for_line_which_includes_newline(line)?; } - Ok(paxhtml::Element::Raw { - html: html_generator.finalize(), - }) + let b = paxhtml::builder::Builder::new(bump); + Ok(b.raw(&html_generator.finalize())) } }
    {convert_children(evaluator, &caption.content)}
    {convert_children(evaluator, &cell.content)}