Fix unhighlighted deleted regions in diffs (#64202)

#63145 exposed a bug in the the `snapshot_with_edits` and `fast_forward`
methods that we use to evolve the base text buffer for diffs: we
preserved the value of the `tree_sitter_data` field from the old
snapshot. That causes any diff whose base text has changed to have stale
bracket ranges and now stale syntax highlighting as well, which can
manifest as unhighlighted text in the deleted portion of an expanded
diff hunk.

This PR fixes the issue by clearing the `tree_sitter_data` field in
`snapshot_with_edits`.

Release Notes:

- N/A (problem exists in nightly only)
This commit is contained in:
Cole Miller authored and GitHub committed 2026-09-14 20:29:36 +00:00
1 parent 411b31e9a5
commit d705c7f1ae
2 files changed
+46

No files matched your search

+1
View File
@@ -3551,6 +3551,7 @@ impl Buffer {
snapshot.text = new_text.snapshot.clone();
snapshot.syntax = syntax;
snapshot.tree_sitter_data = Arc::new(TreeSitterData::new(&snapshot.text));
EditedBufferSnapshot {
text: new_text,
+45
View File
@@ -5460,6 +5460,51 @@ fn test_chunk_highlights_follow_edits_and_theme_changes(cx: &mut TestAppContext)
);
}
#[gpui::test]
async fn test_snapshot_with_edits_refreshes_chunk_highlights(cx: &mut TestAppContext) {
cx.update(|cx| init_settings(cx, |_| {}));
let language = keyword_and_function_lang();
let theme = keyword_and_function_theme();
language.set_theme(&theme);
let keyword = theme_highlight_id(&theme, "keyword");
let function = theme_highlight_id(&theme, "function");
let row = "fn replacement() {}\n";
let text = row.repeat(MAX_ROWS_IN_A_CHUNK as usize + 1);
for original_text in ["", "fn original() {}"] {
let buffer =
cx.new(|cx| Buffer::local(original_text, cx).with_language(language.clone(), cx));
cx.run_until_parked();
let original_snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot());
let original_highlights =
merged_highlight_runs(&original_snapshot, 0..original_snapshot.len());
let edited = buffer
.update(cx, |buffer, cx| {
buffer.snapshot_with_edits([(0..buffer.len(), text.clone())], cx)
})
.await;
let snapshot = edited.snapshot();
let expected = vec![
("fn".to_string(), keyword),
("replacement".to_string(), function),
];
assert_eq!(merged_highlight_runs(snapshot, 0..row.len()), expected);
let last_row_start = row.len() * MAX_ROWS_IN_A_CHUNK as usize;
assert_eq!(
merged_highlight_runs(snapshot, last_row_start..snapshot.len()),
expected,
);
assert_eq!(
merged_highlight_runs(&original_snapshot, 0..original_snapshot.len()),
original_highlights,
);
}
}
#[gpui::test]
fn test_chunk_highlights_across_row_chunk_seeks(cx: &mut TestAppContext) {
cx.update(|cx| init_settings(cx, |_| {}));