Update selection when keeping/rejecting hunks (#27902)

Release Notes:

- N/A
This commit is contained in:
Antonio Scandurra 2025-04-02 10:38:26 +02:00 committed by GitHub
parent 57d7bc23ae
commit c1d6dfd832
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 367 additions and 114 deletions

View file

@ -138,7 +138,7 @@
}
},
{
"context": "Editor && !assistant_diff",
"context": "Editor && !agent_diff",
"bindings": {
"ctrl-k ctrl-r": "git::Restore",
"ctrl-alt-y": "git::ToggleStaged",
@ -147,7 +147,7 @@
}
},
{
"context": "AssistantDiff",
"context": "AgentDiff",
"bindings": {
"ctrl-y": "agent::Keep",
"ctrl-k ctrl-r": "agent::Reject"
@ -645,7 +645,7 @@
"bindings": {
"enter": "agent::Chat",
"ctrl-i": "agent::ToggleProfileSelector",
"shift-ctrl-r": "agent::OpenAssistantDiff"
"shift-ctrl-r": "agent::OpenAgentDiff"
}
},
{

View file

@ -228,7 +228,7 @@
}
},
{
"context": "Editor && !assistant_diff",
"context": "Editor && !agent_diff",
"use_key_equivalents": true,
"bindings": {
"cmd-alt-z": "git::Restore",
@ -238,7 +238,7 @@
}
},
{
"context": "AssistantDiff",
"context": "AgentDiff",
"use_key_equivalents": true,
"bindings": {
"cmd-y": "agent::Keep",
@ -305,7 +305,7 @@
"bindings": {
"enter": "agent::Chat",
"cmd-i": "agent::ToggleProfileSelector",
"shift-ctrl-r": "agent::OpenAssistantDiff"
"shift-ctrl-r": "agent::OpenAgentDiff"
}
},
{

View file

@ -3,8 +3,9 @@ use anyhow::Result;
use buffer_diff::DiffHunkStatus;
use collections::HashSet;
use editor::{
Direction, Editor, EditorEvent, MultiBuffer, ToPoint,
AnchorRangeExt, Direction, Editor, EditorEvent, MultiBuffer, ToPoint,
actions::{GoToHunk, GoToPreviousHunk},
scroll::Autoscroll,
};
use gpui::{
Action, AnyElement, AnyView, App, Entity, EventEmitter, FocusHandle, Focusable, SharedString,
@ -26,7 +27,7 @@ use workspace::{
searchable::SearchableItemHandle,
};
pub struct AssistantDiff {
pub struct AgentDiff {
multibuffer: Entity<MultiBuffer>,
editor: Entity<Editor>,
thread: Entity<Thread>,
@ -36,7 +37,7 @@ pub struct AssistantDiff {
_subscriptions: Vec<Subscription>,
}
impl AssistantDiff {
impl AgentDiff {
pub fn deploy(
thread: Entity<Thread>,
workspace: WeakEntity<Workspace>,
@ -45,7 +46,7 @@ impl AssistantDiff {
) -> Result<()> {
let existing_diff = workspace.update(cx, |workspace, cx| {
workspace
.items_of_type::<AssistantDiff>(cx)
.items_of_type::<AgentDiff>(cx)
.find(|diff| diff.read(cx).thread == thread)
})?;
if let Some(existing_diff) = existing_diff {
@ -53,10 +54,10 @@ impl AssistantDiff {
workspace.activate_item(&existing_diff, true, true, window, cx);
})
} else {
let assistant_diff =
cx.new(|cx| AssistantDiff::new(thread.clone(), workspace.clone(), window, cx));
let agent_diff =
cx.new(|cx| AgentDiff::new(thread.clone(), workspace.clone(), window, cx));
workspace.update(cx, |workspace, cx| {
workspace.add_item_to_center(Box::new(assistant_diff), window, cx);
workspace.add_item_to_center(Box::new(agent_diff), window, cx);
})
}
}
@ -72,7 +73,7 @@ impl AssistantDiff {
let project = thread.read(cx).project().clone();
let render_diff_hunk_controls = Arc::new({
let assistant_diff = cx.entity();
let agent_diff = cx.entity();
move |row,
status: &DiffHunkStatus,
hunk_range,
@ -87,7 +88,7 @@ impl AssistantDiff {
hunk_range,
is_created_file,
line_height,
&assistant_diff,
&agent_diff,
editor,
window,
cx,
@ -100,7 +101,7 @@ impl AssistantDiff {
editor.disable_inline_diagnostics();
editor.set_expand_all_diff_hunks(cx);
editor.set_render_diff_hunk_controls(render_diff_hunk_controls, cx);
editor.register_addon(AssistantDiffAddon);
editor.register_addon(AgentDiffAddon);
editor
});
@ -141,6 +142,7 @@ impl AssistantDiff {
let snapshot = buffer.read(cx).snapshot();
let diff = diff_handle.read(cx);
let diff_hunk_ranges = diff
.hunks_intersecting_range(
language::Anchor::MIN..language::Anchor::MAX,
@ -166,9 +168,25 @@ impl AssistantDiff {
self.editor.update(cx, |editor, cx| {
if was_empty {
editor.change_selections(None, window, cx, |selections| {
selections.select_ranges([0..0])
});
let first_hunk = editor
.diff_hunks_in_ranges(
&[editor::Anchor::min()..editor::Anchor::max()],
&self.multibuffer.read(cx).read(cx),
)
.next();
if let Some(first_hunk) = first_hunk {
let first_hunk_start = first_hunk.multi_buffer_range().start;
editor.change_selections(
Some(Autoscroll::fit()),
window,
cx,
|selections| {
selections
.select_anchor_ranges([first_hunk_start..first_hunk_start]);
},
)
}
}
if is_excerpt_newly_added
@ -222,45 +240,32 @@ impl AssistantDiff {
}
}
fn keep(&mut self, _: &crate::Keep, _window: &mut Window, cx: &mut Context<Self>) {
fn keep(&mut self, _: &crate::Keep, window: &mut Window, cx: &mut Context<Self>) {
let ranges = self
.editor
.read(cx)
.selections
.disjoint_anchor_ranges()
.collect::<Vec<_>>();
let snapshot = self.multibuffer.read(cx).snapshot(cx);
let diff_hunks_in_ranges = self
.editor
.read(cx)
.diff_hunks_in_ranges(&ranges, &snapshot)
.collect::<Vec<_>>();
for hunk in diff_hunks_in_ranges {
let buffer = self.multibuffer.read(cx).buffer(hunk.buffer_id);
if let Some(buffer) = buffer {
self.thread.update(cx, |thread, cx| {
thread.keep_edits_in_range(buffer, hunk.buffer_range, cx)
});
}
}
self.keep_edits_in_ranges(ranges, window, cx);
}
fn reject(&mut self, _: &crate::Reject, window: &mut Window, cx: &mut Context<Self>) {
let ranges = self
.editor
.update(cx, |editor, cx| editor.selections.ranges(cx));
self.editor.update(cx, |editor, cx| {
editor.restore_hunks_in_ranges(ranges, window, cx)
})
.read(cx)
.selections
.disjoint_anchor_ranges()
.collect::<Vec<_>>();
self.reject_edits_in_ranges(ranges, window, cx);
}
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)
})
self.reject_edits_in_ranges(
vec![editor::Anchor::min()..editor::Anchor::max()],
window,
cx,
);
}
fn keep_all(&mut self, _: &crate::KeepAll, _window: &mut Window, cx: &mut Context<Self>) {
@ -270,30 +275,110 @@ impl AssistantDiff {
fn keep_edits_in_ranges(
&mut self,
hunk_ranges: Vec<Range<editor::Anchor>>,
ranges: Vec<Range<editor::Anchor>>,
window: &mut Window,
cx: &mut Context<Self>,
) {
let snapshot = self.multibuffer.read(cx).snapshot(cx);
let diff_hunks_in_ranges = self
.editor
.read(cx)
.diff_hunks_in_ranges(&hunk_ranges, &snapshot)
.diff_hunks_in_ranges(&ranges, &snapshot)
.collect::<Vec<_>>();
let newest_cursor = self.editor.update(cx, |editor, cx| {
editor.selections.newest::<Point>(cx).head()
});
if diff_hunks_in_ranges.iter().any(|hunk| {
hunk.row_range
.contains(&multi_buffer::MultiBufferRow(newest_cursor.row))
}) {
self.update_selection(&diff_hunks_in_ranges, window, cx);
}
for hunk in diff_hunks_in_ranges {
for hunk in &diff_hunks_in_ranges {
let buffer = self.multibuffer.read(cx).buffer(hunk.buffer_id);
if let Some(buffer) = buffer {
self.thread.update(cx, |thread, cx| {
thread.keep_edits_in_range(buffer, hunk.buffer_range, cx)
thread.keep_edits_in_range(buffer, hunk.buffer_range.clone(), cx)
});
}
}
}
fn reject_edits_in_ranges(
&mut self,
ranges: Vec<Range<editor::Anchor>>,
window: &mut Window,
cx: &mut Context<Self>,
) {
let snapshot = self.multibuffer.read(cx).snapshot(cx);
let diff_hunks_in_ranges = self
.editor
.read(cx)
.diff_hunks_in_ranges(&ranges, &snapshot)
.collect::<Vec<_>>();
let newest_cursor = self.editor.update(cx, |editor, cx| {
editor.selections.newest::<Point>(cx).head()
});
if diff_hunks_in_ranges.iter().any(|hunk| {
hunk.row_range
.contains(&multi_buffer::MultiBufferRow(newest_cursor.row))
}) {
self.update_selection(&diff_hunks_in_ranges, window, cx);
}
let point_ranges = ranges
.into_iter()
.map(|range| range.to_point(&snapshot))
.collect();
self.editor.update(cx, |editor, cx| {
editor.restore_hunks_in_ranges(point_ranges, window, cx)
});
}
fn update_selection(
&mut self,
diff_hunks: &[multi_buffer::MultiBufferDiffHunk],
window: &mut Window,
cx: &mut Context<Self>,
) {
let snapshot = self.multibuffer.read(cx).snapshot(cx);
let target_hunk = diff_hunks
.last()
.and_then(|last_kept_hunk| {
let last_kept_hunk_end = last_kept_hunk.multi_buffer_range().end;
self.editor
.read(cx)
.diff_hunks_in_ranges(&[last_kept_hunk_end..editor::Anchor::max()], &snapshot)
.skip(1)
.next()
})
.or_else(|| {
let first_kept_hunk = diff_hunks.first()?;
let first_kept_hunk_start = first_kept_hunk.multi_buffer_range().start;
self.editor
.read(cx)
.diff_hunks_in_ranges(
&[editor::Anchor::min()..first_kept_hunk_start],
&snapshot,
)
.next()
});
if let Some(target_hunk) = target_hunk {
self.editor.update(cx, |editor, cx| {
editor.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
let next_hunk_start = target_hunk.multi_buffer_range().start;
selections.select_anchor_ranges([next_hunk_start..next_hunk_start]);
})
});
}
}
}
impl EventEmitter<EditorEvent> for AssistantDiff {}
impl EventEmitter<EditorEvent> for AgentDiff {}
impl Focusable for AssistantDiff {
impl Focusable for AgentDiff {
fn focus_handle(&self, cx: &App) -> FocusHandle {
if self.multibuffer.read(cx).is_empty() {
self.focus_handle.clone()
@ -303,7 +388,7 @@ impl Focusable for AssistantDiff {
}
}
impl Item for AssistantDiff {
impl Item for AgentDiff {
type Event = EditorEvent;
fn tab_icon(&self, _window: &Window, _cx: &App) -> Option<Icon> {
@ -330,7 +415,7 @@ impl Item for AssistantDiff {
}
fn tab_tooltip_text(&self, _: &App) -> Option<SharedString> {
Some("Assistant Diff".into())
Some("Agent Diff".into())
}
fn tab_content(&self, params: TabContentParams, _window: &Window, cx: &App) -> AnyElement {
@ -467,17 +552,13 @@ impl Item for AssistantDiff {
}
}
impl Render for AssistantDiff {
impl Render for AgentDiff {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let is_empty = self.multibuffer.read(cx).is_empty();
div()
.track_focus(&self.focus_handle)
.key_context(if is_empty {
"EmptyPane"
} else {
"AssistantDiff"
})
.key_context(if is_empty { "EmptyPane" } else { "AgentDiff" })
.on_action(cx.listener(Self::keep))
.on_action(cx.listener(Self::reject))
.on_action(cx.listener(Self::reject_all))
@ -498,7 +579,7 @@ fn render_diff_hunk_controls(
hunk_range: Range<editor::Anchor>,
is_created_file: bool,
line_height: Pixels,
assistant_diff: &Entity<AssistantDiff>,
agent_diff: &Entity<AgentDiff>,
editor: &Entity<Editor>,
window: &mut Window,
cx: &mut App,
@ -519,7 +600,7 @@ fn render_diff_hunk_controls(
.occlude()
.shadow_md()
.children(vec![
Button::new("reject", "Reject")
Button::new(("reject", row as u64), "Reject")
.disabled(is_created_file)
.key_binding(
KeyBinding::for_action_in(
@ -531,12 +612,14 @@ fn render_diff_hunk_controls(
.map(|kb| kb.size(rems_from_px(12.))),
)
.on_click({
let editor = editor.clone();
let agent_diff = agent_diff.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);
agent_diff.update(cx, |diff, cx| {
diff.reject_edits_in_ranges(
vec![hunk_range.start..hunk_range.start],
window,
cx,
);
});
}
}),
@ -551,10 +634,14 @@ fn render_diff_hunk_controls(
.map(|kb| kb.size(rems_from_px(12.))),
)
.on_click({
let assistant_diff = assistant_diff.clone();
move |_event, _window, cx| {
assistant_diff.update(cx, |diff, cx| {
diff.keep_edits_in_ranges(vec![hunk_range.start..hunk_range.start], cx);
let agent_diff = agent_diff.clone();
move |_event, window, cx| {
agent_diff.update(cx, |diff, cx| {
diff.keep_edits_in_ranges(
vec![hunk_range.start..hunk_range.start],
window,
cx,
);
});
}
}),
@ -639,38 +726,38 @@ fn render_diff_hunk_controls(
.into_any_element()
}
struct AssistantDiffAddon;
struct AgentDiffAddon;
impl editor::Addon for AssistantDiffAddon {
impl editor::Addon for AgentDiffAddon {
fn to_any(&self) -> &dyn std::any::Any {
self
}
fn extend_key_context(&self, key_context: &mut gpui::KeyContext, _: &App) {
key_context.add("assistant_diff");
key_context.add("agent_diff");
}
}
pub struct AssistantDiffToolbar {
assistant_diff: Option<WeakEntity<AssistantDiff>>,
pub struct AgentDiffToolbar {
agent_diff: Option<WeakEntity<AgentDiff>>,
_workspace: WeakEntity<Workspace>,
}
impl AssistantDiffToolbar {
impl AgentDiffToolbar {
pub fn new(workspace: &Workspace, _: &mut Context<Self>) -> Self {
Self {
assistant_diff: None,
agent_diff: None,
_workspace: workspace.weak_handle(),
}
}
fn assistant_diff(&self, _: &App) -> Option<Entity<AssistantDiff>> {
self.assistant_diff.as_ref()?.upgrade()
fn agent_diff(&self, _: &App) -> Option<Entity<AgentDiff>> {
self.agent_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);
if let Some(agent_diff) = self.agent_diff(cx) {
agent_diff.focus_handle(cx).focus(window);
}
let action = action.boxed_clone();
cx.defer(move |cx| {
@ -679,19 +766,19 @@ impl AssistantDiffToolbar {
}
}
impl EventEmitter<ToolbarItemEvent> for AssistantDiffToolbar {}
impl EventEmitter<ToolbarItemEvent> for AgentDiffToolbar {}
impl ToolbarItemView for AssistantDiffToolbar {
impl ToolbarItemView for AgentDiffToolbar {
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))
self.agent_diff = active_pane_item
.and_then(|item| item.act_as::<AgentDiff>(cx))
.map(|entity| entity.downgrade());
if self.assistant_diff.is_some() {
if self.agent_diff.is_some() {
ToolbarItemLocation::PrimaryRight
} else {
ToolbarItemLocation::Hidden
@ -707,14 +794,14 @@ impl ToolbarItemView for AssistantDiffToolbar {
}
}
impl Render for AssistantDiffToolbar {
impl Render for AgentDiffToolbar {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let assistant_diff = match self.assistant_diff(cx) {
let agent_diff = match self.agent_diff(cx) {
Some(ad) => ad,
None => return div(),
};
let is_empty = assistant_diff.read(cx).multibuffer.read(cx).is_empty();
let is_empty = agent_diff.read(cx).multibuffer.read(cx).is_empty();
if is_empty {
return div();
@ -741,3 +828,161 @@ impl Render for AssistantDiffToolbar {
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{ThreadStore, thread_store};
use assistant_settings::AssistantSettings;
use context_server::ContextServerSettings;
use editor::EditorSettings;
use gpui::TestAppContext;
use project::{FakeFs, Project};
use prompt_store::PromptBuilder;
use serde_json::json;
use settings::{Settings, SettingsStore};
use std::sync::Arc;
use theme::ThemeSettings;
use util::path;
#[gpui::test]
async fn test_agent_diff(cx: &mut TestAppContext) {
cx.update(|cx| {
let settings_store = SettingsStore::test(cx);
cx.set_global(settings_store);
language::init(cx);
Project::init_settings(cx);
AssistantSettings::register(cx);
thread_store::init(cx);
workspace::init_settings(cx);
ThemeSettings::register(cx);
ContextServerSettings::register(cx);
EditorSettings::register(cx);
});
let fs = FakeFs::new(cx.executor());
fs.insert_tree(
path!("/test"),
json!({"file1": "abc\ndef\nghi\njkl\nmno\npqr\nstu\nvwx\nyz"}),
)
.await;
let project = Project::test(fs, [path!("/test").as_ref()], cx).await;
let buffer_path = project
.read_with(cx, |project, cx| {
project.find_project_path("test/file1", cx)
})
.unwrap();
let thread_store = cx.update(|cx| {
ThreadStore::new(
project.clone(),
Arc::default(),
Arc::new(PromptBuilder::new(None).unwrap()),
cx,
)
.unwrap()
});
let thread = thread_store.update(cx, |store, cx| store.create_thread(cx));
let action_log = thread.read_with(cx, |thread, _| thread.action_log().clone());
let (workspace, cx) =
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
let agent_diff = cx.new_window_entity(|window, cx| {
AgentDiff::new(thread.clone(), workspace.downgrade(), window, cx)
});
let editor = agent_diff.read_with(cx, |diff, _cx| diff.editor.clone());
let buffer = project
.update(cx, |project, cx| project.open_buffer(buffer_path, cx))
.await
.unwrap();
cx.update(|_, cx| {
action_log.update(cx, |log, cx| log.buffer_read(buffer.clone(), cx));
buffer.update(cx, |buffer, cx| {
buffer
.edit(
[
(Point::new(1, 1)..Point::new(1, 2), "E"),
(Point::new(3, 2)..Point::new(3, 3), "L"),
(Point::new(5, 0)..Point::new(5, 1), "P"),
(Point::new(7, 1)..Point::new(7, 2), "W"),
],
None,
cx,
)
.unwrap()
});
action_log.update(cx, |log, cx| log.buffer_edited(buffer.clone(), cx));
});
cx.run_until_parked();
// When opening the assistant diff, the cursor is positioned on the first hunk.
assert_eq!(
editor.read_with(cx, |editor, cx| editor.text(cx)),
"abc\ndef\ndEf\nghi\njkl\njkL\nmno\npqr\nPqr\nstu\nvwx\nvWx\nyz"
);
assert_eq!(
editor
.update(cx, |editor, cx| editor.selections.newest::<Point>(cx))
.range(),
Point::new(1, 0)..Point::new(1, 0)
);
// After keeping a hunk, the cursor should be positioned on the second hunk.
agent_diff.update_in(cx, |diff, window, cx| diff.keep(&crate::Keep, window, cx));
cx.run_until_parked();
assert_eq!(
editor.read_with(cx, |editor, cx| editor.text(cx)),
"abc\ndEf\nghi\njkl\njkL\nmno\npqr\nPqr\nstu\nvwx\nvWx\nyz"
);
assert_eq!(
editor
.update(cx, |editor, cx| editor.selections.newest::<Point>(cx))
.range(),
Point::new(3, 0)..Point::new(3, 0)
);
// Restoring a hunk also moves the cursor to the next hunk, possibly cycling if it's at the end.
editor.update_in(cx, |editor, window, cx| {
editor.change_selections(None, window, cx, |selections| {
selections.select_ranges([Point::new(10, 0)..Point::new(10, 0)])
});
});
agent_diff.update_in(cx, |diff, window, cx| {
diff.reject(&crate::Reject, window, cx)
});
cx.run_until_parked();
assert_eq!(
editor.read_with(cx, |editor, cx| editor.text(cx)),
"abc\ndEf\nghi\njkl\njkL\nmno\npqr\nPqr\nstu\nvwx\nyz"
);
assert_eq!(
editor
.update(cx, |editor, cx| editor.selections.newest::<Point>(cx))
.range(),
Point::new(3, 0)..Point::new(3, 0)
);
// Keeping a range that doesn't intersect the current selection doesn't move it.
agent_diff.update_in(cx, |diff, window, cx| {
let position = editor
.read(cx)
.buffer()
.read(cx)
.read(cx)
.anchor_before(Point::new(7, 0));
diff.keep_edits_in_ranges(vec![position..position], window, cx)
});
cx.run_until_parked();
assert_eq!(
editor.read_with(cx, |editor, cx| editor.text(cx)),
"abc\ndEf\nghi\njkl\njkL\nmno\nPqr\nstu\nvwx\nyz"
);
assert_eq!(
editor
.update(cx, |editor, cx| editor.selections.newest::<Point>(cx))
.range(),
Point::new(3, 0)..Point::new(3, 0)
);
}
}

View file

@ -1,6 +1,6 @@
mod active_thread;
mod agent_diff;
mod assistant_configuration;
mod assistant_diff;
mod assistant_model_selector;
mod assistant_panel;
mod buffer_codegen;
@ -41,7 +41,7 @@ pub use crate::assistant_panel::{AssistantPanel, ConcreteAssistantPanelDelegate}
pub use crate::inline_assistant::InlineAssistant;
pub use crate::thread::{Message, RequestKind, Thread, ThreadEvent};
pub use crate::thread_store::ThreadStore;
pub use assistant_diff::{AssistantDiff, AssistantDiffToolbar};
pub use agent_diff::{AgentDiff, AgentDiffToolbar};
actions!(
agent,
@ -65,7 +65,7 @@ actions!(
RemoveFocusedContext,
AcceptSuggestedContext,
OpenActiveThreadAsMarkdown,
OpenAssistantDiff,
OpenAgentDiff,
Keep,
Reject,
RejectAll,

View file

@ -42,8 +42,8 @@ use crate::thread::{Thread, ThreadError, ThreadId};
use crate::thread_history::{PastContext, PastThread, ThreadHistory};
use crate::thread_store::ThreadStore;
use crate::{
AssistantDiff, InlineAssistant, NewPromptEditor, NewThread, OpenActiveThreadAsMarkdown,
OpenAssistantDiff, OpenConfiguration, OpenHistory, ToggleContextPicker,
AgentDiff, InlineAssistant, NewPromptEditor, NewThread, OpenActiveThreadAsMarkdown,
OpenAgentDiff, OpenConfiguration, OpenHistory, ToggleContextPicker,
};
action_with_deprecated_aliases!(
@ -94,11 +94,11 @@ pub fn init(cx: &mut App) {
panel.update(cx, |panel, cx| panel.open_configuration(window, cx));
}
})
.register_action(|workspace, _: &OpenAssistantDiff, window, cx| {
.register_action(|workspace, _: &OpenAgentDiff, window, cx| {
if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {
workspace.focus_panel::<AssistantPanel>(window, cx);
panel.update(cx, |panel, cx| {
panel.open_assistant_diff(&OpenAssistantDiff, window, cx);
panel.open_agent_diff(&OpenAgentDiff, window, cx);
});
}
});
@ -475,14 +475,14 @@ impl AssistantPanel {
})
}
pub fn open_assistant_diff(
pub fn open_agent_diff(
&mut self,
_: &OpenAssistantDiff,
_: &OpenAgentDiff,
window: &mut Window,
cx: &mut Context<Self>,
) {
let thread = self.thread.read(cx).thread().clone();
AssistantDiff::deploy(thread, self.workspace.clone(), window, cx).log_err();
AgentDiff::deploy(thread, self.workspace.clone(), window, cx).log_err();
}
pub(crate) fn open_configuration(&mut self, window: &mut Window, cx: &mut Context<Self>) {
@ -1330,7 +1330,7 @@ impl Render for AssistantPanel {
}))
.on_action(cx.listener(Self::open_active_thread_as_markdown))
.on_action(cx.listener(Self::deploy_prompt_library))
.on_action(cx.listener(Self::open_assistant_diff))
.on_action(cx.listener(Self::open_agent_diff))
.child(self.render_toolbar(window, cx))
.map(|parent| match self.active_view {
ActiveView::Thread => parent

View file

@ -31,7 +31,7 @@ use crate::profile_selector::ProfileSelector;
use crate::thread::{RequestKind, Thread};
use crate::thread_store::ThreadStore;
use crate::{
AssistantDiff, Chat, ChatMode, NewThread, OpenAssistantDiff, RemoveAllContext, ThreadEvent,
AgentDiff, Chat, ChatMode, NewThread, OpenAgentDiff, RemoveAllContext, ThreadEvent,
ToggleContextPicker, ToggleProfileSelector,
};
@ -318,7 +318,7 @@ impl MessageEditor {
}
fn handle_review_click(&self, window: &mut Window, cx: &mut Context<Self>) {
AssistantDiff::deploy(self.thread.clone(), self.workspace.clone(), window, cx).log_err();
AgentDiff::deploy(self.thread.clone(), self.workspace.clone(), window, cx).log_err();
}
}
@ -527,7 +527,7 @@ impl Render for MessageEditor {
.label_size(LabelSize::Small)
.key_binding(
KeyBinding::for_action_in(
&OpenAssistantDiff,
&OpenAgentDiff,
&focus_handle,
window,
cx,

View file

@ -225,15 +225,17 @@ impl Tool for FindReplaceFileTool {
return Err(err)
};
let snapshot = buffer.update(cx, |buffer, cx| {
buffer.finalize_last_transaction();
buffer.apply_diff(diff, cx);
buffer.finalize_last_transaction();
buffer.snapshot()
})?;
action_log.update(cx, |log, cx| {
log.buffer_edited(buffer.clone(), cx)
let snapshot = cx.update(|cx| {
let snapshot = buffer.update(cx, |buffer, cx| {
buffer.finalize_last_transaction();
buffer.apply_diff(diff, cx);
buffer.finalize_last_transaction();
buffer.snapshot()
});
action_log.update(cx, |log, cx| {
log.buffer_edited(buffer.clone(), cx)
});
snapshot
})?;
project.update( cx, |project, cx| {

View file

@ -163,6 +163,12 @@ impl MultiBufferDiffHunk {
self.diff_base_byte_range == (0..0)
&& self.buffer_range == (text::Anchor::MIN..text::Anchor::MAX)
}
pub fn multi_buffer_range(&self) -> Range<Anchor> {
let start = Anchor::in_buffer(self.excerpt_id, self.buffer_id, self.buffer_range.start);
let end = Anchor::in_buffer(self.excerpt_id, self.buffer_id, self.buffer_range.end);
start..end
}
}
#[derive(PartialEq, Eq, Ord, PartialOrd, Clone, Hash, Debug)]

View file

@ -8,7 +8,7 @@ mod quick_action_bar;
#[cfg(target_os = "windows")]
pub(crate) mod windows_only_instance;
use agent::AssistantDiffToolbar;
use agent::AgentDiffToolbar;
use anyhow::Context as _;
pub use app_menus::*;
use assets::Assets;
@ -938,8 +938,8 @@ fn initialize_pane(
toolbar.add_item(migration_banner, window, cx);
let project_diff_toolbar = cx.new(|cx| ProjectDiffToolbar::new(workspace, 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);
let agent_diff_toolbar = cx.new(|cx| AgentDiffToolbar::new(workspace, cx));
toolbar.add_item(agent_diff_toolbar, window, cx);
})
});
}