From 5a2cab948307ff7938574a5eade425ab599f0163 Mon Sep 17 00:00:00 2001 From: aiongg <67543387+aiongg@users.noreply.github.com> Date: Sun, 8 Nov 2026 07:34:41 +0000 Subject: [PATCH] Apply review fixes - crates/model/src/edit.rs - crates/engine/src/tests.rs --- crates/engine/src/tests.rs | 11 +++++++++++ crates/model/src/edit.rs | 13 +++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs index 3ad7dd759..6946d1438 100644 --- a/crates/engine/src/tests.rs +++ b/crates/engine/src/tests.rs @@ -112,6 +112,17 @@ fn forward_delete_removes_whole_grapheme_cluster() { let t = s.execute("text.get", &json!({"id": title})).unwrap(); assert_eq!(t["text"], rest); } + // Inside a cluster, Delete stops at that cluster's end and leaves the next flag intact. + let mut s = session(); + let title = s.doc().unwrap().current_slide().unwrap().shapes[0].id.0; + s.execute("text.edit", &json!({"id": title})).unwrap(); + s.execute("text.selectAll", &json!({})).unwrap(); + s.execute("text.insert", &json!({"text": "\u{1F1FA}\u{1F1F8}\u{1F1E8}\u{1F1E6}"})).unwrap(); + s.execute("text.move", &json!({"to": "start"})).unwrap(); + s.execute("text.move", &json!({"to": "right"})).unwrap(); + s.execute("text.delete", &json!({"dir": "forward"})).unwrap(); + let t = s.execute("text.get", &json!({"id": title})).unwrap(); + assert_eq!(t["text"], "\u{1F1FA}\u{1F1E8}\u{1F1E6}"); } /// `text.set` treats CRLF (or a lone CR) as one paragraph break and keeps no `\r` in the text. diff --git a/crates/model/src/edit.rs b/crates/model/src/edit.rs index 080dade69..7e72a3650 100644 --- a/crates/model/src/edit.rs +++ b/crates/model/src/edit.rs @@ -424,14 +424,15 @@ fn smart_space(body: &mut TextBody, at: Pos) -> Pos { pub fn cluster_end(body: &TextBody, pos: Pos) -> Pos { let (pi, ch) = clamp(body, pos); let text = body.paragraphs.get(pi).map(Paragraph::text).unwrap_or_default(); - match text.chars().nth(ch) { - Some(_) => { - let rest: String = text.chars().skip(ch).collect(); - let n = rest.graphemes(true).next().map_or(1, |g| g.chars().count()); - (pi, ch + n) + // Boundaries come from the whole paragraph, so a caret inside a cluster deletes only to its end. + let mut end = 0; + for g in text.graphemes(true) { + end += g.chars().count(); + if end > ch { + return (pi, end); } - None => char_move(body, pos, true), } + char_move(body, pos, true) } /// One character left/right across paragraph boundaries.