From fb6830c4dd4e37d26d43593ae8c768576c94f311 Mon Sep 17 00:00:00 2001 From: Dew Date: Mon, 19 Oct 2026 23:23:25 +0700 Subject: [PATCH] M7: review follow-up, opening spaces are not a Thai row of their own MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A Thai Add text paragraph that opens with spaces held a row of only those spaces. When the first word did not fit after them, that row was ended (trimmed to an empty row) and the opening spaces were lost. A row of only spaces now counts as empty when deciding to end it, so the first word stays on the first row after the spaces, or breaks between clusters there. Reported by the CodeRabbit review on #3492; the new case fails without the fix (at 16 pt the rows were "", "ป", "ระ", ...). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_018cECVMvRnc9mHNFfVp7nXd --- crates/edit/src/added/layout.rs | 7 ++++--- crates/edit/src/tests.rs | 11 ++++++++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/crates/edit/src/added/layout.rs b/crates/edit/src/added/layout.rs index 0520ada0ec..62f24c5c4c 100644 --- a/crates/edit/src/added/layout.rs +++ b/crates/edit/src/added/layout.rs @@ -102,13 +102,14 @@ fn thai_lines(t: &AddedText, para: &str, width: f64) -> Result, Edit for word in words { // The word's own width: the spaces after it may hang past the row's end. let ink: f64 = word.iter().filter(|(s, _)| !blank(s)).map(|(_, w)| w).sum(); - if !line.is_empty() && used + ink > width { + // A row holding only the spaces that open the paragraph has nothing to end yet. + if !blank(&line) && used + ink > width { end_row(&mut lines, &mut line); used = 0.0; } - let too_wide = line.is_empty() && ink > width; + let too_wide = blank(&line) && ink > width; for (text, advance) in word { - if too_wide && !line.is_empty() && !blank(&text) && used + advance > width { + if too_wide && !blank(&line) && !blank(&text) && used + advance > width { end_row(&mut lines, &mut line); used = 0.0; } diff --git a/crates/edit/src/tests.rs b/crates/edit/src/tests.rs index ff91fef668..d8ba0a8aeb 100644 --- a/crates/edit/src/tests.rs +++ b/crates/edit/src/tests.rs @@ -5815,8 +5815,17 @@ fn added_thai_text_wraps_between_dictionary_words() { let rows = super::added::lines(&spaced); assert!(rows.iter().all(|r| !r.starts_with(' ') && !r.ends_with(' ')), "{rows:?}"); assert_eq!(rows.concat(), "น้ำประปาสะอาดปลอดภัย"); - let narrow = AddedText { rect: [0.0, 0.0, 16.0, 200.0], text: "กิ่กิ่กิ่".into(), ..t }; + let narrow = AddedText { rect: [0.0, 0.0, 16.0, 200.0], text: "กิ่กิ่กิ่".into(), ..t.clone() }; assert_eq!(super::added::lines(&narrow), ["กิ่", "กิ่", "กิ่"]); + // Spaces that open a paragraph are kept, but they are not a row of their own: a first word + // too wide to fit after them stays on the first row (or breaks between clusters there). + for width in [16.0, 40.0, 60.0, 90.0] { + let indented = AddedText { rect: [0.0, 0.0, width, 400.0], text: " ประปาสะอาด".into(), ..t.clone() }; + let rows = super::added::lines(&indented); + assert!(rows.iter().all(|r| !r.trim().is_empty()), "an empty row at width {width}: {rows:?}"); + assert!(rows[0].starts_with(" "), "the opening spaces are kept at width {width}: {rows:?}"); + assert_eq!(rows.concat().trim_start(), "ประปาสะอาด"); + } } #[test]