Proposed changes editor features (#18373)

This PR adds some more functionality to the Proposed Changes Editor
view, which we'll be using in
https://github.com/zed-industries/zed/pull/18240 for allowing the
assistant to propose changes to a set of buffers.

* Add an `Apply All` button, and fully implement applying of changes to
the base buffer
* Make the proposed changes editor searchable
* Fix a bug in branch buffers' diff state management

Release Notes:

- N/A
This commit is contained in:
Max Brunsfeld 2024-09-25 16:33:00 -07:00 committed by GitHub
parent 3161aedcb0
commit 6167688a63
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 240 additions and 75 deletions

View file

@ -6,10 +6,13 @@ use language::{Buffer, BufferEvent, Capability};
use multi_buffer::{ExcerptRange, MultiBuffer};
use project::Project;
use smol::stream::StreamExt;
use std::{ops::Range, time::Duration};
use std::{any::TypeId, ops::Range, time::Duration};
use text::ToOffset;
use ui::prelude::*;
use workspace::Item;
use workspace::{
searchable::SearchableItemHandle, Item, ItemHandle as _, ToolbarItemEvent, ToolbarItemLocation,
ToolbarItemView,
};
pub struct ProposedChangesEditor {
editor: View<Editor>,
@ -23,6 +26,10 @@ pub struct ProposedChangesBuffer<T> {
pub ranges: Vec<Range<T>>,
}
pub struct ProposedChangesEditorToolbar {
current_editor: Option<View<ProposedChangesEditor>>,
}
impl ProposedChangesEditor {
pub fn new<T: ToOffset>(
buffers: Vec<ProposedChangesBuffer<T>>,
@ -96,6 +103,17 @@ impl ProposedChangesEditor {
self.recalculate_diffs_tx.unbounded_send(buffer).ok();
}
}
fn apply_all_changes(&self, cx: &mut ViewContext<Self>) {
let buffers = self.editor.read(cx).buffer.read(cx).all_buffers();
for branch_buffer in buffers {
if let Some(base_buffer) = branch_buffer.read(cx).diff_base_buffer() {
base_buffer.update(cx, |base_buffer, cx| {
base_buffer.merge(&branch_buffer, None, cx)
});
}
}
}
}
impl Render for ProposedChangesEditor {
@ -122,4 +140,66 @@ impl Item for ProposedChangesEditor {
fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
Some("Proposed changes".into())
}
fn as_searchable(&self, _: &View<Self>) -> Option<Box<dyn SearchableItemHandle>> {
Some(Box::new(self.editor.clone()))
}
fn act_as_type<'a>(
&'a self,
type_id: TypeId,
self_handle: &'a View<Self>,
_: &'a AppContext,
) -> Option<gpui::AnyView> {
if type_id == TypeId::of::<Self>() {
Some(self_handle.to_any())
} else if type_id == TypeId::of::<Editor>() {
Some(self.editor.to_any())
} else {
None
}
}
}
impl ProposedChangesEditorToolbar {
pub fn new() -> Self {
Self {
current_editor: None,
}
}
fn get_toolbar_item_location(&self) -> ToolbarItemLocation {
if self.current_editor.is_some() {
ToolbarItemLocation::PrimaryRight
} else {
ToolbarItemLocation::Hidden
}
}
}
impl Render for ProposedChangesEditorToolbar {
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
let editor = self.current_editor.clone();
Button::new("apply-changes", "Apply All").on_click(move |_, cx| {
if let Some(editor) = &editor {
editor.update(cx, |editor, cx| {
editor.apply_all_changes(cx);
});
}
})
}
}
impl EventEmitter<ToolbarItemEvent> for ProposedChangesEditorToolbar {}
impl ToolbarItemView for ProposedChangesEditorToolbar {
fn set_active_pane_item(
&mut self,
active_pane_item: Option<&dyn workspace::ItemHandle>,
_cx: &mut ViewContext<Self>,
) -> workspace::ToolbarItemLocation {
self.current_editor =
active_pane_item.and_then(|item| item.downcast::<ProposedChangesEditor>());
self.get_toolbar_item_location()
}
}