Compare commits

...

1 Commits

Author SHA1 Message Date
Conrad Irwin
c570f95736 WIP
Co-authored-by: Nathan Sobo <nathan@zed.dev>
2025-04-28 15:44:17 -06:00
6 changed files with 141 additions and 23 deletions

View File

@@ -3,6 +3,7 @@ mod agent_diff;
mod assistant_configuration;
mod assistant_model_selector;
mod assistant_panel;
pub mod batch_assist;
mod buffer_codegen;
mod context;
mod context_picker;

View File

@@ -0,0 +1,64 @@
use editor::Editor;
use gpui::{Entity, EventEmitter};
use ui::{Render, Styled, div, px};
use workspace::{ToolbarItemEvent, ToolbarItemLocation};
use crate::{buffer_codegen::BufferCodegen, inline_prompt_editor::PromptEditor};
pub struct BatchAssistToolbarItem {
visible: bool,
assist: Option<Entity<BatchCodegen>>,
active_editor: Option<Entity<Editor>>,
// prompt_editor: Entity<PromptEditor>,
}
impl BatchAssistToolbarItem {
pub fn new() -> Self {
Self {
visible: false,
assist: None,
active_editor: None,
}
}
pub fn deploy(&mut self, cx: &mut ui::Context<Self>) {
self.visible = true;
}
}
impl EventEmitter<ToolbarItemEvent> for BatchAssistToolbarItem {}
impl workspace::ToolbarItemView for BatchAssistToolbarItem {
fn set_active_pane_item(
&mut self,
active_pane_item: Option<&dyn workspace::ItemHandle>,
_window: &mut ui::Window,
cx: &mut ui::Context<Self>,
) -> workspace::ToolbarItemLocation {
if let Some(editor) = active_pane_item.and_then(|item| item.act_as::<Editor>(cx)) {
self.active_editor = Some(editor);
ToolbarItemLocation::Secondary
} else {
ToolbarItemLocation::Hidden
}
}
}
impl Render for BatchAssistToolbarItem {
fn render(
&mut self,
window: &mut ui::Window,
cx: &mut ui::Context<Self>,
) -> impl ui::IntoElement {
dbg!(&self.active_editor);
if self.visible {
div().bg(gpui::red()).w(px(100.)).h(px(100.))
} else {
div()
}
}
}
pub struct BatchCodegen {
codegens: Vec<Entity<BufferCodegen>>,
}

View File

