Skip to content

Commit 76933fc

Browse files
committed
refactor(llm_stream): remove pulldown-cmark dependency and associated markdown parsing function
1 parent c65a69c commit 76933fc

2 files changed

Lines changed: 19 additions & 71 deletions

File tree

crates/llm_stream/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,4 @@ tempfile = "3.19.1"
3737
syntect = { version = "5.2.0", default-features = false, features = [
3838
"default-fancy",
3939
] }
40-
pulldown-cmark = "0.13.0"
4140
nom = "7.1"

crates/llm_stream/src/printer.rs

Lines changed: 19 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use nom::{
77
error::Error,
88
IResult,
99
};
10-
use pulldown_cmark::{CodeBlockKind, Event, Parser, Tag, TagEnd};
1110
use syntect::{
1211
easy::HighlightLines,
1312
highlighting::{Style, Theme, ThemeSet},
@@ -18,8 +17,7 @@ use syntect::{
1817
static SYNTAX_SET: LazyLock<SyntaxSet> = LazyLock::new(SyntaxSet::load_defaults_newlines);
1918
static THEME: LazyLock<Theme> = LazyLock::new(|| {
2019
let theme_data = include_str!("../assets/themes/tokyonight/tokyonight-storm.tmTheme");
21-
ThemeSet::load_from_reader(&mut std::io::Cursor::new(theme_data))
22-
.unwrap()
20+
ThemeSet::load_from_reader(&mut std::io::Cursor::new(theme_data)).unwrap()
2321
});
2422
static MARKDOWN_SYNTAX: LazyLock<&SyntaxReference> =
2523
LazyLock::new(|| SYNTAX_SET.find_syntax_by_name("Markdown").unwrap());
@@ -29,55 +27,6 @@ static TERMINAL_WIDTH: LazyLock<usize> = LazyLock::new(|| {
2927
.unwrap_or(80)
3028
});
3129

32-
pub fn markdown_to_24_bit_terminal_escaped(markdown: &str) -> String {
33-
let mut sr = SYNTAX_SET.find_syntax_plain_text();
34-
let mut output = String::new();
35-
let mut code = String::new();
36-
let mut code_block = false;
37-
38-
for event in Parser::new(markdown) {
39-
match event {
40-
Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(lang))) => {
41-
let lang = lang.trim();
42-
sr = SYNTAX_SET
43-
.find_syntax_by_token(lang)
44-
.unwrap_or_else(|| SYNTAX_SET.find_syntax_plain_text());
45-
code_block = true;
46-
}
47-
Event::End(TagEnd::CodeBlock) => {
48-
let mut highlighter = HighlightLines::new(sr, &THEME);
49-
for line in LinesWithEndings::from(&format!("\n{}\n", code)) {
50-
let ranges: Vec<(Style, &str)> =
51-
highlighter.highlight_line(line, &SYNTAX_SET).unwrap();
52-
let escaped = as_24_bit_terminal_escaped(&ranges[..], false);
53-
output.push_str(&escaped);
54-
}
55-
56-
code = String::new();
57-
code_block = false;
58-
}
59-
60-
Event::Text(t) => {
61-
if code_block {
62-
code.push_str(&t);
63-
} else {
64-
output.push_str(&t);
65-
}
66-
}
67-
68-
Event::Start(Tag::Paragraph) => {
69-
if !output.is_empty() {
70-
output.push('\n');
71-
}
72-
}
73-
74-
_ => (),
75-
}
76-
}
77-
78-
output
79-
}
80-
8130
#[derive(Debug, Clone, PartialEq)]
8231
pub enum MarkdownElement {
8332
Text(String),
@@ -144,31 +93,31 @@ fn parse_remaining_text(input: &str) -> IResult<&str, MarkdownElement> {
14493
fn wrap_text_to_terminal_width(text: &str) -> String {
14594
let width = *TERMINAL_WIDTH;
14695
let mut result = String::new();
147-
96+
14897
// Split by newlines but preserve empty lines
14998
let lines: Vec<&str> = text.split('\n').collect();
150-
99+
151100
for (i, line) in lines.iter().enumerate() {
152101
if i > 0 {
153102
result.push('\n');
154103
}
155-
104+
156105
if line.len() <= width {
157106
// Line fits within width, keep as is
158107
result.push_str(line);
159108
} else {
160109
// Line needs wrapping
161110
let words: Vec<&str> = line.split_whitespace().collect();
162111
let mut current_line = String::new();
163-
112+
164113
for word in words {
165114
// Check if adding this word would exceed width
166115
let potential_length = if current_line.is_empty() {
167116
word.len()
168117
} else {
169118
current_line.len() + 1 + word.len() // +1 for space
170119
};
171-
120+
172121
if potential_length <= width {
173122
// Word fits, add it to current line
174123
if !current_line.is_empty() {
@@ -182,18 +131,18 @@ fn wrap_text_to_terminal_width(text: &str) -> String {
182131
result.push('\n');
183132
current_line.clear();
184133
}
185-
134+
186135
// Handle very long words that exceed width
187136
if word.len() > width {
188137
// Split the word itself
189138
let mut remaining_word = word;
190139
while !remaining_word.is_empty() {
191140
let chunk_size = width.min(remaining_word.len());
192141
let chunk = &remaining_word[..chunk_size];
193-
142+
194143
result.push_str(chunk);
195144
remaining_word = &remaining_word[chunk_size..];
196-
145+
197146
if !remaining_word.is_empty() {
198147
result.push('\n');
199148
}
@@ -204,14 +153,14 @@ fn wrap_text_to_terminal_width(text: &str) -> String {
204153
}
205154
}
206155
}
207-
156+
208157
// Add remaining content in current_line
209158
if !current_line.is_empty() {
210159
result.push_str(&current_line);
211160
}
212161
}
213162
}
214-
163+
215164
result
216165
}
217166

@@ -470,12 +419,12 @@ mod tests {
470419
// Create a long line that will exceed typical terminal width
471420
let text = "This is a very long line that should definitely exceed the terminal width and therefore needs to be wrapped at appropriate word boundaries to ensure readability.";
472421
let wrapped = wrap_text_to_terminal_width(text);
473-
422+
474423
// Check that no line exceeds terminal width
475424
for line in wrapped.lines() {
476425
assert!(line.len() <= *TERMINAL_WIDTH, "Line too long: '{}'", line);
477426
}
478-
427+
479428
// Check that the text is preserved (all words should still be there)
480429
let original_words: Vec<&str> = text.split_whitespace().collect();
481430
let wrapped_words: Vec<&str> = wrapped.split_whitespace().collect();
@@ -486,12 +435,12 @@ mod tests {
486435
fn test_wrap_text_multiple_lines() {
487436
let text = "Short line.\nThis is a very long line that should definitely exceed the terminal width and therefore needs to be wrapped.\nAnother short line.";
488437
let wrapped = wrap_text_to_terminal_width(text);
489-
438+
490439
// Check that no line exceeds terminal width
491440
for line in wrapped.lines() {
492441
assert!(line.len() <= *TERMINAL_WIDTH, "Line too long: '{}'", line);
493442
}
494-
443+
495444
// Check that short lines are preserved
496445
let lines: Vec<&str> = wrapped.lines().collect();
497446
assert_eq!(lines[0], "Short line.");
@@ -502,7 +451,7 @@ mod tests {
502451
fn test_wrap_text_preserve_empty_lines() {
503452
let text = "First line.\n\nThird line.";
504453
let wrapped = wrap_text_to_terminal_width(text);
505-
454+
506455
let lines: Vec<&str> = wrapped.lines().collect();
507456
assert_eq!(lines.len(), 3);
508457
assert_eq!(lines[0], "First line.");
@@ -515,12 +464,12 @@ mod tests {
515464
// Create a single word that exceeds terminal width
516465
let long_word = "a".repeat(*TERMINAL_WIDTH + 10);
517466
let wrapped = wrap_text_to_terminal_width(&long_word);
518-
467+
519468
// Should be split into chunks
520469
for line in wrapped.lines() {
521470
assert!(line.len() <= *TERMINAL_WIDTH, "Line too long: '{}'", line);
522471
}
523-
472+
524473
// All characters should be preserved
525474
let wrapped_chars: String = wrapped.chars().filter(|&c| c != '\n').collect();
526475
assert_eq!(wrapped_chars, long_word);
@@ -531,7 +480,7 @@ mod tests {
531480
// Test that text parsing applies wrapping
532481
let long_text = "This is a very long line that should definitely exceed the terminal width and therefore needs to be wrapped at appropriate word boundaries.";
533482
let input = format!("{}```", long_text);
534-
483+
535484
if let Ok((_, element)) = parse_text_content(&input) {
536485
if let MarkdownElement::Text(content) = element {
537486
// Check that no line exceeds terminal width

0 commit comments

Comments
 (0)