mirror of
https://github.com/storytold/deckcraft.git
synced 2026-11-13 13:43:50 +00:00
Delete a whole grapheme cluster with the forward Delete key
Found while testing crates/engine/src/cmd/text/caret.rs:60 (delete_dir, forward) with crates/model/src/edit.rs:423 (char_move) Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com> Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c16db2ef3e
commit
3e2dc9ca7f
3 files changed
+32
-1
No files matched your search
@@ -56,7 +56,7 @@ pub(crate) fn delete_dir(s: &mut Session, dir: &str) -> Result<Value> {
|
||||
ed::delete(body, a, b)
|
||||
} else {
|
||||
let other = match dir.as_str() {
|
||||
"forward" => ed::char_move(body, a, true),
|
||||
"forward" => ed::cluster_end(body, a),
|
||||
_ => {
|
||||
// Backspace at the start of an indented/bulleted paragraph outdents first.
|
||||
if a.1 == 0
|
||||
|
||||
@@ -98,6 +98,22 @@ fn text_editing_flow() {
|
||||
assert_eq!(s.doc().unwrap().selection.shapes.len(), 1);
|
||||
}
|
||||
|
||||
/// Forward Delete removes a whole grapheme cluster, not just its first scalar.
|
||||
#[test]
|
||||
fn forward_delete_removes_whole_grapheme_cluster() {
|
||||
for (input, rest) in [("\u{1F44D}\u{1F3FD}y", "y"), ("e\u{301}x", "x")] {
|
||||
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": input})).unwrap();
|
||||
s.execute("text.move", &json!({"to": "start"})).unwrap();
|
||||
s.execute("text.delete", &json!({"dir": "forward"})).unwrap();
|
||||
let t = s.execute("text.get", &json!({"id": title})).unwrap();
|
||||
assert_eq!(t["text"], rest);
|
||||
}
|
||||
}
|
||||
|
||||
/// `text.set` treats CRLF (or a lone CR) as one paragraph break and keeps no `\r` in the text.
|
||||
#[test]
|
||||
fn text_set_crlf_makes_clean_paragraphs() {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
use crate::text::{Paragraph, Run, RunKind, RunProps, TextBody};
|
||||
use crate::words;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
/// (paragraph, char offset in the paragraph).
|
||||
pub type Pos = (usize, usize);
|
||||
@@ -419,6 +420,20 @@ fn smart_space(body: &mut TextBody, at: Pos) -> Pos {
|
||||
at
|
||||
}
|
||||
|
||||
/// The position after the grapheme cluster at `pos`, or the next paragraph's start at a paragraph end.
|
||||
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)
|
||||
}
|
||||
None => char_move(body, pos, true),
|
||||
}
|
||||
}
|
||||
|
||||
/// One character left/right across paragraph boundaries.
|
||||
pub fn char_move(body: &TextBody, pos: Pos, forward: bool) -> Pos {
|
||||
let (pi, ch) = clamp(body, pos);
|
||||
|
||||
Reference in new issue
Block a user