fix(render): fix code block rendering and quote escaping

Two issues fixed:

1. Language-less code blocks (``` without lang) were not accumulating
   text content. The guard `code_block_lang.is_some()` was false for
   them, so content fell through to regular text rendering.

   Fix: Add `in_code_block` flag to track code block state separately
   from language presence.

2. Single quotes in code blocks were being HTML-escaped as ',
   breaking CSP headers like 'self' in documentation.

   Fix: Create code_escape/code_escape_into in escape.rs that only
   escapes <, >, & (required to prevent HTML tag injection) but
   preserves quotes (safe inside <pre><code> content).

Rationale for code_escape:
- < and > MUST be escaped to prevent browser interpreting code as HTML
- & MUST be escaped to prevent HTML entity interpretation
- Quotes are safe inside element content (no attribute context)

Also:
- Add test for unlabeled code block quote preservation

All 71 tests pass.
This commit is contained in:
Timothy DeHerrera
2026-02-05 17:26:17 -07:00
parent 1e5ed28788
commit 8df8aa434f
3 changed files with 55 additions and 11 deletions

View File

@@ -25,6 +25,27 @@ pub fn html_escape_into(out: &mut String, s: &str) {
}
}
/// Escape characters for safe embedding in code blocks.
///
/// Only escapes `&`, `<`, `>` — quotes are safe inside `<pre><code>`.
pub fn code_escape(s: &str) -> String {
let mut result = String::with_capacity(s.len());
code_escape_into(&mut result, s);
result
}
/// Escape code block characters into an existing string.
pub fn code_escape_into(out: &mut String, s: &str) {
for c in s.chars() {
match c {
'&' => out.push_str("&amp;"),
'<' => out.push_str("&lt;"),
'>' => out.push_str("&gt;"),
_ => out.push(c),
}
}
}
/// Escape XML special characters for safe embedding in XML documents.
///
/// Escapes: `&`, `<`, `>`, `"`, `'`