Introduce "Keep All" and "Reject All" buttons when reviewing assistant edits (#27724)
Release Notes: - N/A
This commit is contained in:
parent
8add90d7cb
commit
7fe6188f8e
7 changed files with 183 additions and 30 deletions
|
@ -40,7 +40,7 @@ pub use crate::assistant_panel::{AssistantPanel, ConcreteAssistantPanelDelegate}
|
||||||
pub use crate::inline_assistant::InlineAssistant;
|
pub use crate::inline_assistant::InlineAssistant;
|
||||||
pub use crate::thread::{Message, RequestKind, Thread, ThreadEvent};
|
pub use crate::thread::{Message, RequestKind, Thread, ThreadEvent};
|
||||||
pub use crate::thread_store::ThreadStore;
|
pub use crate::thread_store::ThreadStore;
|
||||||
pub use assistant_diff::AssistantDiff;
|
pub use assistant_diff::{AssistantDiff, AssistantDiffToolbar};
|
||||||
|
|
||||||
actions!(
|
actions!(
|
||||||
assistant2,
|
assistant2,
|
||||||
|
@ -66,7 +66,9 @@ actions!(
|
||||||
AcceptSuggestedContext,
|
AcceptSuggestedContext,
|
||||||
OpenActiveThreadAsMarkdown,
|
OpenActiveThreadAsMarkdown,
|
||||||
ToggleKeep,
|
ToggleKeep,
|
||||||
Reject
|
Reject,
|
||||||
|
RejectAll,
|
||||||
|
KeepAll
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,10 @@ use editor::{
|
||||||
Direction, Editor, EditorEvent, MultiBuffer, ToPoint,
|
Direction, Editor, EditorEvent, MultiBuffer, ToPoint,
|
||||||
};
|
};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
prelude::*, AnyElement, AnyView, App, Entity, EventEmitter, FocusHandle, Focusable,
|
prelude::*, Action, AnyElement, AnyView, App, Entity, EventEmitter, FocusHandle, Focusable,
|
||||||
SharedString, Subscription, Task, WeakEntity, Window,
|
SharedString, Subscription, Task, WeakEntity, Window,
|
||||||
};
|
};
|
||||||
use language::{Capability, DiskState, OffsetRangeExt};
|
use language::{Capability, DiskState, OffsetRangeExt, Point};
|
||||||
use multi_buffer::PathKey;
|
use multi_buffer::PathKey;
|
||||||
use project::{Project, ProjectPath};
|
use project::{Project, ProjectPath};
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -18,11 +18,12 @@ use std::{
|
||||||
ops::Range,
|
ops::Range,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
use ui::{prelude::*, IconButtonShape, Tooltip};
|
use ui::{prelude::*, IconButtonShape, KeyBinding, Tooltip};
|
||||||
use workspace::{
|
use workspace::{
|
||||||
item::{BreadcrumbText, ItemEvent, TabContentParams},
|
item::{BreadcrumbText, ItemEvent, TabContentParams},
|
||||||
searchable::SearchableItemHandle,
|
searchable::SearchableItemHandle,
|
||||||
Item, ItemHandle, ItemNavHistory, ToolbarItemLocation, Workspace,
|
Item, ItemHandle, ItemNavHistory, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView,
|
||||||
|
Workspace,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct AssistantDiff {
|
pub struct AssistantDiff {
|
||||||
|
@ -78,6 +79,7 @@ impl AssistantDiff {
|
||||||
is_created_file,
|
is_created_file,
|
||||||
line_height,
|
line_height,
|
||||||
_editor: &Entity<Editor>,
|
_editor: &Entity<Editor>,
|
||||||
|
window: &mut Window,
|
||||||
cx: &mut App| {
|
cx: &mut App| {
|
||||||
render_diff_hunk_controls(
|
render_diff_hunk_controls(
|
||||||
row,
|
row,
|
||||||
|
@ -86,6 +88,7 @@ impl AssistantDiff {
|
||||||
is_created_file,
|
is_created_file,
|
||||||
line_height,
|
line_height,
|
||||||
&assistant_diff,
|
&assistant_diff,
|
||||||
|
window,
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -253,6 +256,18 @@ impl AssistantDiff {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reject_all(&mut self, _: &crate::RejectAll, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
self.editor.update(cx, |editor, cx| {
|
||||||
|
let max_point = editor.buffer().read(cx).read(cx).max_point();
|
||||||
|
editor.restore_hunks_in_ranges(vec![Point::zero()..max_point], window, cx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keep_all(&mut self, _: &crate::KeepAll, _window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
self.thread
|
||||||
|
.update(cx, |thread, cx| thread.keep_all_edits(cx));
|
||||||
|
}
|
||||||
|
|
||||||
fn review_diff_hunks(
|
fn review_diff_hunks(
|
||||||
&mut self,
|
&mut self,
|
||||||
hunk_ranges: Vec<Range<editor::Anchor>>,
|
hunk_ranges: Vec<Range<editor::Anchor>>,
|
||||||
|
@ -465,6 +480,8 @@ impl Render for AssistantDiff {
|
||||||
})
|
})
|
||||||
.on_action(cx.listener(Self::toggle_keep))
|
.on_action(cx.listener(Self::toggle_keep))
|
||||||
.on_action(cx.listener(Self::reject))
|
.on_action(cx.listener(Self::reject))
|
||||||
|
.on_action(cx.listener(Self::reject_all))
|
||||||
|
.on_action(cx.listener(Self::keep_all))
|
||||||
.bg(cx.theme().colors().editor_background)
|
.bg(cx.theme().colors().editor_background)
|
||||||
.flex()
|
.flex()
|
||||||
.items_center()
|
.items_center()
|
||||||
|
@ -482,6 +499,7 @@ fn render_diff_hunk_controls(
|
||||||
is_created_file: bool,
|
is_created_file: bool,
|
||||||
line_height: Pixels,
|
line_height: Pixels,
|
||||||
assistant_diff: &Entity<AssistantDiff>,
|
assistant_diff: &Entity<AssistantDiff>,
|
||||||
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> AnyElement {
|
) -> AnyElement {
|
||||||
let editor = assistant_diff.read(cx).editor.clone();
|
let editor = assistant_diff.read(cx).editor.clone();
|
||||||
|
@ -501,7 +519,43 @@ fn render_diff_hunk_controls(
|
||||||
.shadow_md()
|
.shadow_md()
|
||||||
.children(if status.has_secondary_hunk() {
|
.children(if status.has_secondary_hunk() {
|
||||||
vec![
|
vec![
|
||||||
|
Button::new("reject", "Reject")
|
||||||
|
.key_binding(KeyBinding::for_action_in(
|
||||||
|
&crate::Reject,
|
||||||
|
&editor.read(cx).focus_handle(cx),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
))
|
||||||
|
.tooltip({
|
||||||
|
let focus_handle = editor.focus_handle(cx);
|
||||||
|
move |window, cx| {
|
||||||
|
Tooltip::for_action_in(
|
||||||
|
"Reject Hunk",
|
||||||
|
&crate::Reject,
|
||||||
|
&focus_handle,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on_click({
|
||||||
|
let editor = editor.clone();
|
||||||
|
move |_event, window, cx| {
|
||||||
|
editor.update(cx, |editor, cx| {
|
||||||
|
let snapshot = editor.snapshot(window, cx);
|
||||||
|
let point = hunk_range.start.to_point(&snapshot.buffer_snapshot);
|
||||||
|
editor.restore_hunks_in_ranges(vec![point..point], window, cx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.disabled(is_created_file),
|
||||||
Button::new(("keep", row as u64), "Keep")
|
Button::new(("keep", row as u64), "Keep")
|
||||||
|
.key_binding(KeyBinding::for_action_in(
|
||||||
|
&crate::ToggleKeep,
|
||||||
|
&editor.read(cx).focus_handle(cx),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
))
|
||||||
.tooltip({
|
.tooltip({
|
||||||
let focus_handle = editor.focus_handle(cx);
|
let focus_handle = editor.focus_handle(cx);
|
||||||
move |window, cx| {
|
move |window, cx| {
|
||||||
|
@ -526,30 +580,6 @@ fn render_diff_hunk_controls(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
Button::new("reject", "Reject")
|
|
||||||
.tooltip({
|
|
||||||
let focus_handle = editor.focus_handle(cx);
|
|
||||||
move |window, cx| {
|
|
||||||
Tooltip::for_action_in(
|
|
||||||
"Reject Hunk",
|
|
||||||
&crate::Reject,
|
|
||||||
&focus_handle,
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.on_click({
|
|
||||||
let editor = editor.clone();
|
|
||||||
move |_event, window, cx| {
|
|
||||||
editor.update(cx, |editor, cx| {
|
|
||||||
let snapshot = editor.snapshot(window, cx);
|
|
||||||
let point = hunk_range.start.to_point(&snapshot.buffer_snapshot);
|
|
||||||
editor.restore_hunks_in_ranges(vec![point..point], window, cx);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.disabled(is_created_file),
|
|
||||||
]
|
]
|
||||||
} else {
|
} else {
|
||||||
vec![Button::new(("review", row as u64), "Review")
|
vec![Button::new(("review", row as u64), "Review")
|
||||||
|
@ -663,3 +693,89 @@ impl editor::Addon for AssistantDiffAddon {
|
||||||
key_context.add("assistant_diff");
|
key_context.add("assistant_diff");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct AssistantDiffToolbar {
|
||||||
|
assistant_diff: Option<WeakEntity<AssistantDiff>>,
|
||||||
|
_workspace: WeakEntity<Workspace>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AssistantDiffToolbar {
|
||||||
|
pub fn new(workspace: &Workspace, _: &mut Context<Self>) -> Self {
|
||||||
|
Self {
|
||||||
|
assistant_diff: None,
|
||||||
|
_workspace: workspace.weak_handle(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assistant_diff(&self, _: &App) -> Option<Entity<AssistantDiff>> {
|
||||||
|
self.assistant_diff.as_ref()?.upgrade()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dispatch_action(&self, action: &dyn Action, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
if let Some(assistant_diff) = self.assistant_diff(cx) {
|
||||||
|
assistant_diff.focus_handle(cx).focus(window);
|
||||||
|
}
|
||||||
|
let action = action.boxed_clone();
|
||||||
|
cx.defer(move |cx| {
|
||||||
|
cx.dispatch_action(action.as_ref());
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventEmitter<ToolbarItemEvent> for AssistantDiffToolbar {}
|
||||||
|
|
||||||
|
impl ToolbarItemView for AssistantDiffToolbar {
|
||||||
|
fn set_active_pane_item(
|
||||||
|
&mut self,
|
||||||
|
active_pane_item: Option<&dyn ItemHandle>,
|
||||||
|
_: &mut Window,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) -> ToolbarItemLocation {
|
||||||
|
self.assistant_diff = active_pane_item
|
||||||
|
.and_then(|item| item.act_as::<AssistantDiff>(cx))
|
||||||
|
.map(|entity| entity.downgrade());
|
||||||
|
if self.assistant_diff.is_some() {
|
||||||
|
ToolbarItemLocation::PrimaryRight
|
||||||
|
} else {
|
||||||
|
ToolbarItemLocation::Hidden
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pane_focus_update(
|
||||||
|
&mut self,
|
||||||
|
_pane_focused: bool,
|
||||||
|
_window: &mut Window,
|
||||||
|
_cx: &mut Context<Self>,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for AssistantDiffToolbar {
|
||||||
|
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
|
if self.assistant_diff(cx).is_none() {
|
||||||
|
return div();
|
||||||
|
}
|
||||||
|
|
||||||
|
h_group_xl()
|
||||||
|
.my_neg_1()
|
||||||
|
.items_center()
|
||||||
|
.py_1()
|
||||||
|
.pl_2()
|
||||||
|
.pr_1()
|
||||||
|
.flex_wrap()
|
||||||
|
.justify_between()
|
||||||
|
.child(
|
||||||
|
h_group_sm()
|
||||||
|
.child(
|
||||||
|
Button::new("reject-all", "Reject All").on_click(cx.listener(
|
||||||
|
|this, _, window, cx| {
|
||||||
|
this.dispatch_action(&crate::RejectAll, window, cx)
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.child(Button::new("keep-all", "Keep All").on_click(cx.listener(
|
||||||
|
|this, _, window, cx| this.dispatch_action(&crate::KeepAll, window, cx),
|
||||||
|
))),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1542,6 +1542,13 @@ impl Thread {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Keeps all edits across all buffers at once.
|
||||||
|
/// This provides a more performant alternative to calling review_edits_in_range for each buffer.
|
||||||
|
pub fn keep_all_edits(&mut self, cx: &mut Context<Self>) {
|
||||||
|
self.action_log
|
||||||
|
.update(cx, |action_log, _cx| action_log.keep_all_edits());
|
||||||
|
}
|
||||||
|
|
||||||
pub fn action_log(&self) -> &Entity<ActionLog> {
|
pub fn action_log(&self) -> &Entity<ActionLog> {
|
||||||
&self.action_log
|
&self.action_log
|
||||||
}
|
}
|
||||||
|
|
|
@ -353,6 +353,28 @@ impl ActionLog {
|
||||||
tracked_buffer.schedule_diff_update();
|
tracked_buffer.schedule_diff_update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Keep all edits across all buffers.
|
||||||
|
/// This is a more performant alternative to calling review_edits_in_range for each buffer.
|
||||||
|
pub fn keep_all_edits(&mut self) {
|
||||||
|
// Process all tracked buffers
|
||||||
|
for (_, tracked_buffer) in self.tracked_buffers.iter_mut() {
|
||||||
|
match &mut tracked_buffer.change {
|
||||||
|
Change::Deleted { reviewed, .. } => {
|
||||||
|
*reviewed = true;
|
||||||
|
}
|
||||||
|
Change::Edited {
|
||||||
|
unreviewed_edit_ids,
|
||||||
|
accepted_edit_ids,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
accepted_edit_ids.extend(unreviewed_edit_ids.drain());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tracked_buffer.schedule_diff_update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the set of buffers that contain changes that haven't been reviewed by the user.
|
/// Returns the set of buffers that contain changes that haven't been reviewed by the user.
|
||||||
pub fn changed_buffers(&self, cx: &App) -> BTreeMap<Entity<Buffer>, ChangedBuffer> {
|
pub fn changed_buffers(&self, cx: &App) -> BTreeMap<Entity<Buffer>, ChangedBuffer> {
|
||||||
self.tracked_buffers
|
self.tracked_buffers
|
||||||
|
|
|
@ -228,6 +228,7 @@ pub type RenderDiffHunkControlsFn = Arc<
|
||||||
bool,
|
bool,
|
||||||
Pixels,
|
Pixels,
|
||||||
&Entity<Editor>,
|
&Entity<Editor>,
|
||||||
|
&mut Window,
|
||||||
&mut App,
|
&mut App,
|
||||||
) -> AnyElement,
|
) -> AnyElement,
|
||||||
>;
|
>;
|
||||||
|
@ -20011,6 +20012,7 @@ fn render_diff_hunk_controls(
|
||||||
is_created_file: bool,
|
is_created_file: bool,
|
||||||
line_height: Pixels,
|
line_height: Pixels,
|
||||||
editor: &Entity<Editor>,
|
editor: &Entity<Editor>,
|
||||||
|
_window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> AnyElement {
|
) -> AnyElement {
|
||||||
h_flex()
|
h_flex()
|
||||||
|
|
|
@ -4013,6 +4013,7 @@ impl EditorElement {
|
||||||
*is_created_file,
|
*is_created_file,
|
||||||
line_height,
|
line_height,
|
||||||
&editor,
|
&editor,
|
||||||
|
window,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
let size =
|
let size =
|
||||||
|
|
|
@ -11,6 +11,7 @@ pub(crate) mod windows_only_instance;
|
||||||
use anyhow::Context as _;
|
use anyhow::Context as _;
|
||||||
pub use app_menus::*;
|
pub use app_menus::*;
|
||||||
use assets::Assets;
|
use assets::Assets;
|
||||||
|
use assistant2::AssistantDiffToolbar;
|
||||||
use assistant_context_editor::AssistantPanelDelegate;
|
use assistant_context_editor::AssistantPanelDelegate;
|
||||||
use breadcrumbs::Breadcrumbs;
|
use breadcrumbs::Breadcrumbs;
|
||||||
use client::{zed_urls, ZED_URL_SCHEME};
|
use client::{zed_urls, ZED_URL_SCHEME};
|
||||||
|
@ -939,6 +940,8 @@ fn initialize_pane(
|
||||||
toolbar.add_item(migration_banner, window, cx);
|
toolbar.add_item(migration_banner, window, cx);
|
||||||
let project_diff_toolbar = cx.new(|cx| ProjectDiffToolbar::new(workspace, cx));
|
let project_diff_toolbar = cx.new(|cx| ProjectDiffToolbar::new(workspace, cx));
|
||||||
toolbar.add_item(project_diff_toolbar, window, cx);
|
toolbar.add_item(project_diff_toolbar, window, cx);
|
||||||
|
let assistant_diff_toolbar = cx.new(|cx| AssistantDiffToolbar::new(workspace, cx));
|
||||||
|
toolbar.add_item(assistant_diff_toolbar, window, cx);
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue