From 809018e15f39f2d31cd7c6f523d1ba062cdc5a0e Mon Sep 17 00:00:00 2001 From: Danilo Leal Date: Thu, 18 Dec 2025 17:52:17 -0300 Subject: [PATCH] Readd changes post merge with main --- crates/gpui/src/text_system/line_wrapper.rs | 105 ++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/crates/gpui/src/text_system/line_wrapper.rs b/crates/gpui/src/text_system/line_wrapper.rs index 95cd55d04443c6b2c351bf8533ccb57d49e8dcd9..8148537029ade11d822fec6296cde22f802066c5 100644 --- a/crates/gpui/src/text_system/line_wrapper.rs +++ b/crates/gpui/src/text_system/line_wrapper.rs @@ -179,6 +179,65 @@ impl LineWrapper { } } + /// Truncate a line of text from the start to the given width. + /// Truncates from the beginning, e.g., "…ong text here" + pub fn truncate_line_start<'a>( + &mut self, + line: SharedString, + truncate_width: Pixels, + truncation_prefix: &str, + runs: &'a [TextRun], + ) -> (SharedString, Cow<'a, [TextRun]>) { + // First, measure the full line width to see if truncation is needed + let full_width: Pixels = line.chars().map(|c| self.width_for_char(c)).sum(); + + if full_width <= truncate_width { + return (line, Cow::Borrowed(runs)); + } + + let prefix_width: Pixels = truncation_prefix + .chars() + .map(|c| self.width_for_char(c)) + .sum(); + + let available_width = truncate_width - prefix_width; + + if available_width <= px(0.) { + return ( + SharedString::from(truncation_prefix.to_string()), + Cow::Owned(vec![]), + ); + } + + // Work backwards from the end to find where to start the visible text + let char_indices: Vec<(usize, char)> = line.char_indices().collect(); + let mut width_from_end = px(0.); + let mut start_byte_index = line.len(); + + for (byte_index, c) in char_indices.iter().rev() { + let char_width = self.width_for_char(*c); + if width_from_end + char_width > available_width { + break; + } + width_from_end += char_width; + start_byte_index = *byte_index; + } + + if start_byte_index == 0 { + return (line, Cow::Borrowed(runs)); + } + + let result = SharedString::from(format!( + "{}{}", + truncation_prefix, + &line[start_byte_index..] + )); + let mut runs = runs.to_vec(); + update_runs_after_start_truncation(&result, truncation_prefix, start_byte_index, &mut runs); + + (result, Cow::Owned(runs)) + } + /// Any character in this list should be treated as a word character, /// meaning it can be part of a word that should not be wrapped. pub(crate) fn is_word_char(c: char) -> bool { @@ -258,6 +317,52 @@ fn update_runs_after_truncation(result: &str, ellipsis: &str, runs: &mut Vec, +) { + let prefix_len = prefix.len(); + + let mut bytes_to_skip = bytes_removed; + let mut first_relevant_run = 0; + + for (index, run) in runs.iter().enumerate() { + if bytes_to_skip >= run.len { + bytes_to_skip -= run.len; + first_relevant_run = index + 1; + } else { + break; + } + } + + if first_relevant_run > 0 { + runs.drain(0..first_relevant_run); + } + + if !runs.is_empty() && bytes_to_skip > 0 { + runs[0].len -= bytes_to_skip; + } + + if !runs.is_empty() { + runs[0].len += prefix_len; + } else { + runs.push(TextRun { + len: result.len(), + ..Default::default() + }); + } + + let total_run_len: usize = runs.iter().map(|r| r.len).sum(); + if total_run_len != result.len() && !runs.is_empty() { + let diff = result.len() as isize - total_run_len as isize; + if let Some(last) = runs.last_mut() { + last.len = (last.len as isize + diff) as usize; + } + } +} + /// A fragment of a line that can be wrapped. pub enum LineFragment<'a> { /// A text fragment consisting of characters.