@@ -44,6 +44,7 @@ use workspace::{ItemHandle, Toast, Workspace, dock::Panel, notifications::Notifi
use zed_actions::agent::OpenConfiguration;
use crate::AssistantPanel;
use crate::batch_assist::BatchAssistToolbarItem;
use crate::buffer_codegen::{BufferCodegen, CodegenAlternative, CodegenEvent};
use crate::context_store::ContextStore;
use crate::inline_prompt_editor::{CodegenStatus, InlineAssistId, PromptEditor, PromptEditorEvent};
@@ -335,12 +336,37 @@ impl InlineAssistant {
window: &mut Window,
cx: &mut App,
) {
// if there's multiple selections
// find the pane containing this editor, grab the assist toolbar off it, show it
let (snapshot, initial_selections) = editor.update(cx, |editor, cx| {
(
editor.snapshot(window, cx),
editor.selections.all::<Point>(cx),
)
});
if initial_selections.len() > 1 {
cx.defer(move |cx| {
let assist = workspace
.update(cx, |workspace, cx| {
workspace
.active_pane()
.read(cx)
.toolbar()
.read(cx)
.item_of_type::<BatchAssistToolbarItem>()
})
.ok()
.flatten();
let Some(assist) = assist else {
dbg!("oops");
return;
};
assist.update(cx, |assist, cx| assist.deploy(cx));
});
return;
}
let mut selections = Vec::<Selection<Point>>::new();
let mut newest_selection = None;

View File

@@ -1,4 +1,5 @@
use crate::assistant_model_selector::AssistantModelSelector;
use crate::batch_assist::BatchCodegen;
use crate::buffer_codegen::BufferCodegen;
use crate::context_picker::ContextPicker;
use crate::context_store::ContextStore;
@@ -33,7 +34,7 @@ use ui::{
use util::ResultExt;
use workspace::Workspace;
pub struct PromptEditor<T> {
pub struct PromptEditor {
pub editor: Entity<Editor>,
mode: PromptEditorMode,
context_store: Entity<ContextStore>,
@@ -48,12 +49,11 @@ pub struct PromptEditor<T> {
editor_subscriptions: Vec<Subscription>,
_context_strip_subscription: Subscription,
show_rate_limit_notice: bool,
_phantom: std::marker::PhantomData<T>,
}
impl<T: 'static> EventEmitter<PromptEditorEvent> for PromptEditor<T> {}
impl EventEmitter<PromptEditorEvent> for PromptEditor {}
impl<T: 'static> Render for PromptEditor<T> {
impl Render for PromptEditor {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let ui_font_size = ThemeSettings::get_global(cx).ui_font_size(cx);
let mut buttons = Vec::new();
@@ -74,6 +74,7 @@ impl<T: 'static> Render for PromptEditor<T> {
gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0)
}
PromptEditorMode::Batch { .. } => px(0.),
PromptEditorMode::Terminal { .. } => {
// Give the equivalent of the same left-padding that we're using on the right
Pixels::from(40.0)
@@ -82,6 +83,7 @@ impl<T: 'static> Render for PromptEditor<T> {
let bottom_padding = match &self.mode {
PromptEditorMode::Buffer { .. } => Pixels::from(0.),
PromptEditorMode::Batch { .. } => Pixels::from(0.),
PromptEditorMode::Terminal { .. } => Pixels::from(8.0),
};
@@ -208,18 +210,19 @@ impl<T: 'static> Render for PromptEditor<T> {
}
}
impl<T: 'static> Focusable for PromptEditor<T> {
impl Focusable for PromptEditor {
fn focus_handle(&self, cx: &App) -> FocusHandle {
self.editor.focus_handle(cx)
}
}
impl<T: 'static> PromptEditor<T> {
impl PromptEditor {
const MAX_LINES: u8 = 8;
fn codegen_status<'a>(&'a self, cx: &'a App) -> &'a CodegenStatus {
match &self.mode {
PromptEditorMode::Buffer { codegen, .. } => codegen.read(cx).status(cx),
PromptEditorMode::Batch { .. } => todo!(),
PromptEditorMode::Terminal { codegen, .. } => &codegen.read(cx).status,
}
}
@@ -269,6 +272,7 @@ impl<T: 'static> PromptEditor<T> {
"Transform"
}
}
PromptEditorMode::Batch { .. } => "Transform",
PromptEditorMode::Terminal { .. } => "Generate",
};
@@ -448,6 +452,7 @@ impl<T: 'static> PromptEditor<T> {
GenerationMode::Transform
}
}
PromptEditorMode::Batch { .. } => GenerationMode::Transform,
PromptEditorMode::Terminal { .. } => GenerationMode::Generate,
};
@@ -535,7 +540,9 @@ impl<T: 'static> PromptEditor<T> {
}))
.into_any_element(),
],
PromptEditorMode::Buffer { .. } => vec![accept],
PromptEditorMode::Buffer { .. } | PromptEditorMode::Batch { .. } => {
vec![accept]
}
}
}
}
@@ -552,6 +559,9 @@ impl<T: 'static> PromptEditor<T> {
PromptEditorMode::Buffer { codegen, .. } => {
codegen.update(cx, |codegen, cx| codegen.cycle_prev(cx));
}
PromptEditorMode::Batch { .. } => {
todo!()
}
PromptEditorMode::Terminal { .. } => {
// no cycle buttons in terminal mode
}
@@ -563,6 +573,9 @@ impl<T: 'static> PromptEditor<T> {
PromptEditorMode::Buffer { codegen, .. } => {
codegen.update(cx, |codegen, cx| codegen.cycle_next(cx));
}
PromptEditorMode::Batch { .. } => {
todo!()
}
PromptEditorMode::Terminal { .. } => {
// no cycle buttons in terminal mode
}
@@ -796,6 +809,9 @@ pub enum PromptEditorMode {
codegen: Entity<BufferCodegen>,
gutter_dimensions: Arc<Mutex<GutterDimensions>>,
},
Batch {
codegen: Entity<BatchCodegen>,
},
Terminal {
id: TerminalInlineAssistId,
codegen: Entity<TerminalCodegen>,
@@ -823,7 +839,7 @@ impl InlineAssistId {
}
}
impl PromptEditor<BufferCodegen> {
impl PromptEditor {
pub fn new_buffer(
id: InlineAssistId,
gutter_dimensions: Arc<Mutex<GutterDimensions>>,
@@ -835,9 +851,9 @@ impl PromptEditor<BufferCodegen> {
workspace: WeakEntity<Workspace>,
thread_store: Option<WeakEntity<ThreadStore>>,
window: &mut Window,
cx: &mut Context<PromptEditor<BufferCodegen>>,
) -> PromptEditor<BufferCodegen> {
let codegen_subscription = cx.observe(&codegen, Self::handle_codegen_changed);
cx: &mut Context<PromptEditor>,
) -> PromptEditor {
let codegen_subscription = cx.observe(&codegen, Self::handle_buffer_codegen_changed);
let mode = PromptEditorMode::Buffer {
id,
codegen,
@@ -880,7 +896,7 @@ impl PromptEditor<BufferCodegen> {
let context_strip_subscription =
cx.subscribe_in(&context_strip, window, Self::handle_context_strip_event);
let mut this: PromptEditor<BufferCodegen> = PromptEditor {
let mut this: PromptEditor = PromptEditor {
editor: prompt_editor.clone(),
context_store,
context_strip,
@@ -904,17 +920,16 @@ impl PromptEditor<BufferCodegen> {
_context_strip_subscription: context_strip_subscription,
show_rate_limit_notice: false,
mode,
_phantom: Default::default(),
};
this.subscribe_to_editor(window, cx);
this
}
fn handle_codegen_changed(
fn handle_buffer_codegen_changed(
&mut self,
_: Entity<BufferCodegen>,
cx: &mut Context<PromptEditor<BufferCodegen>>,
cx: &mut Context<PromptEditor>,
) {
match self.codegen_status(cx) {
CodegenStatus::Idle => {
@@ -949,6 +964,7 @@ impl PromptEditor<BufferCodegen> {
pub fn id(&self) -> InlineAssistId {
match &self.mode {
PromptEditorMode::Buffer { id, .. } => *id,
PromptEditorMode::Batch { .. } => unreachable!(),
PromptEditorMode::Terminal { .. } => unreachable!(),
}
}
@@ -956,6 +972,7 @@ impl PromptEditor<BufferCodegen> {
pub fn codegen(&self) -> &Entity<BufferCodegen> {
match &self.mode {
PromptEditorMode::Buffer { codegen, .. } => codegen,
PromptEditorMode::Batch { .. } => unreachable!(),
PromptEditorMode::Terminal { .. } => unreachable!(),
}
}
@@ -965,6 +982,7 @@ impl PromptEditor<BufferCodegen> {
PromptEditorMode::Buffer {
gutter_dimensions, ..
} => gutter_dimensions,
PromptEditorMode::Batch { .. } => unreachable!(),
PromptEditorMode::Terminal { .. } => unreachable!(),
}
}
@@ -981,7 +999,7 @@ impl TerminalInlineAssistId {
}
}
impl PromptEditor<TerminalCodegen> {
impl PromptEditor {
pub fn new_terminal(
id: TerminalInlineAssistId,
prompt_history: VecDeque<String>,
@@ -994,7 +1012,7 @@ impl PromptEditor<TerminalCodegen> {
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let codegen_subscription = cx.observe(&codegen, Self::handle_codegen_changed);
let codegen_subscription = cx.observe(&codegen, Self::handle_terminal_codegen_changed);
let mode = PromptEditorMode::Terminal {
id,
codegen,
@@ -1057,7 +1075,6 @@ impl PromptEditor<TerminalCodegen> {
_context_strip_subscription: context_strip_subscription,
mode,
show_rate_limit_notice: false,
_phantom: Default::default(),
};
this.count_lines(cx);
this.subscribe_to_editor(window, cx);
@@ -1084,12 +1101,17 @@ impl PromptEditor<TerminalCodegen> {
cx.emit(PromptEditorEvent::Resized { height_in_lines });
}
}
PromptEditorMode::Batch { .. } => unreachable!(),
PromptEditorMode::Buffer { .. } => unreachable!(),
}
}
fn handle_codegen_changed(&mut self, _: Entity<TerminalCodegen>, cx: &mut Context<Self>) {
match &self.codegen().read(cx).status {
fn handle_terminal_codegen_changed(
&mut self,
_: Entity<TerminalCodegen>,
cx: &mut Context<Self>,
) {
match &self.terminal_codegen().read(cx).status {
CodegenStatus::Idle => {
self.editor
.update(cx, |editor, _| editor.set_read_only(false));
@@ -1106,16 +1128,18 @@ impl PromptEditor<TerminalCodegen> {
}
}
pub fn codegen(&self) -> &Entity<TerminalCodegen> {
pub fn terminal_codegen(&self) -> &Entity<TerminalCodegen> {
match &self.mode {
PromptEditorMode::Buffer { .. } => unreachable!(),
PromptEditorMode::Batch { .. } => unreachable!(),
PromptEditorMode::Terminal { codegen, .. } => codegen,
}
}
pub fn id(&self) -> TerminalInlineAssistId {
pub fn terminal_inline_assist_id(&self) -> TerminalInlineAssistId {
match &self.mode {
PromptEditorMode::Buffer { .. } => unreachable!(),
PromptEditorMode::Batch { .. } => unreachable!(),
PromptEditorMode::Terminal { id, .. } => *id,
}
}

View File

@@ -144,7 +144,7 @@ impl TerminalInlineAssistant {
window: &mut Window,
cx: &mut App,
) {
let assist_id = prompt_editor.read(cx).id();
let assist_id = prompt_editor.read(cx).terminal_inline_assist_id();
match event {
PromptEditorEvent::StartRequested => {
self.start_assist(assist_id, cx);

View File

@@ -9,6 +9,7 @@ mod quick_action_bar;
pub(crate) mod windows_only_instance;
use agent::AgentDiffToolbar;
use agent::batch_assist::BatchAssistToolbarItem;
use anyhow::Context as _;
pub use app_menus::*;
use assets::Assets;
@@ -917,6 +918,8 @@ fn initialize_pane(
toolbar.add_item(project_diff_toolbar, window, cx);
let agent_diff_toolbar = cx.new(|_cx| AgentDiffToolbar::new());
toolbar.add_item(agent_diff_toolbar, window, cx);
let batch_assist = cx.new(|_cx| BatchAssistToolbarItem::new());
toolbar.add_item(batch_assist, window, cx);
})
});
}