From 69c0231230038f7e1569359c276da034cc300ae0 Mon Sep 17 00:00:00 2001 From: Leynos Date: Thu, 17 Jul 2025 23:17:33 +0100 Subject: [PATCH 1/5] Add test for backslash line breaks --- src/wrap.rs | 28 +++++++++++++++++++------- tests/data/hard_linebreak_expected.txt | 4 ++++ tests/data/hard_linebreak_input.txt | 3 +++ tests/integration.rs | 7 +++++++ 4 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 tests/data/hard_linebreak_expected.txt create mode 100644 tests/data/hard_linebreak_input.txt diff --git a/src/wrap.rs b/src/wrap.rs index 2117f974..1759c1a0 100644 --- a/src/wrap.rs +++ b/src/wrap.rs @@ -301,17 +301,31 @@ pub fn wrap_text(lines: &[String], width: usize) -> Vec { indent = line.chars().take_while(|c| c.is_whitespace()).collect(); } let trimmed_end = line.trim_end(); - let hard_break = line.ends_with(" ") - || trimmed_end.ends_with("
") - || trimmed_end.ends_with("
") - || trimmed_end.ends_with("
"); - let text = trimmed_end + let mut hard_break = false; + let mut text = trimmed_end .trim_end_matches("
") .trim_end_matches("
") .trim_end_matches("
") - .trim_end_matches(' ') - .trim_start() .to_string(); + + if trimmed_end.ends_with("
") + || trimmed_end.ends_with("
") + || trimmed_end.ends_with("
") + { + hard_break = true; + } + + if line.ends_with(" ") { + hard_break = true; + text = text.trim_end_matches(' ').to_string(); + } + + if text.ends_with('\\') && !text.ends_with("\\\\") { + hard_break = true; + } + + text = text.trim_start().to_string(); + buf.push((text, hard_break)); } diff --git a/tests/data/hard_linebreak_expected.txt b/tests/data/hard_linebreak_expected.txt new file mode 100644 index 00000000..4996c57b --- /dev/null +++ b/tests/data/hard_linebreak_expected.txt @@ -0,0 +1,4 @@ +Scenarios live under `tests/features/`. Step implementations in `tests` share \ +a common `World` struct that uses `figment::Jail` for isolation. Each scenario +demonstration example \ +executes asynchronously with `tokio`. diff --git a/tests/data/hard_linebreak_input.txt b/tests/data/hard_linebreak_input.txt new file mode 100644 index 00000000..beaecb97 --- /dev/null +++ b/tests/data/hard_linebreak_input.txt @@ -0,0 +1,3 @@ +Scenarios live under `tests/features/`. Step implementations in `tests` share \ +a common `World` struct that uses `figment::Jail` for isolation. Each scenario demonstration example \ +executes asynchronously with `tokio`. diff --git a/tests/integration.rs b/tests/integration.rs index 4920bade..d629a745 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -794,6 +794,13 @@ fn test_preserve_hard_line_breaks() { assert_eq!(output[1], "Line two follows."); } +#[test] +fn test_wrap_hard_linebreak_backslash() { + let input: Vec = include_lines!("data/hard_linebreak_input.txt"); + let expected: Vec = include_lines!("data/hard_linebreak_expected.txt"); + assert_eq!(process_stream(&input), expected); +} + #[test] /// Tests that `process_stream` preserves complex table formatting without modification. /// From 7f01df80b0a26ceec9556fcd0c2ab1e750419a8e Mon Sep 17 00:00:00 2001 From: Leynos Date: Thu, 17 Jul 2025 23:58:57 +0100 Subject: [PATCH 2/5] Refine hard break detection --- src/wrap.rs | 34 ++++++++++++++++------------------ tests/integration.rs | 24 ++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/src/wrap.rs b/src/wrap.rs index 1759c1a0..03967f4c 100644 --- a/src/wrap.rs +++ b/src/wrap.rs @@ -301,30 +301,28 @@ pub fn wrap_text(lines: &[String], width: usize) -> Vec { indent = line.chars().take_while(|c| c.is_whitespace()).collect(); } let trimmed_end = line.trim_end(); - let mut hard_break = false; - let mut text = trimmed_end + let without_br = trimmed_end .trim_end_matches("
") .trim_end_matches("
") - .trim_end_matches("
") - .to_string(); - - if trimmed_end.ends_with("
") - || trimmed_end.ends_with("
") - || trimmed_end.ends_with("
") - { - hard_break = true; - } + .trim_end_matches("
"); - if line.ends_with(" ") { - hard_break = true; - text = text.trim_end_matches(' ').to_string(); - } + let is_trailing_spaces = line.ends_with(" "); + let is_html_br = trimmed_end != without_br; - if text.ends_with('\\') && !text.ends_with("\\\\") { - hard_break = true; + // Count trailing backslashes to handle escaped pairs correctly + let mut backslashes = 0; + for ch in without_br.chars().rev() { + if ch == '\\' { + backslashes += 1; + } else { + break; + } } + let is_backslash_escape = backslashes % 2 == 1; + + let hard_break = is_trailing_spaces || is_html_br || is_backslash_escape; - text = text.trim_start().to_string(); + let text = without_br.trim_start().trim_end_matches(' ').to_string(); buf.push((text, hard_break)); } diff --git a/tests/integration.rs b/tests/integration.rs index d629a745..9993582c 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -801,6 +801,30 @@ fn test_wrap_hard_linebreak_backslash() { assert_eq!(process_stream(&input), expected); } +#[test] +fn test_wrap_hard_linebreak_backslash_edge_cases() { + let input = vec![ + String::from("This line ends with two backslashes: \\\\"), + String::from("This line ends with a single backslash: \\"), + String::from(" \\ "), + String::from("\\"), + String::from("Text before \\ and after"), + String::from(" \\"), + String::new(), + ]; + let expected = vec![ + String::from( + "This line ends with two backslashes: \\\\ This line ends with a single backslash:", + ), + String::from("\\"), + String::from("\\"), + String::from("\\"), + String::from("Text before \\ and after \\"), + String::new(), + ]; + assert_eq!(process_stream(&input), expected); +} + #[test] /// Tests that `process_stream` preserves complex table formatting without modification. /// From 96b31123bee72de8f171ac7b7f489a267e8b2674 Mon Sep 17 00:00:00 2001 From: Leynos Date: Fri, 18 Jul 2025 00:34:29 +0100 Subject: [PATCH 3/5] Refine hard break detection --- src/wrap.rs | 13 ++----------- tests/integration.rs | 36 +++++++++++++++++------------------- 2 files changed, 19 insertions(+), 30 deletions(-) diff --git a/src/wrap.rs b/src/wrap.rs index 03967f4c..b9e56fd4 100644 --- a/src/wrap.rs +++ b/src/wrap.rs @@ -308,17 +308,8 @@ pub fn wrap_text(lines: &[String], width: usize) -> Vec { let is_trailing_spaces = line.ends_with(" "); let is_html_br = trimmed_end != without_br; - - // Count trailing backslashes to handle escaped pairs correctly - let mut backslashes = 0; - for ch in without_br.chars().rev() { - if ch == '\\' { - backslashes += 1; - } else { - break; - } - } - let is_backslash_escape = backslashes % 2 == 1; + let backslash_count = without_br.chars().rev().take_while(|&c| c == '\\').count(); + let is_backslash_escape = backslash_count % 2 == 1; let hard_break = is_trailing_spaces || is_html_br || is_backslash_escape; diff --git a/tests/integration.rs b/tests/integration.rs index 9993582c..131902d0 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -803,25 +803,23 @@ fn test_wrap_hard_linebreak_backslash() { #[test] fn test_wrap_hard_linebreak_backslash_edge_cases() { - let input = vec![ - String::from("This line ends with two backslashes: \\\\"), - String::from("This line ends with a single backslash: \\"), - String::from(" \\ "), - String::from("\\"), - String::from("Text before \\ and after"), - String::from(" \\"), - String::new(), - ]; - let expected = vec![ - String::from( - "This line ends with two backslashes: \\\\ This line ends with a single backslash:", - ), - String::from("\\"), - String::from("\\"), - String::from("\\"), - String::from("Text before \\ and after \\"), - String::new(), - ]; + let input = lines_vec!( + "This line ends with two backslashes: \\\\", + "This line ends with a single backslash: \\", + " \\ ", + "\\", + "Text before \\ and after", + " \\", + "", + ); + let expected = lines_vec!( + "This line ends with two backslashes: \\\\ This line ends with a single backslash:", + "\\", + "\\", + "\\", + "Text before \\ and after \\", + "", + ); assert_eq!(process_stream(&input), expected); } From d2cd566d781ccdaf90af59dead81593b42b1ba55 Mon Sep 17 00:00:00 2001 From: Leynos Date: Fri, 18 Jul 2025 18:38:55 +0100 Subject: [PATCH 4/5] Refine backslash break detection --- src/wrap.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/wrap.rs b/src/wrap.rs index b9e56fd4..61b86351 100644 --- a/src/wrap.rs +++ b/src/wrap.rs @@ -301,19 +301,22 @@ pub fn wrap_text(lines: &[String], width: usize) -> Vec { indent = line.chars().take_while(|c| c.is_whitespace()).collect(); } let trimmed_end = line.trim_end(); - let without_br = trimmed_end + let text_without_html_breaks = trimmed_end .trim_end_matches("
") .trim_end_matches("
") .trim_end_matches("
"); let is_trailing_spaces = line.ends_with(" "); - let is_html_br = trimmed_end != without_br; - let backslash_count = without_br.chars().rev().take_while(|&c| c == '\\').count(); + let is_html_br = trimmed_end != text_without_html_breaks; + let backslash_count = trimmed_end.chars().rev().take_while(|&c| c == '\\').count(); let is_backslash_escape = backslash_count % 2 == 1; let hard_break = is_trailing_spaces || is_html_br || is_backslash_escape; - let text = without_br.trim_start().trim_end_matches(' ').to_string(); + let text = text_without_html_breaks + .trim_start() + .trim_end_matches(' ') + .to_string(); buf.push((text, hard_break)); } From 9ecb7c2f9b7ed24bb2709344fe6cb2bc144b7969 Mon Sep 17 00:00:00 2001 From: Leynos Date: Fri, 18 Jul 2025 20:01:05 +0100 Subject: [PATCH 5/5] Fix backslash hard break detection --- src/wrap.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/wrap.rs b/src/wrap.rs index 61b86351..a2f1d180 100644 --- a/src/wrap.rs +++ b/src/wrap.rs @@ -308,7 +308,12 @@ pub fn wrap_text(lines: &[String], width: usize) -> Vec { let is_trailing_spaces = line.ends_with(" "); let is_html_br = trimmed_end != text_without_html_breaks; - let backslash_count = trimmed_end.chars().rev().take_while(|&c| c == '\\').count(); + let backslash_count = line + .trim_end() + .chars() + .rev() + .take_while(|&c| c == '\\') + .count(); let is_backslash_escape = backslash_count % 2 == 1; let hard_break = is_trailing_spaces || is_html_br || is_backslash_escape;