diff --git a/crates/assistant_tools/src/edit_file_tool.rs b/crates/assistant_tools/src/edit_file_tool.rs index d13f9891c3af1933ee49428c223d3e6737871047..fc155ef5032a37ac983b757e34f33e802f75d2c4 100644 --- a/crates/assistant_tools/src/edit_file_tool.rs +++ b/crates/assistant_tools/src/edit_file_tool.rs @@ -636,8 +636,11 @@ impl EditFileToolCard { // Create a buffer diff with the current text as the base let buffer_diff = cx.new(|cx| { let mut diff = BufferDiff::new(&text_snapshot, cx); + let base_text = buffer_snapshot.text(); + let language = buffer_snapshot.language().cloned(); let _ = diff.set_base_text( - buffer_snapshot.clone(), + Some(Arc::new(base_text)), + language, language_registry, text_snapshot, cx, diff --git a/crates/buffer_diff/src/buffer_diff.rs b/crates/buffer_diff/src/buffer_diff.rs index b20dad4ebbcc5990bd0a6a165375ca62481e609f..c9eed79f5e49816ff30ed0a5480539631790f984 100644 --- a/crates/buffer_diff/src/buffer_diff.rs +++ b/crates/buffer_diff/src/buffer_diff.rs @@ -1158,34 +1158,22 @@ impl BufferDiff { self.hunks_intersecting_range(start..end, buffer, cx) } - pub fn set_base_text_buffer( - &mut self, - base_buffer: Entity, - buffer: text::BufferSnapshot, - cx: &mut Context, - ) -> oneshot::Receiver<()> { - let base_buffer = base_buffer.read(cx); - let language_registry = base_buffer.language_registry(); - let base_buffer = base_buffer.snapshot(); - self.set_base_text(base_buffer, language_registry, buffer, cx) - } - /// Used in cases where the change set isn't derived from git. pub fn set_base_text( &mut self, - base_buffer: language::BufferSnapshot, + base_text: Option>, + language: Option>, language_registry: Option>, buffer: text::BufferSnapshot, cx: &mut Context, ) -> oneshot::Receiver<()> { let (tx, rx) = oneshot::channel(); let this = cx.weak_entity(); - let base_text = Arc::new(base_buffer.text()); let snapshot = BufferDiffSnapshot::new_with_base_text( buffer.clone(), - Some(base_text), - base_buffer.language().cloned(), + base_text, + language, language_registry, cx, ); diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 5edc7f3c061efe05bd4112bcbf152695ab56c50d..3d350751f1aaae4cb7bbd4e2b278969af438863d 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -34,7 +34,6 @@ mod lsp_ext; mod mouse_context_menu; pub mod movement; mod persistence; -mod proposed_changes_editor; mod rust_analyzer_ext; pub mod scroll; mod selections_collection; @@ -70,9 +69,7 @@ pub use multi_buffer::{ Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, PathKey, RowInfo, ToOffset, ToPoint, }; -pub use proposed_changes_editor::{ - ProposedChangeLocation, ProposedChangesEditor, ProposedChangesEditorToolbar, -}; + pub use text::Bias; use ::git::{ @@ -20472,65 +20469,6 @@ impl Editor { self.searchable } - fn open_proposed_changes_editor( - &mut self, - _: &OpenProposedChangesEditor, - window: &mut Window, - cx: &mut Context, - ) { - let Some(workspace) = self.workspace() else { - cx.propagate(); - return; - }; - - let selections = self.selections.all::(cx); - let multi_buffer = self.buffer.read(cx); - let multi_buffer_snapshot = multi_buffer.snapshot(cx); - let mut new_selections_by_buffer = HashMap::default(); - for selection in selections { - for (buffer, range, _) in - multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end) - { - let mut range = range.to_point(buffer); - range.start.column = 0; - range.end.column = buffer.line_len(range.end.row); - new_selections_by_buffer - .entry(multi_buffer.buffer(buffer.remote_id()).unwrap()) - .or_insert(Vec::new()) - .push(range) - } - } - - let proposed_changes_buffers = new_selections_by_buffer - .into_iter() - .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges }) - .collect::>(); - let proposed_changes_editor = cx.new(|cx| { - ProposedChangesEditor::new( - "Proposed changes", - proposed_changes_buffers, - self.project.clone(), - window, - cx, - ) - }); - - window.defer(cx, move |window, cx| { - workspace.update(cx, |workspace, cx| { - workspace.active_pane().update(cx, |pane, cx| { - pane.add_item( - Box::new(proposed_changes_editor), - true, - true, - None, - window, - cx, - ); - }); - }); - }); - } - pub fn open_excerpts_in_split( &mut self, _: &OpenExcerptsSplit, diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 9822ec23d5af41ee6fbfdd7c471f6fcc9437c78b..bfed5dc1420205844291885cddf276ebf18b411f 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -440,7 +440,6 @@ impl EditorElement { register_action(editor, window, Editor::toggle_code_actions); register_action(editor, window, Editor::open_excerpts); register_action(editor, window, Editor::open_excerpts_in_split); - register_action(editor, window, Editor::open_proposed_changes_editor); register_action(editor, window, Editor::toggle_soft_wrap); register_action(editor, window, Editor::toggle_tab_bar); register_action(editor, window, Editor::toggle_line_numbers); diff --git a/crates/editor/src/proposed_changes_editor.rs b/crates/editor/src/proposed_changes_editor.rs deleted file mode 100644 index 2d4710a8d44a023f0c3206ad0c327a34c36fdac4..0000000000000000000000000000000000000000 --- a/crates/editor/src/proposed_changes_editor.rs +++ /dev/null @@ -1,516 +0,0 @@ -use crate::{ApplyAllDiffHunks, Editor, EditorEvent, SelectionEffects, SemanticsProvider}; -use buffer_diff::BufferDiff; -use collections::HashSet; -use futures::{channel::mpsc, future::join_all}; -use gpui::{App, Entity, EventEmitter, Focusable, Render, Subscription, Task}; -use language::{Buffer, BufferEvent, Capability}; -use multi_buffer::{ExcerptRange, MultiBuffer}; -use project::Project; -use smol::stream::StreamExt; -use std::{any::TypeId, ops::Range, rc::Rc, time::Duration}; -use text::ToOffset; -use ui::{ButtonLike, KeyBinding, prelude::*}; -use workspace::{ - Item, ItemHandle as _, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace, - item::SaveOptions, searchable::SearchableItemHandle, -}; - -pub struct ProposedChangesEditor { - editor: Entity, - multibuffer: Entity, - title: SharedString, - buffer_entries: Vec, - _recalculate_diffs_task: Task>, - recalculate_diffs_tx: mpsc::UnboundedSender, -} - -pub struct ProposedChangeLocation { - pub buffer: Entity, - pub ranges: Vec>, -} - -struct BufferEntry { - base: Entity, - branch: Entity, - _subscription: Subscription, -} - -pub struct ProposedChangesEditorToolbar { - current_editor: Option>, -} - -struct RecalculateDiff { - buffer: Entity, - debounce: bool, -} - -/// A provider of code semantics for branch buffers. -/// -/// Requests in edited regions will return nothing, but requests in unchanged -/// regions will be translated into the base buffer's coordinates. -struct BranchBufferSemanticsProvider(Rc); - -impl ProposedChangesEditor { - pub fn new( - title: impl Into, - locations: Vec>, - project: Option>, - window: &mut Window, - cx: &mut Context, - ) -> Self { - let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite)); - let (recalculate_diffs_tx, mut recalculate_diffs_rx) = mpsc::unbounded(); - let mut this = Self { - editor: cx.new(|cx| { - let mut editor = Editor::for_multibuffer(multibuffer.clone(), project, window, cx); - editor.set_expand_all_diff_hunks(cx); - editor.set_completion_provider(None); - editor.clear_code_action_providers(); - editor.set_semantics_provider( - editor - .semantics_provider() - .map(|provider| Rc::new(BranchBufferSemanticsProvider(provider)) as _), - ); - editor - }), - multibuffer, - title: title.into(), - buffer_entries: Vec::new(), - recalculate_diffs_tx, - _recalculate_diffs_task: cx.spawn_in(window, async move |this, cx| { - let mut buffers_to_diff = HashSet::default(); - while let Some(mut recalculate_diff) = recalculate_diffs_rx.next().await { - buffers_to_diff.insert(recalculate_diff.buffer); - - while recalculate_diff.debounce { - cx.background_executor() - .timer(Duration::from_millis(50)) - .await; - let mut had_further_changes = false; - while let Ok(next_recalculate_diff) = recalculate_diffs_rx.try_next() { - let next_recalculate_diff = next_recalculate_diff?; - recalculate_diff.debounce &= next_recalculate_diff.debounce; - buffers_to_diff.insert(next_recalculate_diff.buffer); - had_further_changes = true; - } - if !had_further_changes { - break; - } - } - - let recalculate_diff_futures = this - .update(cx, |this, cx| { - buffers_to_diff - .drain() - .filter_map(|buffer| { - let buffer = buffer.read(cx); - let base_buffer = buffer.base_buffer()?; - let buffer = buffer.text_snapshot(); - let diff = - this.multibuffer.read(cx).diff_for(buffer.remote_id())?; - Some(diff.update(cx, |diff, cx| { - diff.set_base_text_buffer(base_buffer.clone(), buffer, cx) - })) - }) - .collect::>() - }) - .ok()?; - - join_all(recalculate_diff_futures).await; - } - None - }), - }; - this.reset_locations(locations, window, cx); - this - } - - pub fn branch_buffer_for_base(&self, base_buffer: &Entity) -> Option> { - self.buffer_entries.iter().find_map(|entry| { - if &entry.base == base_buffer { - Some(entry.branch.clone()) - } else { - None - } - }) - } - - pub fn set_title(&mut self, title: SharedString, cx: &mut Context) { - self.title = title; - cx.notify(); - } - - pub fn reset_locations( - &mut self, - locations: Vec>, - window: &mut Window, - cx: &mut Context, - ) { - // Undo all branch changes - for entry in &self.buffer_entries { - let base_version = entry.base.read(cx).version(); - entry.branch.update(cx, |buffer, cx| { - let undo_counts = buffer - .operations() - .iter() - .filter_map(|(timestamp, _)| { - if !base_version.observed(*timestamp) { - Some((*timestamp, u32::MAX)) - } else { - None - } - }) - .collect(); - buffer.undo_operations(undo_counts, cx); - }); - } - - self.multibuffer.update(cx, |multibuffer, cx| { - multibuffer.clear(cx); - }); - - let mut buffer_entries = Vec::new(); - let mut new_diffs = Vec::new(); - for location in locations { - let branch_buffer; - if let Some(ix) = self - .buffer_entries - .iter() - .position(|entry| entry.base == location.buffer) - { - let entry = self.buffer_entries.remove(ix); - branch_buffer = entry.branch.clone(); - buffer_entries.push(entry); - } else { - branch_buffer = location.buffer.update(cx, |buffer, cx| buffer.branch(cx)); - new_diffs.push(cx.new(|cx| { - let mut diff = BufferDiff::new(&branch_buffer.read(cx).snapshot(), cx); - let _ = diff.set_base_text_buffer( - location.buffer.clone(), - branch_buffer.read(cx).text_snapshot(), - cx, - ); - diff - })); - buffer_entries.push(BufferEntry { - branch: branch_buffer.clone(), - base: location.buffer.clone(), - _subscription: cx.subscribe(&branch_buffer, Self::on_buffer_event), - }); - } - - self.multibuffer.update(cx, |multibuffer, cx| { - multibuffer.push_excerpts( - branch_buffer, - location - .ranges - .into_iter() - .map(|range| ExcerptRange::new(range)), - cx, - ); - }); - } - - self.buffer_entries = buffer_entries; - self.editor.update(cx, |editor, cx| { - editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| { - selections.refresh() - }); - editor.buffer.update(cx, |buffer, cx| { - for diff in new_diffs { - buffer.add_diff(diff, cx) - } - }) - }); - } - - pub fn recalculate_all_buffer_diffs(&self) { - for (ix, entry) in self.buffer_entries.iter().enumerate().rev() { - self.recalculate_diffs_tx - .unbounded_send(RecalculateDiff { - buffer: entry.branch.clone(), - debounce: ix > 0, - }) - .ok(); - } - } - - fn on_buffer_event( - &mut self, - buffer: Entity, - event: &BufferEvent, - _cx: &mut Context, - ) { - if let BufferEvent::Operation { .. } = event { - self.recalculate_diffs_tx - .unbounded_send(RecalculateDiff { - buffer, - debounce: true, - }) - .ok(); - } - } -} - -impl Render for ProposedChangesEditor { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { - div() - .size_full() - .key_context("ProposedChangesEditor") - .child(self.editor.clone()) - } -} - -impl Focusable for ProposedChangesEditor { - fn focus_handle(&self, cx: &App) -> gpui::FocusHandle { - self.editor.focus_handle(cx) - } -} - -impl EventEmitter for ProposedChangesEditor {} - -impl Item for ProposedChangesEditor { - type Event = EditorEvent; - - fn tab_icon(&self, _window: &Window, _cx: &App) -> Option { - Some(Icon::new(IconName::Diff)) - } - - fn tab_content_text(&self, _detail: usize, _cx: &App) -> SharedString { - self.title.clone() - } - - fn as_searchable(&self, _: &Entity) -> Option> { - Some(Box::new(self.editor.clone())) - } - - fn act_as_type<'a>( - &'a self, - type_id: TypeId, - self_handle: &'a Entity, - _: &'a App, - ) -> Option { - if type_id == TypeId::of::() { - Some(self_handle.to_any()) - } else if type_id == TypeId::of::() { - Some(self.editor.to_any()) - } else { - None - } - } - - fn added_to_workspace( - &mut self, - workspace: &mut Workspace, - window: &mut Window, - cx: &mut Context, - ) { - self.editor.update(cx, |editor, cx| { - Item::added_to_workspace(editor, workspace, window, cx) - }); - } - - fn deactivated(&mut self, window: &mut Window, cx: &mut Context) { - self.editor - .update(cx, |editor, cx| editor.deactivated(window, cx)); - } - - fn navigate( - &mut self, - data: Box, - window: &mut Window, - cx: &mut Context, - ) -> bool { - self.editor - .update(cx, |editor, cx| Item::navigate(editor, data, window, cx)) - } - - fn set_nav_history( - &mut self, - nav_history: workspace::ItemNavHistory, - window: &mut Window, - cx: &mut Context, - ) { - self.editor.update(cx, |editor, cx| { - Item::set_nav_history(editor, nav_history, window, cx) - }); - } - - fn can_save(&self, cx: &App) -> bool { - self.editor.read(cx).can_save(cx) - } - - fn save( - &mut self, - options: SaveOptions, - project: Entity, - window: &mut Window, - cx: &mut Context, - ) -> Task> { - self.editor.update(cx, |editor, cx| { - Item::save(editor, options, project, window, cx) - }) - } -} - -impl ProposedChangesEditorToolbar { - pub fn new() -> Self { - Self { - current_editor: None, - } - } - - fn get_toolbar_item_location(&self) -> ToolbarItemLocation { - if self.current_editor.is_some() { - ToolbarItemLocation::PrimaryRight - } else { - ToolbarItemLocation::Hidden - } - } -} - -impl Render for ProposedChangesEditorToolbar { - fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { - let button_like = ButtonLike::new("apply-changes").child(Label::new("Apply All")); - - match &self.current_editor { - Some(editor) => { - let focus_handle = editor.focus_handle(cx); - let keybinding = - KeyBinding::for_action_in(&ApplyAllDiffHunks, &focus_handle, window, cx) - .map(|binding| binding.into_any_element()); - - button_like.children(keybinding).on_click({ - move |_event, window, cx| { - focus_handle.dispatch_action(&ApplyAllDiffHunks, window, cx) - } - }) - } - None => button_like.disabled(true), - } - } -} - -impl EventEmitter for ProposedChangesEditorToolbar {} - -impl ToolbarItemView for ProposedChangesEditorToolbar { - fn set_active_pane_item( - &mut self, - active_pane_item: Option<&dyn workspace::ItemHandle>, - _window: &mut Window, - _cx: &mut Context, - ) -> workspace::ToolbarItemLocation { - self.current_editor = - active_pane_item.and_then(|item| item.downcast::()); - self.get_toolbar_item_location() - } -} - -impl BranchBufferSemanticsProvider { - fn to_base( - &self, - buffer: &Entity, - positions: &[text::Anchor], - cx: &App, - ) -> Option> { - let base_buffer = buffer.read(cx).base_buffer()?; - let version = base_buffer.read(cx).version(); - if positions - .iter() - .any(|position| !version.observed(position.timestamp)) - { - return None; - } - Some(base_buffer) - } -} - -impl SemanticsProvider for BranchBufferSemanticsProvider { - fn hover( - &self, - buffer: &Entity, - position: text::Anchor, - cx: &mut App, - ) -> Option>>> { - let buffer = self.to_base(buffer, &[position], cx)?; - self.0.hover(&buffer, position, cx) - } - - fn inlay_hints( - &self, - buffer: Entity, - range: Range, - cx: &mut App, - ) -> Option>>> { - let buffer = self.to_base(&buffer, &[range.start, range.end], cx)?; - self.0.inlay_hints(buffer, range, cx) - } - - fn inline_values( - &self, - _: Entity, - _: Range, - _: &mut App, - ) -> Option>>> { - None - } - - fn resolve_inlay_hint( - &self, - hint: project::InlayHint, - buffer: Entity, - server_id: lsp::LanguageServerId, - cx: &mut App, - ) -> Option>> { - let buffer = self.to_base(&buffer, &[], cx)?; - self.0.resolve_inlay_hint(hint, buffer, server_id, cx) - } - - fn supports_inlay_hints(&self, buffer: &Entity, cx: &mut App) -> bool { - if let Some(buffer) = self.to_base(buffer, &[], cx) { - self.0.supports_inlay_hints(&buffer, cx) - } else { - false - } - } - - fn document_highlights( - &self, - buffer: &Entity, - position: text::Anchor, - cx: &mut App, - ) -> Option>>> { - let buffer = self.to_base(buffer, &[position], cx)?; - self.0.document_highlights(&buffer, position, cx) - } - - fn definitions( - &self, - buffer: &Entity, - position: text::Anchor, - kind: crate::GotoDefinitionKind, - cx: &mut App, - ) -> Option>>>> { - let buffer = self.to_base(buffer, &[position], cx)?; - self.0.definitions(&buffer, position, kind, cx) - } - - fn range_for_rename( - &self, - _: &Entity, - _: text::Anchor, - _: &mut App, - ) -> Option>>>> { - None - } - - fn perform_rename( - &self, - _: &Entity, - _: text::Anchor, - _: String, - _: &mut App, - ) -> Option>> { - None - } -} diff --git a/crates/git/src/repository.rs b/crates/git/src/repository.rs index 6d7dafff32e0d692702eba0bc0725280242a1013..3e5768c8e4eb26455f0524ab658d073921ae35b6 100644 --- a/crates/git/src/repository.rs +++ b/crates/git/src/repository.rs @@ -661,6 +661,7 @@ impl GitRepository for RealGitRepository { .context("starting git show process")?; let diff_stdout = String::from_utf8_lossy(&diff_output.stdout); + dbg!(&diff_stdout); let changes = parse_git_diff_name_status(&diff_stdout); let mut cat_file_process = util::command::new_std_command("git") @@ -683,7 +684,14 @@ impl GitRepository for RealGitRepository { StatusCode::Modified => { writeln!(&mut stdin, "{commit}:{}", path.display())?; } - StatusCode::Added => {} + StatusCode::Added => { + files.push(CommitFile { + path: path.into(), + old_text: None, + new_text: None, + }); + continue; + } StatusCode::Deleted => { writeln!(&mut stdin, "{commit}:{}", path.display())?; } @@ -694,34 +702,21 @@ impl GitRepository for RealGitRepository { info_line.clear(); stdout.read_line(&mut info_line)?; + dbg!(&info_line); + let len = info_line.trim_end().parse().with_context(|| { format!("invalid object size output from cat-file {info_line}") })?; let mut text = vec![0; len]; stdout.read_exact(&mut text)?; stdout.read_exact(&mut newline)?; - let text = String::from_utf8_lossy(&text).to_string(); - let old_text = match status_code { - StatusCode::Modified => { - info_line.clear(); - stdout.read_line(&mut info_line)?; - let len = info_line.trim_end().parse().with_context(|| { - format!("invalid object size output from cat-file {}", info_line) - })?; - let mut parent_text = vec![0; len]; - stdout.read_exact(&mut parent_text)?; - stdout.read_exact(&mut newline)?; - Some(String::from_utf8_lossy(&parent_text).to_string()) - } - StatusCode::Added => None, - StatusCode::Deleted => Some(text), - _ => continue, - }; + let text = String::from_utf8_lossy(&text).to_string(); + dbg!(&text); files.push(CommitFile { path: path.into(), - old_text, + old_text: Some(text), new_text: None, }) } diff --git a/crates/git_ui/src/project_diff.rs b/crates/git_ui/src/project_diff.rs index ffd4af9eec7f0ffdfa2d88850053b8a81e735bc2..e0a11f66e4e8f4df015581c349c602e3da39ade1 100644 --- a/crates/git_ui/src/project_diff.rs +++ b/crates/git_ui/src/project_diff.rs @@ -5,7 +5,7 @@ use crate::{ remote_button::{render_publish_button, render_push_button}, }; use anyhow::Result; -use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus}; +use buffer_diff::{BufferDiff, BufferDiffSnapshot, DiffHunkSecondaryStatus}; use collections::HashSet; use editor::{ Editor, EditorEvent, SelectionEffects, @@ -17,7 +17,7 @@ use futures::StreamExt; use git::{ Commit, StageAll, StageAndNext, ToggleStaged, UnstageAll, UnstageAndNext, repository::{Branch, Upstream, UpstreamTracking, UpstreamTrackingStatus}, - status::FileStatus, + status::{FileStatus, TrackedStatus}, }; use gpui::{ Action, AnyElement, AnyView, App, AppContext as _, AsyncWindowContext, Entity, EventEmitter, @@ -210,6 +210,7 @@ impl ProjectDiff { }); } DiffBaseKind::MergeBaseOfDefaultBranch => { + diff_display_editor.start_temporary_diff_override(); diff_display_editor.set_render_diff_hunk_controls( Arc::new(|_, _, _, _, _, _, _, _| gpui::Empty.into_any_element()), cx, @@ -267,7 +268,7 @@ impl ProjectDiff { let (mut send, recv) = postage::watch::channel::<()>(); let worker = window.spawn(cx, { let this = cx.weak_entity(); - async |cx| Self::handle_status_updates(this, diff_base_kind, recv, cx).await + async move |cx| Self::handle_status_updates(this, diff_base_kind, recv, cx).await }); // Kick off a refresh immediately *send.borrow_mut() = (); @@ -576,30 +577,113 @@ impl ProjectDiff { &mut self, window: &mut Window, cx: &mut Context, - ) -> Task<()> { + ) -> Task> { + let project = self.project.clone(); + let language_registry = project.read(cx).languages().clone(); let Some(repo) = self.git_store.read(cx).active_repository() else { self.multibuffer.update(cx, |multibuffer, cx| { multibuffer.clear(cx); }); - return; + return Task::ready(Ok(())); }; - let default_branch = repo.read(cx).default_branch(); - let task = cx.spawn(async move |this, cx| { - let Some(default_branch) = default_branch.await else { - return; + let default_branch = repo.update(cx, |repo, _| repo.default_branch()); + cx.spawn_in(window, async move |this, cx| { + let Some(default_branch) = default_branch.await?? else { + return Ok(()); + }; + let Some(merge_base) = repo + .update(cx, |repo, _| { + repo.merge_base("HEAD".to_string(), default_branch.into()) + })? + .await? + else { + return Ok(()); }; - let merge_base = repo - .update(cx, |repo, cx| { - repo.merge_base("HEAD".to_string(), default_branch) - }) - .await?; let diff = repo - .update(cx, |repo, cx| repo.diff_to_commit(merge_base)) - .await?; - }); + .update(cx, |repo, _| repo.diff_to_commit(merge_base))? + .await??; + + for file in diff.files { + let Some(path) = repo.update(cx, |repo, cx| { + repo.repo_path_to_project_path(&file.path, cx) + })? + else { + continue; + }; + let open_buffer = project + .update(cx, |project, cx| project.open_buffer(path.clone(), cx))? + .await; + + let mut status = FileStatus::Tracked(TrackedStatus { + index_status: git::status::StatusCode::Unmodified, + worktree_status: git::status::StatusCode::Modified, + }); + let buffer = match open_buffer { + Ok(buffer) => buffer, + Err(err) => { + let exists = project.read_with(cx, |project, cx| { + project.entry_for_path(&path, cx).is_some() + })?; + if exists { + return Err(err); + } + status = FileStatus::Tracked(TrackedStatus { + index_status: git::status::StatusCode::Unmodified, + worktree_status: git::status::StatusCode::Deleted, + }); + cx.new(|cx| Buffer::local("", cx))? + } + }; + + let buffer_snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot())?; + let namespace = if file.old_text.is_none() { + NEW_NAMESPACE + } else { + TRACKED_NAMESPACE + }; + + let buffer_diff = cx.new(|cx| BufferDiff::new(&buffer_snapshot, cx))?; + buffer_diff + .update(cx, |buffer_diff, cx| { + buffer_diff.set_base_text( + file.old_text.map(Arc::new), + buffer_snapshot.language().cloned(), + Some(language_registry.clone()), + buffer_snapshot.text, + cx, + ) + })? + .await?; + + this.read_with(cx, |this, cx| { + BufferDiffSnapshot::new_with_base_buffer( + buffer.clone(), + base_text, + this.base_text().clone(), + cx, + ) + })? + .await; - cx.foreground_executor() - .spawn(async move |this, cx| task.await.log_err()) + this.update_in(cx, |this, window, cx| { + this.multibuffer.update(cx, |multibuffer, cx| { + multibuffer.add_diff(buffer_diff.clone(), cx); + }); + this.register_buffer( + DiffBuffer { + path_key: PathKey::namespaced(namespace, file.path.0), + buffer, + diff: buffer_diff, + file_status: status, + }, + window, + cx, + ); + })?; + } + + Ok(()) + }) } pub async fn handle_status_updates( @@ -627,7 +711,8 @@ impl ProjectDiff { this.update_in(cx, |this, window, cx| { this.refresh_merge_base_of_default_branch(window, cx) })? - .await; + .await + .log_err(); } }; diff --git a/crates/project/src/buffer_store.rs b/crates/project/src/buffer_store.rs index 89bd4b27c9c47470a781e0ff322f5ef4a29b4927..4e2131af680353db5656003a5674eaa5a2424a3a 100644 --- a/crates/project/src/buffer_store.rs +++ b/crates/project/src/buffer_store.rs @@ -1249,15 +1249,6 @@ impl BufferStore { .log_err(); } - // TODO(max): do something - // client - // .send(proto::UpdateStagedText { - // project_id, - // buffer_id: buffer_id.into(), - // diff_base: buffer.diff_base().map(ToString::to_string), - // }) - // .log_err(); - client .send(proto::BufferReloaded { project_id, diff --git a/crates/project/src/git_store.rs b/crates/project/src/git_store.rs index 28dbc63d9be48c8421d3e85aa497be57283bbd69..134b003f62b3100ddb231b478d931790b7dc65ae 100644 --- a/crates/project/src/git_store.rs +++ b/crates/project/src/git_store.rs @@ -91,6 +91,7 @@ struct SharedDiffs { struct BufferGitState { unstaged_diff: Option>, uncommitted_diff: Option>, + branch_diff: Option>, conflict_set: Option>, recalculate_diff_task: Option>>, reparse_conflict_markers_task: Option>>, @@ -592,6 +593,12 @@ impl GitStore { cx.background_spawn(async move { task.await.map_err(|e| anyhow!("{e}")) }) } + pub fn open_diff_from_default_branch( + &mut self, + cx: &mut Context, + ) -> Task, Entity)>>> { + } + pub fn open_uncommitted_diff( &mut self, buffer: Entity, @@ -670,11 +677,10 @@ impl GitStore { let text_snapshot = buffer.text_snapshot(); this.loading_diffs.remove(&(buffer_id, kind)); - let git_store = cx.weak_entity(); let diff_state = this .diffs .entry(buffer_id) - .or_insert_with(|| cx.new(|_| BufferGitState::new(git_store))); + .or_insert_with(|| cx.new(|_| BufferGitState::new())); let diff = cx.new(|cx| BufferDiff::new(&text_snapshot, cx)); @@ -755,11 +761,10 @@ impl GitStore { let is_unmerged = self .repository_and_path_for_buffer_id(buffer_id, cx) .is_some_and(|(repo, path)| repo.read(cx).snapshot.has_conflict(&path)); - let git_store = cx.weak_entity(); let buffer_git_state = self .diffs .entry(buffer_id) - .or_insert_with(|| cx.new(|_| BufferGitState::new(git_store))); + .or_insert_with(|| cx.new(|_| BufferGitState::new())); let conflict_set = cx.new(|cx| ConflictSet::new(buffer_id, is_unmerged, cx)); self._subscriptions @@ -2347,10 +2352,11 @@ impl GitStore { } impl BufferGitState { - fn new(_git_store: WeakEntity) -> Self { + fn new() -> Self { Self { unstaged_diff: Default::default(), uncommitted_diff: Default::default(), + branch_diff: Default::default(), recalculate_diff_task: Default::default(), language: Default::default(), language_registry: Default::default(), diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 96f0f261dcce9268976f92ec028f0581fb648913..dc4ea24bcd5908ad2e958bedd1a02802cc54afd8 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -17,7 +17,6 @@ use breadcrumbs::Breadcrumbs; use client::zed_urls; use collections::VecDeque; use debugger_ui::debugger_panel::DebugPanel; -use editor::ProposedChangesEditorToolbar; use editor::{Editor, MultiBuffer}; use feature_flags::{FeatureFlagAppExt, PanicFeatureFlag}; use futures::future::Either; @@ -980,8 +979,6 @@ fn initialize_pane( ) }); toolbar.add_item(buffer_search_bar.clone(), window, cx); - let proposed_change_bar = cx.new(|_| ProposedChangesEditorToolbar::new()); - toolbar.add_item(proposed_change_bar, window, cx); let quick_action_bar = cx.new(|cx| QuickActionBar::new(buffer_search_bar, workspace, cx)); toolbar.add_item(quick_action_bar, window, cx);