Update selection when keeping/rejecting hunks (#27902)
Release Notes: - N/A
This commit is contained in:
parent
57d7bc23ae
commit
c1d6dfd832
9 changed files with 367 additions and 114 deletions
|
@ -138,7 +138,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"context": "Editor && !assistant_diff",
|
"context": "Editor && !agent_diff",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"ctrl-k ctrl-r": "git::Restore",
|
"ctrl-k ctrl-r": "git::Restore",
|
||||||
"ctrl-alt-y": "git::ToggleStaged",
|
"ctrl-alt-y": "git::ToggleStaged",
|
||||||
|
@ -147,7 +147,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"context": "AssistantDiff",
|
"context": "AgentDiff",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"ctrl-y": "agent::Keep",
|
"ctrl-y": "agent::Keep",
|
||||||
"ctrl-k ctrl-r": "agent::Reject"
|
"ctrl-k ctrl-r": "agent::Reject"
|
||||||
|
@ -645,7 +645,7 @@
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"enter": "agent::Chat",
|
"enter": "agent::Chat",
|
||||||
"ctrl-i": "agent::ToggleProfileSelector",
|
"ctrl-i": "agent::ToggleProfileSelector",
|
||||||
"shift-ctrl-r": "agent::OpenAssistantDiff"
|
"shift-ctrl-r": "agent::OpenAgentDiff"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -228,7 +228,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"context": "Editor && !assistant_diff",
|
"context": "Editor && !agent_diff",
|
||||||
"use_key_equivalents": true,
|
"use_key_equivalents": true,
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"cmd-alt-z": "git::Restore",
|
"cmd-alt-z": "git::Restore",
|
||||||
|
@ -238,7 +238,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"context": "AssistantDiff",
|
"context": "AgentDiff",
|
||||||
"use_key_equivalents": true,
|
"use_key_equivalents": true,
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"cmd-y": "agent::Keep",
|
"cmd-y": "agent::Keep",
|
||||||
|
@ -305,7 +305,7 @@
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"enter": "agent::Chat",
|
"enter": "agent::Chat",
|
||||||
"cmd-i": "agent::ToggleProfileSelector",
|
"cmd-i": "agent::ToggleProfileSelector",
|
||||||
"shift-ctrl-r": "agent::OpenAssistantDiff"
|
"shift-ctrl-r": "agent::OpenAgentDiff"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,8 +3,9 @@ use anyhow::Result;
|
||||||
use buffer_diff::DiffHunkStatus;
|
use buffer_diff::DiffHunkStatus;
|
||||||
use collections::HashSet;
|
use collections::HashSet;
|
||||||
use editor::{
|
use editor::{
|
||||||
Direction, Editor, EditorEvent, MultiBuffer, ToPoint,
|
AnchorRangeExt, Direction, Editor, EditorEvent, MultiBuffer, ToPoint,
|
||||||
actions::{GoToHunk, GoToPreviousHunk},
|
actions::{GoToHunk, GoToPreviousHunk},
|
||||||
|
scroll::Autoscroll,
|
||||||
};
|
};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
Action, AnyElement, AnyView, App, Entity, EventEmitter, FocusHandle, Focusable, SharedString,
|
Action, AnyElement, AnyView, App, Entity, EventEmitter, FocusHandle, Focusable, SharedString,
|
||||||
|
@ -26,7 +27,7 @@ use workspace::{
|
||||||
searchable::SearchableItemHandle,
|
searchable::SearchableItemHandle,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct AssistantDiff {
|
pub struct AgentDiff {
|
||||||
multibuffer: Entity<MultiBuffer>,
|
multibuffer: Entity<MultiBuffer>,
|
||||||
editor: Entity<Editor>,
|
editor: Entity<Editor>,
|
||||||
thread: Entity<Thread>,
|
thread: Entity<Thread>,
|
||||||
|
@ -36,7 +37,7 @@ pub struct AssistantDiff {
|
||||||
_subscriptions: Vec<Subscription>,
|
_subscriptions: Vec<Subscription>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AssistantDiff {
|
impl AgentDiff {
|
||||||
pub fn deploy(
|
pub fn deploy(
|
||||||
thread: Entity<Thread>,
|
thread: Entity<Thread>,
|
||||||
workspace: WeakEntity<Workspace>,
|
workspace: WeakEntity<Workspace>,
|
||||||
|
@ -45,7 +46,7 @@ impl AssistantDiff {
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let existing_diff = workspace.update(cx, |workspace, cx| {
|
let existing_diff = workspace.update(cx, |workspace, cx| {
|
||||||
workspace
|
workspace
|
||||||
.items_of_type::<AssistantDiff>(cx)
|
.items_of_type::<AgentDiff>(cx)
|
||||||
.find(|diff| diff.read(cx).thread == thread)
|
.find(|diff| diff.read(cx).thread == thread)
|
||||||
})?;
|
})?;
|
||||||
if let Some(existing_diff) = existing_diff {
|
if let Some(existing_diff) = existing_diff {
|
||||||
|
@ -53,10 +54,10 @@ impl AssistantDiff {
|
||||||
workspace.activate_item(&existing_diff, true, true, window, cx);
|
workspace.activate_item(&existing_diff, true, true, window, cx);
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
let assistant_diff =
|
let agent_diff =
|
||||||
cx.new(|cx| AssistantDiff::new(thread.clone(), workspace.clone(), window, cx));
|
cx.new(|cx| AgentDiff::new(thread.clone(), workspace.clone(), window, cx));
|
||||||
workspace.update(cx, |workspace, 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 project = thread.read(cx).project().clone();
|
||||||
let render_diff_hunk_controls = Arc::new({
|
let render_diff_hunk_controls = Arc::new({
|
||||||
let assistant_diff = cx.entity();
|
let agent_diff = cx.entity();
|
||||||
move |row,
|
move |row,
|
||||||
status: &DiffHunkStatus,
|
status: &DiffHunkStatus,
|
||||||
hunk_range,
|
hunk_range,
|
||||||
|
@ -87,7 +88,7 @@ impl AssistantDiff {
|
||||||
hunk_range,
|
hunk_range,
|
||||||
is_created_file,
|
is_created_file,
|
||||||
line_height,
|
line_height,
|
||||||
&assistant_diff,
|
&agent_diff,
|
||||||
editor,
|
editor,
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
|
@ -100,7 +101,7 @@ impl AssistantDiff {
|
||||||
editor.disable_inline_diagnostics();
|
editor.disable_inline_diagnostics();
|
||||||
editor.set_expand_all_diff_hunks(cx);
|
editor.set_expand_all_diff_hunks(cx);
|
||||||
editor.set_render_diff_hunk_controls(render_diff_hunk_controls, cx);
|
editor.set_render_diff_hunk_controls(render_diff_hunk_controls, cx);
|
||||||
editor.register_addon(AssistantDiffAddon);
|
editor.register_addon(AgentDiffAddon);
|
||||||
editor
|
editor
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -141,6 +142,7 @@ impl AssistantDiff {
|
||||||
|
|
||||||
let snapshot = buffer.read(cx).snapshot();
|
let snapshot = buffer.read(cx).snapshot();
|
||||||
let diff = diff_handle.read(cx);
|
let diff = diff_handle.read(cx);
|
||||||
|
|
||||||
let diff_hunk_ranges = diff
|
let diff_hunk_ranges = diff
|
||||||
.hunks_intersecting_range(
|
.hunks_intersecting_range(
|
||||||
language::Anchor::MIN..language::Anchor::MAX,
|
language::Anchor::MIN..language::Anchor::MAX,
|
||||||
|
@ -166,9 +168,25 @@ impl AssistantDiff {
|
||||||
|
|
||||||
self.editor.update(cx, |editor, cx| {
|
self.editor.update(cx, |editor, cx| {
|
||||||
if was_empty {
|
if was_empty {
|
||||||
editor.change_selections(None, window, cx, |selections| {
|
let first_hunk = editor
|
||||||
selections.select_ranges([0..0])
|
.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
|
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
|
let ranges = self
|
||||||
.editor
|
.editor
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.selections
|
.selections
|
||||||
.disjoint_anchor_ranges()
|
.disjoint_anchor_ranges()
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
self.keep_edits_in_ranges(ranges, window, cx);
|
||||||
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)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reject(&mut self, _: &crate::Reject, window: &mut Window, cx: &mut Context<Self>) {
|
fn reject(&mut self, _: &crate::Reject, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
let ranges = self
|
let ranges = self
|
||||||
.editor
|
.editor
|
||||||
.update(cx, |editor, cx| editor.selections.ranges(cx));
|
.read(cx)
|
||||||
self.editor.update(cx, |editor, cx| {
|
.selections
|
||||||
editor.restore_hunks_in_ranges(ranges, window, cx)
|
.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>) {
|
fn reject_all(&mut self, _: &crate::RejectAll, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
self.editor.update(cx, |editor, cx| {
|
self.reject_edits_in_ranges(
|
||||||
let max_point = editor.buffer().read(cx).read(cx).max_point();
|
vec![editor::Anchor::min()..editor::Anchor::max()],
|
||||||
editor.restore_hunks_in_ranges(vec![Point::zero()..max_point], window, cx)
|
window,
|
||||||
})
|
cx,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn keep_all(&mut self, _: &crate::KeepAll, _window: &mut Window, cx: &mut Context<Self>) {
|
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(
|
fn keep_edits_in_ranges(
|
||||||
&mut self,
|
&mut self,
|
||||||
hunk_ranges: Vec<Range<editor::Anchor>>,
|
ranges: Vec<Range<editor::Anchor>>,
|
||||||
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
let snapshot = self.multibuffer.read(cx).snapshot(cx);
|
let snapshot = self.multibuffer.read(cx).snapshot(cx);
|
||||||
let diff_hunks_in_ranges = self
|
let diff_hunks_in_ranges = self
|
||||||
.editor
|
.editor
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.diff_hunks_in_ranges(&hunk_ranges, &snapshot)
|
.diff_hunks_in_ranges(&ranges, &snapshot)
|
||||||
.collect::<Vec<_>>();
|
.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);
|
let buffer = self.multibuffer.read(cx).buffer(hunk.buffer_id);
|
||||||
if let Some(buffer) = buffer {
|
if let Some(buffer) = buffer {
|
||||||
self.thread.update(cx, |thread, cx| {
|
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 {
|
fn focus_handle(&self, cx: &App) -> FocusHandle {
|
||||||
if self.multibuffer.read(cx).is_empty() {
|
if self.multibuffer.read(cx).is_empty() {
|
||||||
self.focus_handle.clone()
|
self.focus_handle.clone()
|
||||||
|
@ -303,7 +388,7 @@ impl Focusable for AssistantDiff {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Item for AssistantDiff {
|
impl Item for AgentDiff {
|
||||||
type Event = EditorEvent;
|
type Event = EditorEvent;
|
||||||
|
|
||||||
fn tab_icon(&self, _window: &Window, _cx: &App) -> Option<Icon> {
|
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> {
|
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 {
|
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 {
|
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
let is_empty = self.multibuffer.read(cx).is_empty();
|
let is_empty = self.multibuffer.read(cx).is_empty();
|
||||||
|
|
||||||
div()
|
div()
|
||||||
.track_focus(&self.focus_handle)
|
.track_focus(&self.focus_handle)
|
||||||
.key_context(if is_empty {
|
.key_context(if is_empty { "EmptyPane" } else { "AgentDiff" })
|
||||||
"EmptyPane"
|
|
||||||
} else {
|
|
||||||
"AssistantDiff"
|
|
||||||
})
|
|
||||||
.on_action(cx.listener(Self::keep))
|
.on_action(cx.listener(Self::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::reject_all))
|
||||||
|
@ -498,7 +579,7 @@ fn render_diff_hunk_controls(
|
||||||
hunk_range: Range<editor::Anchor>,
|
hunk_range: Range<editor::Anchor>,
|
||||||
is_created_file: bool,
|
is_created_file: bool,
|
||||||
line_height: Pixels,
|
line_height: Pixels,
|
||||||
assistant_diff: &Entity<AssistantDiff>,
|
agent_diff: &Entity<AgentDiff>,
|
||||||
editor: &Entity<Editor>,
|
editor: &Entity<Editor>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
|
@ -519,7 +600,7 @@ fn render_diff_hunk_controls(
|
||||||
.occlude()
|
.occlude()
|
||||||
.shadow_md()
|
.shadow_md()
|
||||||
.children(vec![
|
.children(vec![
|
||||||
Button::new("reject", "Reject")
|
Button::new(("reject", row as u64), "Reject")
|
||||||
.disabled(is_created_file)
|
.disabled(is_created_file)
|
||||||
.key_binding(
|
.key_binding(
|
||||||
KeyBinding::for_action_in(
|
KeyBinding::for_action_in(
|
||||||
|
@ -531,12 +612,14 @@ fn render_diff_hunk_controls(
|
||||||
.map(|kb| kb.size(rems_from_px(12.))),
|
.map(|kb| kb.size(rems_from_px(12.))),
|
||||||
)
|
)
|
||||||
.on_click({
|
.on_click({
|
||||||
let editor = editor.clone();
|
let agent_diff = agent_diff.clone();
|
||||||
move |_event, window, cx| {
|
move |_event, window, cx| {
|
||||||
editor.update(cx, |editor, cx| {
|
agent_diff.update(cx, |diff, cx| {
|
||||||
let snapshot = editor.snapshot(window, cx);
|
diff.reject_edits_in_ranges(
|
||||||
let point = hunk_range.start.to_point(&snapshot.buffer_snapshot);
|
vec![hunk_range.start..hunk_range.start],
|
||||||
editor.restore_hunks_in_ranges(vec![point..point], window, cx);
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
@ -551,10 +634,14 @@ fn render_diff_hunk_controls(
|
||||||
.map(|kb| kb.size(rems_from_px(12.))),
|
.map(|kb| kb.size(rems_from_px(12.))),
|
||||||
)
|
)
|
||||||
.on_click({
|
.on_click({
|
||||||
let assistant_diff = assistant_diff.clone();
|
let agent_diff = agent_diff.clone();
|
||||||
move |_event, _window, cx| {
|
move |_event, window, cx| {
|
||||||
assistant_diff.update(cx, |diff, cx| {
|
agent_diff.update(cx, |diff, cx| {
|
||||||
diff.keep_edits_in_ranges(vec![hunk_range.start..hunk_range.start], 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()
|
.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 {
|
fn to_any(&self) -> &dyn std::any::Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extend_key_context(&self, key_context: &mut gpui::KeyContext, _: &App) {
|
fn extend_key_context(&self, key_context: &mut gpui::KeyContext, _: &App) {
|
||||||
key_context.add("assistant_diff");
|
key_context.add("agent_diff");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AssistantDiffToolbar {
|
pub struct AgentDiffToolbar {
|
||||||
assistant_diff: Option<WeakEntity<AssistantDiff>>,
|
agent_diff: Option<WeakEntity<AgentDiff>>,
|
||||||
_workspace: WeakEntity<Workspace>,
|
_workspace: WeakEntity<Workspace>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AssistantDiffToolbar {
|
impl AgentDiffToolbar {
|
||||||
pub fn new(workspace: &Workspace, _: &mut Context<Self>) -> Self {
|
pub fn new(workspace: &Workspace, _: &mut Context<Self>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
assistant_diff: None,
|
agent_diff: None,
|
||||||
_workspace: workspace.weak_handle(),
|
_workspace: workspace.weak_handle(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assistant_diff(&self, _: &App) -> Option<Entity<AssistantDiff>> {
|
fn agent_diff(&self, _: &App) -> Option<Entity<AgentDiff>> {
|
||||||
self.assistant_diff.as_ref()?.upgrade()
|
self.agent_diff.as_ref()?.upgrade()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dispatch_action(&self, action: &dyn Action, window: &mut Window, cx: &mut Context<Self>) {
|
fn dispatch_action(&self, action: &dyn Action, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
if let Some(assistant_diff) = self.assistant_diff(cx) {
|
if let Some(agent_diff) = self.agent_diff(cx) {
|
||||||
assistant_diff.focus_handle(cx).focus(window);
|
agent_diff.focus_handle(cx).focus(window);
|
||||||
}
|
}
|
||||||
let action = action.boxed_clone();
|
let action = action.boxed_clone();
|
||||||
cx.defer(move |cx| {
|
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(
|
fn set_active_pane_item(
|
||||||
&mut self,
|
&mut self,
|
||||||
active_pane_item: Option<&dyn ItemHandle>,
|
active_pane_item: Option<&dyn ItemHandle>,
|
||||||
_: &mut Window,
|
_: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> ToolbarItemLocation {
|
) -> ToolbarItemLocation {
|
||||||
self.assistant_diff = active_pane_item
|
self.agent_diff = active_pane_item
|
||||||
.and_then(|item| item.act_as::<AssistantDiff>(cx))
|
.and_then(|item| item.act_as::<AgentDiff>(cx))
|
||||||
.map(|entity| entity.downgrade());
|
.map(|entity| entity.downgrade());
|
||||||
if self.assistant_diff.is_some() {
|
if self.agent_diff.is_some() {
|
||||||
ToolbarItemLocation::PrimaryRight
|
ToolbarItemLocation::PrimaryRight
|
||||||
} else {
|
} else {
|
||||||
ToolbarItemLocation::Hidden
|
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 {
|
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,
|
Some(ad) => ad,
|
||||||
None => return div(),
|
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 {
|
if is_empty {
|
||||||
return div();
|
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)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
mod active_thread;
|
mod active_thread;
|
||||||
|
mod agent_diff;
|
||||||
mod assistant_configuration;
|
mod assistant_configuration;
|
||||||
mod assistant_diff;
|
|
||||||
mod assistant_model_selector;
|
mod assistant_model_selector;
|
||||||
mod assistant_panel;
|
mod assistant_panel;
|
||||||
mod buffer_codegen;
|
mod buffer_codegen;
|
||||||
|
@ -41,7 +41,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, AssistantDiffToolbar};
|
pub use agent_diff::{AgentDiff, AgentDiffToolbar};
|
||||||
|
|
||||||
actions!(
|
actions!(
|
||||||
agent,
|
agent,
|
||||||
|
@ -65,7 +65,7 @@ actions!(
|
||||||
RemoveFocusedContext,
|
RemoveFocusedContext,
|
||||||
AcceptSuggestedContext,
|
AcceptSuggestedContext,
|
||||||
OpenActiveThreadAsMarkdown,
|
OpenActiveThreadAsMarkdown,
|
||||||
OpenAssistantDiff,
|
OpenAgentDiff,
|
||||||
Keep,
|
Keep,
|
||||||
Reject,
|
Reject,
|
||||||
RejectAll,
|
RejectAll,
|
||||||
|
|
|
@ -42,8 +42,8 @@ use crate::thread::{Thread, ThreadError, ThreadId};
|
||||||
use crate::thread_history::{PastContext, PastThread, ThreadHistory};
|
use crate::thread_history::{PastContext, PastThread, ThreadHistory};
|
||||||
use crate::thread_store::ThreadStore;
|
use crate::thread_store::ThreadStore;
|
||||||
use crate::{
|
use crate::{
|
||||||
AssistantDiff, InlineAssistant, NewPromptEditor, NewThread, OpenActiveThreadAsMarkdown,
|
AgentDiff, InlineAssistant, NewPromptEditor, NewThread, OpenActiveThreadAsMarkdown,
|
||||||
OpenAssistantDiff, OpenConfiguration, OpenHistory, ToggleContextPicker,
|
OpenAgentDiff, OpenConfiguration, OpenHistory, ToggleContextPicker,
|
||||||
};
|
};
|
||||||
|
|
||||||
action_with_deprecated_aliases!(
|
action_with_deprecated_aliases!(
|
||||||
|
@ -94,11 +94,11 @@ pub fn init(cx: &mut App) {
|
||||||
panel.update(cx, |panel, cx| panel.open_configuration(window, cx));
|
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) {
|
if let Some(panel) = workspace.panel::<AssistantPanel>(cx) {
|
||||||
workspace.focus_panel::<AssistantPanel>(window, cx);
|
workspace.focus_panel::<AssistantPanel>(window, cx);
|
||||||
panel.update(cx, |panel, 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,
|
&mut self,
|
||||||
_: &OpenAssistantDiff,
|
_: &OpenAgentDiff,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
let thread = self.thread.read(cx).thread().clone();
|
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>) {
|
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::open_active_thread_as_markdown))
|
||||||
.on_action(cx.listener(Self::deploy_prompt_library))
|
.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))
|
.child(self.render_toolbar(window, cx))
|
||||||
.map(|parent| match self.active_view {
|
.map(|parent| match self.active_view {
|
||||||
ActiveView::Thread => parent
|
ActiveView::Thread => parent
|
||||||
|
|
|
@ -31,7 +31,7 @@ use crate::profile_selector::ProfileSelector;
|
||||||
use crate::thread::{RequestKind, Thread};
|
use crate::thread::{RequestKind, Thread};
|
||||||
use crate::thread_store::ThreadStore;
|
use crate::thread_store::ThreadStore;
|
||||||
use crate::{
|
use crate::{
|
||||||
AssistantDiff, Chat, ChatMode, NewThread, OpenAssistantDiff, RemoveAllContext, ThreadEvent,
|
AgentDiff, Chat, ChatMode, NewThread, OpenAgentDiff, RemoveAllContext, ThreadEvent,
|
||||||
ToggleContextPicker, ToggleProfileSelector,
|
ToggleContextPicker, ToggleProfileSelector,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -318,7 +318,7 @@ impl MessageEditor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_review_click(&self, window: &mut Window, cx: &mut Context<Self>) {
|
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)
|
.label_size(LabelSize::Small)
|
||||||
.key_binding(
|
.key_binding(
|
||||||
KeyBinding::for_action_in(
|
KeyBinding::for_action_in(
|
||||||
&OpenAssistantDiff,
|
&OpenAgentDiff,
|
||||||
&focus_handle,
|
&focus_handle,
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
|
|
|
@ -225,15 +225,17 @@ impl Tool for FindReplaceFileTool {
|
||||||
return Err(err)
|
return Err(err)
|
||||||
};
|
};
|
||||||
|
|
||||||
let snapshot = buffer.update(cx, |buffer, cx| {
|
let snapshot = cx.update(|cx| {
|
||||||
buffer.finalize_last_transaction();
|
let snapshot = buffer.update(cx, |buffer, cx| {
|
||||||
buffer.apply_diff(diff, cx);
|
buffer.finalize_last_transaction();
|
||||||
buffer.finalize_last_transaction();
|
buffer.apply_diff(diff, cx);
|
||||||
buffer.snapshot()
|
buffer.finalize_last_transaction();
|
||||||
})?;
|
buffer.snapshot()
|
||||||
|
});
|
||||||
action_log.update(cx, |log, cx| {
|
action_log.update(cx, |log, cx| {
|
||||||
log.buffer_edited(buffer.clone(), cx)
|
log.buffer_edited(buffer.clone(), cx)
|
||||||
|
});
|
||||||
|
snapshot
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
project.update( cx, |project, cx| {
|
project.update( cx, |project, cx| {
|
||||||
|
|
|
@ -163,6 +163,12 @@ impl MultiBufferDiffHunk {
|
||||||
self.diff_base_byte_range == (0..0)
|
self.diff_base_byte_range == (0..0)
|
||||||
&& self.buffer_range == (text::Anchor::MIN..text::Anchor::MAX)
|
&& 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)]
|
#[derive(PartialEq, Eq, Ord, PartialOrd, Clone, Hash, Debug)]
|
||||||
|
|
|
@ -8,7 +8,7 @@ mod quick_action_bar;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub(crate) mod windows_only_instance;
|
pub(crate) mod windows_only_instance;
|
||||||
|
|
||||||
use agent::AssistantDiffToolbar;
|
use agent::AgentDiffToolbar;
|
||||||
use anyhow::Context as _;
|
use anyhow::Context as _;
|
||||||
pub use app_menus::*;
|
pub use app_menus::*;
|
||||||
use assets::Assets;
|
use assets::Assets;
|
||||||
|
@ -938,8 +938,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));
|
let agent_diff_toolbar = cx.new(|cx| AgentDiffToolbar::new(workspace, cx));
|
||||||
toolbar.add_item(assistant_diff_toolbar, window, cx);
|
toolbar.add_item(agent_diff_toolbar, window, cx);
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue