diff --git a/crates/assistant/src/inline_assistant.rs b/crates/assistant/src/inline_assistant.rs index 0b528c0d41..d2891caa88 100644 --- a/crates/assistant/src/inline_assistant.rs +++ b/crates/assistant/src/inline_assistant.rs @@ -6,7 +6,7 @@ use anyhow::Result; use client::telemetry::Telemetry; use collections::{hash_map, HashMap, HashSet, VecDeque}; use editor::{ - actions::{MoveDown, MoveUp}, + actions::{MoveDown, MoveUp, SelectAll}, display_map::{ BlockContext, BlockDisposition, BlockId, BlockProperties, BlockStyle, RenderBlock, }, @@ -186,7 +186,7 @@ impl InlineAssistant { editor.register_action( move |_: &editor::actions::Newline, cx: &mut WindowContext| { InlineAssistant::update_global(cx, |this, cx| { - this.handle_editor_action(assist_id, false, cx) + this.handle_editor_newline(assist_id, cx) }) }, ) @@ -195,7 +195,7 @@ impl InlineAssistant { editor.register_action( move |_: &editor::actions::Cancel, cx: &mut WindowContext| { InlineAssistant::update_global(cx, |this, cx| { - this.handle_editor_action(assist_id, true, cx) + this.handle_editor_cancel(assist_id, cx) }) }, ) @@ -298,12 +298,7 @@ impl InlineAssistant { } } - fn handle_editor_action( - &mut self, - assist_id: InlineAssistId, - undo: bool, - cx: &mut WindowContext, - ) { + fn handle_editor_newline(&mut self, assist_id: InlineAssistId, cx: &mut WindowContext) { let Some(assist) = self.pending_assists.get(&assist_id) else { return; }; @@ -317,9 +312,7 @@ impl InlineAssistant { if editor.selections.count() == 1 { let selection = editor.selections.newest::(cx); if assist_range.contains(&selection.start) && assist_range.contains(&selection.end) { - if undo { - self.finish_inline_assist(assist_id, true, cx); - } else if matches!(assist.codegen.read(cx).status, CodegenStatus::Pending) { + if matches!(assist.codegen.read(cx).status, CodegenStatus::Pending) { self.dismiss_inline_assist(assist_id, cx); } else { self.finish_inline_assist(assist_id, false, cx); @@ -332,6 +325,36 @@ impl InlineAssistant { cx.propagate(); } + fn handle_editor_cancel(&mut self, assist_id: InlineAssistId, cx: &mut WindowContext) { + let Some(assist) = self.pending_assists.get(&assist_id) else { + return; + }; + let Some(editor) = assist.editor.upgrade() else { + return; + }; + + let buffer = editor.read(cx).buffer().read(cx).snapshot(cx); + let assist_range = assist.codegen.read(cx).range().to_offset(&buffer); + let editor = editor.read(cx); + if let Some(decorations) = assist.editor_decorations.as_ref() { + if editor.selections.count() == 1 { + let selection = editor.selections.newest::(cx); + if assist_range.contains(&selection.start) && assist_range.contains(&selection.end) + { + decorations.prompt_editor.update(cx, |prompt_editor, cx| { + prompt_editor.editor.update(cx, |editor, cx| { + editor.select_all(&SelectAll, cx); + editor.focus(cx); + }); + }); + return; + } + } + } + + cx.propagate(); + } + fn handle_editor_event( &mut self, assist_id: InlineAssistId, @@ -818,7 +841,7 @@ enum InlineAssistEditorEvent { struct InlineAssistEditor { id: InlineAssistId, height_in_lines: u8, - prompt_editor: View, + editor: View, edited_since_done: bool, gutter_dimensions: Arc>, prompt_history: VecDeque, @@ -838,6 +861,13 @@ impl Render for InlineAssistEditor { let buttons = match &self.codegen.read(cx).status { CodegenStatus::Idle => { vec![ + IconButton::new("cancel", IconName::Close) + .icon_color(Color::Muted) + .size(ButtonSize::None) + .tooltip(|cx| Tooltip::for_action("Cancel Assist", &menu::Cancel, cx)) + .on_click(cx.listener(|_, _, cx| { + cx.emit(InlineAssistEditorEvent::CancelRequested) + })), IconButton::new("start", IconName::Sparkle) .icon_color(Color::Muted) .size(ButtonSize::None) @@ -848,17 +878,17 @@ impl Render for InlineAssistEditor { cx.emit(InlineAssistEditorEvent::StartRequested) }), ), - IconButton::new("cancel", IconName::Close) - .icon_color(Color::Muted) - .size(ButtonSize::None) - .tooltip(|cx| Tooltip::for_action("Cancel Assist", &menu::Cancel, cx)) - .on_click(cx.listener(|_, _, cx| { - cx.emit(InlineAssistEditorEvent::CancelRequested) - })), ] } CodegenStatus::Pending => { vec![ + IconButton::new("cancel", IconName::Close) + .icon_color(Color::Muted) + .size(ButtonSize::None) + .tooltip(|cx| Tooltip::text("Cancel Assist", cx)) + .on_click(cx.listener(|_, _, cx| { + cx.emit(InlineAssistEditorEvent::CancelRequested) + })), IconButton::new("stop", IconName::Stop) .icon_color(Color::Error) .size(ButtonSize::None) @@ -874,17 +904,17 @@ impl Render for InlineAssistEditor { .on_click( cx.listener(|_, _, cx| cx.emit(InlineAssistEditorEvent::StopRequested)), ), - IconButton::new("cancel", IconName::Close) - .icon_color(Color::Muted) - .size(ButtonSize::None) - .tooltip(|cx| Tooltip::text("Cancel Assist", cx)) - .on_click(cx.listener(|_, _, cx| { - cx.emit(InlineAssistEditorEvent::CancelRequested) - })), ] } CodegenStatus::Error(_) | CodegenStatus::Done => { vec![ + IconButton::new("cancel", IconName::Close) + .icon_color(Color::Muted) + .size(ButtonSize::None) + .tooltip(|cx| Tooltip::for_action("Cancel Assist", &menu::Cancel, cx)) + .on_click(cx.listener(|_, _, cx| { + cx.emit(InlineAssistEditorEvent::CancelRequested) + })), if self.edited_since_done { IconButton::new("restart", IconName::RotateCw) .icon_color(Color::Info) @@ -910,13 +940,6 @@ impl Render for InlineAssistEditor { cx.emit(InlineAssistEditorEvent::ConfirmRequested); })) }, - IconButton::new("cancel", IconName::Close) - .icon_color(Color::Muted) - .size(ButtonSize::None) - .tooltip(|cx| Tooltip::for_action("Cancel Assist", &menu::Cancel, cx)) - .on_click(cx.listener(|_, _, cx| { - cx.emit(InlineAssistEditorEvent::CancelRequested) - })), ] } }; @@ -1005,7 +1028,7 @@ impl Render for InlineAssistEditor { impl FocusableView for InlineAssistEditor { fn focus_handle(&self, cx: &AppContext) -> FocusHandle { - self.prompt_editor.focus_handle(cx) + self.editor.focus_handle(cx) } } @@ -1038,7 +1061,7 @@ impl InlineAssistEditor { let mut this = Self { id, height_in_lines: 1, - prompt_editor, + editor: prompt_editor, edited_since_done: false, gutter_dimensions, prompt_history, @@ -1053,14 +1076,14 @@ impl InlineAssistEditor { } fn prompt(&self, cx: &AppContext) -> String { - self.prompt_editor.read(cx).text(cx) + self.editor.read(cx).text(cx) } fn count_lines(&mut self, cx: &mut ViewContext) { let height_in_lines = cmp::max( 2, // Make the editor at least two lines tall, to account for padding and buttons. cmp::min( - self.prompt_editor + self.editor .update(cx, |editor, cx| editor.max_point(cx).row().0 + 1), Self::MAX_LINES as u32, ), @@ -1084,7 +1107,7 @@ impl InlineAssistEditor { ) { match event { EditorEvent::Edited { .. } => { - let prompt = self.prompt_editor.read(cx).text(cx); + let prompt = self.editor.read(cx).text(cx); if self .prompt_history_ix .map_or(true, |ix| self.prompt_history[ix] != prompt) @@ -1103,16 +1126,16 @@ impl InlineAssistEditor { fn handle_codegen_changed(&mut self, _: Model, cx: &mut ViewContext) { match &self.codegen.read(cx).status { CodegenStatus::Idle => { - self.prompt_editor + self.editor .update(cx, |editor, _| editor.set_read_only(false)); } CodegenStatus::Pending => { - self.prompt_editor + self.editor .update(cx, |editor, _| editor.set_read_only(true)); } CodegenStatus::Done | CodegenStatus::Error(_) => { self.edited_since_done = false; - self.prompt_editor + self.editor .update(cx, |editor, _| editor.set_read_only(false)); } } @@ -1152,7 +1175,7 @@ impl InlineAssistEditor { if ix > 0 { self.prompt_history_ix = Some(ix - 1); let prompt = self.prompt_history[ix - 1].as_str(); - self.prompt_editor.update(cx, |editor, cx| { + self.editor.update(cx, |editor, cx| { editor.set_text(prompt, cx); editor.move_to_beginning(&Default::default(), cx); }); @@ -1160,7 +1183,7 @@ impl InlineAssistEditor { } else if !self.prompt_history.is_empty() { self.prompt_history_ix = Some(self.prompt_history.len() - 1); let prompt = self.prompt_history[self.prompt_history.len() - 1].as_str(); - self.prompt_editor.update(cx, |editor, cx| { + self.editor.update(cx, |editor, cx| { editor.set_text(prompt, cx); editor.move_to_beginning(&Default::default(), cx); }); @@ -1172,14 +1195,14 @@ impl InlineAssistEditor { if ix < self.prompt_history.len() - 1 { self.prompt_history_ix = Some(ix + 1); let prompt = self.prompt_history[ix + 1].as_str(); - self.prompt_editor.update(cx, |editor, cx| { + self.editor.update(cx, |editor, cx| { editor.set_text(prompt, cx); editor.move_to_end(&Default::default(), cx) }); } else { self.prompt_history_ix = None; let prompt = self.pending_prompt.as_str(); - self.prompt_editor.update(cx, |editor, cx| { + self.editor.update(cx, |editor, cx| { editor.set_text(prompt, cx); editor.move_to_end(&Default::default(), cx) }); @@ -1190,7 +1213,7 @@ impl InlineAssistEditor { fn render_prompt_editor(&self, cx: &mut ViewContext) -> impl IntoElement { let settings = ThemeSettings::get_global(cx); let text_style = TextStyle { - color: if self.prompt_editor.read(cx).read_only(cx) { + color: if self.editor.read(cx).read_only(cx) { cx.theme().colors().text_disabled } else { cx.theme().colors().text @@ -1207,7 +1230,7 @@ impl InlineAssistEditor { white_space: WhiteSpace::Normal, }; EditorElement::new( - &self.prompt_editor, + &self.editor, EditorStyle { background: cx.theme().colors().editor_background, local_player: cx.theme().players().local(),