Skip to content

Commit 3070d4b

Browse files
committed
Patch HTML
1 parent aafb3bb commit 3070d4b

3 files changed

Lines changed: 107 additions & 119 deletions

File tree

src/main.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,11 +461,17 @@ async fn process_files_in_chunks(
461461

462462
// Handle markdown files differently - render them directly without code blocks
463463
if file.path.ends_with(".md") || file.path.ends_with(".markdown") {
464+
if verbose {
465+
println!(" 📝 Processing markdown file: {} (no code block)", file.path);
466+
}
464467
final_markdown.push_str(&processed_content);
465468
if !processed_content.ends_with('\n') {
466469
final_markdown.push('\n');
467470
}
468471
} else {
472+
if verbose {
473+
println!(" 💻 Processing code file: {} (with code block)", file.path);
474+
}
469475
// For code files, wrap in code blocks with language highlighting
470476
if let Some(language) = &file.language {
471477
final_markdown.push_str(&format!("```{}\n", language));

src/markdown_generator.rs

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -69,22 +69,33 @@ impl MarkdownGenerator {
6969
markdown.push_str(&format!("### {} {{#{sanitized_path}}}\n\n", escaped_path));
7070
markdown.push_str(&format!("**Size:** {}\n\n", MarkdownGenerator::format_file_size(file.size)));
7171

72-
if let Some(language) = &file.language {
73-
markdown.push_str(&format!("```{}\n", language));
72+
// Handle markdown files differently - render them directly without code blocks
73+
if file.path.ends_with(".md") || file.path.ends_with(".markdown") {
74+
// Process content to prevent LaTeX errors
75+
let processed_content = self.process_content_for_latex(&file.content);
76+
markdown.push_str(&processed_content);
77+
if !processed_content.ends_with('\n') {
78+
markdown.push('\n');
79+
}
7480
} else {
75-
markdown.push_str("```\n");
76-
}
77-
78-
// Process content to prevent LaTeX errors
79-
let processed_content = self.process_content_for_latex(&file.content);
80-
markdown.push_str(&processed_content);
81-
82-
// Ensure there's always a newline before closing backticks
83-
if !processed_content.ends_with('\n') {
84-
markdown.push('\n');
81+
// For code files, wrap in code blocks with language highlighting
82+
if let Some(language) = &file.language {
83+
markdown.push_str(&format!("```{}\n", language));
84+
} else {
85+
markdown.push_str("```\n");
86+
}
87+
88+
// Process content to prevent LaTeX errors
89+
let processed_content = self.process_content_for_latex(&file.content);
90+
markdown.push_str(&processed_content);
91+
92+
// Ensure there's always a newline before closing backticks
93+
if !processed_content.ends_with('\n') {
94+
markdown.push('\n');
95+
}
96+
97+
markdown.push_str("```\n\n");
8598
}
86-
87-
markdown.push_str("```\n\n");
8899
markdown.push_str("---\n\n");
89100
}
90101

@@ -261,7 +272,7 @@ mod tests {
261272
}
262273
];
263274

264-
let markdown = generator.generate_markdown(files, "test-repo").unwrap();
275+
let markdown = generator.generate_markdown(&files, "test-repo").unwrap();
265276
assert!(markdown.contains("# test-repo"));
266277
assert!(markdown.contains("## Table of Contents"));
267278
assert!(markdown.contains("## File Structure"));

src/renderer/html.rs

Lines changed: 75 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use anyhow::Result;
2-
use pulldown_cmark::{Event, html, Parser, Tag, TagEnd, CodeBlockKind};
2+
use pulldown_cmark::{Event, html, Tag, TagEnd, CowStr};
33
use crate::renderer::{DocumentRenderer, DocumentMetadata};
44
use crate::syntax::highlighter::SyntaxHighlighter;
55

@@ -9,75 +9,66 @@ impl HtmlRenderer {
99
pub fn new() -> Self {
1010
Self
1111
}
12-
13-
fn process_markdown_with_syntax_highlighting(&self, markdown: &str) -> Result<String> {
14-
let parser = Parser::new(markdown);
15-
let mut events = Vec::new();
16-
let mut in_code_block = false;
17-
let mut code_lang = String::new();
18-
let mut code_content = String::new();
19-
12+
}
13+
14+
impl DocumentRenderer for HtmlRenderer {
15+
fn render(&self, events: Vec<Event>, metadata: &DocumentMetadata) -> Result<Vec<u8>> {
2016
// Initialize syntax highlighter
21-
let highlighter = SyntaxHighlighter::new()
22-
.map_err(|e| anyhow::anyhow!("Failed to create syntax highlighter: {}", e))?;
17+
let highlighter = SyntaxHighlighter::new()?;
2318

24-
for event in parser {
25-
match event {
26-
Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(lang))) => {
27-
in_code_block = true;
28-
code_lang = lang.to_string();
29-
code_content.clear();
30-
}
31-
Event::End(TagEnd::CodeBlock) => {
32-
if in_code_block {
33-
// Apply syntax highlighting
34-
let highlighted = if !code_lang.is_empty() {
35-
highlighter.highlight_to_html(&code_content, Some(&code_lang))
36-
} else {
37-
code_content.replace('&', "&amp;").replace('<', "&lt;").replace('>', "&gt;")
38-
};
39-
40-
// Add syntax highlighting CSS classes and wrap in proper HTML
41-
let html_block = format!(
42-
r#"<div class="highlight"><pre class="code-block language-{}"><code class="language-{}">{}</code></pre></div>"#,
43-
code_lang, code_lang, highlighted
44-
);
45-
events.push(Event::Html(html_block.into()));
46-
47-
in_code_block = false;
19+
// Process events to add syntax highlighting
20+
let mut processed_events = Vec::new();
21+
let mut i = 0;
22+
23+
while i < events.len() {
24+
match &events[i] {
25+
Event::Start(Tag::CodeBlock(kind)) => {
26+
// Extract language from code block
27+
let language = match kind {
28+
pulldown_cmark::CodeBlockKind::Fenced(lang) => {
29+
if lang.is_empty() { None } else { Some(lang.as_ref()) }
30+
}
31+
_ => None,
32+
};
33+
34+
// Find the corresponding text and end events
35+
i += 1;
36+
let mut code_content = String::new();
37+
while i < events.len() {
38+
match &events[i] {
39+
Event::Text(text) => {
40+
code_content.push_str(text);
41+
}
42+
Event::End(TagEnd::CodeBlock) => {
43+
break;
44+
}
45+
_ => {}
46+
}
47+
i += 1;
48+
}
49+
50+
// Generate highlighted HTML
51+
if language.is_some() {
52+
let highlighted_html = highlighter.highlight_to_html(&code_content, language);
53+
let wrapped_html = format!("<pre>{}</pre>", highlighted_html);
54+
processed_events.push(Event::Html(CowStr::Boxed(wrapped_html.into_boxed_str())));
55+
} else {
56+
// No language specified, use regular code block
57+
processed_events.push(Event::Start(Tag::CodeBlock(kind.clone())));
58+
processed_events.push(Event::Text(CowStr::Boxed(code_content.into_boxed_str())));
59+
processed_events.push(Event::End(TagEnd::CodeBlock));
4860
}
49-
}
50-
Event::Text(text) if in_code_block => {
51-
code_content.push_str(&text);
5261
}
5362
_ => {
54-
if !in_code_block {
55-
events.push(event);
56-
}
63+
processed_events.push(events[i].clone());
5764
}
5865
}
66+
i += 1;
5967
}
6068

61-
let mut html_output = String::new();
62-
html::push_html(&mut html_output, events.into_iter());
63-
64-
Ok(html_output)
65-
}
66-
}
67-
68-
impl DocumentRenderer for HtmlRenderer {
69-
fn render(&self, events: Vec<Event>, metadata: &DocumentMetadata) -> Result<Vec<u8>> {
70-
// Convert markdown events to HTML body
69+
// Convert processed events to HTML
7170
let mut body_html = String::new();
72-
html::push_html(&mut body_html, events.into_iter());
73-
74-
// Replace \newpage with CSS page break (handle different markdown outputs)
75-
body_html = body_html.replace("<p>\\newpage</p>", r#"<div style="page-break-before: always;"></div>"#);
76-
body_html = body_html.replace("\\newpage", r#"<div style="page-break-before: always;"></div>"#);
77-
78-
fn render_markdown(&self, markdown: &str, metadata: &DocumentMetadata) -> Result<Vec<u8>> {
79-
// Process markdown with syntax highlighting
80-
let mut body_html = self.process_markdown_with_syntax_highlighting(markdown)?;
71+
html::push_html(&mut body_html, processed_events.into_iter());
8172

8273
// Replace \newpage with CSS page break (handle different markdown outputs)
8374
body_html = body_html.replace("<p>\\newpage</p>", r#"<div style="page-break-before: always;"></div>"#);
@@ -181,6 +172,19 @@ impl DocumentRenderer for HtmlRenderer {
181172
border-bottom: 1px solid #e1e4e8;
182173
}}
183174
175+
/* Syntect syntax highlighting styles */
176+
.source {{ background-color: transparent; }}
177+
[class*="comment"] {{ color: #6a737d; font-style: italic; }}
178+
[class*="keyword"] {{ color: #d73a49; font-weight: bold; }}
179+
[class*="storage"] {{ color: #d73a49; font-weight: bold; }}
180+
[class*="string"] {{ color: #032f62; }}
181+
[class*="constant"] {{ color: #005cc5; }}
182+
[class*="entity"] {{ color: #6f42c1; }}
183+
[class*="support"] {{ color: #005cc5; }}
184+
[class*="variable"] {{ color: #e36209; }}
185+
[class*="punctuation"] {{ color: #24292e; }}
186+
[class*="meta"] {{ color: #24292e; }}
187+
184188
@media (prefers-color-scheme: dark) {{
185189
body {{
186190
background-color: #0d1117;
@@ -221,51 +225,18 @@ impl DocumentRenderer for HtmlRenderer {
221225
color: #8b949e;
222226
border-bottom-color: #30363d;
223227
}}
224-
}}
225-
226-
/* Syntax highlighting styles */
227-
.highlight {{
228-
background-color: #f8f9fa;
229-
border-radius: 6px;
230-
overflow-x: auto;
231-
}}
232-
233-
.code-block {{
234-
background-color: transparent;
235-
padding: 1rem;
236-
margin: 0;
237-
font-family: 'SF Mono', Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
238-
font-size: 0.875rem;
239-
line-height: 1.45;
240-
}}
241-
242-
/* Syntect CSS classes for syntax highlighting */
243-
.syntect-keyword {{ color: #d73a49; font-weight: bold; }}
244-
.syntect-string {{ color: #032f62; }}
245-
.syntect-comment {{ color: #6f42c1; font-style: italic; }}
246-
.syntect-function {{ color: #6f42c1; }}
247-
.syntect-type {{ color: #005cc5; }}
248-
.syntect-number {{ color: #005cc5; }}
249-
.syntect-constant {{ color: #005cc5; }}
250-
.syntect-variable {{ color: #e36209; }}
251-
.syntect-operator {{ color: #d73a49; }}
252-
.syntect-preprocessor {{ color: #735c0f; }}
253-
254-
@media (prefers-color-scheme: dark) {{
255-
.highlight {{
256-
background-color: #161b22;
257-
}}
258228
259-
.syntect-keyword {{ color: #ff7b72; }}
260-
.syntect-string {{ color: #a5d6ff; }}
261-
.syntect-comment {{ color: #8b949e; }}
262-
.syntect-function {{ color: #d2a8ff; }}
263-
.syntect-type {{ color: #79c0ff; }}
264-
.syntect-number {{ color: #79c0ff; }}
265-
.syntect-constant {{ color: #79c0ff; }}
266-
.syntect-variable {{ color: #ffa657; }}
267-
.syntect-operator {{ color: #ff7b72; }}
268-
.syntect-preprocessor {{ color: #e3b341; }}
229+
/* Dark mode syntax highlighting */
230+
[class*="comment"] {{ color: #8b949e; }}
231+
[class*="keyword"] {{ color: #ff7b72; }}
232+
[class*="storage"] {{ color: #ff7b72; }}
233+
[class*="string"] {{ color: #a5d6ff; }}
234+
[class*="constant"] {{ color: #79c0ff; }}
235+
[class*="entity"] {{ color: #d2a8ff; }}
236+
[class*="support"] {{ color: #79c0ff; }}
237+
[class*="variable"] {{ color: #ffa657; }}
238+
[class*="punctuation"] {{ color: #c9d1d9; }}
239+
[class*="meta"] {{ color: #c9d1d9; }}
269240
}}
270241
</style>
271242
</head>

0 commit comments

Comments
 (0)