mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-24 10:27:00 -06:00
doc: runs of a file that reach past the text can't overflow a text edit
StyleRun start/end are usize taken straight from a .figcraft document.json (nothing clamps them on load; only the REST and SVG importers normalize), so TextData::replace_range's p - removed + inserted panics in a debug or test build and wraps to zero in release, where normalize then drops the run and the layer silently loses its formatting. Shift the runs with saturating arithmetic; normalize drops whatever is left. Regression test: text_runs_past_the_text_never_overflow — panics at crates/doc/src/text.rs:605 on main, passes with the clamp.
This commit is contained in:
@@ -168,6 +168,27 @@ fn text_runs_restyle_and_replace() {
|
||||
assert_eq!(t.characters, "");
|
||||
}
|
||||
|
||||
/// A run that reaches past the text (a file can carry one: nothing clamps `runs` on load)
|
||||
/// must not overflow the arithmetic that shifts the runs along, and must not leave the
|
||||
/// text unwritable.
|
||||
#[test]
|
||||
fn text_runs_past_the_text_never_overflow() {
|
||||
let style = || TextStyle { font_style: "Bold".into(), ..Default::default() };
|
||||
for end in [usize::MAX, usize::MAX - 1, 1_000_000] {
|
||||
let mut t = TextData::new("hi");
|
||||
t.runs = vec![StyleRun { start: 0, end, style: style() }];
|
||||
// Typing at the caret, and replacing a selection: both shift `end`.
|
||||
t.replace_range(0, 0, "x");
|
||||
assert_eq!(t.characters, "xhi", "end {end}");
|
||||
t.replace_range(0, 3, "yo");
|
||||
assert_eq!(t.characters, "yo", "end {end}");
|
||||
// The text is still writable and the runs are back inside it.
|
||||
t.replace_range(0, 0, "!");
|
||||
assert_eq!(t.characters, "!yo", "end {end}");
|
||||
assert!(t.runs.iter().all(|r| r.start < r.end && r.end <= t.char_count()), "end {end}: {:?}", t.runs);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn text_case() {
|
||||
assert_eq!(TextCase::Title.apply("hello big world"), "Hello Big World");
|
||||
|
||||
@@ -602,7 +602,9 @@ impl TextData {
|
||||
if p <= start {
|
||||
p
|
||||
} else if p >= end {
|
||||
p - removed + inserted
|
||||
// Runs of a file that reach past the text (nothing clamps them on load)
|
||||
// would otherwise overflow here; `normalize` below drops what is left.
|
||||
p.saturating_sub(removed).saturating_add(inserted)
|
||||
} else {
|
||||
start + inserted
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user