feat: add text cleared with ctrl+c to the history so it can be recovered with up arrow (#5470)
https://github.com/user-attachments/assets/5eed882e-6a54-4f2c-8f21-14fa0d0ef347
This commit is contained in:
@@ -242,8 +242,7 @@ impl ChatComposer {
|
|||||||
let Some(text) = self.history.on_entry_response(log_id, offset, entry) else {
|
let Some(text) = self.history.on_entry_response(log_id, offset, entry) else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
self.textarea.set_text(&text);
|
self.set_text_content(text);
|
||||||
self.textarea.set_cursor(0);
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,9 +315,15 @@ impl ChatComposer {
|
|||||||
self.sync_file_search_popup();
|
self.sync_file_search_popup();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn clear_for_ctrl_c(&mut self) {
|
pub(crate) fn clear_for_ctrl_c(&mut self) -> Option<String> {
|
||||||
|
if self.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let previous = self.current_text();
|
||||||
self.set_text_content(String::new());
|
self.set_text_content(String::new());
|
||||||
self.history.reset_navigation();
|
self.history.reset_navigation();
|
||||||
|
self.history.record_local_submission(&previous);
|
||||||
|
Some(previous)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current composer text.
|
/// Get the current composer text.
|
||||||
@@ -896,8 +901,7 @@ impl ChatComposer {
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
if let Some(text) = replace_text {
|
if let Some(text) = replace_text {
|
||||||
self.textarea.set_text(&text);
|
self.set_text_content(text);
|
||||||
self.textarea.set_cursor(0);
|
|
||||||
return (InputResult::None, true);
|
return (InputResult::None, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1828,6 +1832,28 @@ mod tests {
|
|||||||
assert!(!composer.esc_backtrack_hint);
|
assert!(!composer.esc_backtrack_hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn clear_for_ctrl_c_records_cleared_draft() {
|
||||||
|
let (tx, _rx) = unbounded_channel::<AppEvent>();
|
||||||
|
let sender = AppEventSender::new(tx);
|
||||||
|
let mut composer = ChatComposer::new(
|
||||||
|
true,
|
||||||
|
sender,
|
||||||
|
false,
|
||||||
|
"Ask Codex to do anything".to_string(),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
composer.set_text_content("draft text".to_string());
|
||||||
|
assert_eq!(composer.clear_for_ctrl_c(), Some("draft text".to_string()));
|
||||||
|
assert!(composer.is_empty());
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
composer.history.navigate_up(&composer.app_event_tx),
|
||||||
|
Some("draft text".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn question_mark_only_toggles_on_first_char() {
|
fn question_mark_only_toggles_on_first_char() {
|
||||||
use crossterm::event::KeyCode;
|
use crossterm::event::KeyCode;
|
||||||
|
|||||||
@@ -692,6 +692,40 @@ fn ctrl_c_shutdown_ignores_caps_lock() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ctrl_c_cleared_prompt_is_recoverable_via_history() {
|
||||||
|
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual();
|
||||||
|
|
||||||
|
chat.bottom_pane.insert_str("draft message ");
|
||||||
|
chat.bottom_pane
|
||||||
|
.attach_image(PathBuf::from("/tmp/preview.png"), 24, 42, "png");
|
||||||
|
let placeholder = "[preview.png 24x42]";
|
||||||
|
assert!(
|
||||||
|
chat.bottom_pane.composer_text().ends_with(placeholder),
|
||||||
|
"expected placeholder {placeholder:?} in composer text"
|
||||||
|
);
|
||||||
|
|
||||||
|
chat.handle_key_event(KeyEvent::new(KeyCode::Char('c'), KeyModifiers::CONTROL));
|
||||||
|
assert!(chat.bottom_pane.composer_text().is_empty());
|
||||||
|
assert_matches!(op_rx.try_recv(), Err(TryRecvError::Empty));
|
||||||
|
assert!(chat.bottom_pane.ctrl_c_quit_hint_visible());
|
||||||
|
|
||||||
|
chat.handle_key_event(KeyEvent::new(KeyCode::Up, KeyModifiers::NONE));
|
||||||
|
let restored_text = chat.bottom_pane.composer_text();
|
||||||
|
assert!(
|
||||||
|
restored_text.ends_with(placeholder),
|
||||||
|
"expected placeholder {placeholder:?} after history recall"
|
||||||
|
);
|
||||||
|
assert!(restored_text.starts_with("draft message "));
|
||||||
|
assert!(!chat.bottom_pane.ctrl_c_quit_hint_visible());
|
||||||
|
|
||||||
|
let images = chat.bottom_pane.take_recent_submission_images();
|
||||||
|
assert!(
|
||||||
|
images.is_empty(),
|
||||||
|
"attachments are not preserved in history recall"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn exec_history_cell_shows_working_then_completed() {
|
fn exec_history_cell_shows_working_then_completed() {
|
||||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||||
|
|||||||
Reference in New Issue
Block a user