From 544a29c93b5b3f4965c40d6ce5582e4b350a7f65 Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Sat, 24 Oct 2026 17:17:22 +0000 Subject: [PATCH] Keep style runs disjoint when inserting text at a run boundary Found while testing crates/doc/src/text.rs:592 (TextData::replace_range, with normalize at :573) Co-Authored-By: Claude Haiku 5.5 Co-Authored-By: Claude Sonnet 5.5 --- crates/doc/src/tests.rs | 10 ++++++++++ crates/doc/src/text.rs | 9 ++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/crates/doc/src/tests.rs b/crates/doc/src/tests.rs index 1a006c26..f8e69f15 100644 --- a/crates/doc/src/tests.rs +++ b/crates/doc/src/tests.rs @@ -559,3 +559,13 @@ fn is_ancestor_holds_for_very_deep_chains() { assert!(d.is_ancestor(root, leaf)); assert!(matches!(d.reparent(root, leaf, None), Err(DocError::Cycle(..)))); } + +#[test] +fn text_insert_at_run_boundary_keeps_runs_disjoint() { + let mut t = TextData::new("0123456789"); + t.restyle(0, 5, |s| s.font_size = 20.0); + t.restyle(5, 10, |s| s.font_size = 30.0); + t.replace_range(5, 5, "xx"); + let runs: Vec<(usize, usize)> = t.runs.iter().map(|r| (r.start, r.end)).collect(); + assert_eq!(runs, vec![(0, 7), (7, 12)]); +} diff --git a/crates/doc/src/text.rs b/crates/doc/src/text.rs index 458426ad..d2abfed9 100644 --- a/crates/doc/src/text.rs +++ b/crates/doc/src/text.rs @@ -579,7 +579,14 @@ impl TextData { } self.runs.sort_by_key(|r| r.start); let mut merged: Vec = Vec::with_capacity(self.runs.len()); - for r in std::mem::take(&mut self.runs) { + for mut r in std::mem::take(&mut self.runs) { + // Runs never overlap: a later run starts where the previous one ends at the earliest. + if let Some(prev) = merged.last() { + r.start = r.start.max(prev.end); + if r.start >= r.end { + continue; + } + } match merged.last_mut() { Some(prev) if prev.end == r.start && prev.style == r.style => prev.end = r.end, _ => merged.push(r),