diff --git a/crates/agent_ui/src/conversation_view.rs b/crates/agent_ui/src/conversation_view.rs index 5ed33487f212d1513f13f00a2f07b3e352cd5fc3..0f4777629136d138048b3f1844433118e6154adc 100644 --- a/crates/agent_ui/src/conversation_view.rs +++ b/crates/agent_ui/src/conversation_view.rs @@ -1173,12 +1173,19 @@ impl ConversationView { &mut self, index: usize, inserted_text: Option<&str>, + cursor_offset: Option, window: &mut Window, cx: &mut Context, ) { if let Some(active) = self.active_thread() { active.update(cx, |active, cx| { - active.move_queued_message_to_main_editor(index, inserted_text, window, cx); + active.move_queued_message_to_main_editor( + index, + inserted_text, + cursor_offset, + window, + cx, + ); }); } } @@ -2192,8 +2199,16 @@ impl ConversationView { &editor, window, move |this, _editor, event, window, cx| match event { - MessageEditorEvent::InputAttempted(text) => this - .move_queued_message_to_main_editor(index, Some(text.as_ref()), window, cx), + MessageEditorEvent::InputAttempted { + text, + cursor_offset, + } => this.move_queued_message_to_main_editor( + index, + Some(text.as_ref()), + Some(*cursor_offset), + window, + cx, + ), MessageEditorEvent::LostFocus => { this.save_queued_message_at_index(index, cx); } @@ -6480,7 +6495,7 @@ pub(crate) mod tests { // Main editor must be empty for this path — it is by default, but // assert to make the precondition explicit. assert!(thread.message_editor.read(cx).is_empty(cx)); - thread.move_queued_message_to_main_editor(0, None, window, cx); + thread.move_queued_message_to_main_editor(0, None, None, window, cx); }); cx.run_until_parked(); @@ -6525,7 +6540,7 @@ pub(crate) mod tests { vec![], cx, ); - thread.move_queued_message_to_main_editor(0, None, window, cx); + thread.move_queued_message_to_main_editor(0, None, None, window, cx); }); cx.run_until_parked(); diff --git a/crates/agent_ui/src/conversation_view/thread_view.rs b/crates/agent_ui/src/conversation_view/thread_view.rs index b1b7ca98923f5dda70e5b256caeed6c4372584f2..f30b324b804d2821eae71b543d485b823367598e 100644 --- a/crates/agent_ui/src/conversation_view/thread_view.rs +++ b/crates/agent_ui/src/conversation_view/thread_view.rs @@ -585,7 +585,7 @@ impl ThreadView { self.cancel_editing(&Default::default(), window, cx); } MessageEditorEvent::LostFocus => {} - MessageEditorEvent::InputAttempted(_) => {} + MessageEditorEvent::InputAttempted { .. } => {} } } @@ -722,7 +722,7 @@ impl ThreadView { ViewEvent::MessageEditorEvent(_editor, MessageEditorEvent::Cancel) => { self.cancel_editing(&Default::default(), window, cx); } - ViewEvent::MessageEditorEvent(_editor, MessageEditorEvent::InputAttempted(_)) => {} + ViewEvent::MessageEditorEvent(_editor, MessageEditorEvent::InputAttempted { .. }) => {} ViewEvent::OpenDiffLocation { path, position, @@ -1440,6 +1440,7 @@ impl ThreadView { &mut self, index: usize, inserted_text: Option<&str>, + cursor_offset: Option, window: &mut Window, cx: &mut Context, ) -> bool { @@ -1455,6 +1456,9 @@ impl ThreadView { if message_editor.read(cx).is_empty(cx) { message_editor.update(cx, |editor, cx| { editor.set_message(queued_content, window, cx); + if let Some(offset) = cursor_offset { + editor.set_cursor_offset(offset, window, cx); + } if let Some(inserted_text) = inserted_text.as_deref() { editor.insert_text(inserted_text, window, cx); } @@ -1463,8 +1467,16 @@ impl ThreadView { return true; } + // Adjust cursor offset accounting for existing content + let existing_len = message_editor.read(cx).text(cx).len(); + let separator = "\n\n"; + message_editor.update(cx, |editor, cx| { - editor.append_message(queued_content, Some("\n\n"), window, cx); + editor.append_message(queued_content, Some(separator), window, cx); + if let Some(offset) = cursor_offset { + let adjusted_offset = existing_len + separator.len() + offset; + editor.set_cursor_offset(adjusted_offset, window, cx); + } if let Some(inserted_text) = inserted_text.as_deref() { editor.insert_text(inserted_text, window, cx); } @@ -3038,7 +3050,7 @@ impl ThreadView { }) .on_click(cx.listener(move |this, _, window, cx| { this.move_queued_message_to_main_editor( - index, None, window, cx, + index, None, None, window, cx, ); })), ) @@ -3112,7 +3124,7 @@ impl ThreadView { }) .on_click(cx.listener(move |this, _, window, cx| { this.move_queued_message_to_main_editor( - index, None, window, cx, + index, None, None, window, cx, ); })), ) @@ -8169,7 +8181,7 @@ impl Render for ThreadView { cx.notify(); })) .on_action(cx.listener(|this, _: &EditFirstQueuedMessage, window, cx| { - this.move_queued_message_to_main_editor(0, None, window, cx); + this.move_queued_message_to_main_editor(0, None, None, window, cx); })) .on_action(cx.listener(|this, _: &ClearMessageQueue, _, cx| { this.local_queued_messages.clear(); diff --git a/crates/agent_ui/src/message_editor.rs b/crates/agent_ui/src/message_editor.rs index a4f444cfe7364dad64098eeb33b40078055a66d6..df8ab3d08aaaa77f9490603efe03ade5d1ecff4d 100644 --- a/crates/agent_ui/src/message_editor.rs +++ b/crates/agent_ui/src/message_editor.rs @@ -151,7 +151,10 @@ pub enum MessageEditorEvent { Cancel, Focus, LostFocus, - InputAttempted(Arc), + InputAttempted { + text: Arc, + cursor_offset: usize, + }, } impl EventEmitter for MessageEditor {} @@ -257,7 +260,15 @@ impl MessageEditor { && editor.read(cx).read_only(cx) && !text.is_empty() { - cx.emit(MessageEditorEvent::InputAttempted(text.clone())); + let editor = editor.read(cx); + let cursor_anchor = editor.selections.newest_anchor().head(); + let cursor_offset = cursor_anchor + .to_offset(&editor.buffer().read(cx).snapshot(cx)) + .0; + cx.emit(MessageEditorEvent::InputAttempted { + text: text.clone(), + cursor_offset, + }); } if let EditorEvent::Edited { .. } = event @@ -1580,6 +1591,21 @@ impl MessageEditor { self.editor.read(cx).text(cx) } + pub fn set_cursor_offset( + &mut self, + offset: usize, + window: &mut Window, + cx: &mut Context, + ) { + self.editor.update(cx, |editor, cx| { + let snapshot = editor.buffer().read(cx).snapshot(cx); + let offset = snapshot.clip_offset(MultiBufferOffset(offset), text::Bias::Left); + editor.change_selections(Default::default(), window, cx, |selections| { + selections.select_ranges([offset..offset]); + }); + }); + } + pub fn insert_text(&mut self, text: &str, window: &mut Window, cx: &mut Context) { if text.is_empty() { return;