diff --git a/crates/buffer_diff/src/buffer_diff.rs b/crates/buffer_diff/src/buffer_diff.rs index 9dfddf54f7..2a6c31744e 100644 --- a/crates/buffer_diff/src/buffer_diff.rs +++ b/crates/buffer_diff/src/buffer_diff.rs @@ -837,8 +837,8 @@ impl BufferDiff { language: Option>, language_registry: Option>, cx: &mut AsyncApp, - ) -> anyhow::Result>> { - let snapshot = if base_text_changed || language_changed { + ) -> anyhow::Result { + let inner = if base_text_changed || language_changed { cx.update(|cx| { Self::build( buffer.clone(), @@ -860,18 +860,45 @@ impl BufferDiff { })? .await }; - - this.update(cx, |this, _| this.set_state(snapshot, &buffer)) + Ok(BufferDiffSnapshot { + inner, + secondary_diff: None, + }) } - pub fn update_diff_from( + pub fn set_snapshot( &mut self, buffer: &text::BufferSnapshot, - other: &Entity, + new_snapshot: BufferDiffSnapshot, + language_changed: bool, + secondary_changed_range: Option>, cx: &mut Context, ) -> Option> { - let other = other.read(cx).inner.clone(); - self.set_state(other, buffer) + let changed_range = self.set_state(new_snapshot.inner, buffer); + if language_changed { + cx.emit(BufferDiffEvent::LanguageChanged); + } + + let changed_range = match (secondary_changed_range, changed_range) { + (None, None) => None, + (Some(unstaged_range), None) => self.range_to_hunk_range(unstaged_range, &buffer, cx), + (None, Some(uncommitted_range)) => Some(uncommitted_range), + (Some(unstaged_range), Some(uncommitted_range)) => { + let mut start = uncommitted_range.start; + let mut end = uncommitted_range.end; + if let Some(unstaged_range) = self.range_to_hunk_range(unstaged_range, &buffer, cx) + { + start = unstaged_range.start.min(&uncommitted_range.start, &buffer); + end = unstaged_range.end.max(&uncommitted_range.end, &buffer); + } + Some(start..end) + } + }; + + cx.emit(BufferDiffEvent::DiffChanged { + changed_range: changed_range.clone(), + }); + changed_range } fn set_state( diff --git a/crates/project/src/buffer_store.rs b/crates/project/src/buffer_store.rs index 0ee4f0c8ea..04de596eb6 100644 --- a/crates/project/src/buffer_store.rs +++ b/crates/project/src/buffer_store.rs @@ -6,7 +6,7 @@ use crate::{ }; use ::git::{parse_git_remote_url, BuildPermalinkParams, GitHostingProviderRegistry}; use anyhow::{anyhow, bail, Context as _, Result}; -use buffer_diff::{BufferDiff, BufferDiffEvent}; +use buffer_diff::BufferDiff; use client::Client; use collections::{hash_map, HashMap, HashSet}; use fs::Fs; @@ -217,39 +217,29 @@ impl BufferDiffState { _ => false, }; self.recalculate_diff_task = Some(cx.spawn(|this, mut cx| async move { - let mut unstaged_changed_range = None; + let mut new_unstaged_diff = None; if let Some(unstaged_diff) = &unstaged_diff { - unstaged_changed_range = BufferDiff::update_diff( - unstaged_diff.clone(), - buffer.clone(), - index, - index_changed, - language_changed, - language.clone(), - language_registry.clone(), - &mut cx, - ) - .await?; - - unstaged_diff.update(&mut cx, |_, cx| { - if language_changed { - cx.emit(BufferDiffEvent::LanguageChanged); - } - if let Some(changed_range) = unstaged_changed_range.clone() { - cx.emit(BufferDiffEvent::DiffChanged { - changed_range: Some(changed_range), - }) - } - })?; + new_unstaged_diff = Some( + BufferDiff::update_diff( + unstaged_diff.clone(), + buffer.clone(), + index, + index_changed, + language_changed, + language.clone(), + language_registry.clone(), + &mut cx, + ) + .await?, + ); } + let mut new_uncommitted_diff = None; if let Some(uncommitted_diff) = &uncommitted_diff { - let uncommitted_changed_range = - if let (Some(unstaged_diff), true) = (&unstaged_diff, index_matches_head) { - uncommitted_diff.update(&mut cx, |uncommitted_diff, cx| { - uncommitted_diff.update_diff_from(&buffer, unstaged_diff, cx) - })? - } else { + new_uncommitted_diff = if index_matches_head { + new_unstaged_diff.clone() + } else { + Some( BufferDiff::update_diff( uncommitted_diff.clone(), buffer.clone(), @@ -260,32 +250,32 @@ impl BufferDiffState { language_registry.clone(), &mut cx, ) - .await? - }; + .await?, + ) + } + } + let unstaged_changed_range = if let Some((unstaged_diff, new_unstaged_diff)) = + unstaged_diff.as_ref().zip(new_unstaged_diff.clone()) + { + unstaged_diff.update(&mut cx, |diff, cx| { + diff.set_snapshot(&buffer, new_unstaged_diff, language_changed, None, cx) + })? + } else { + None + }; + + if let Some((uncommitted_diff, new_uncommitted_diff)) = + uncommitted_diff.as_ref().zip(new_uncommitted_diff.clone()) + { uncommitted_diff.update(&mut cx, |uncommitted_diff, cx| { - if language_changed { - cx.emit(BufferDiffEvent::LanguageChanged); - } - let changed_range = match (unstaged_changed_range, uncommitted_changed_range) { - (None, None) => None, - (Some(unstaged_range), None) => { - uncommitted_diff.range_to_hunk_range(unstaged_range, &buffer, cx) - } - (None, Some(uncommitted_range)) => Some(uncommitted_range), - (Some(unstaged_range), Some(uncommitted_range)) => { - let mut start = uncommitted_range.start; - let mut end = uncommitted_range.end; - if let Some(unstaged_range) = - uncommitted_diff.range_to_hunk_range(unstaged_range, &buffer, cx) - { - start = unstaged_range.start.min(&uncommitted_range.start, &buffer); - end = unstaged_range.end.max(&uncommitted_range.end, &buffer); - } - Some(start..end) - } - }; - cx.emit(BufferDiffEvent::DiffChanged { changed_range }); + uncommitted_diff.set_snapshot( + &buffer, + new_uncommitted_diff, + language_changed, + unstaged_changed_range, + cx, + ); })?; } @@ -813,8 +803,7 @@ impl LocalBufferStore { let Some(buffer) = buffer.upgrade() else { continue; }; - let buffer = buffer.read(cx); - let Some(file) = File::from_dyn(buffer.file()) else { + let Some(file) = File::from_dyn(buffer.read(cx).file()) else { continue; }; if file.worktree != worktree_handle { @@ -825,7 +814,6 @@ impl LocalBufferStore { .iter() .any(|(work_dir, _)| file.path.starts_with(work_dir)) { - let snapshot = buffer.text_snapshot(); let has_unstaged_diff = diff_state .unstaged_diff .as_ref() @@ -835,7 +823,7 @@ impl LocalBufferStore { .as_ref() .is_some_and(|set| set.is_upgradable()); diff_state_updates.push(( - snapshot.clone(), + buffer, file.path.clone(), has_unstaged_diff.then(|| diff_state.index_text.clone()), has_uncommitted_diff.then(|| diff_state.head_text.clone()), @@ -854,36 +842,33 @@ impl LocalBufferStore { .background_spawn(async move { diff_state_updates .into_iter() - .filter_map( - |(buffer_snapshot, path, current_index_text, current_head_text)| { - let local_repo = snapshot.local_repo_for_path(&path)?; - let relative_path = local_repo.relativize(&path).ok()?; - let index_text = if current_index_text.is_some() { - local_repo.repo().load_index_text(&relative_path) - } else { - None - }; - let head_text = if current_head_text.is_some() { - local_repo.repo().load_committed_text(&relative_path) - } else { - None - }; + .filter_map(|(buffer, path, current_index_text, current_head_text)| { + let local_repo = snapshot.local_repo_for_path(&path)?; + let relative_path = local_repo.relativize(&path).ok()?; + let index_text = if current_index_text.is_some() { + local_repo.repo().load_index_text(&relative_path) + } else { + None + }; + let head_text = if current_head_text.is_some() { + local_repo.repo().load_committed_text(&relative_path) + } else { + None + }; - // Avoid triggering a diff update if the base text has not changed. - if let Some((current_index, current_head)) = - current_index_text.as_ref().zip(current_head_text.as_ref()) + // Avoid triggering a diff update if the base text has not changed. + if let Some((current_index, current_head)) = + current_index_text.as_ref().zip(current_head_text.as_ref()) + { + if current_index.as_deref() == index_text.as_ref() + && current_head.as_deref() == head_text.as_ref() { - if current_index.as_deref() == index_text.as_ref() - && current_head.as_deref() == head_text.as_ref() - { - return None; - } + return None; } + } - let diff_bases_change = match ( - current_index_text.is_some(), - current_head_text.is_some(), - ) { + let diff_bases_change = + match (current_index_text.is_some(), current_head_text.is_some()) { (true, true) => Some(if index_text == head_text { DiffBasesChange::SetBoth(head_text) } else { @@ -896,17 +881,17 @@ impl LocalBufferStore { (false, true) => Some(DiffBasesChange::SetHead(head_text)), (false, false) => None, }; - Some((buffer_snapshot, diff_bases_change)) - }, - ) + + Some((buffer, diff_bases_change)) + }) .collect::>() }) .await; this.update(&mut cx, |this, cx| { - for (buffer_snapshot, diff_bases_change) in diff_bases_changes_by_buffer { + for (buffer, diff_bases_change) in diff_bases_changes_by_buffer { let Some(OpenBuffer::Complete { diff_state, .. }) = - this.opened_buffers.get_mut(&buffer_snapshot.remote_id()) + this.opened_buffers.get_mut(&buffer.read(cx).remote_id()) else { continue; }; @@ -917,8 +902,9 @@ impl LocalBufferStore { diff_state.update(cx, |diff_state, cx| { use proto::update_diff_bases::Mode; + let buffer = buffer.read(cx); if let Some((client, project_id)) = this.downstream_client.as_ref() { - let buffer_id = buffer_snapshot.remote_id().to_proto(); + let buffer_id = buffer.remote_id().to_proto(); let (staged_text, committed_text, mode) = match diff_bases_change .clone() { @@ -942,8 +928,11 @@ impl LocalBufferStore { client.send(message).log_err(); } - let _ = - diff_state.diff_bases_changed(buffer_snapshot, diff_bases_change, cx); + let _ = diff_state.diff_bases_changed( + buffer.text_snapshot(), + diff_bases_change, + cx, + ); }); } })