Allow cursor to be moved into an unconfirmed prompt editor via esc (#13134)

This also swaps the icons in the prompt editor.

Release Notes:

- N/A
This commit is contained in:
Antonio Scandurra 2024-06-17 12:19:06 +02:00 committed by GitHub
parent 0afb3abfd2
commit 03c54623d4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

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