git: Move diff hunk operations onto buffer diffs (#63556)
# Objective Keep Git-specific diff-base knowledge in `GitStore`, where Git operations are defined. Related to FR-135. ## Solution Give each `BufferDiff` its supported hunk operations. Keep editor hooks limited to rendering custom controls. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - N/A
This commit is contained in:
1 parent
99f85efa71
commit
aecaacdb44
21 files changed
+501
-603
No files matched your search
@@ -89,13 +89,10 @@ impl Diff {
|
||||
let language = buffer.read(cx).language().cloned();
|
||||
let language_registry = buffer.read(cx).language_registry();
|
||||
let buffer_diff = cx.new(|cx| {
|
||||
BufferDiff::new_unchanged(
|
||||
&buffer_text_snapshot,
|
||||
language,
|
||||
language_registry,
|
||||
buffer_diff::DiffBaseKind::Custom,
|
||||
cx,
|
||||
)
|
||||
let mut diff =
|
||||
BufferDiff::new_unchanged(&buffer_text_snapshot, language, language_registry, cx);
|
||||
diff.set_operations(Arc::new(buffer_diff::RestoreDiffOperations));
|
||||
diff
|
||||
});
|
||||
|
||||
let multibuffer = cx.new(|cx| {
|
||||
@@ -396,13 +393,9 @@ async fn build_buffer_diff(
|
||||
let base_text = base_text_exists.then(|| old_text);
|
||||
|
||||
let diff = cx.new(|cx| {
|
||||
BufferDiff::new(
|
||||
&buffer,
|
||||
language,
|
||||
language_registry,
|
||||
buffer_diff::DiffBaseKind::Custom,
|
||||
cx,
|
||||
)
|
||||
let mut diff = BufferDiff::new(&buffer, language, language_registry, cx);
|
||||
diff.set_operations(Arc::new(buffer_diff::RestoreDiffOperations));
|
||||
diff
|
||||
});
|
||||
diff.update(cx, |diff, cx| {
|
||||
diff.set_base_text(base_text, buffer.text, cx)
|
||||
|
||||
@@ -160,13 +160,9 @@ impl ActionLog {
|
||||
let language = buffer.read(cx).language().cloned();
|
||||
let language_registry = buffer.read(cx).language_registry();
|
||||
let diff = cx.new(|cx| {
|
||||
BufferDiff::new(
|
||||
&text_snapshot,
|
||||
language,
|
||||
language_registry,
|
||||
buffer_diff::DiffBaseKind::Custom,
|
||||
cx,
|
||||
)
|
||||
let mut diff = BufferDiff::new(&text_snapshot, language, language_registry, cx);
|
||||
diff.set_operations(Arc::new(buffer_diff::RestoreDiffOperations));
|
||||
diff
|
||||
});
|
||||
let (diff_update_tx, diff_update_rx) = mpsc::unbounded();
|
||||
let diff_base;
|
||||
|
||||
@@ -6,8 +6,8 @@ use anyhow::Result;
|
||||
use buffer_diff::DiffHunkStatus;
|
||||
use collections::{HashMap, HashSet};
|
||||
use editor::{
|
||||
DiffHunkDelegate, Direction, Editor, EditorEvent, EditorSettings, MultiBuffer,
|
||||
MultiBufferSnapshot, ResolvedDiffHunks, SelectionEffects, SplittableEditor, ToPoint,
|
||||
DiffHunkRenderer, Direction, Editor, EditorEvent, EditorSettings, MultiBuffer,
|
||||
MultiBufferSnapshot, SelectionEffects, SplittableEditor, ToPoint,
|
||||
actions::{GoToHunk, GoToPreviousHunk},
|
||||
multibuffer_context_lines,
|
||||
scroll::Autoscroll,
|
||||
@@ -101,7 +101,7 @@ impl AgentDiffPane {
|
||||
cx,
|
||||
);
|
||||
diff_display_editor
|
||||
.set_diff_hunk_delegate(Some(agent_diff_delegate(&thread, workspace.clone())), cx);
|
||||
.set_diff_hunk_renderer(Some(agent_diff_renderer(&thread, workspace.clone())), cx);
|
||||
diff_display_editor.update_editors(cx, |editor, _cx| {
|
||||
editor.register_addon(AgentDiffAddon);
|
||||
});
|
||||
@@ -734,41 +734,22 @@ impl Render for AgentDiffPane {
|
||||
}
|
||||
}
|
||||
|
||||
struct AgentDiffDelegate {
|
||||
struct AgentDiffHunkRenderer {
|
||||
thread: Entity<AcpThread>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
}
|
||||
|
||||
fn agent_diff_delegate(
|
||||
fn agent_diff_renderer(
|
||||
thread: &Entity<AcpThread>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
) -> Arc<dyn DiffHunkDelegate> {
|
||||
Arc::new(AgentDiffDelegate {
|
||||
) -> Arc<dyn DiffHunkRenderer> {
|
||||
Arc::new(AgentDiffHunkRenderer {
|
||||
thread: thread.clone(),
|
||||
workspace,
|
||||
})
|
||||
}
|
||||
|
||||
impl DiffHunkDelegate for AgentDiffDelegate {
|
||||
fn toggle(
|
||||
&self,
|
||||
_hunks: Vec<ResolvedDiffHunks>,
|
||||
_editor: &mut Editor,
|
||||
_window: &mut Window,
|
||||
_cx: &mut Context<Editor>,
|
||||
) {
|
||||
}
|
||||
|
||||
fn stage_or_unstage(
|
||||
&self,
|
||||
_stage: bool,
|
||||
_hunks: Vec<ResolvedDiffHunks>,
|
||||
_editor: &mut Editor,
|
||||
_window: &mut Window,
|
||||
_cx: &mut Context<Editor>,
|
||||
) {
|
||||
}
|
||||
|
||||
impl DiffHunkRenderer for AgentDiffHunkRenderer {
|
||||
fn render_hunk_controls(
|
||||
&self,
|
||||
row: u32,
|
||||
@@ -1579,7 +1560,7 @@ impl AgentDiff {
|
||||
for (editor, _) in self.reviewing_editors.drain() {
|
||||
editor
|
||||
.update(cx, |editor, cx| {
|
||||
editor.set_diff_hunk_delegate(None, cx);
|
||||
editor.set_diff_hunk_renderer(None, cx);
|
||||
editor.unregister_addon::<EditorAgentDiffAddon>();
|
||||
})
|
||||
.ok();
|
||||
@@ -1628,8 +1609,8 @@ impl AgentDiff {
|
||||
|
||||
if previous_state.is_none() {
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.set_diff_hunk_delegate(
|
||||
Some(agent_diff_delegate(&thread, workspace.clone())),
|
||||
editor.set_diff_hunk_renderer(
|
||||
Some(agent_diff_renderer(&thread, workspace.clone())),
|
||||
cx,
|
||||
);
|
||||
editor.set_expand_all_diff_hunks(cx);
|
||||
@@ -1678,7 +1659,7 @@ impl AgentDiff {
|
||||
if in_workspace {
|
||||
editor
|
||||
.update(cx, |editor, cx| {
|
||||
editor.set_diff_hunk_delegate(None, cx);
|
||||
editor.set_diff_hunk_renderer(None, cx);
|
||||
editor.unregister_addon::<EditorAgentDiffAddon>();
|
||||
})
|
||||
.ok();
|
||||
|
||||
@@ -6,7 +6,7 @@ use agent_client_protocol::schema::v1 as acp;
|
||||
use agent_settings::AgentSettings;
|
||||
use collections::{HashMap, HashSet};
|
||||
use editor::{
|
||||
Editor, EditorEvent, EditorMode, MinimapVisibility, RestoreOnlyUnstagedDiffHunkDelegate,
|
||||
Editor, EditorEvent, EditorMode, HiddenUnstagedDiffHunkRenderer, MinimapVisibility,
|
||||
SizingBehavior,
|
||||
};
|
||||
use gpui::{
|
||||
@@ -685,7 +685,7 @@ fn create_editor_diff(
|
||||
editor.set_show_code_actions(false, cx);
|
||||
editor.set_show_git_diff_gutter(false, cx);
|
||||
editor.set_expand_all_diff_hunks(cx);
|
||||
editor.set_diff_hunk_delegate(Some(Arc::new(RestoreOnlyUnstagedDiffHunkDelegate)), cx);
|
||||
editor.set_diff_hunk_renderer(Some(Arc::new(HiddenUnstagedDiffHunkRenderer)), cx);
|
||||
editor.set_text_style_refinement(diff_editor_text_style_refinement(cx));
|
||||
editor
|
||||
})
|
||||
|
||||
@@ -25,23 +25,63 @@ pub struct BufferDiff {
|
||||
diff_snapshot: Option<BufferDiffSnapshot>,
|
||||
secondary_diff: Option<Entity<BufferDiff>>,
|
||||
buffer_snapshot: text::BufferSnapshot,
|
||||
base_kind: DiffBaseKind,
|
||||
operations: Option<Arc<dyn DiffOperations>>,
|
||||
}
|
||||
|
||||
/// Where this diff's base text came from. Only diffs whose base is HEAD
|
||||
/// support staging and restoring hunks; a diff against any other base (e.g.
|
||||
/// the merge base with another branch) would rewrite committed work.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum DiffBaseKind {
|
||||
/// The buffer's committed (HEAD) content.
|
||||
Head,
|
||||
/// The buffer's index (staged) content.
|
||||
Index,
|
||||
/// An arbitrary blob, such as the merge base with another branch.
|
||||
Oid,
|
||||
/// Arbitrary caller-provided text, such as an agent's original text,
|
||||
/// the clipboard, or another file.
|
||||
Custom,
|
||||
pub trait DiffOperations {
|
||||
fn supports_staging(&self) -> bool;
|
||||
fn supports_unstaging(&self) -> bool;
|
||||
fn supports_restore(&self) -> bool;
|
||||
fn stage(
|
||||
&self,
|
||||
_diff: Entity<BufferDiff>,
|
||||
_buffer: Option<Entity<language::Buffer>>,
|
||||
_buffer_ranges: Vec<Range<Anchor>>,
|
||||
_cx: &mut App,
|
||||
) {
|
||||
}
|
||||
fn unstage(
|
||||
&self,
|
||||
_diff: Entity<BufferDiff>,
|
||||
_buffer: Option<Entity<language::Buffer>>,
|
||||
_buffer_ranges: Vec<Range<Anchor>>,
|
||||
_cx: &mut App,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RestoreDiffOperations;
|
||||
|
||||
impl DiffOperations for RestoreDiffOperations {
|
||||
fn supports_staging(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn supports_unstaging(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn supports_restore(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
struct TestDiffOperations;
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
impl DiffOperations for TestDiffOperations {
|
||||
fn supports_staging(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn supports_unstaging(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn supports_restore(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -1570,7 +1610,6 @@ impl BufferDiff {
|
||||
buffer: &text::BufferSnapshot,
|
||||
language: Option<Arc<Language>>,
|
||||
language_registry: Option<Arc<LanguageRegistry>>,
|
||||
base_kind: DiffBaseKind,
|
||||
cx: &mut App,
|
||||
) -> Self {
|
||||
let base_text = cx.new(|cx| {
|
||||
@@ -1589,14 +1628,13 @@ impl BufferDiff {
|
||||
diff_snapshot: None,
|
||||
buffer_snapshot: buffer.clone(),
|
||||
secondary_diff: None,
|
||||
base_kind,
|
||||
operations: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_base_text_buffer(
|
||||
buffer: &text::BufferSnapshot,
|
||||
base_text_buffer: Entity<language::Buffer>,
|
||||
base_kind: DiffBaseKind,
|
||||
_cx: &mut App,
|
||||
) -> Self {
|
||||
BufferDiff {
|
||||
@@ -1605,7 +1643,7 @@ impl BufferDiff {
|
||||
diff_snapshot: None,
|
||||
buffer_snapshot: buffer.clone(),
|
||||
secondary_diff: None,
|
||||
base_kind,
|
||||
operations: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1613,7 +1651,6 @@ impl BufferDiff {
|
||||
buffer: &text::BufferSnapshot,
|
||||
language: Option<Arc<Language>>,
|
||||
language_registry: Option<Arc<LanguageRegistry>>,
|
||||
base_kind: DiffBaseKind,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let base_text = buffer.text();
|
||||
@@ -1644,7 +1681,7 @@ impl BufferDiff {
|
||||
diff_snapshot: Some(diff_snapshot),
|
||||
buffer_snapshot: buffer.clone(),
|
||||
secondary_diff: None,
|
||||
base_kind,
|
||||
operations: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1654,7 +1691,8 @@ impl BufferDiff {
|
||||
buffer: &text::BufferSnapshot,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let mut this = BufferDiff::new(buffer, None, None, DiffBaseKind::Head, cx);
|
||||
let mut this = BufferDiff::new(buffer, None, None, cx);
|
||||
this.set_operations(Arc::new(TestDiffOperations));
|
||||
let mut base_text = base_text.to_owned();
|
||||
text::LineEnding::normalize(&mut base_text);
|
||||
let base_text_buffer = cx.new(|cx| {
|
||||
@@ -1678,14 +1716,12 @@ impl BufferDiff {
|
||||
self.secondary_diff = Some(diff);
|
||||
}
|
||||
|
||||
pub fn base_kind(&self) -> DiffBaseKind {
|
||||
self.base_kind
|
||||
pub fn set_operations(&mut self, operations: Arc<dyn DiffOperations>) {
|
||||
self.operations = Some(operations);
|
||||
}
|
||||
|
||||
/// Whether hunks in this diff can be staged or restored: true only when
|
||||
/// the diff's base is HEAD.
|
||||
pub fn is_stageable(&self) -> bool {
|
||||
self.base_kind == DiffBaseKind::Head
|
||||
pub fn operations(&self) -> Option<Arc<dyn DiffOperations>> {
|
||||
self.operations.clone()
|
||||
}
|
||||
|
||||
pub fn secondary_diff(&self) -> Option<Entity<BufferDiff>> {
|
||||
@@ -2482,8 +2518,7 @@ mod tests {
|
||||
],
|
||||
);
|
||||
|
||||
diff = cx
|
||||
.update(|cx| BufferDiff::new(&buffer, None, None, DiffBaseKind::Head, cx).snapshot(cx));
|
||||
diff = cx.update(|cx| BufferDiff::new(&buffer, None, None, cx).snapshot(cx));
|
||||
assert_hunks::<&str, _>(
|
||||
diff.hunks_intersecting_range(
|
||||
Anchor::min_max_range_for_buffer(buffer.remote_id()),
|
||||
@@ -3188,8 +3223,7 @@ mod tests {
|
||||
|
||||
let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text_1);
|
||||
|
||||
let empty_diff = cx
|
||||
.update(|cx| BufferDiff::new(&buffer, None, None, DiffBaseKind::Head, cx).snapshot(cx));
|
||||
let empty_diff = cx.update(|cx| BufferDiff::new(&buffer, None, None, cx).snapshot(cx));
|
||||
let diff_1 = BufferDiffSnapshot::new_sync(&buffer, base_text.clone(), cx);
|
||||
let DiffChanged {
|
||||
changed_range,
|
||||
@@ -4347,8 +4381,7 @@ mod tests {
|
||||
);
|
||||
let buffer_snapshot = buffer.snapshot();
|
||||
|
||||
let diff =
|
||||
cx.new(|cx| BufferDiff::new(&buffer_snapshot, None, None, DiffBaseKind::Head, cx));
|
||||
let diff = cx.new(|cx| BufferDiff::new(&buffer_snapshot, None, None, cx));
|
||||
diff.update(cx, |diff, cx| {
|
||||
diff.set_base_text(Some(Arc::from(base_text_crlf)), buffer_snapshot.clone(), cx)
|
||||
})
|
||||
|
||||
@@ -605,7 +605,6 @@ impl RatePredictionsModal {
|
||||
&predicted_buffer_snapshot.text,
|
||||
predicted_buffer_snapshot.language().cloned(),
|
||||
predicted_buffer.read(cx).language_registry(),
|
||||
buffer_diff::DiffBaseKind::Custom,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
@@ -712,7 +711,6 @@ impl RatePredictionsModal {
|
||||
&expected_buffer_snapshot.text,
|
||||
expected_buffer_snapshot.language().cloned(),
|
||||
expected_buffer.read(cx).language_registry(),
|
||||
buffer_diff::DiffBaseKind::Custom,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
@@ -108,9 +108,8 @@ pub use element::{
|
||||
};
|
||||
pub use git::blame::{BlameRenderer, GitBlame};
|
||||
pub use git::{
|
||||
DiffHunkDelegate, ResolvedDiffHunk, ResolvedDiffHunks, RestoreOnlyDiffHunkDelegate,
|
||||
RestoreOnlyUnstagedDiffHunkDelegate, UncommittedDiffHunkDelegate, render_diff_hunk_controls,
|
||||
set_blame_renderer,
|
||||
DefaultDiffHunkRenderer, DiffHunkRenderer, HiddenDiffHunkRenderer,
|
||||
HiddenUnstagedDiffHunkRenderer, render_diff_hunk_controls, set_blame_renderer,
|
||||
};
|
||||
pub(crate) use git::{DiffHunkKey, StoredReviewComment};
|
||||
use git::{DiffReviewDragState, DiffReviewOverlay, InlineBlamePopover};
|
||||
@@ -1158,7 +1157,8 @@ pub struct Editor {
|
||||
registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
|
||||
language_detection_task: Task<()>,
|
||||
load_diff_task: Option<Shared<Task<()>>>,
|
||||
diff_hunk_delegate: Option<Arc<dyn DiffHunkDelegate>>,
|
||||
diff_hunk_renderer: Option<Arc<dyn DiffHunkRenderer>>,
|
||||
diff_hunk_action_target: Option<WeakEntity<Editor>>,
|
||||
selection_mark_mode: bool,
|
||||
toggle_fold_multiple_buffers: Task<()>,
|
||||
_scroll_cursor_center_top_bottom_task: Task<()>,
|
||||
@@ -2507,7 +2507,8 @@ impl Editor {
|
||||
serialize_folds: Task::ready(()),
|
||||
text_style_refinement: None,
|
||||
load_diff_task: None,
|
||||
diff_hunk_delegate: None,
|
||||
diff_hunk_renderer: None,
|
||||
diff_hunk_action_target: None,
|
||||
minimap: None,
|
||||
change_list: ChangeList::new(),
|
||||
mode,
|
||||
|
||||
@@ -28119,7 +28119,7 @@ async fn test_merge_base_diff_hunks_are_read_only(cx: &mut TestAppContext) {
|
||||
.read(cx)
|
||||
.diff_for(buffer_id)
|
||||
.expect("buffer should have a display diff");
|
||||
assert!(!diff.read(cx).is_stageable());
|
||||
assert!(diff.read(cx).operations().is_none());
|
||||
});
|
||||
editor.update_in(cx, |editor, window, cx| {
|
||||
editor.select_all(&SelectAll, window, cx);
|
||||
|
||||
@@ -4664,7 +4664,7 @@ impl EditorElement {
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> (Vec<AnyElement>, Vec<(DisplayRow, Bounds<Pixels>)>) {
|
||||
let diff_hunk_delegate = editor.read(cx).diff_hunk_delegate();
|
||||
let diff_hunk_renderer = editor.read(cx).diff_hunk_renderer();
|
||||
let hovered_diff_hunk_row = editor.read(cx).hovered_diff_hunk_row;
|
||||
let sticky_top = text_hitbox.bounds.top() + sticky_header_height;
|
||||
|
||||
@@ -4736,7 +4736,7 @@ impl EditorElement {
|
||||
sticky_top.min(max_y)
|
||||
};
|
||||
|
||||
let mut element = diff_hunk_delegate.render_hunk_controls(
|
||||
let mut element = diff_hunk_renderer.render_hunk_controls(
|
||||
display_row_range.start.0,
|
||||
status,
|
||||
multi_buffer_range.clone(),
|
||||
@@ -6609,7 +6609,7 @@ impl EditorElement {
|
||||
let unstaged = !self
|
||||
.editor
|
||||
.read(cx)
|
||||
.diff_hunk_delegate()
|
||||
.diff_hunk_renderer()
|
||||
.render_hunk_as_staged(&status, cx);
|
||||
let unstaged_hollow = matches!(
|
||||
ProjectSettings::get_global(cx).git.hunk_style,
|
||||
@@ -9398,7 +9398,7 @@ impl Element for EditorElement {
|
||||
};
|
||||
|
||||
let (diff_hunk_controls, diff_hunk_control_bounds) =
|
||||
if is_read_only && self.editor.read(cx).diff_hunk_delegate.is_none() {
|
||||
if is_read_only && self.editor.read(cx).diff_hunk_renderer.is_none() {
|
||||
(vec![], vec![])
|
||||
} else {
|
||||
self.layout_diff_hunk_controls(
|
||||
|
||||
+244
-256
@@ -9,65 +9,20 @@ use buffer_diff::{BufferDiff, DiffHunkStatus, DiffHunkStatusKind};
|
||||
use project::git_store::Repository;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ResolvedDiffHunk {
|
||||
pub buffer_range: Range<text::Anchor>,
|
||||
pub diff_base_byte_range: Range<usize>,
|
||||
pub status: DiffHunkStatus,
|
||||
struct ResolvedDiffHunk {
|
||||
buffer_range: Range<text::Anchor>,
|
||||
diff_base_byte_range: Range<usize>,
|
||||
status: DiffHunkStatus,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ResolvedDiffHunks {
|
||||
pub diff: Entity<BufferDiff>,
|
||||
pub buffer_id: BufferId,
|
||||
pub buffer: Option<Entity<Buffer>>,
|
||||
pub hunks: Vec<ResolvedDiffHunk>,
|
||||
struct ResolvedDiffHunks {
|
||||
diff: Entity<BufferDiff>,
|
||||
buffer: Option<Entity<Buffer>>,
|
||||
hunks: Vec<ResolvedDiffHunk>,
|
||||
}
|
||||
|
||||
pub trait DiffHunkDelegate {
|
||||
fn toggle(
|
||||
&self,
|
||||
hunks: Vec<ResolvedDiffHunks>,
|
||||
editor: &mut Editor,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Editor>,
|
||||
);
|
||||
|
||||
fn stage_or_unstage(
|
||||
&self,
|
||||
stage: bool,
|
||||
hunks: Vec<ResolvedDiffHunks>,
|
||||
editor: &mut Editor,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Editor>,
|
||||
);
|
||||
|
||||
fn restore(
|
||||
&self,
|
||||
hunks: Vec<ResolvedDiffHunks>,
|
||||
editor: &mut Editor,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Editor>,
|
||||
) {
|
||||
if hunks.is_empty() || editor.read_only(cx) {
|
||||
return;
|
||||
}
|
||||
self.stage_or_unstage(false, hunks.clone(), editor, window, cx);
|
||||
editor.transact(window, cx, |editor, window, cx| {
|
||||
editor.restore_diff_hunks(hunks, cx);
|
||||
let selections = editor
|
||||
.selections
|
||||
.all::<MultiBufferOffset>(&editor.display_snapshot(cx));
|
||||
editor.change_selections(
|
||||
SelectionEffects::no_scroll(),
|
||||
window,
|
||||
cx,
|
||||
|selections_state| {
|
||||
selections_state.select(selections);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
pub trait DiffHunkRenderer {
|
||||
fn render_hunk_controls(
|
||||
&self,
|
||||
row: u32,
|
||||
@@ -85,63 +40,9 @@ pub trait DiffHunkDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UncommittedDiffHunkDelegate;
|
||||
|
||||
impl DiffHunkDelegate for UncommittedDiffHunkDelegate {
|
||||
fn toggle(
|
||||
&self,
|
||||
hunks: Vec<ResolvedDiffHunks>,
|
||||
editor: &mut Editor,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Editor>,
|
||||
) {
|
||||
let stage = hunks
|
||||
.iter()
|
||||
.flat_map(|hunks| hunks.hunks.iter())
|
||||
.any(|hunk| hunk.status.has_secondary_hunk());
|
||||
self.stage_or_unstage(stage, hunks, editor, window, cx);
|
||||
}
|
||||
|
||||
fn stage_or_unstage(
|
||||
&self,
|
||||
stage: bool,
|
||||
hunks: Vec<ResolvedDiffHunks>,
|
||||
editor: &mut Editor,
|
||||
_window: &mut Window,
|
||||
cx: &mut Context<Editor>,
|
||||
) {
|
||||
let Some(project) = editor.project() else {
|
||||
return;
|
||||
};
|
||||
for hunks in hunks {
|
||||
let Some(buffer) = hunks.buffer else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let ranges = hunks
|
||||
.hunks
|
||||
.into_iter()
|
||||
.map(|hunk| hunk.buffer_range)
|
||||
.collect::<Vec<_>>();
|
||||
if ranges.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let secondary_diff = hunks.diff.read(cx).secondary_diff();
|
||||
project
|
||||
.update(cx, |project, cx| {
|
||||
if stage {
|
||||
let Some(secondary_diff) = secondary_diff else {
|
||||
return Err(anyhow::anyhow!("diff has no unstaged secondary"));
|
||||
};
|
||||
project.stage_hunks(buffer, secondary_diff, ranges, cx)
|
||||
} else {
|
||||
project.unstage_uncommitted_hunks(buffer, hunks.diff, ranges, cx)
|
||||
}
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
pub struct DefaultDiffHunkRenderer;
|
||||
|
||||
impl DiffHunkRenderer for DefaultDiffHunkRenderer {
|
||||
fn render_hunk_controls(
|
||||
&self,
|
||||
row: u32,
|
||||
@@ -166,37 +67,9 @@ impl DiffHunkDelegate for UncommittedDiffHunkDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RestoreOnlyDiffHunkDelegate;
|
||||
|
||||
impl DiffHunkDelegate for RestoreOnlyDiffHunkDelegate {
|
||||
fn toggle(
|
||||
&self,
|
||||
_hunks: Vec<ResolvedDiffHunks>,
|
||||
_editor: &mut Editor,
|
||||
_window: &mut Window,
|
||||
_cx: &mut Context<Editor>,
|
||||
) {
|
||||
}
|
||||
|
||||
fn stage_or_unstage(
|
||||
&self,
|
||||
_stage: bool,
|
||||
_hunks: Vec<ResolvedDiffHunks>,
|
||||
_editor: &mut Editor,
|
||||
_window: &mut Window,
|
||||
_cx: &mut Context<Editor>,
|
||||
) {
|
||||
}
|
||||
|
||||
fn restore(
|
||||
&self,
|
||||
_hunks: Vec<ResolvedDiffHunks>,
|
||||
_editor: &mut Editor,
|
||||
_window: &mut Window,
|
||||
_cx: &mut Context<Editor>,
|
||||
) {
|
||||
}
|
||||
pub struct HiddenDiffHunkRenderer;
|
||||
|
||||
impl DiffHunkRenderer for HiddenDiffHunkRenderer {
|
||||
fn render_hunk_controls(
|
||||
&self,
|
||||
_row: u32,
|
||||
@@ -212,28 +85,9 @@ impl DiffHunkDelegate for RestoreOnlyDiffHunkDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RestoreOnlyUnstagedDiffHunkDelegate;
|
||||
|
||||
impl DiffHunkDelegate for RestoreOnlyUnstagedDiffHunkDelegate {
|
||||
fn toggle(
|
||||
&self,
|
||||
_hunks: Vec<ResolvedDiffHunks>,
|
||||
_editor: &mut Editor,
|
||||
_window: &mut Window,
|
||||
_cx: &mut Context<Editor>,
|
||||
) {
|
||||
}
|
||||
|
||||
fn stage_or_unstage(
|
||||
&self,
|
||||
_stage: bool,
|
||||
_hunks: Vec<ResolvedDiffHunks>,
|
||||
_editor: &mut Editor,
|
||||
_window: &mut Window,
|
||||
_cx: &mut Context<Editor>,
|
||||
) {
|
||||
}
|
||||
pub struct HiddenUnstagedDiffHunkRenderer;
|
||||
|
||||
impl DiffHunkRenderer for HiddenUnstagedDiffHunkRenderer {
|
||||
fn render_hunk_controls(
|
||||
&self,
|
||||
_row: u32,
|
||||
@@ -465,7 +319,6 @@ impl Editor {
|
||||
if !resolved_hunks.is_empty() {
|
||||
resolved.push(ResolvedDiffHunks {
|
||||
diff,
|
||||
buffer_id: main_buffer_id,
|
||||
buffer,
|
||||
hunks: resolved_hunks,
|
||||
});
|
||||
@@ -475,24 +328,24 @@ impl Editor {
|
||||
resolved
|
||||
}
|
||||
|
||||
pub fn diff_hunk_delegate(&self) -> Arc<dyn DiffHunkDelegate> {
|
||||
self.diff_hunk_delegate
|
||||
pub fn diff_hunk_renderer(&self) -> Arc<dyn DiffHunkRenderer> {
|
||||
self.diff_hunk_renderer
|
||||
.clone()
|
||||
.unwrap_or_else(|| Arc::new(UncommittedDiffHunkDelegate))
|
||||
.unwrap_or_else(|| Arc::new(DefaultDiffHunkRenderer))
|
||||
}
|
||||
|
||||
pub fn set_diff_hunk_delegate(
|
||||
pub fn set_diff_hunk_renderer(
|
||||
&mut self,
|
||||
delegate: Option<Arc<dyn DiffHunkDelegate>>,
|
||||
renderer: Option<Arc<dyn DiffHunkRenderer>>,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let had_delegate = self.diff_hunk_delegate.is_some();
|
||||
let has_delegate = delegate.is_some();
|
||||
self.diff_hunk_delegate = delegate;
|
||||
let had_renderer = self.diff_hunk_renderer.is_some();
|
||||
let has_renderer = renderer.is_some();
|
||||
self.diff_hunk_renderer = renderer;
|
||||
|
||||
if !had_delegate && has_delegate {
|
||||
if !had_renderer && has_renderer {
|
||||
self.load_diff_task.take();
|
||||
} else if had_delegate && !has_delegate {
|
||||
} else if had_renderer && !has_renderer {
|
||||
self.buffer.update(cx, |buffer, cx| {
|
||||
buffer.set_all_diff_hunks_collapsed(cx);
|
||||
});
|
||||
@@ -512,6 +365,10 @@ impl Editor {
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub(crate) fn set_diff_hunk_action_target(&mut self, target: Option<WeakEntity<Editor>>) {
|
||||
self.diff_hunk_action_target = target;
|
||||
}
|
||||
|
||||
pub fn git_blame_inline_enabled(&self) -> bool {
|
||||
self.git_blame_inline_enabled
|
||||
}
|
||||
@@ -1062,7 +919,7 @@ impl Editor {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn restore_diff_hunks(&mut self, hunks: Vec<ResolvedDiffHunks>, cx: &mut Context<Self>) {
|
||||
fn restore_diff_hunks(&mut self, hunks: Vec<ResolvedDiffHunks>, cx: &mut Context<Self>) {
|
||||
let mut revert_changes = Vec::new();
|
||||
for hunks in hunks {
|
||||
let Some(buffer) = hunks.buffer else {
|
||||
@@ -1774,36 +1631,104 @@ impl Editor {
|
||||
pub fn apply_toggle(
|
||||
&mut self,
|
||||
hunks: Vec<MultiBufferDiffHunk>,
|
||||
window: &mut Window,
|
||||
_window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let mut hunks = self.resolve_diff_hunks(hunks, cx);
|
||||
if self.diff_hunk_delegate.is_none() {
|
||||
hunks.retain(|hunks| hunks.diff.read(cx).is_stageable());
|
||||
}
|
||||
let hunks = self.resolve_diff_hunks(hunks, cx);
|
||||
if hunks.is_empty() {
|
||||
return;
|
||||
}
|
||||
let delegate = self.diff_hunk_delegate();
|
||||
delegate.toggle(hunks, self, window, cx);
|
||||
if let Some(target) = self.diff_hunk_action_target.clone() {
|
||||
target
|
||||
.update(cx, |editor, cx| {
|
||||
editor.toggle_resolved_diff_hunks(hunks, cx);
|
||||
})
|
||||
.log_err();
|
||||
return;
|
||||
}
|
||||
self.toggle_resolved_diff_hunks(hunks, cx);
|
||||
}
|
||||
|
||||
fn toggle_resolved_diff_hunks(
|
||||
&mut self,
|
||||
hunks: Vec<ResolvedDiffHunks>,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
for hunks in hunks {
|
||||
let Some(operations) = hunks.diff.read(cx).operations() else {
|
||||
continue;
|
||||
};
|
||||
let stage = if !operations.supports_unstaging() {
|
||||
true
|
||||
} else if !operations.supports_staging() {
|
||||
false
|
||||
} else {
|
||||
hunks
|
||||
.hunks
|
||||
.iter()
|
||||
.any(|hunk| hunk.status.has_secondary_hunk())
|
||||
};
|
||||
let ranges = hunks
|
||||
.hunks
|
||||
.into_iter()
|
||||
.map(|hunk| hunk.buffer_range)
|
||||
.collect();
|
||||
if stage {
|
||||
operations.stage(hunks.diff, hunks.buffer, ranges, cx);
|
||||
} else {
|
||||
operations.unstage(hunks.diff, hunks.buffer, ranges, cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_stage_or_unstage(
|
||||
&mut self,
|
||||
stage: bool,
|
||||
hunks: Vec<MultiBufferDiffHunk>,
|
||||
window: &mut Window,
|
||||
_window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let mut hunks = self.resolve_diff_hunks(hunks, cx);
|
||||
if self.diff_hunk_delegate.is_none() {
|
||||
hunks.retain(|hunks| hunks.diff.read(cx).is_stageable());
|
||||
}
|
||||
let hunks = self.resolve_diff_hunks(hunks, cx);
|
||||
if hunks.is_empty() {
|
||||
return;
|
||||
}
|
||||
let delegate = self.diff_hunk_delegate();
|
||||
delegate.stage_or_unstage(stage, hunks, self, window, cx);
|
||||
if let Some(target) = self.diff_hunk_action_target.clone() {
|
||||
target
|
||||
.update(cx, |editor, cx| {
|
||||
editor.stage_or_unstage_resolved_diff_hunks(stage, hunks, cx);
|
||||
})
|
||||
.log_err();
|
||||
return;
|
||||
}
|
||||
self.stage_or_unstage_resolved_diff_hunks(stage, hunks, cx);
|
||||
}
|
||||
|
||||
fn stage_or_unstage_resolved_diff_hunks(
|
||||
&mut self,
|
||||
stage: bool,
|
||||
hunks: Vec<ResolvedDiffHunks>,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
for hunks in hunks {
|
||||
let Some(operations) = hunks.diff.read(cx).operations() else {
|
||||
continue;
|
||||
};
|
||||
if (stage && !operations.supports_staging())
|
||||
|| (!stage && !operations.supports_unstaging())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let ranges = hunks
|
||||
.hunks
|
||||
.into_iter()
|
||||
.map(|hunk| hunk.buffer_range)
|
||||
.collect();
|
||||
if stage {
|
||||
operations.stage(hunks.diff, hunks.buffer, ranges, cx);
|
||||
} else {
|
||||
operations.unstage(hunks.diff, hunks.buffer, ranges, cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_restore(
|
||||
@@ -1812,15 +1737,65 @@ impl Editor {
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let mut hunks = self.resolve_diff_hunks(hunks, cx);
|
||||
if self.diff_hunk_delegate.is_none() {
|
||||
hunks.retain(|hunks| hunks.diff.read(cx).is_stageable());
|
||||
}
|
||||
let hunks = self.resolve_diff_hunks(hunks, cx);
|
||||
if hunks.is_empty() {
|
||||
return;
|
||||
}
|
||||
let delegate = self.diff_hunk_delegate();
|
||||
delegate.restore(hunks, self, window, cx);
|
||||
if let Some(target) = self.diff_hunk_action_target.clone() {
|
||||
target
|
||||
.update(cx, |editor, cx| {
|
||||
editor.restore_resolved_diff_hunks(hunks, window, cx);
|
||||
})
|
||||
.log_err();
|
||||
return;
|
||||
}
|
||||
self.restore_resolved_diff_hunks(hunks, window, cx);
|
||||
}
|
||||
|
||||
fn restore_resolved_diff_hunks(
|
||||
&mut self,
|
||||
hunks: Vec<ResolvedDiffHunks>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
if self.read_only(cx) {
|
||||
return;
|
||||
}
|
||||
let mut restorable_hunks = Vec::new();
|
||||
for hunks in hunks {
|
||||
let Some(operations) = hunks.diff.read(cx).operations() else {
|
||||
continue;
|
||||
};
|
||||
if !operations.supports_restore() {
|
||||
continue;
|
||||
}
|
||||
if operations.supports_unstaging() {
|
||||
let ranges = hunks
|
||||
.hunks
|
||||
.iter()
|
||||
.map(|hunk| hunk.buffer_range.clone())
|
||||
.collect();
|
||||
operations.unstage(hunks.diff.clone(), hunks.buffer.clone(), ranges, cx);
|
||||
}
|
||||
restorable_hunks.push(hunks);
|
||||
}
|
||||
if restorable_hunks.is_empty() {
|
||||
return;
|
||||
}
|
||||
self.transact(window, cx, |editor, window, cx| {
|
||||
editor.restore_diff_hunks(restorable_hunks, cx);
|
||||
let selections = editor
|
||||
.selections
|
||||
.all::<MultiBufferOffset>(&editor.display_snapshot(cx));
|
||||
editor.change_selections(
|
||||
SelectionEffects::no_scroll(),
|
||||
window,
|
||||
cx,
|
||||
|selections_state| {
|
||||
selections_state.select(selections);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
|
||||
@@ -3063,15 +3038,23 @@ pub fn render_diff_hunk_controls(
|
||||
_window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> AnyElement {
|
||||
let stageable = hunk_range
|
||||
let operations = hunk_range
|
||||
.start
|
||||
.buffer_id()
|
||||
.and_then(|buffer_id| editor.read(cx).buffer().read(cx).diff_for(buffer_id))
|
||||
.is_some_and(|diff| diff.read(cx).is_stageable());
|
||||
let show_stage_restore = stageable
|
||||
&& ProjectSettings::get_global(cx)
|
||||
.git
|
||||
.show_stage_restore_buttons;
|
||||
.and_then(|diff| diff.read(cx).operations());
|
||||
let show_stage_restore = ProjectSettings::get_global(cx)
|
||||
.git
|
||||
.show_stage_restore_buttons;
|
||||
let supports_staging = operations
|
||||
.as_ref()
|
||||
.is_some_and(|operations| operations.supports_staging());
|
||||
let supports_unstaging = operations
|
||||
.as_ref()
|
||||
.is_some_and(|operations| operations.supports_unstaging());
|
||||
let supports_restore = operations
|
||||
.as_ref()
|
||||
.is_some_and(|operations| operations.supports_restore());
|
||||
|
||||
h_flex()
|
||||
.h(line_height)
|
||||
@@ -3087,64 +3070,69 @@ pub fn render_diff_hunk_controls(
|
||||
.gap_1()
|
||||
.block_mouse_except_scroll()
|
||||
.shadow_md()
|
||||
.when(show_stage_restore, |el| {
|
||||
el.child(if status.has_secondary_hunk() {
|
||||
Button::new(("stage", row as u64), "Stage")
|
||||
.alpha(if status.is_pending() { 0.66 } else { 1.0 })
|
||||
.tooltip({
|
||||
let focus_handle = editor.focus_handle(cx);
|
||||
move |_window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Stage Hunk",
|
||||
&::git::ToggleStaged,
|
||||
&focus_handle,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
})
|
||||
.on_click({
|
||||
let editor = editor.clone();
|
||||
move |_event, window, cx| {
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.stage_or_unstage_diff_hunks(
|
||||
true,
|
||||
vec![hunk_range.start..hunk_range.start],
|
||||
window,
|
||||
.when(
|
||||
show_stage_restore
|
||||
&& ((status.has_secondary_hunk() && supports_staging)
|
||||
|| (!status.has_secondary_hunk() && supports_unstaging)),
|
||||
|el| {
|
||||
el.child(if status.has_secondary_hunk() {
|
||||
Button::new(("stage", row as u64), "Stage")
|
||||
.alpha(if status.is_pending() { 0.66 } else { 1.0 })
|
||||
.tooltip({
|
||||
let focus_handle = editor.focus_handle(cx);
|
||||
move |_window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Stage Hunk",
|
||||
&::git::ToggleStaged,
|
||||
&focus_handle,
|
||||
cx,
|
||||
);
|
||||
});
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Button::new(("unstage", row as u64), "Unstage")
|
||||
.alpha(if status.is_pending() { 0.66 } else { 1.0 })
|
||||
.tooltip({
|
||||
let focus_handle = editor.focus_handle(cx);
|
||||
move |_window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Unstage Hunk",
|
||||
&::git::ToggleStaged,
|
||||
&focus_handle,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
})
|
||||
.on_click({
|
||||
let editor = editor.clone();
|
||||
move |_event, window, cx| {
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.stage_or_unstage_diff_hunks(
|
||||
false,
|
||||
vec![hunk_range.start..hunk_range.start],
|
||||
window,
|
||||
)
|
||||
}
|
||||
})
|
||||
.on_click({
|
||||
let editor = editor.clone();
|
||||
move |_event, window, cx| {
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.stage_or_unstage_diff_hunks(
|
||||
true,
|
||||
vec![hunk_range.start..hunk_range.start],
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
});
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Button::new(("unstage", row as u64), "Unstage")
|
||||
.alpha(if status.is_pending() { 0.66 } else { 1.0 })
|
||||
.tooltip({
|
||||
let focus_handle = editor.focus_handle(cx);
|
||||
move |_window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"Unstage Hunk",
|
||||
&::git::ToggleStaged,
|
||||
&focus_handle,
|
||||
cx,
|
||||
);
|
||||
});
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
.when(show_stage_restore, |el| {
|
||||
)
|
||||
}
|
||||
})
|
||||
.on_click({
|
||||
let editor = editor.clone();
|
||||
move |_event, window, cx| {
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.stage_or_unstage_diff_hunks(
|
||||
false,
|
||||
vec![hunk_range.start..hunk_range.start],
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
});
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
)
|
||||
.when(show_stage_restore && supports_restore, |el| {
|
||||
el.child(
|
||||
Button::new(("restore", row as u64), "Restore")
|
||||
.tooltip({
|
||||
@@ -3269,7 +3257,7 @@ impl Editor {
|
||||
let buffer = self.buffer.clone();
|
||||
cx.spawn(async move |_, cx| {
|
||||
let diffs = future::join_all(tasks).await;
|
||||
if editor.read_with(cx, |editor, _cx| editor.diff_hunk_delegate.is_some()) {
|
||||
if editor.read_with(cx, |editor, _cx| editor.diff_hunk_renderer.is_some()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+20
-78
@@ -22,14 +22,12 @@ use rope::Point;
|
||||
use settings::{DiffViewStyle, SeedQuerySetting, Settings, SettingsStore, update_settings_file};
|
||||
use text::{Bias, BufferId, OffsetRangeExt as _, Patch, ToPoint as _};
|
||||
|
||||
use ui::{Toggleable as _, Tooltip, prelude::*, render_modifiers};
|
||||
use util::ResultExt as _;
|
||||
|
||||
use crate::{
|
||||
display_map::CompanionExcerptPatch,
|
||||
element::SplitSide,
|
||||
split_editor_view::{SplitEditorState, SplitEditorView},
|
||||
};
|
||||
use ui::{Toggleable as _, Tooltip, prelude::*, render_modifiers};
|
||||
use workspace::{
|
||||
ActivatePaneLeft, ActivatePaneRight, Item, ToolbarItemLocation, Workspace,
|
||||
item::{ItemBufferKind, ItemEvent, SaveOptions, TabContentParams},
|
||||
@@ -37,8 +35,8 @@ use workspace::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
Autoscroll, DiffHunkDelegate, Editor, EditorEvent, EditorSettings, ResolvedDiffHunks,
|
||||
ToggleSoftWrap, UncommittedDiffHunkDelegate,
|
||||
Autoscroll, DefaultDiffHunkRenderer, DiffHunkRenderer, Editor, EditorEvent, EditorSettings,
|
||||
ToggleSoftWrap,
|
||||
actions::{DisableBreakpoint, EditLogBreakpoint, EnableBreakpoint, ToggleBreakpoint},
|
||||
display_map::Companion,
|
||||
};
|
||||
@@ -151,63 +149,11 @@ fn translate_lhs_selections_to_rhs(
|
||||
translated
|
||||
}
|
||||
|
||||
struct SplitLhsDiffHunkDelegate {
|
||||
struct SplitLhsDiffHunkRenderer {
|
||||
splittable: WeakEntity<SplittableEditor>,
|
||||
}
|
||||
|
||||
impl DiffHunkDelegate for SplitLhsDiffHunkDelegate {
|
||||
fn toggle(
|
||||
&self,
|
||||
hunks: Vec<ResolvedDiffHunks>,
|
||||
_editor: &mut Editor,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Editor>,
|
||||
) {
|
||||
self.splittable
|
||||
.update(cx, |splittable, cx| {
|
||||
splittable.rhs_editor.update(cx, |editor, cx| {
|
||||
let delegate = editor.diff_hunk_delegate();
|
||||
delegate.toggle(hunks, editor, window, cx);
|
||||
});
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
|
||||
fn stage_or_unstage(
|
||||
&self,
|
||||
stage: bool,
|
||||
hunks: Vec<ResolvedDiffHunks>,
|
||||
_editor: &mut Editor,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Editor>,
|
||||
) {
|
||||
self.splittable
|
||||
.update(cx, |splittable, cx| {
|
||||
splittable.rhs_editor.update(cx, |editor, cx| {
|
||||
let delegate = editor.diff_hunk_delegate();
|
||||
delegate.stage_or_unstage(stage, hunks, editor, window, cx);
|
||||
});
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
|
||||
fn restore(
|
||||
&self,
|
||||
hunks: Vec<ResolvedDiffHunks>,
|
||||
_editor: &mut Editor,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Editor>,
|
||||
) {
|
||||
self.splittable
|
||||
.update(cx, |splittable, cx| {
|
||||
splittable.rhs_editor.update(cx, |editor, cx| {
|
||||
let delegate = editor.diff_hunk_delegate();
|
||||
delegate.restore(hunks, editor, window, cx);
|
||||
});
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
|
||||
impl DiffHunkRenderer for SplitLhsDiffHunkRenderer {
|
||||
fn render_hunk_controls(
|
||||
&self,
|
||||
row: u32,
|
||||
@@ -222,8 +168,8 @@ impl DiffHunkDelegate for SplitLhsDiffHunkDelegate {
|
||||
let Some(splittable) = self.splittable.upgrade() else {
|
||||
return gpui::Empty.into_any_element();
|
||||
};
|
||||
let delegate = splittable.read(cx).rhs_editor.read(cx).diff_hunk_delegate();
|
||||
delegate.render_hunk_controls(
|
||||
let renderer = splittable.read(cx).rhs_editor.read(cx).diff_hunk_renderer();
|
||||
renderer.render_hunk_controls(
|
||||
row,
|
||||
status,
|
||||
hunk_range,
|
||||
@@ -239,8 +185,8 @@ impl DiffHunkDelegate for SplitLhsDiffHunkDelegate {
|
||||
let Some(splittable) = self.splittable.upgrade() else {
|
||||
return false;
|
||||
};
|
||||
let delegate = splittable.read(cx).rhs_editor.read(cx).diff_hunk_delegate();
|
||||
delegate.render_hunk_as_staged(status, cx)
|
||||
let renderer = splittable.read(cx).rhs_editor.read(cx).diff_hunk_renderer();
|
||||
renderer.render_hunk_as_staged(status, cx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -636,13 +582,13 @@ impl SplittableEditor {
|
||||
self.lhs.is_some()
|
||||
}
|
||||
|
||||
pub fn set_diff_hunk_delegate(
|
||||
pub fn set_diff_hunk_renderer(
|
||||
&self,
|
||||
delegate: Option<Arc<dyn DiffHunkDelegate>>,
|
||||
renderer: Option<Arc<dyn DiffHunkRenderer>>,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.rhs_editor.update(cx, |editor, cx| {
|
||||
editor.set_diff_hunk_delegate(delegate, cx);
|
||||
editor.set_diff_hunk_renderer(renderer, cx);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -683,7 +629,7 @@ impl SplittableEditor {
|
||||
editor.disable_inline_diagnostics();
|
||||
editor.disable_mouse_wheel_zoom();
|
||||
editor.set_minimap_visibility(crate::MinimapVisibility::Disabled, window, cx);
|
||||
editor.set_diff_hunk_delegate(Some(Arc::new(UncommittedDiffHunkDelegate)), cx);
|
||||
editor.set_diff_hunk_renderer(Some(Arc::new(DefaultDiffHunkRenderer)), cx);
|
||||
editor
|
||||
});
|
||||
// TODO(split-diff) we might want to tag editor events with whether they came from rhs/lhs
|
||||
@@ -782,15 +728,19 @@ impl SplittableEditor {
|
||||
});
|
||||
|
||||
let splittable = cx.weak_entity();
|
||||
let rhs_editor = self.rhs_editor.downgrade();
|
||||
let lhs_editor = cx.new(|cx| {
|
||||
let mut editor =
|
||||
Editor::for_multibuffer(lhs_multibuffer.clone(), Some(project.clone()), window, cx);
|
||||
editor.set_number_deleted_lines(true, cx);
|
||||
editor.set_delegate_expand_excerpts(true);
|
||||
editor.set_diff_hunk_delegate(
|
||||
Some(Arc::new(SplitLhsDiffHunkDelegate { splittable })),
|
||||
editor.set_diff_hunk_renderer(
|
||||
Some(Arc::new(SplitLhsDiffHunkRenderer {
|
||||
splittable: splittable.clone(),
|
||||
})),
|
||||
cx,
|
||||
);
|
||||
editor.set_diff_hunk_action_target(Some(rhs_editor));
|
||||
editor.set_delegate_open_excerpts(true);
|
||||
editor.set_show_vertical_scrollbar(false, cx);
|
||||
editor.disable_lsp_data();
|
||||
@@ -3894,15 +3844,7 @@ mod tests {
|
||||
.unindent();
|
||||
|
||||
let buffer2 = cx.new(|cx| Buffer::local(current_text.to_string(), cx));
|
||||
let diff2 = cx.new(|cx| {
|
||||
BufferDiff::new(
|
||||
&buffer2.read(cx).text_snapshot(),
|
||||
None,
|
||||
None,
|
||||
buffer_diff::DiffBaseKind::Custom,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
let diff2 = cx.new(|cx| BufferDiff::new(&buffer2.read(cx).text_snapshot(), None, None, cx));
|
||||
|
||||
editor.update(cx, |editor, cx| {
|
||||
let path1 = PathKey::sorted(0);
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::{
|
||||
use agent_settings::AgentSettings;
|
||||
use anyhow::{Context as _, Result, anyhow};
|
||||
use editor::{
|
||||
Addon, Editor, EditorEvent, RestoreOnlyDiffHunkDelegate, SplittableEditor,
|
||||
Addon, Editor, EditorEvent, HiddenDiffHunkRenderer, SplittableEditor,
|
||||
actions::SendReviewToAgent,
|
||||
};
|
||||
use git::{repository::DiffType, status::FileStatus};
|
||||
@@ -338,7 +338,7 @@ impl BranchDiff {
|
||||
Capability::ReadWrite,
|
||||
"No changes",
|
||||
move |editor, cx| {
|
||||
editor.set_diff_hunk_delegate(Some(Arc::new(RestoreOnlyDiffHunkDelegate)), cx);
|
||||
editor.set_diff_hunk_renderer(Some(Arc::new(HiddenDiffHunkRenderer)), cx);
|
||||
editor.rhs_editor().update(cx, move |rhs_editor, _cx| {
|
||||
rhs_editor.set_read_only(false);
|
||||
rhs_editor.register_addon(BranchDiffAddon {
|
||||
|
||||
@@ -2,7 +2,7 @@ use anyhow::{Context as _, Result};
|
||||
use buffer_diff::BufferDiff;
|
||||
use collections::HashMap;
|
||||
use editor::{
|
||||
Addon, Editor, EditorEvent, EditorSettings, MultiBuffer, RestoreOnlyDiffHunkDelegate,
|
||||
Addon, Editor, EditorEvent, EditorSettings, HiddenDiffHunkRenderer, MultiBuffer,
|
||||
SplittableEditor, hover_markdown_style, multibuffer_context_lines,
|
||||
};
|
||||
use futures_lite::future::yield_now;
|
||||
@@ -326,7 +326,7 @@ impl CommitView {
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
editor.set_diff_hunk_delegate(Some(Arc::new(RestoreOnlyDiffHunkDelegate)), cx);
|
||||
editor.set_diff_hunk_renderer(Some(Arc::new(HiddenDiffHunkRenderer)), cx);
|
||||
|
||||
editor.rhs_editor().update(cx, |editor, cx| {
|
||||
editor.set_show_bookmarks(false, cx);
|
||||
@@ -417,7 +417,6 @@ impl CommitView {
|
||||
&snapshot,
|
||||
snapshot.language().cloned(),
|
||||
Some(language_registry.clone()),
|
||||
buffer_diff::DiffBaseKind::Oid,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
@@ -1159,15 +1158,8 @@ async fn build_buffer_diff(
|
||||
let language = cx.update(|_, cx| buffer.read(cx).language().cloned())?;
|
||||
let buffer = cx.update(|_, cx| buffer.read(cx).snapshot())?;
|
||||
|
||||
let diff = cx.new(|cx| {
|
||||
BufferDiff::new(
|
||||
&buffer.text,
|
||||
language,
|
||||
Some(language_registry.clone()),
|
||||
buffer_diff::DiffBaseKind::Oid,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
let diff =
|
||||
cx.new(|cx| BufferDiff::new(&buffer.text, language, Some(language_registry.clone()), cx));
|
||||
|
||||
diff.update(cx, |diff, cx| {
|
||||
diff.set_base_text(
|
||||
@@ -1346,7 +1338,7 @@ impl Item for CommitView {
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
editor.set_diff_hunk_delegate(Some(Arc::new(RestoreOnlyDiffHunkDelegate)), cx);
|
||||
editor.set_diff_hunk_renderer(Some(Arc::new(HiddenDiffHunkRenderer)), cx);
|
||||
editor.rhs_editor().update(cx, |editor, cx| {
|
||||
editor.set_show_bookmarks(false, cx);
|
||||
editor.set_show_breakpoints(false, cx);
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use anyhow::Result;
|
||||
use buffer_diff::BufferDiff;
|
||||
use editor::{
|
||||
Editor, EditorEvent, MultiBuffer, RestoreOnlyUnstagedDiffHunkDelegate,
|
||||
multibuffer_context_lines,
|
||||
Editor, EditorEvent, HiddenUnstagedDiffHunkRenderer, MultiBuffer, multibuffer_context_lines,
|
||||
};
|
||||
use git_ui_core::file_diff_view::build_buffer_diff;
|
||||
use gpui::{
|
||||
@@ -199,7 +198,7 @@ impl MultiDiffView {
|
||||
let editor = cx.new(|cx| {
|
||||
let mut editor =
|
||||
Editor::for_multibuffer(multibuffer, Some(project.clone()), window, cx);
|
||||
editor.set_diff_hunk_delegate(Some(Arc::new(RestoreOnlyUnstagedDiffHunkDelegate)), cx);
|
||||
editor.set_diff_hunk_renderer(Some(Arc::new(HiddenUnstagedDiffHunkRenderer)), cx);
|
||||
editor.disable_diagnostics(cx);
|
||||
editor.set_expand_all_diff_hunks(cx);
|
||||
editor
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::{
|
||||
use anyhow::{Context as _, Result};
|
||||
use buffer_diff::DiffHunkSecondaryStatus;
|
||||
use editor::{
|
||||
Editor, EditorEvent, SplittableEditor, UncommittedDiffHunkDelegate,
|
||||
DefaultDiffHunkRenderer, Editor, EditorEvent, SplittableEditor,
|
||||
actions::{GoToHunk, GoToPreviousHunk, SendReviewToAgent},
|
||||
};
|
||||
use git::{Commit, StageAll, StageAndNext, ToggleStaged, UnstageAll, UnstageAndNext};
|
||||
@@ -243,7 +243,7 @@ impl ProjectDiff {
|
||||
Capability::ReadWrite,
|
||||
"No uncommitted changes",
|
||||
move |editor, cx| {
|
||||
editor.set_diff_hunk_delegate(Some(Arc::new(UncommittedDiffHunkDelegate)), cx);
|
||||
editor.set_diff_hunk_renderer(Some(Arc::new(DefaultDiffHunkRenderer)), cx);
|
||||
editor.rhs_editor().update(cx, |rhs_editor, _cx| {
|
||||
rhs_editor.set_read_only(false);
|
||||
rhs_editor.register_addon(GitPanelAddon {
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::{
|
||||
use anyhow::{Context as _, Result};
|
||||
use buffer_diff::DiffHunkStatus;
|
||||
use editor::{
|
||||
DiffHunkDelegate, Editor, EditorEvent, ResolvedDiffHunks, SplittableEditor,
|
||||
DiffHunkRenderer, Editor, EditorEvent, SplittableEditor,
|
||||
actions::{GoToHunk, GoToPreviousHunk},
|
||||
};
|
||||
use git::{Commit, UnstageAll, UnstageAndNext};
|
||||
@@ -26,7 +26,6 @@ use std::{
|
||||
sync::Arc,
|
||||
};
|
||||
use ui::{DiffStat, Divider, Icon, Tooltip, Window, prelude::*};
|
||||
use util::ResultExt as _;
|
||||
use workspace::{
|
||||
ItemNavHistory, SerializableItem, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView,
|
||||
Workspace,
|
||||
@@ -34,50 +33,9 @@ use workspace::{
|
||||
searchable::SearchableItemHandle,
|
||||
};
|
||||
|
||||
pub(crate) struct StagedDiffDelegate;
|
||||
|
||||
impl DiffHunkDelegate for StagedDiffDelegate {
|
||||
fn toggle(
|
||||
&self,
|
||||
hunks: Vec<ResolvedDiffHunks>,
|
||||
editor: &mut Editor,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Editor>,
|
||||
) {
|
||||
self.stage_or_unstage(false, hunks, editor, window, cx);
|
||||
}
|
||||
|
||||
fn stage_or_unstage(
|
||||
&self,
|
||||
stage: bool,
|
||||
hunks: Vec<ResolvedDiffHunks>,
|
||||
editor: &mut Editor,
|
||||
_window: &mut Window,
|
||||
cx: &mut Context<Editor>,
|
||||
) {
|
||||
if stage {
|
||||
return;
|
||||
}
|
||||
let Some(project) = editor.project().cloned() else {
|
||||
return;
|
||||
};
|
||||
for hunks in hunks {
|
||||
let index_ranges = hunks
|
||||
.hunks
|
||||
.into_iter()
|
||||
.map(|hunk| hunk.buffer_range)
|
||||
.collect::<Vec<_>>();
|
||||
if index_ranges.is_empty() {
|
||||
continue;
|
||||
}
|
||||
project
|
||||
.update(cx, |project, cx| {
|
||||
project.unstage_staged_hunks(hunks.diff, index_ranges, cx)
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
pub(crate) struct StagedDiffHunkRenderer;
|
||||
|
||||
impl DiffHunkRenderer for StagedDiffHunkRenderer {
|
||||
fn render_hunk_controls(
|
||||
&self,
|
||||
row: u32,
|
||||
@@ -228,7 +186,7 @@ impl StagedDiff {
|
||||
Capability::ReadOnly,
|
||||
"No staged changes",
|
||||
move |editor, cx| {
|
||||
editor.set_diff_hunk_delegate(Some(Arc::new(StagedDiffDelegate)), cx);
|
||||
editor.set_diff_hunk_renderer(Some(Arc::new(StagedDiffHunkRenderer)), cx);
|
||||
editor.rhs_editor().update(cx, |rhs_editor, _cx| {
|
||||
rhs_editor.set_read_only(true);
|
||||
rhs_editor.register_addon(GitPanelAddon {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use anyhow::Result;
|
||||
use buffer_diff::BufferDiff;
|
||||
use editor::{
|
||||
Editor, EditorEvent, EditorSettings, MultiBuffer, RestoreOnlyUnstagedDiffHunkDelegate,
|
||||
Editor, EditorEvent, EditorSettings, HiddenUnstagedDiffHunkRenderer, MultiBuffer,
|
||||
SplittableEditor, ToPoint, actions::DiffClipboardWithSelectionData,
|
||||
};
|
||||
use futures::{FutureExt, select_biased};
|
||||
@@ -117,12 +117,13 @@ impl TextDiffView {
|
||||
cx,
|
||||
);
|
||||
let diff_buffer = cx.new(|cx| {
|
||||
BufferDiff::new_with_base_text_buffer(
|
||||
let mut diff = BufferDiff::new_with_base_text_buffer(
|
||||
&source_buffer_snapshot.text,
|
||||
clipboard_buffer.clone(),
|
||||
buffer_diff::DiffBaseKind::Custom,
|
||||
cx,
|
||||
)
|
||||
);
|
||||
diff.set_operations(Arc::new(buffer_diff::RestoreDiffOperations));
|
||||
diff
|
||||
});
|
||||
|
||||
let task = window.spawn(cx, async move |cx| {
|
||||
@@ -185,8 +186,7 @@ impl TextDiffView {
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
splittable
|
||||
.set_diff_hunk_delegate(Some(Arc::new(RestoreOnlyUnstagedDiffHunkDelegate)), cx);
|
||||
splittable.set_diff_hunk_renderer(Some(Arc::new(HiddenUnstagedDiffHunkRenderer)), cx);
|
||||
splittable
|
||||
});
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::{
|
||||
use anyhow::{Context as _, Result};
|
||||
use buffer_diff::DiffHunkStatus;
|
||||
use editor::{
|
||||
DiffHunkDelegate, Editor, EditorEvent, ResolvedDiffHunks, SplittableEditor,
|
||||
DiffHunkRenderer, Editor, EditorEvent, SplittableEditor,
|
||||
actions::{GoToHunk, GoToPreviousHunk},
|
||||
};
|
||||
use git::{StageAll, StageAndNext};
|
||||
@@ -26,7 +26,6 @@ use std::{
|
||||
sync::Arc,
|
||||
};
|
||||
use ui::{DiffStat, Divider, Icon, Tooltip, Window, prelude::*};
|
||||
use util::ResultExt as _;
|
||||
use workspace::{
|
||||
ItemNavHistory, SerializableItem, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView,
|
||||
Workspace,
|
||||
@@ -34,79 +33,9 @@ use workspace::{
|
||||
searchable::SearchableItemHandle,
|
||||
};
|
||||
|
||||
pub(crate) struct UnstagedDiffDelegate;
|
||||
|
||||
impl DiffHunkDelegate for UnstagedDiffDelegate {
|
||||
fn toggle(
|
||||
&self,
|
||||
hunks: Vec<ResolvedDiffHunks>,
|
||||
editor: &mut Editor,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Editor>,
|
||||
) {
|
||||
self.stage_or_unstage(true, hunks, editor, window, cx);
|
||||
}
|
||||
|
||||
fn stage_or_unstage(
|
||||
&self,
|
||||
stage: bool,
|
||||
hunks: Vec<ResolvedDiffHunks>,
|
||||
editor: &mut Editor,
|
||||
_window: &mut Window,
|
||||
cx: &mut Context<Editor>,
|
||||
) {
|
||||
if !stage {
|
||||
return;
|
||||
}
|
||||
let Some(project) = editor.project().cloned() else {
|
||||
return;
|
||||
};
|
||||
for hunks in hunks {
|
||||
let Some(buffer) = hunks.buffer else {
|
||||
continue;
|
||||
};
|
||||
let worktree_ranges = hunks
|
||||
.hunks
|
||||
.into_iter()
|
||||
.map(|hunk| hunk.buffer_range)
|
||||
.collect::<Vec<_>>();
|
||||
if worktree_ranges.is_empty() {
|
||||
continue;
|
||||
}
|
||||
project
|
||||
.update(cx, |project, cx| {
|
||||
project.stage_hunks(buffer, hunks.diff, worktree_ranges, cx)
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
|
||||
fn restore(
|
||||
&self,
|
||||
hunks: Vec<ResolvedDiffHunks>,
|
||||
editor: &mut Editor,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Editor>,
|
||||
) {
|
||||
if hunks.is_empty() || editor.read_only(cx) {
|
||||
return;
|
||||
}
|
||||
editor.transact(window, cx, |editor, window, cx| {
|
||||
editor.restore_diff_hunks(hunks, cx);
|
||||
let selections = editor
|
||||
.selections
|
||||
.all::<editor::MultiBufferOffset>(&editor.display_snapshot(cx));
|
||||
editor.change_selections(
|
||||
editor::SelectionEffects::no_scroll(),
|
||||
window,
|
||||
cx,
|
||||
|selections_state| {
|
||||
selections_state.select(selections);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
pub(crate) struct UnstagedDiffHunkRenderer;
|
||||
|
||||
impl DiffHunkRenderer for UnstagedDiffHunkRenderer {
|
||||
fn render_hunk_controls(
|
||||
&self,
|
||||
row: u32,
|
||||
@@ -282,7 +211,7 @@ impl UnstagedDiff {
|
||||
Capability::ReadWrite,
|
||||
"No unstaged changes",
|
||||
move |editor, cx| {
|
||||
editor.set_diff_hunk_delegate(Some(Arc::new(UnstagedDiffDelegate)), cx);
|
||||
editor.set_diff_hunk_renderer(Some(Arc::new(UnstagedDiffHunkRenderer)), cx);
|
||||
editor.rhs_editor().update(cx, |rhs_editor, _cx| {
|
||||
rhs_editor.set_read_only(false);
|
||||
rhs_editor.register_addon(GitPanelAddon {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use anyhow::Result;
|
||||
use buffer_diff::BufferDiff;
|
||||
use editor::{
|
||||
Editor, EditorEvent, EditorSettings, MultiBuffer, RestoreOnlyUnstagedDiffHunkDelegate,
|
||||
Editor, EditorEvent, EditorSettings, HiddenUnstagedDiffHunkRenderer, MultiBuffer,
|
||||
SplittableEditor,
|
||||
};
|
||||
use futures::{FutureExt, select_biased};
|
||||
@@ -124,8 +124,7 @@ impl FileDiffView {
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
splittable
|
||||
.set_diff_hunk_delegate(Some(Arc::new(RestoreOnlyUnstagedDiffHunkDelegate)), cx);
|
||||
splittable.set_diff_hunk_renderer(Some(Arc::new(HiddenUnstagedDiffHunkRenderer)), cx);
|
||||
splittable
|
||||
});
|
||||
|
||||
@@ -214,13 +213,14 @@ pub async fn build_buffer_diff(
|
||||
let language_registry = new_buffer.read_with(cx, |buffer, _| buffer.language_registry());
|
||||
|
||||
let diff = cx.new(|cx| {
|
||||
BufferDiff::new(
|
||||
let mut diff = BufferDiff::new(
|
||||
&new_buffer_snapshot.text,
|
||||
new_buffer_snapshot.language().cloned(),
|
||||
language_registry,
|
||||
buffer_diff::DiffBaseKind::Custom,
|
||||
cx,
|
||||
)
|
||||
);
|
||||
diff.set_operations(Arc::new(buffer_diff::RestoreDiffOperations));
|
||||
diff
|
||||
});
|
||||
|
||||
diff.update(cx, |diff, cx| {
|
||||
|
||||
+101
-16
@@ -5,7 +5,7 @@ pub mod job_debug_queue;
|
||||
pub mod pending_op;
|
||||
|
||||
use crate::{
|
||||
ProjectEnvironment, ProjectItem, ProjectPath,
|
||||
Project, ProjectEnvironment, ProjectItem, ProjectPath,
|
||||
buffer_store::{BufferStore, BufferStoreEvent},
|
||||
project_settings::ProjectSettings,
|
||||
trusted_worktrees::{
|
||||
@@ -15,7 +15,9 @@ use crate::{
|
||||
};
|
||||
use anyhow::{Context as _, Result, anyhow, bail};
|
||||
use askpass::{AskPassDelegate, EncryptedPassword, IKnowWhatIAmDoingAndIHaveReadTheDocs};
|
||||
use buffer_diff::{BufferDiff, DiffHunk, DiffHunkSecondaryStatus, PendingHunk, PendingSense};
|
||||
use buffer_diff::{
|
||||
BufferDiff, DiffHunk, DiffHunkSecondaryStatus, DiffOperations, PendingHunk, PendingSense,
|
||||
};
|
||||
use client::ProjectId;
|
||||
use collections::HashMap;
|
||||
pub use conflict_set::{ConflictRegion, ConflictSet, ConflictSetSnapshot, ConflictSetUpdate};
|
||||
@@ -98,6 +100,7 @@ use zeroize::Zeroize;
|
||||
|
||||
pub struct GitStore {
|
||||
state: GitStoreState,
|
||||
project: Option<WeakEntity<Project>>,
|
||||
buffer_store: Entity<BufferStore>,
|
||||
worktree_store: Entity<WorktreeStore>,
|
||||
repositories: HashMap<RepositoryId, Entity<Repository>>,
|
||||
@@ -310,6 +313,68 @@ enum DiffKind {
|
||||
SinceOid(Option<git::Oid>),
|
||||
}
|
||||
|
||||
struct GitDiffOperations {
|
||||
project: WeakEntity<Project>,
|
||||
kind: DiffKind,
|
||||
}
|
||||
|
||||
impl DiffOperations for GitDiffOperations {
|
||||
fn supports_staging(&self) -> bool {
|
||||
matches!(self.kind, DiffKind::Unstaged | DiffKind::Uncommitted)
|
||||
}
|
||||
|
||||
fn supports_unstaging(&self) -> bool {
|
||||
matches!(self.kind, DiffKind::Staged | DiffKind::Uncommitted)
|
||||
}
|
||||
|
||||
fn supports_restore(&self) -> bool {
|
||||
matches!(self.kind, DiffKind::Unstaged | DiffKind::Uncommitted)
|
||||
}
|
||||
|
||||
fn stage(
|
||||
&self,
|
||||
diff: Entity<BufferDiff>,
|
||||
buffer: Option<Entity<Buffer>>,
|
||||
buffer_ranges: Vec<Range<Anchor>>,
|
||||
cx: &mut App,
|
||||
) {
|
||||
let result = self.project.update(cx, |project, cx| match self.kind {
|
||||
DiffKind::Unstaged => {
|
||||
let buffer = buffer.context("unstaged diff has no worktree buffer")?;
|
||||
project.stage_hunks(buffer, diff, buffer_ranges, cx)
|
||||
}
|
||||
DiffKind::Uncommitted => {
|
||||
let buffer = buffer.context("uncommitted diff has no worktree buffer")?;
|
||||
let secondary_diff = diff
|
||||
.read(cx)
|
||||
.secondary_diff()
|
||||
.context("diff has no unstaged secondary")?;
|
||||
project.stage_hunks(buffer, secondary_diff, buffer_ranges, cx)
|
||||
}
|
||||
_ => Ok(()),
|
||||
});
|
||||
result.and_then(|result| result).log_err();
|
||||
}
|
||||
|
||||
fn unstage(
|
||||
&self,
|
||||
diff: Entity<BufferDiff>,
|
||||
buffer: Option<Entity<Buffer>>,
|
||||
buffer_ranges: Vec<Range<Anchor>>,
|
||||
cx: &mut App,
|
||||
) {
|
||||
let result = self.project.update(cx, |project, cx| match self.kind {
|
||||
DiffKind::Staged => project.unstage_staged_hunks(diff, buffer_ranges, cx),
|
||||
DiffKind::Uncommitted => {
|
||||
let buffer = buffer.context("uncommitted diff has no worktree buffer")?;
|
||||
project.unstage_uncommitted_hunks(buffer, diff, buffer_ranges, cx)
|
||||
}
|
||||
_ => Ok(()),
|
||||
});
|
||||
result.and_then(|result| result).log_err();
|
||||
}
|
||||
}
|
||||
|
||||
struct IndexTextFile {
|
||||
path: Arc<RelPath>,
|
||||
full_path: PathBuf,
|
||||
@@ -942,6 +1007,7 @@ impl GitStore {
|
||||
let diff_base_setting = ProjectSettings::get_global(cx).git.diff_base;
|
||||
GitStore {
|
||||
state,
|
||||
project: None,
|
||||
buffer_store,
|
||||
worktree_store,
|
||||
repositories: HashMap::default(),
|
||||
@@ -958,6 +1024,10 @@ impl GitStore {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_project(&mut self, project: WeakEntity<Project>) {
|
||||
self.project = Some(project);
|
||||
}
|
||||
|
||||
pub fn init(client: &AnyProtoClient) {
|
||||
client.add_entity_request_handler(Self::handle_get_remotes);
|
||||
client.add_entity_request_handler(Self::handle_get_branches);
|
||||
@@ -1628,7 +1698,6 @@ impl GitStore {
|
||||
&buffer_snapshot,
|
||||
buffer_snapshot.language().cloned(),
|
||||
language_registry,
|
||||
buffer_diff::DiffBaseKind::Oid,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
@@ -1801,10 +1870,11 @@ impl GitStore {
|
||||
this.loading_diffs.remove(&(buffer_id, kind));
|
||||
|
||||
let git_store = cx.weak_entity();
|
||||
let project = this.project.clone();
|
||||
let diff_state = this
|
||||
.diffs
|
||||
.entry(buffer_id)
|
||||
.or_insert_with(|| cx.new(|cx| BufferGitState::new(git_store, cx)));
|
||||
.or_insert_with(|| cx.new(|cx| BufferGitState::new(git_store.clone(), cx)));
|
||||
|
||||
let existing_unstaged_diff = diff_state.read(cx).unstaged_diff();
|
||||
|
||||
@@ -1820,12 +1890,15 @@ impl GitStore {
|
||||
diff_state.get_or_create_index_text_buffer(index_text_file.clone(), cx)
|
||||
});
|
||||
cx.new(|cx| {
|
||||
BufferDiff::new_with_base_text_buffer(
|
||||
let mut diff = BufferDiff::new_with_base_text_buffer(
|
||||
&text_snapshot,
|
||||
base_text_buffer,
|
||||
buffer_diff::DiffBaseKind::Index,
|
||||
cx,
|
||||
)
|
||||
);
|
||||
if let Some(project) = project.clone() {
|
||||
diff.set_operations(Arc::new(GitDiffOperations { project, kind }));
|
||||
}
|
||||
diff
|
||||
})
|
||||
}
|
||||
DiffKind::Staged => {
|
||||
@@ -1848,12 +1921,15 @@ impl GitStore {
|
||||
let index_text_snapshot = index_text_buffer.read(cx).text_snapshot();
|
||||
staged_index_text_buffer = Some(index_text_buffer);
|
||||
cx.new(|cx| {
|
||||
BufferDiff::new_with_base_text_buffer(
|
||||
let mut diff = BufferDiff::new_with_base_text_buffer(
|
||||
&index_text_snapshot,
|
||||
base_text_buffer,
|
||||
buffer_diff::DiffBaseKind::Head,
|
||||
cx,
|
||||
)
|
||||
);
|
||||
if let Some(project) = project.clone() {
|
||||
diff.set_operations(Arc::new(GitDiffOperations { project, kind }));
|
||||
}
|
||||
diff
|
||||
})
|
||||
}
|
||||
DiffKind::Uncommitted => {
|
||||
@@ -1861,12 +1937,15 @@ impl GitStore {
|
||||
diff_state.get_or_create_head_text_buffer(cx)
|
||||
});
|
||||
cx.new(|cx| {
|
||||
BufferDiff::new_with_base_text_buffer(
|
||||
let mut diff = BufferDiff::new_with_base_text_buffer(
|
||||
&text_snapshot,
|
||||
base_text_buffer,
|
||||
buffer_diff::DiffBaseKind::Head,
|
||||
cx,
|
||||
)
|
||||
);
|
||||
if let Some(project) = project.clone() {
|
||||
diff.set_operations(Arc::new(GitDiffOperations { project, kind }));
|
||||
}
|
||||
diff
|
||||
})
|
||||
}
|
||||
DiffKind::SinceOid(_) => {
|
||||
@@ -1901,12 +1980,18 @@ impl GitStore {
|
||||
let base_text_buffer =
|
||||
diff_state.get_or_create_index_text_buffer(index_text_file, cx);
|
||||
let unstaged_diff = cx.new(|cx| {
|
||||
BufferDiff::new_with_base_text_buffer(
|
||||
let mut diff = BufferDiff::new_with_base_text_buffer(
|
||||
&text_snapshot,
|
||||
base_text_buffer,
|
||||
buffer_diff::DiffBaseKind::Index,
|
||||
cx,
|
||||
)
|
||||
);
|
||||
if let Some(project) = project.clone() {
|
||||
diff.set_operations(Arc::new(GitDiffOperations {
|
||||
project,
|
||||
kind: DiffKind::Unstaged,
|
||||
}));
|
||||
}
|
||||
diff
|
||||
});
|
||||
diff_state.unstaged_diff = Some(unstaged_diff.downgrade());
|
||||
unstaged_diff
|
||||
|
||||
@@ -1305,6 +1305,7 @@ impl Project {
|
||||
cx,
|
||||
)
|
||||
});
|
||||
git_store.update(cx, |git_store, _| git_store.set_project(weak_self.clone()));
|
||||
|
||||
let task_store = cx.new(|cx| {
|
||||
TaskStore::local(
|
||||
@@ -1550,6 +1551,7 @@ impl Project {
|
||||
cx,
|
||||
)
|
||||
});
|
||||
git_store.update(cx, |git_store, _| git_store.set_project(weak_self.clone()));
|
||||
|
||||
let task_store = cx.new(|cx| {
|
||||
TaskStore::remote(
|
||||
@@ -1857,6 +1859,7 @@ impl Project {
|
||||
let snippets = SnippetProvider::new(fs.clone(), BTreeSet::from_iter([]), cx);
|
||||
|
||||
let weak_self = cx.weak_entity();
|
||||
git_store.update(cx, |git_store, _| git_store.set_project(weak_self.clone()));
|
||||
let context_server_store = cx.new(|cx| {
|
||||
ContextServerStore::local(worktree_store.clone(), Some(weak_self), false, cx)
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user