Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 41 additions & 1 deletion cot-site-macros/src/md_pages/rendering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::fmt::Write;
use comrak::html::{
ChildRendering, Context, format_document_with_formatter, format_node_default, render_sourcepos,
};
use comrak::nodes::{AstNode, NodeLink, NodeValue};
use comrak::nodes::{AstNode, NodeCodeBlock, NodeLink, NodeValue};
use comrak::options::Plugins;
use comrak::{Arena, Options, parse_document};
use cot_site_common::Version;
Expand Down Expand Up @@ -47,10 +47,36 @@ fn format_node_custom<'a>(
match node.data.borrow().value {
NodeValue::Table(_) => render_table_custom(context, node, entering),
NodeValue::Link(ref ln) => render_link_custom(context, node, entering, ln),
NodeValue::CodeBlock(ref cb) => render_code_block_custom(context, node, entering, cb),
_ => format_node_default(context, node, entering),
}
}

fn render_code_block_custom<'a>(
context: &mut Context<PageContext>,
_node: &'a AstNode<'a>,
entering: bool,
cb: &NodeCodeBlock,
) -> Result<ChildRendering, std::fmt::Error> {
let mut new_cb = cb.clone();
new_cb.literal = remove_hidden_lines(&cb.literal);

let node = AstNode::from(NodeValue::CodeBlock(Box::new(new_cb)));

format_node_default(context, &node, entering)
Comment on lines +55 to +66
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

render_code_block_custom clones the entire NodeCodeBlock and allocates a new String for every code block, even when there are no hidden # lines to remove. You can avoid the extra work by doing a quick check (e.g., whether the literal starts with # or contains \n# ) and falling back to format_node_default on the original node when no filtering is needed.

Copilot uses AI. Check for mistakes.
}

fn remove_hidden_lines(input: &str) -> String {
let mut literal = String::new();
for line in input.lines() {
if !line.starts_with("# ") {
literal.push_str(line);
literal.push('\n');
Comment on lines +71 to +74
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove_hidden_lines rebuilds the code block text via input.lines() and always appends \n for each retained line. This can change the original code block content beyond hiding # lines (e.g., it drops trailing blank lines and may force a trailing newline even if the literal didn’t have one). Consider filtering while preserving the original line terminators (e.g., iterate with split_inclusive('\n') / track ends_with('\n')) so only the intended lines are removed and whitespace/newline structure is unchanged.

Suggested change
for line in input.lines() {
if !line.starts_with("# ") {
literal.push_str(line);
literal.push('\n');
for line in input.split_inclusive('\n') {
let line_content = line.strip_suffix('\n').unwrap_or(line);
if !line_content.starts_with("# ") {
literal.push_str(line);

Copilot uses AI. Check for mistakes.
}
}
literal
}

fn render_table_custom<'a>(
context: &mut Context<PageContext>,
node: &'a AstNode<'a>,
Expand Down Expand Up @@ -234,4 +260,18 @@ mod tests {
);
test_resolve!("cot", "cot");
}

#[test]
fn test_code_block_filtering() {
let md = "```rust\n# hidden\nvisible\n```";
let mut options = Options::default();
options.render.r#unsafe = true;
let plugins = Plugins::default();
let version = Version::new(0, 5, 0);

let html = markdown_to_html(md, &options, &plugins, version);

assert!(html.contains("visible"));
assert!(!html.contains("hidden"));
}
}
Loading