mirror of
https://github.com/storytold/figcraft.git
synced 2026-11-13 13:52:34 +00:00
M4: a list item made by Enter has no start of its own, so a split list restarts at 1, as measured
Enter copied the item's start (a typed "5. " gives 4) onto the new item, so once a middle item was unlisted the next one restarted at its copied start. Every paragraph an insertion creates now has start_offset 0; the first item keeps its start. Measured in Figma: unlisting "b" in 5. 6. 7. leaves "c" at 1., and the same for a list started with 1. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Model-Effort: Opus 5.5 drove; Opus 5.5 briefed; Sonnet 5.5 wrote at medium
This commit is contained in:
1 parent
b8f05d1948
commit
b190e675b9
2 files changed
+55
-3
No files matched your search
@@ -319,6 +319,14 @@ fn insert_model(s: &mut Session, text: &str, ime_commit: bool) -> Result<ImeInse
|
||||
}
|
||||
}
|
||||
t.replace_range(a, b, &text);
|
||||
// As measured in Figma (2026-11-11): an item made by Enter has no start of its own, so
|
||||
// unlisting the item before it makes it restart at 1 rather than at the copied start.
|
||||
// Every paragraph after the first that the insertion made loses the start it was copied.
|
||||
for (i, c) in text.chars().enumerate() {
|
||||
if c == '\n' {
|
||||
t.set_para_lists(a + i + 1, a + i + 1, |pl| pl.start_offset = 0);
|
||||
}
|
||||
}
|
||||
te.caret = a + text.chars().count();
|
||||
te.anchor = te.caret;
|
||||
})?;
|
||||
|
||||
@@ -130,14 +130,58 @@ fn typed_number_starts_the_list_there() {
|
||||
let d = data(&s, t);
|
||||
assert_eq!(d.characters, "x\ny");
|
||||
assert_eq!(lists(&s, t), [(N, 0), (N, 0)]);
|
||||
// Enter copies the list onto the new item; `list_markers` ignores the start of an item that
|
||||
// goes on counting (a numbered item right after a numbered item at its level), so 5. then 6.
|
||||
assert_eq!((d.para_list(0).start_offset, d.para_list(1).start_offset), (4, 4), "the copy is ignored: the next item counts on");
|
||||
// An item made by Enter has no start of its own and goes on counting: 5. then 6.
|
||||
assert_eq!((d.para_list(0).start_offset, d.para_list(1).start_offset), (4, 0), "the new item has no start");
|
||||
s.undo().unwrap();
|
||||
assert_eq!(data(&s, t).characters, "", "one undo step for the session");
|
||||
assert_eq!(data(&s, t).para_list(0).start_offset, 0);
|
||||
}
|
||||
|
||||
/// Type `first` ("5. a" style), then Enter, "b", Enter, "c", leaving the caret in "c".
|
||||
fn three_typed_items(s: &mut Session, first: &str) -> NodeId {
|
||||
let t = text(s, "x");
|
||||
s.execute("node.set", &json!({"id": t, "props": {"characters": ""}})).unwrap();
|
||||
begin(s, t, Some(0), false).unwrap();
|
||||
for c in first.chars() {
|
||||
insert(s, &c.to_string()).unwrap();
|
||||
}
|
||||
for step in ["Enter", "b", "Enter", "c"] {
|
||||
if step == "Enter" {
|
||||
key(s, step, false, false, false).unwrap();
|
||||
} else {
|
||||
insert(s, step).unwrap();
|
||||
}
|
||||
}
|
||||
t
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_list_split_by_unlisting_a_middle_item_restarts_at_one() {
|
||||
// As measured in Figma (2026-11-11): typed "5. a", Enter, "b", Enter, "c" reads 5. 6. 7.;
|
||||
// Backspace at the start of "b" takes its marker and "c" then reads 1., for "1. " too.
|
||||
// `list_markers` counts on after a numbered item and restarts a numbered item that follows a
|
||||
// plain paragraph at its own start, so the starts and the neighbours give the markers.
|
||||
for (first, start) in [("5. a", 4), ("1. a", 0)] {
|
||||
let mut s = Session::new();
|
||||
let t = three_typed_items(&mut s, first);
|
||||
assert_eq!(
|
||||
data(&s, t).characters,
|
||||
"a
|
||||
b
|
||||
c"
|
||||
);
|
||||
// The guard: unsplit, "b" and "c" go on counting (5. 6. 7.).
|
||||
let d = data(&s, t);
|
||||
assert_eq!(lists(&s, t), [(N, 0); 3], "{first}: no split");
|
||||
assert_eq!((d.para_list(0).start_offset, d.para_list(1).start_offset, d.para_list(2).start_offset), (start, 0, 0), "{first}");
|
||||
set_caret(&mut s, 2, false).unwrap();
|
||||
key(&mut s, "Backspace", false, false, false).unwrap();
|
||||
let d = data(&s, t);
|
||||
assert_eq!(lists(&s, t), [(N, 0), (P, 0), (N, 0)], "{first}: split");
|
||||
assert_eq!(d.para_list(2).start_offset, 0, "{first}: c restarts at 1 (the item made by Enter has no start)");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_keys_at_list_items() {
|
||||
// (chars, lists before, caret, key, shift, chars after, lists after, derivation)
|
||||
|
||||
Reference in new issue
Block a user