Detailed changes
@@ -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,
@@ -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<language::Buffer>,
- buffer: text::BufferSnapshot,
- cx: &mut Context<Self>,
- ) -> 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<Arc<String>>,
+ language: Option<Arc<Language>>,
language_registry: Option<Arc<LanguageRegistry>>,
buffer: text::BufferSnapshot,
cx: &mut Context<Self>,
) -> 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,
);
@@ -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<Self>,
- ) {
- let Some(workspace) = self.workspace() else {
- cx.propagate();
- return;
- };
-
- let selections = self.selections.all::<usize>(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::<Vec<_>>();
- 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,
@@ -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);
@@ -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<Editor>,
- multibuffer: Entity<MultiBuffer>,
- title: SharedString,
- buffer_entries: Vec<BufferEntry>,
- _recalculate_diffs_task: Task<Option<()>>,
- recalculate_diffs_tx: mpsc::UnboundedSender<RecalculateDiff>,
-}
-
-pub struct ProposedChangeLocation<T> {
- pub buffer: Entity<Buffer>,
- pub ranges: Vec<Range<T>>,
-}
-
-struct BufferEntry {
- base: Entity<Buffer>,
- branch: Entity<Buffer>,
- _subscription: Subscription,
-}
-
-pub struct ProposedChangesEditorToolbar {
- current_editor: Option<Entity<ProposedChangesEditor>>,
-}
-
-struct RecalculateDiff {
- buffer: Entity<Buffer>,
- 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<dyn SemanticsProvider>);
-
-impl ProposedChangesEditor {
- pub fn new<T: Clone + ToOffset>(
- title: impl Into<SharedString>,
- locations: Vec<ProposedChangeLocation<T>>,
- project: Option<Entity<Project>>,
- window: &mut Window,
- cx: &mut Context<Self>,
- ) -> 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::<Vec<_>>()
- })
- .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<Buffer>) -> Option<Entity<Buffer>> {
- 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>) {
- self.title = title;
- cx.notify();
- }
-
- pub fn reset_locations<T: Clone + ToOffset>(
- &mut self,
- locations: Vec<ProposedChangeLocation<T>>,
- window: &mut Window,
- cx: &mut Context<Self>,
- ) {
- // 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<Buffer>,
- event: &BufferEvent,
- _cx: &mut Context<Self>,
- ) {
- 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<Self>) -> 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<EditorEvent> for ProposedChangesEditor {}
-
-impl Item for ProposedChangesEditor {
- type Event = EditorEvent;
-
- fn tab_icon(&self, _window: &Window, _cx: &App) -> Option<Icon> {
- Some(Icon::new(IconName::Diff))
- }
-
- fn tab_content_text(&self, _detail: usize, _cx: &App) -> SharedString {
- self.title.clone()
- }
-
- fn as_searchable(&self, _: &Entity<Self>) -> Option<Box<dyn SearchableItemHandle>> {
- Some(Box::new(self.editor.clone()))
- }
-
- fn act_as_type<'a>(
- &'a self,
- type_id: TypeId,
- self_handle: &'a Entity<Self>,
- _: &'a App,
- ) -> Option<gpui::AnyView> {
- if type_id == TypeId::of::<Self>() {
- Some(self_handle.to_any())
- } else if type_id == TypeId::of::<Editor>() {
- Some(self.editor.to_any())
- } else {
- None
- }
- }
-
- fn added_to_workspace(
- &mut self,
- workspace: &mut Workspace,
- window: &mut Window,
- cx: &mut Context<Self>,
- ) {
- 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>) {
- self.editor
- .update(cx, |editor, cx| editor.deactivated(window, cx));
- }
-
- fn navigate(
- &mut self,
- data: Box<dyn std::any::Any>,
- window: &mut Window,
- cx: &mut Context<Self>,
- ) -> 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>,
- ) {
- 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<Project>,
- window: &mut Window,
- cx: &mut Context<Self>,
- ) -> Task<anyhow::Result<()>> {
- 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<Self>) -> 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<ToolbarItemEvent> 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<Self>,
- ) -> workspace::ToolbarItemLocation {
- self.current_editor =
- active_pane_item.and_then(|item| item.downcast::<ProposedChangesEditor>());
- self.get_toolbar_item_location()
- }
-}
-
-impl BranchBufferSemanticsProvider {
- fn to_base(
- &self,
- buffer: &Entity<Buffer>,
- positions: &[text::Anchor],
- cx: &App,
- ) -> Option<Entity<Buffer>> {
- 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<Buffer>,
- position: text::Anchor,
- cx: &mut App,
- ) -> Option<Task<Option<Vec<project::Hover>>>> {
- let buffer = self.to_base(buffer, &[position], cx)?;
- self.0.hover(&buffer, position, cx)
- }
-
- fn inlay_hints(
- &self,
- buffer: Entity<Buffer>,
- range: Range<text::Anchor>,
- cx: &mut App,
- ) -> Option<Task<anyhow::Result<Vec<project::InlayHint>>>> {
- let buffer = self.to_base(&buffer, &[range.start, range.end], cx)?;
- self.0.inlay_hints(buffer, range, cx)
- }
-
- fn inline_values(
- &self,
- _: Entity<Buffer>,
- _: Range<text::Anchor>,
- _: &mut App,
- ) -> Option<Task<anyhow::Result<Vec<project::InlayHint>>>> {
- None
- }
-
- fn resolve_inlay_hint(
- &self,
- hint: project::InlayHint,
- buffer: Entity<Buffer>,
- server_id: lsp::LanguageServerId,
- cx: &mut App,
- ) -> Option<Task<anyhow::Result<project::InlayHint>>> {
- let buffer = self.to_base(&buffer, &[], cx)?;
- self.0.resolve_inlay_hint(hint, buffer, server_id, cx)
- }
-
- fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, 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<Buffer>,
- position: text::Anchor,
- cx: &mut App,
- ) -> Option<Task<anyhow::Result<Vec<project::DocumentHighlight>>>> {
- let buffer = self.to_base(buffer, &[position], cx)?;
- self.0.document_highlights(&buffer, position, cx)
- }
-
- fn definitions(
- &self,
- buffer: &Entity<Buffer>,
- position: text::Anchor,
- kind: crate::GotoDefinitionKind,
- cx: &mut App,
- ) -> Option<Task<anyhow::Result<Option<Vec<project::LocationLink>>>>> {
- let buffer = self.to_base(buffer, &[position], cx)?;
- self.0.definitions(&buffer, position, kind, cx)
- }
-
- fn range_for_rename(
- &self,
- _: &Entity<Buffer>,
- _: text::Anchor,
- _: &mut App,
- ) -> Option<Task<anyhow::Result<Option<Range<text::Anchor>>>>> {
- None
- }
-
- fn perform_rename(
- &self,
- _: &Entity<Buffer>,
- _: text::Anchor,
- _: String,
- _: &mut App,
- ) -> Option<Task<anyhow::Result<project::ProjectTransaction>>> {
- None
- }
-}
@@ -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,
})
}
@@ -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<Self>,
- ) -> Task<()> {
+ ) -> Task<Result<()>> {
+ 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();
}
};
@@ -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,
@@ -91,6 +91,7 @@ struct SharedDiffs {
struct BufferGitState {
unstaged_diff: Option<WeakEntity<BufferDiff>>,
uncommitted_diff: Option<WeakEntity<BufferDiff>>,
+ branch_diff: Option<WeakEntity<BufferDiff>>,
conflict_set: Option<WeakEntity<ConflictSet>>,
recalculate_diff_task: Option<Task<Result<()>>>,
reparse_conflict_markers_task: Option<Task<Result<()>>>,
@@ -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<Self>,
+ ) -> Task<Result<Vec<(Entity<Buffer>, Entity<BufferDiff>)>>> {
+ }
+
pub fn open_uncommitted_diff(
&mut self,
buffer: Entity<Buffer>,
@@ -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<GitStore>) -> 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(),
@@ -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);