From d1e2a1f20c3234a0b4338c30390794f0f6ff7ca6 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 6 Oct 2025 13:04:45 +0200 Subject: [PATCH] gpui: Assert validity of text runs for `StyleText` (#39581) Should help with figuring out the char boundary panic in text shaping on windows Release Notes: - N/A *or* Added/Fixed/Improved ... --- crates/file_finder/src/open_path_prompt.rs | 4 ++-- crates/gpui/src/elements/text.rs | 20 +++++++++++++++++--- crates/gpui/src/text_system/line_wrapper.rs | 6 +----- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/crates/file_finder/src/open_path_prompt.rs b/crates/file_finder/src/open_path_prompt.rs index b0417b1d13fc4f82c8b16b0ac87249405b6f4129..b199c61a5e3b78ab656add6b0b1cfcc80a3ada74 100644 --- a/crates/file_finder/src/open_path_prompt.rs +++ b/crates/file_finder/src/open_path_prompt.rs @@ -755,7 +755,7 @@ impl PickerDelegate for OpenPathDelegate { .with_default_highlights( &window.text_style(), vec![( - delta..delta + label_len, + delta..label_len, HighlightStyle::color(Color::Conflict.color(cx)), )], ) @@ -765,7 +765,7 @@ impl PickerDelegate for OpenPathDelegate { .with_default_highlights( &window.text_style(), vec![( - delta..delta + label_len, + delta..label_len, HighlightStyle::color(Color::Created.color(cx)), )], ) diff --git a/crates/gpui/src/elements/text.rs b/crates/gpui/src/elements/text.rs index b5e071279623611685ea744e38b072284e764e2a..5d34ccfa5d78e09d47b36a2e061fdaa0fbbca45f 100644 --- a/crates/gpui/src/elements/text.rs +++ b/crates/gpui/src/elements/text.rs @@ -180,8 +180,7 @@ impl StyledText { "Can't use `with_default_highlights` and `with_highlights`" ); let runs = Self::compute_runs(&self.text, default_style, highlights); - self.runs = Some(runs); - self + self.with_runs(runs) } /// Set the styling attributes for the given text, as well as @@ -194,7 +193,15 @@ impl StyledText { self.runs.is_none(), "Can't use `with_highlights` and `with_default_highlights`" ); - self.delayed_highlights = Some(highlights.into_iter().collect::>()); + self.delayed_highlights = Some( + highlights + .into_iter() + .inspect(|(run, _)| { + debug_assert!(self.text.is_char_boundary(run.start)); + debug_assert!(self.text.is_char_boundary(run.end)); + }) + .collect::>(), + ); self } @@ -207,8 +214,10 @@ impl StyledText { let mut ix = 0; for (range, highlight) in highlights { if ix < range.start { + debug_assert!(text.is_char_boundary(range.start)); runs.push(default_style.clone().to_run(range.start - ix)); } + debug_assert!(text.is_char_boundary(range.end)); runs.push( default_style .clone() @@ -225,6 +234,11 @@ impl StyledText { /// Set the text runs for this piece of text. pub fn with_runs(mut self, runs: Vec) -> Self { + let mut text = &**self.text; + for run in &runs { + text = text.get(run.len..).expect("invalid text run"); + } + assert!(text.is_empty(), "invalid text run"); self.runs = Some(runs); self } diff --git a/crates/gpui/src/text_system/line_wrapper.rs b/crates/gpui/src/text_system/line_wrapper.rs index d499d78551a5e0e268b575496bbdac5ddf59369c..ea97cbf0d36a393976809d9aee5cb0d4f2dfab63 100644 --- a/crates/gpui/src/text_system/line_wrapper.rs +++ b/crates/gpui/src/text_system/line_wrapper.rs @@ -225,19 +225,15 @@ impl LineWrapper { fn update_runs_after_truncation(result: &str, ellipsis: &str, runs: &mut Vec) { let mut truncate_at = result.len() - ellipsis.len(); - let mut run_end = None; for (run_index, run) in runs.iter_mut().enumerate() { if run.len <= truncate_at { truncate_at -= run.len; } else { run.len = truncate_at + ellipsis.len(); - run_end = Some(run_index + 1); + runs.truncate(run_index + 1); break; } } - if let Some(run_end) = run_end { - runs.truncate(run_end); - } } /// A fragment of a line that can be wrapped.