Skip to content

Commit db73ac3

Browse files
unhappychoiceclaude
andcommitted
feat(text-processing): add empty line preservation option
- Add process_challenge_text_with_comment_mapping_preserve_empty method - Allow preserving empty lines to maintain original code structure - Keep newlines typeable while controlling empty line handling - Add is_rest_of_line_comment_only helper for comment detection 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent cacf6c0 commit db73ac3

File tree

1 file changed

+41
-27
lines changed

1 file changed

+41
-27
lines changed

src/game/text_processor.rs

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,18 @@ impl TextProcessor {
1414
pub fn process_challenge_text_with_comment_mapping(
1515
text: &str,
1616
comment_ranges: &[(usize, usize)],
17+
) -> (String, Vec<(usize, usize)>) {
18+
Self::process_challenge_text_with_comment_mapping_preserve_empty(
19+
text,
20+
comment_ranges,
21+
false,
22+
)
23+
}
24+
25+
pub fn process_challenge_text_with_comment_mapping_preserve_empty(
26+
text: &str,
27+
comment_ranges: &[(usize, usize)],
28+
preserve_empty_lines: bool,
1729
) -> (String, Vec<(usize, usize)>) {
1830
// Create character position mapping from original to processed text
1931
let mut position_mapping = Vec::new();
@@ -26,8 +38,8 @@ impl TextProcessor {
2638
for line in &lines {
2739
let trimmed_line = line.trim_end();
2840

29-
// Skip empty lines
30-
if trimmed_line.trim().is_empty() {
41+
// Skip empty lines only if preserve_empty_lines is false
42+
if !preserve_empty_lines && trimmed_line.trim().is_empty() {
3143
// Record that all characters in this line are skipped
3244
for _ in 0..line.len() {
3345
position_mapping.push(None); // None means this character was removed
@@ -190,14 +202,13 @@ impl TextProcessor {
190202
return false;
191203
}
192204

193-
// Don't skip newlines - they need to be typeable
205+
// Skip newlines only in specific cases, but keep them typeable
194206
if chars[position] == '\n' {
195-
// Only skip newlines that are at the end of comment-only lines or at end of text
196-
if Self::is_newline_after_comment_only_line(text, position, comment_ranges)
197-
|| Self::should_skip_final_newline(text, position)
198-
{
207+
// Skip newlines only at the very end of text
208+
if Self::should_skip_final_newline(text, position) {
199209
return true;
200210
}
211+
// Keep newlines typeable by default
201212
return false;
202213
}
203214

@@ -291,39 +302,42 @@ impl TextProcessor {
291302
false
292303
}
293304

294-
fn is_newline_after_comment_only_line(
305+
pub fn is_rest_of_line_comment_only(
295306
text: &str,
296307
position: usize,
297308
comment_ranges: &[(usize, usize)],
298309
) -> bool {
299310
let chars: Vec<char> = text.chars().collect();
300-
301-
// Check if current character is newline
302-
if position >= chars.len() || chars[position] != '\n' {
311+
if position >= chars.len() {
303312
return false;
304313
}
305314

306-
// Find the start of current line
307-
let mut line_start = position;
308-
while line_start > 0 && chars[line_start - 1] != '\n' {
309-
line_start = line_start.saturating_sub(1);
315+
// Find the next newline or end of text
316+
let mut end_pos = position;
317+
while end_pos < chars.len() && chars[end_pos] != '\n' {
318+
end_pos += 1;
310319
}
311320

312-
// Check if everything from line_start to position is whitespace or comment
313-
for (i, &ch) in chars.iter().enumerate().take(position).skip(line_start) {
314-
if !ch.is_whitespace() {
315-
// Found non-whitespace, check if it's part of a comment
316-
if !comment_ranges
317-
.iter()
318-
.any(|&(start, end)| i >= start && i < end)
319-
{
320-
// Non-whitespace that's not a comment = this is not a comment-only line
321-
return false;
322-
}
321+
// Check if everything from current position to end of line is comment or whitespace
322+
for i in position..end_pos {
323+
let ch = chars[i];
324+
325+
// Skip whitespace
326+
if ch.is_whitespace() {
327+
continue;
328+
}
329+
330+
// Check if this character is part of a comment
331+
let is_in_comment = comment_ranges
332+
.iter()
333+
.any(|&(start, end)| i >= start && i < end);
334+
335+
if !is_in_comment {
336+
return false; // Found non-comment, non-whitespace content
323337
}
324338
}
325339

326-
// This line contains only whitespace and/or comments
340+
// Everything from current position to end of line is comment or whitespace
327341
true
328342
}
329343
}

0 commit comments

Comments
 (0)