feat: add BlockVerbatimNode for block-level raw text output and update related handling in parser and writers

This commit is contained in:
Hong Jiarong 2025-11-18 11:43:29 +08:00
parent 32f1543343
commit 4a0edcbda3
7 changed files with 63 additions and 28 deletions

View file

@ -208,11 +208,11 @@ impl InlineNode {
}
}
/// Verbatim node for raw text output
/// Inline verbatim node for raw text output
#[derive(Debug, PartialEq, Clone)]
#[custom_node(block = false, html_impl = true)]
pub struct VerbatimNode {
/// The content to directly output
/// The content to directly output inline
pub content: EcoString,
}
@ -226,6 +226,25 @@ impl VerbatimNode {
}
}
/// Block verbatim node for raw text output without paragraph wrapping
#[derive(Debug, PartialEq, Clone)]
#[custom_node(block = true, html_impl = true)]
pub struct BlockVerbatimNode {
/// The content to directly output as a block
pub content: EcoString,
}
impl BlockVerbatimNode {
fn write_custom(&self, writer: &mut BlockWriterProxy) -> WriteResult<()> {
writer.write_str(&self.content)?;
Ok(())
}
fn write_html_custom(&self, writer: &mut HtmlWriter) -> HtmlWriteResult<()> {
writer.write_trusted_html(&self.content)
}
}
/// Alert node for alert messages
#[derive(Debug, PartialEq, Clone)]
#[custom_node(block = true, html_impl = false)]

View file

@ -64,7 +64,7 @@ separately rendered SVG
</td><td>
<p>````
````
== Under the Greenwood Tree
by Shakespeare.
@ -74,8 +74,7 @@ Under the greenwood tree #emoji.tree
Who loves to lie with me,
...
```)
````</p>
````
</td>

View file

@ -3,4 +3,4 @@ source: crates/typlite/src/tests.rs
expression: hash
input_file: crates/typlite/src/fixtures/integration/issue-1844.typ
---
siphash128_13:5680ee2c48f93da5b40c162e391ef15
siphash128_13:b700ae0ff0daf675a40354416190674e

View file

@ -14,10 +14,8 @@ use typst_html::{HtmlElement, HtmlNode, tag};
use crate::Result;
use crate::TypliteFeat;
use crate::attributes::{
AlertsAttr, HeadingAttr, RawAttr, TypliteAttrsParser, VerbatimAttr, md_attr,
};
use crate::common::{AlertNode, CenterNode, VerbatimNode};
use crate::attributes::{AlertsAttr, HeadingAttr, RawAttr, TypliteAttrsParser, VerbatimAttr};
use crate::common::{AlertNode, BlockVerbatimNode, CenterNode, VerbatimNode};
use crate::diagnostics::WarningCollector;
use crate::tags::md_tag;
@ -196,9 +194,9 @@ impl HtmlToAstParser {
let attrs = VerbatimAttr::parse(&element.attrs)?;
if attrs.block {
self.flush_inline_buffer();
self.blocks.push(Node::Paragraph(vec![Node::Custom(Box::new(
VerbatimNode { content: attrs.src },
))]));
self.blocks.push(Node::Custom(Box::new(BlockVerbatimNode {
content: attrs.src,
})));
} else {
self.inline_buffer
.push(Node::Custom(Box::new(VerbatimNode { content: attrs.src })));
@ -405,12 +403,8 @@ impl HtmlToAstParser {
}
fn is_verbatim_block(element: &HtmlElement) -> bool {
element
.attrs
.0
.iter()
.find(|(name, _)| *name == md_attr::block)
.and_then(|(_, value)| value.parse::<bool>().ok())
VerbatimAttr::parse(&element.attrs)
.map(|attrs| attrs.block)
.unwrap_or(false)
}

View file

@ -4,13 +4,14 @@ use base64::Engine;
use cmark_writer::ast::{ListItem, Node};
use docx_rs::*;
use ecow::EcoString;
use log::{debug, warn};
use log::debug;
use std::fs;
use std::io::Cursor;
use crate::Result;
use crate::common::{
CenterNode, FigureNode, FormatWriter, HighlightNode, InlineNode, VerbatimNode,
BlockVerbatimNode, CenterNode, FigureNode, FormatWriter, HighlightNode, InlineNode,
VerbatimNode,
};
use super::image_processor::DocxImageProcessor;
@ -253,10 +254,7 @@ impl DocxWriter {
}
node if node.is_custom_type::<VerbatimNode>() => {
let node = node.as_custom_type::<VerbatimNode>().unwrap();
warn!(
"ignoring `m1verbatim` content in DOCX export: {:?}",
node.content
);
run = run.style("CodeInline").add_text(&node.content);
}
// Other inline element types
_ => {
@ -504,6 +502,15 @@ impl DocxWriter {
docx = docx.add_paragraph(para);
}
}
node if node.is_custom_type::<BlockVerbatimNode>() => {
let block_node = node.as_custom_type::<BlockVerbatimNode>().unwrap();
for line in block_node.content.split('\n') {
let para = Paragraph::new()
.style("CodeBlock")
.add_run(Run::new().add_text(line));
docx = docx.add_paragraph(para);
}
}
node if node.is_custom_type::<InlineNode>() => {
let inline_node = node.as_custom_type::<InlineNode>().unwrap();
// Handle InlineNode at block level (convert to paragraph)

View file

@ -8,8 +8,8 @@ use tinymist_std::path::unix_slash;
use crate::Result;
use crate::common::{
CenterNode, ExternalFrameNode, FigureNode, FormatWriter, HighlightNode, InlineNode, ListState,
VerbatimNode,
BlockVerbatimNode, CenterNode, ExternalFrameNode, FigureNode, FormatWriter, HighlightNode,
InlineNode, ListState, VerbatimNode,
};
/// LaTeX writer implementation
@ -289,6 +289,11 @@ impl LaTeXWriter {
self.write_node(child, output)?;
}
}
node if node.is_custom_type::<BlockVerbatimNode>() => {
let block_node = node.as_custom_type::<BlockVerbatimNode>().unwrap();
output.push_str(&block_node.content);
output.push_str("\n\n");
}
node if node.is_custom_type::<VerbatimNode>() => {
let inline_node = node.as_custom_type::<VerbatimNode>().unwrap();
output.push_str(&inline_node.content);

View file

@ -4,7 +4,7 @@ use cmark_writer::ast::Node;
use ecow::EcoString;
use crate::Result;
use crate::common::{ExternalFrameNode, FigureNode, FormatWriter};
use crate::common::{BlockVerbatimNode, ExternalFrameNode, FigureNode, FormatWriter, VerbatimNode};
/// Text writer implementation
#[derive(Default)]
@ -161,6 +161,12 @@ impl TextWriter {
output.push_str(&external_frame.alt_text);
}
}
node if node.is_custom_type::<BlockVerbatimNode>() => {
if let Some(block_node) = node.as_custom_type::<BlockVerbatimNode>() {
output.push_str(&block_node.content);
output.push_str("\n\n");
}
}
node if node.is_custom_type::<crate::common::HighlightNode>() => {
if let Some(highlight) = node.as_custom_type::<crate::common::HighlightNode>() {
for child in &highlight.content {
@ -168,6 +174,11 @@ impl TextWriter {
}
}
}
node if node.is_custom_type::<VerbatimNode>() => {
if let Some(inline_node) = node.as_custom_type::<VerbatimNode>() {
output.push_str(&inline_node.content);
}
}
_ => {}
}
Ok(())