editor: Improve TextWrapper performance. (#1292)

This commit is contained in:
Jason Lee 2025-09-25 20:02:17 +08:00 committed by GitHub
parent 9f1423324a
commit 6b7a481fc8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 18 additions and 70 deletions

View file

@ -2362,7 +2362,7 @@ impl EntityInputHandler for InputState {
diagnostics.reset(&self.text) diagnostics.reset(&self.text)
} }
self.text_wrapper self.text_wrapper
.update(&self.text, &range, &Rope::from(new_text), false, cx); .update(&self.text, &range, &Rope::from(new_text), cx);
self.mode self.mode
.update_highlighter(&range, &self.text, &new_text, true, cx); .update_highlighter(&range, &self.text, &new_text, true, cx);
self.selected_range = (new_offset..new_offset).into(); self.selected_range = (new_offset..new_offset).into();
@ -2415,7 +2415,7 @@ impl EntityInputHandler for InputState {
diagnostics.reset(&self.text) diagnostics.reset(&self.text)
} }
self.text_wrapper self.text_wrapper
.update(&self.text, &range, &Rope::from(new_text), false, cx); .update(&self.text, &range, &Rope::from(new_text), cx);
self.mode self.mode
.update_highlighter(&range, &self.text, &new_text, true, cx); .update_highlighter(&range, &self.text, &new_text, true, cx);
if new_text.is_empty() { if new_text.is_empty() {
@ -2521,7 +2521,6 @@ impl Focusable for InputState {
impl Render for InputState { impl Render for InputState {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
self.text_wrapper.update_all(&self.text, false, cx);
self.mode self.mode
.update_highlighter(&(0..0), &self.text, "", false, cx); .update_highlighter(&(0..0), &self.text, "", false, cx);

View file

@ -1,7 +1,7 @@
use std::ops::Range; use std::ops::Range;
use gpui::{App, Font, LineFragment, Pixels}; use gpui::{App, Font, LineFragment, Pixels};
use ropey::{LineType, Rope}; use ropey::Rope;
use crate::input::RopeExt; use crate::input::RopeExt;
@ -99,7 +99,7 @@ impl TextWrapper {
} }
self.wrap_width = wrap_width; self.wrap_width = wrap_width;
self.update_all(&self.text.clone(), true, cx); self.update_all(&self.text.clone(), cx);
} }
pub(super) fn set_font(&mut self, font: Font, font_size: Pixels, cx: &mut App) { pub(super) fn set_font(&mut self, font: Font, font_size: Pixels, cx: &mut App) {
@ -109,7 +109,7 @@ impl TextWrapper {
self.font = font; self.font = font;
self.font_size = font_size; self.font_size = font_size;
self.update_all(&self.text.clone(), true, cx); self.update_all(&self.text.clone(), cx);
} }
/// Update the text wrapper and recalculate the wrapped lines. /// Update the text wrapper and recalculate the wrapped lines.
@ -126,7 +126,6 @@ impl TextWrapper {
changed_text: &Rope, changed_text: &Rope,
range: &Range<usize>, range: &Range<usize>,
new_text: &Rope, new_text: &Rope,
force: bool,
cx: &mut App, cx: &mut App,
) { ) {
let mut line_wrapper = cx let mut line_wrapper = cx
@ -136,7 +135,6 @@ impl TextWrapper {
changed_text, changed_text,
range, range,
new_text, new_text,
force,
&mut |line_str, wrap_width| { &mut |line_str, wrap_width| {
line_wrapper line_wrapper
.wrap_line(&[LineFragment::text(line_str)], wrap_width) .wrap_line(&[LineFragment::text(line_str)], wrap_width)
@ -150,15 +148,10 @@ impl TextWrapper {
changed_text: &Rope, changed_text: &Rope,
range: &Range<usize>, range: &Range<usize>,
new_text: &Rope, new_text: &Rope,
force: bool,
wrap_line: &mut F, wrap_line: &mut F,
) where ) where
F: FnMut(&str, Pixels) -> Vec<gpui::Boundary>, F: FnMut(&str, Pixels) -> Vec<gpui::Boundary>,
{ {
if self.text.eq(changed_text) && !force {
return;
}
// Remove the old changed lines. // Remove the old changed lines.
let start_row = self.text.offset_to_point(range.start).row; let start_row = self.text.offset_to_point(range.start).row;
let start_row = start_row.min(self.lines.len().saturating_sub(1)); let start_row = start_row.min(self.lines.len().saturating_sub(1));
@ -186,18 +179,10 @@ impl TextWrapper {
let wrap_width = self.wrap_width; let wrap_width = self.wrap_width;
for (ix, line) in changed_text for (ix, line) in Rope::from(changed_text.slice(new_range))
.slice(new_range) .iter_lines()
.lines(LineType::LF_CR)
.enumerate() .enumerate()
{ {
// Remove the last `\n`
let line = if line.len() > 0 && line.chars().last() == Some('\n') {
line.slice(..line.len().saturating_sub(1))
} else {
line
};
let line_str = line.to_string(); let line_str = line.to_string();
let mut wrapped_lines = vec![]; let mut wrapped_lines = vec![];
let mut prev_boundary_ix = 0; let mut prev_boundary_ix = 0;
@ -247,8 +232,8 @@ impl TextWrapper {
/// Update the text wrapper and recalculate the wrapped lines. /// Update the text wrapper and recalculate the wrapped lines.
/// ///
/// If the `text` is the same as the current text, do nothing. /// If the `text` is the same as the current text, do nothing.
pub(crate) fn update_all(&mut self, text: &Rope, force: bool, cx: &mut App) { fn update_all(&mut self, text: &Rope, cx: &mut App) {
self.update(text, &(0..text.len()), &text, force, cx); self.update(text, &(0..text.len()), &text, cx);
} }
} }
@ -275,20 +260,14 @@ mod tests {
vec![] vec![]
} }
wrapper._update(&text, &(0..text.len()), &text, false, &mut fake_wrap_line); wrapper._update(&text, &(0..text.len()), &text, &mut fake_wrap_line);
assert_eq!(wrapper.lines.len(), 4); assert_eq!(wrapper.lines.len(), 4);
// Add a new text to end // Add a new text to end
let range = text.len()..text.len(); let range = text.len()..text.len();
let new_text = "New text"; let new_text = "New text";
text.replace(range.clone(), new_text); text.replace(range.clone(), new_text);
wrapper._update( wrapper._update(&text, &range, &Rope::from(new_text), &mut fake_wrap_line);
&text,
&range,
&Rope::from(new_text),
false,
&mut fake_wrap_line,
);
assert_eq!( assert_eq!(
text.to_string(), text.to_string(),
"Hello, 世界!\nThis is second line.\nThis is third line.\n这里是第 4 行。New text" "Hello, 世界!\nThis is second line.\nThis is third line.\n这里是第 4 行。New text"
@ -299,13 +278,7 @@ mod tests {
let range = 0..5; let range = 0..5;
let new_text = "AAA"; let new_text = "AAA";
text.replace(range.clone(), new_text); text.replace(range.clone(), new_text);
wrapper._update( wrapper._update(&text, &range, &Rope::from(new_text), &mut fake_wrap_line);
&text,
&range,
&Rope::from(new_text),
false,
&mut fake_wrap_line,
);
assert_eq!( assert_eq!(
text.to_string(), text.to_string(),
"AAA, 世界!\nThis is second line.\nThis is third line.\n这里是第 4 行。New text" "AAA, 世界!\nThis is second line.\nThis is third line.\n这里是第 4 行。New text"
@ -317,7 +290,7 @@ mod tests {
let end_offset = text.line_end_offset(1); let end_offset = text.line_end_offset(1);
let range = start_offset..end_offset + 1; let range = start_offset..end_offset + 1;
text.replace(range.clone(), ""); text.replace(range.clone(), "");
wrapper._update(&text, &range, &Rope::from(""), false, &mut fake_wrap_line); wrapper._update(&text, &range, &Rope::from(""), &mut fake_wrap_line);
assert_eq!( assert_eq!(
text.to_string(), text.to_string(),
"AAA, 世界!\nThis is third line.\n这里是第 4 行。New text" "AAA, 世界!\nThis is third line.\n这里是第 4 行。New text"
@ -328,13 +301,7 @@ mod tests {
let range = text.line_start_offset(0)..text.line_end_offset(1) + 1; let range = text.line_start_offset(0)..text.line_end_offset(1) + 1;
let new_text = "This is a new line.\nThis is new line 2.\n"; let new_text = "This is a new line.\nThis is new line 2.\n";
text.replace(range.clone(), new_text); text.replace(range.clone(), new_text);
wrapper._update( wrapper._update(&text, &range, &Rope::from(new_text), &mut fake_wrap_line);
&text,
&range,
&Rope::from(new_text),
false,
&mut fake_wrap_line,
);
assert_eq!( assert_eq!(
text.to_string(), text.to_string(),
"This is a new line.\nThis is new line 2.\n这里是第 4 行。New text" "This is a new line.\nThis is new line 2.\n这里是第 4 行。New text"
@ -345,13 +312,7 @@ mod tests {
let range = text.len()..text.len(); let range = text.len()..text.len();
let new_text = "\nThis is a new line at the end."; let new_text = "\nThis is a new line at the end.";
text.replace(range.clone(), new_text); text.replace(range.clone(), new_text);
wrapper._update( wrapper._update(&text, &range, &Rope::from(new_text), &mut fake_wrap_line);
&text,
&range,
&Rope::from(new_text),
false,
&mut fake_wrap_line,
);
assert_eq!( assert_eq!(
text.to_string(), text.to_string(),
"This is a new line.\nThis is new line 2.\n这里是第 4 行。New text\nThis is a new line at the end." "This is a new line.\nThis is new line 2.\n这里是第 4 行。New text\nThis is a new line at the end."
@ -362,13 +323,7 @@ mod tests {
let range = 0..0; let range = 0..0;
let new_text = "This is a new line at the beginning.\n"; let new_text = "This is a new line at the beginning.\n";
text.replace(range.clone(), new_text); text.replace(range.clone(), new_text);
wrapper._update( wrapper._update(&text, &range, &Rope::from(new_text), &mut fake_wrap_line);
&text,
&range,
&Rope::from(new_text),
false,
&mut fake_wrap_line,
);
assert_eq!( assert_eq!(
text.to_string(), text.to_string(),
"This is a new line at the beginning.\nThis is a new line.\nThis is new line 2.\n这里是第 4 行。New text\nThis is a new line at the end." "This is a new line at the beginning.\nThis is a new line.\nThis is new line 2.\n这里是第 4 行。New text\nThis is a new line at the end."
@ -379,13 +334,7 @@ mod tests {
let range = 0..text.len(); let range = 0..text.len();
let new_text = ""; let new_text = "";
text.replace(range.clone(), new_text); text.replace(range.clone(), new_text);
wrapper._update( wrapper._update(&text, &range, &Rope::from(new_text), &mut fake_wrap_line);
&text,
&range,
&Rope::from(new_text),
false,
&mut fake_wrap_line,
);
assert_eq!(text.to_string(), ""); assert_eq!(text.to_string(), "");
assert_eq!(wrapper.lines.len(), 1); assert_eq!(wrapper.lines.len(), 1);
@ -393,7 +342,7 @@ mod tests {
let range = 0..text.len(); let range = 0..text.len();
let new_text = "This is a full text.\nThis is a second line."; let new_text = "This is a full text.\nThis is a second line.";
text.replace(range.clone(), new_text); text.replace(range.clone(), new_text);
wrapper._update(&text, &range, &text, false, &mut fake_wrap_line); wrapper._update(&text, &range, &text, &mut fake_wrap_line);
assert_eq!( assert_eq!(
text.to_string(), text.to_string(),
"This is a full text.\nThis is a second line." "This is a full text.\nThis is a second line."