Use line-based and word-based diff when reloading and formatting buffers (#25129)
Closes https://github.com/zed-industries/zed/issues/10122 Closes https://github.com/zed-industries/zed/issues/25034 When formatting buffers or reloading them after they change on disk, we performed a diff between the buffer's current contents and the new content. We need this diff in order preserve the positions of cursors and other decorations when updating the buffer's text. In order to handle changes within lines, we would previously compute a *character-wise* diff. This was extremely expensive for large files. This PR gets rid of the character-wise diff, and instead performs a normal line-wise diff. Then, for certain replace hunks, we compute a secondary word-based diff. Also, I've switched to the [`imara-diff`](https://github.com/pascalkuthe/imara-diff) crate, instead of `similar`. Release Notes: - Fixed a hang that could occur when large files were changed on disk or formatted.
This commit is contained in:
parent
1087e05da4
commit
0fdad0c0d6
19 changed files with 429 additions and 343 deletions
|
@ -30,7 +30,7 @@ use gpui::{
|
|||
EventEmitter, FocusHandle, Focusable, FontWeight, Global, HighlightStyle, Subscription, Task,
|
||||
TextStyle, UpdateGlobal, WeakEntity, Window,
|
||||
};
|
||||
use language::{Buffer, IndentKind, Point, Selection, TransactionId};
|
||||
use language::{line_diff, Buffer, IndentKind, Point, Selection, TransactionId};
|
||||
use language_model::{
|
||||
LanguageModel, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage,
|
||||
LanguageModelTextStream, Role,
|
||||
|
@ -3350,52 +3350,29 @@ impl CodegenAlternative {
|
|||
)
|
||||
.collect::<String>();
|
||||
|
||||
let mut old_row = old_range.start.row;
|
||||
let mut new_row = new_range.start.row;
|
||||
let batch_diff =
|
||||
similar::TextDiff::from_lines(old_text.as_str(), new_text.as_str());
|
||||
|
||||
let old_start_row = old_range.start.row;
|
||||
let new_start_row = new_range.start.row;
|
||||
let mut deleted_row_ranges: Vec<(Anchor, RangeInclusive<u32>)> = Vec::new();
|
||||
let mut inserted_row_ranges = Vec::new();
|
||||
for change in batch_diff.iter_all_changes() {
|
||||
let line_count = change.value().lines().count() as u32;
|
||||
match change.tag() {
|
||||
similar::ChangeTag::Equal => {
|
||||
old_row += line_count;
|
||||
new_row += line_count;
|
||||
}
|
||||
similar::ChangeTag::Delete => {
|
||||
let old_end_row = old_row + line_count - 1;
|
||||
let new_row = new_snapshot.anchor_before(Point::new(new_row, 0));
|
||||
|
||||
if let Some((_, last_deleted_row_range)) =
|
||||
deleted_row_ranges.last_mut()
|
||||
{
|
||||
if *last_deleted_row_range.end() + 1 == old_row {
|
||||
*last_deleted_row_range =
|
||||
*last_deleted_row_range.start()..=old_end_row;
|
||||
} else {
|
||||
deleted_row_ranges.push((new_row, old_row..=old_end_row));
|
||||
}
|
||||
} else {
|
||||
deleted_row_ranges.push((new_row, old_row..=old_end_row));
|
||||
}
|
||||
|
||||
old_row += line_count;
|
||||
}
|
||||
similar::ChangeTag::Insert => {
|
||||
let new_end_row = new_row + line_count - 1;
|
||||
let start = new_snapshot.anchor_before(Point::new(new_row, 0));
|
||||
let end = new_snapshot.anchor_before(Point::new(
|
||||
new_end_row,
|
||||
new_snapshot.line_len(MultiBufferRow(new_end_row)),
|
||||
));
|
||||
inserted_row_ranges.push(start..end);
|
||||
new_row += line_count;
|
||||
}
|
||||
for (old_rows, new_rows) in line_diff(&old_text, &new_text) {
|
||||
let old_rows = old_start_row + old_rows.start..old_start_row + old_rows.end;
|
||||
let new_rows = new_start_row + new_rows.start..new_start_row + new_rows.end;
|
||||
if !old_rows.is_empty() {
|
||||
deleted_row_ranges.push((
|
||||
new_snapshot.anchor_before(Point::new(new_rows.start, 0)),
|
||||
old_rows.start..=old_rows.end - 1,
|
||||
));
|
||||
}
|
||||
if !new_rows.is_empty() {
|
||||
let start = new_snapshot.anchor_before(Point::new(new_rows.start, 0));
|
||||
let new_end_row = new_rows.end - 1;
|
||||
let end = new_snapshot.anchor_before(Point::new(
|
||||
new_end_row,
|
||||
new_snapshot.line_len(MultiBufferRow(new_end_row)),
|
||||
));
|
||||
inserted_row_ranges.push(start..end);
|
||||
}
|
||||
}
|
||||
|
||||
(deleted_row_ranges, inserted_row_ranges)
|
||||
})
|
||||
.await;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue