Fix missing hunks in project diff after revert (#25906)
Release Notes: - N/A
This commit is contained in:
parent
b774a4b8d1
commit
9e2b7bc5dc
5 changed files with 158 additions and 11 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -5420,8 +5420,10 @@ dependencies = [
|
|||
"buffer_diff",
|
||||
"collections",
|
||||
"component",
|
||||
"ctor",
|
||||
"db",
|
||||
"editor",
|
||||
"env_logger 0.11.6",
|
||||
"feature_flags",
|
||||
"futures 0.3.31",
|
||||
"fuzzy",
|
||||
|
|
|
@ -57,6 +57,8 @@ zed_actions.workspace = true
|
|||
windows.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
ctor.workspace = true
|
||||
env_logger.workspace = true
|
||||
editor = { workspace = true, features = ["test-support"] }
|
||||
gpui = { workspace = true, features = ["test-support"] }
|
||||
project = { workspace = true, features = ["test-support"] }
|
||||
|
|
|
@ -400,6 +400,7 @@ impl ProjectDiff {
|
|||
self.editor.update(cx, |editor, cx| {
|
||||
if was_empty {
|
||||
editor.change_selections(None, window, cx, |selections| {
|
||||
// TODO select the very beginning (possibly inside a deletion)
|
||||
selections.select_ranges([0..0])
|
||||
});
|
||||
}
|
||||
|
@ -980,6 +981,11 @@ mod tests {
|
|||
|
||||
use super::*;
|
||||
|
||||
#[ctor::ctor]
|
||||
fn init_logger() {
|
||||
env_logger::init();
|
||||
}
|
||||
|
||||
fn init_test(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| {
|
||||
let store = SettingsStore::test(cx);
|
||||
|
@ -1152,4 +1158,107 @@ mod tests {
|
|||
.unindent(),
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_hunks_after_restore_then_modify(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
path!("/project"),
|
||||
json!({
|
||||
".git": {},
|
||||
"foo": "modified\n",
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
let project = Project::test(fs.clone(), [path!("/project").as_ref()], cx).await;
|
||||
let (workspace, cx) =
|
||||
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
|
||||
let buffer = project
|
||||
.update(cx, |project, cx| {
|
||||
project.open_local_buffer(path!("/project/foo"), cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
let buffer_editor = cx.new_window_entity(|window, cx| {
|
||||
Editor::for_buffer(buffer, Some(project.clone()), window, cx)
|
||||
});
|
||||
let diff = cx.new_window_entity(|window, cx| {
|
||||
ProjectDiff::new(project.clone(), workspace, window, cx)
|
||||
});
|
||||
cx.run_until_parked();
|
||||
|
||||
fs.set_head_for_repo(
|
||||
path!("/project/.git").as_ref(),
|
||||
&[("foo".into(), "original\n".into())],
|
||||
);
|
||||
fs.with_git_state(path!("/project/.git").as_ref(), true, |state| {
|
||||
state.statuses = HashMap::from_iter([(
|
||||
"foo".into(),
|
||||
TrackedStatus {
|
||||
index_status: StatusCode::Unmodified,
|
||||
worktree_status: StatusCode::Modified,
|
||||
}
|
||||
.into(),
|
||||
)]);
|
||||
});
|
||||
cx.run_until_parked();
|
||||
|
||||
let diff_editor = diff.update(cx, |diff, _| diff.editor.clone());
|
||||
|
||||
assert_state_with_diff(
|
||||
&diff_editor,
|
||||
cx,
|
||||
&"
|
||||
- original
|
||||
+ ˇmodified
|
||||
"
|
||||
.unindent(),
|
||||
);
|
||||
|
||||
let prev_buffer_hunks =
|
||||
cx.update_window_entity(&buffer_editor, |buffer_editor, window, cx| {
|
||||
let snapshot = buffer_editor.snapshot(window, cx);
|
||||
let snapshot = &snapshot.buffer_snapshot;
|
||||
let prev_buffer_hunks = buffer_editor
|
||||
.diff_hunks_in_ranges(&[editor::Anchor::min()..editor::Anchor::max()], snapshot)
|
||||
.collect::<Vec<_>>();
|
||||
buffer_editor.git_restore(&Default::default(), window, cx);
|
||||
prev_buffer_hunks
|
||||
});
|
||||
assert_eq!(prev_buffer_hunks.len(), 1);
|
||||
cx.run_until_parked();
|
||||
|
||||
let new_buffer_hunks =
|
||||
cx.update_window_entity(&buffer_editor, |buffer_editor, window, cx| {
|
||||
let snapshot = buffer_editor.snapshot(window, cx);
|
||||
let snapshot = &snapshot.buffer_snapshot;
|
||||
let new_buffer_hunks = buffer_editor
|
||||
.diff_hunks_in_ranges(&[editor::Anchor::min()..editor::Anchor::max()], snapshot)
|
||||
.collect::<Vec<_>>();
|
||||
buffer_editor.git_restore(&Default::default(), window, cx);
|
||||
new_buffer_hunks
|
||||
});
|
||||
assert_eq!(new_buffer_hunks.as_slice(), &[]);
|
||||
|
||||
cx.update_window_entity(&buffer_editor, |buffer_editor, window, cx| {
|
||||
buffer_editor.set_text("different\n", window, cx);
|
||||
buffer_editor.save(false, project.clone(), window, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
cx.run_until_parked();
|
||||
|
||||
assert_state_with_diff(
|
||||
&diff_editor,
|
||||
cx,
|
||||
&"
|
||||
- original
|
||||
+ ˇdifferent
|
||||
"
|
||||
.unindent(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2062,7 +2062,12 @@ impl MultiBuffer {
|
|||
if let Some(buffer_state) = buffers.get_mut(&excerpt.buffer_id) {
|
||||
buffer_state.excerpts.retain(|l| l != &excerpt.locator);
|
||||
if buffer_state.excerpts.is_empty() {
|
||||
log::debug!(
|
||||
"removing buffer and diff for buffer {}",
|
||||
excerpt.buffer_id
|
||||
);
|
||||
buffers.remove(&excerpt.buffer_id);
|
||||
self.diffs.remove(&excerpt.buffer_id);
|
||||
}
|
||||
}
|
||||
cursor.next(&());
|
||||
|
@ -2716,6 +2721,11 @@ impl MultiBuffer {
|
|||
snapshot.has_deleted_file = has_deleted_file;
|
||||
snapshot.has_conflict = has_conflict;
|
||||
|
||||
snapshot.diffs.retain(|_, _| false);
|
||||
for (id, diff) in self.diffs.iter() {
|
||||
snapshot.diffs.insert(*id, diff.diff.read(cx).snapshot(cx));
|
||||
}
|
||||
|
||||
excerpts_to_edit.sort_unstable_by_key(|(locator, _, _)| *locator);
|
||||
|
||||
let mut edits = Vec::new();
|
||||
|
@ -3476,7 +3486,10 @@ impl MultiBufferSnapshot {
|
|||
) -> impl Iterator<Item = MultiBufferDiffHunk> + '_ {
|
||||
let query_range = range.start.to_point(self)..range.end.to_point(self);
|
||||
self.lift_buffer_metadata(query_range.clone(), move |buffer, buffer_range| {
|
||||
let diff = self.diffs.get(&buffer.remote_id())?;
|
||||
let Some(diff) = self.diffs.get(&buffer.remote_id()) else {
|
||||
log::debug!("no diff found for {:?}", buffer.remote_id());
|
||||
return None;
|
||||
};
|
||||
let buffer_start = buffer.anchor_before(buffer_range.start);
|
||||
let buffer_end = buffer.anchor_after(buffer_range.end);
|
||||
Some(
|
||||
|
|
|
@ -2160,6 +2160,7 @@ impl ReferenceMultibuffer {
|
|||
.unwrap();
|
||||
let excerpt = self.excerpts.remove(ix);
|
||||
let buffer = excerpt.buffer.read(cx);
|
||||
let id = buffer.remote_id();
|
||||
log::info!(
|
||||
"Removing excerpt {}: {:?}",
|
||||
ix,
|
||||
|
@ -2167,6 +2168,13 @@ impl ReferenceMultibuffer {
|
|||
.text_for_range(excerpt.range.to_offset(buffer))
|
||||
.collect::<String>(),
|
||||
);
|
||||
if !self
|
||||
.excerpts
|
||||
.iter()
|
||||
.any(|excerpt| excerpt.buffer.read(cx).remote_id() == id)
|
||||
{
|
||||
self.diffs.remove(&id);
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_excerpt_after(
|
||||
|
@ -2415,6 +2423,7 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
|
|||
.unwrap_or(10);
|
||||
|
||||
let mut buffers: Vec<Entity<Buffer>> = Vec::new();
|
||||
let mut base_texts: HashMap<BufferId, String> = HashMap::default();
|
||||
let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
|
||||
let mut reference = ReferenceMultibuffer::default();
|
||||
let mut anchors = Vec::new();
|
||||
|
@ -2522,9 +2531,10 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
|
|||
..snapshot.anchor_in_excerpt(excerpt.id, end).unwrap();
|
||||
|
||||
log::info!(
|
||||
"expanding diff hunks in range {:?} (excerpt id {:?}) index {excerpt_ix:?})",
|
||||
"expanding diff hunks in range {:?} (excerpt id {:?}, index {excerpt_ix:?}, buffer id {:?})",
|
||||
range.to_offset(&snapshot),
|
||||
excerpt.id
|
||||
excerpt.id,
|
||||
excerpt.buffer.read(cx).remote_id(),
|
||||
);
|
||||
reference.expand_diff_hunks(excerpt.id, start..end, cx);
|
||||
multibuffer.expand_diff_hunks(vec![range], cx);
|
||||
|
@ -2534,7 +2544,7 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
|
|||
multibuffer.update(cx, |multibuffer, cx| {
|
||||
for buffer in multibuffer.all_buffers() {
|
||||
let snapshot = buffer.read(cx).snapshot();
|
||||
let _ = multibuffer.diff_for(snapshot.remote_id()).unwrap().update(
|
||||
multibuffer.diff_for(snapshot.remote_id()).unwrap().update(
|
||||
cx,
|
||||
|diff, cx| {
|
||||
log::info!(
|
||||
|
@ -2551,17 +2561,16 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
|
|||
}
|
||||
_ => {
|
||||
let buffer_handle = if buffers.is_empty() || rng.gen_bool(0.4) {
|
||||
let base_text = util::RandomCharIter::new(&mut rng)
|
||||
let mut base_text = util::RandomCharIter::new(&mut rng)
|
||||
.take(256)
|
||||
.collect::<String>();
|
||||
|
||||
let buffer = cx.new(|cx| Buffer::local(base_text.clone(), cx));
|
||||
let diff = cx.new(|cx| BufferDiff::new_with_base_text(&base_text, &buffer, cx));
|
||||
|
||||
multibuffer.update(cx, |multibuffer, cx| {
|
||||
reference.add_diff(diff.clone(), cx);
|
||||
multibuffer.add_diff(diff, cx)
|
||||
});
|
||||
text::LineEnding::normalize(&mut base_text);
|
||||
base_texts.insert(
|
||||
buffer.read_with(cx, |buffer, _| buffer.remote_id()),
|
||||
base_text,
|
||||
);
|
||||
buffers.push(buffer);
|
||||
buffers.last().unwrap()
|
||||
} else {
|
||||
|
@ -2595,6 +2604,18 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
|
|||
(start_ix..end_ix, anchor_range)
|
||||
});
|
||||
|
||||
multibuffer.update(cx, |multibuffer, cx| {
|
||||
let id = buffer_handle.read(cx).remote_id();
|
||||
if multibuffer.diff_for(id).is_none() {
|
||||
let base_text = base_texts.get(&id).unwrap();
|
||||
let diff = cx.new(|cx| {
|
||||
BufferDiff::new_with_base_text(base_text, &buffer_handle, cx)
|
||||
});
|
||||
reference.add_diff(diff.clone(), cx);
|
||||
multibuffer.add_diff(diff, cx)
|
||||
}
|
||||
});
|
||||
|
||||
let excerpt_id = multibuffer.update(cx, |multibuffer, cx| {
|
||||
multibuffer
|
||||
.insert_excerpts_after(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue