add undo/redo and macOS modifier handling to input

This commit is contained in:
2026-10-29 14:32:49 +07:00
parent 5d29c61f39
commit d95baae418
3 changed files with 296 additions and 13 deletions
+101
View File
@@ -6,6 +6,7 @@ use gpui_native_core::input::{Input, InputState, InputStateEvent};
use gpui_native_theme::Theme;
use std::cell::RefCell;
use std::rc::Rc;
use std::time::Duration;
struct Harness {
state: Entity<InputState>,
@@ -110,6 +111,106 @@ fn select_all_then_type_replaces_the_value() {
assert_eq!(value(&cx, &state), "z");
}
#[cfg(target_os = "macos")]
#[test]
fn cmd_arrow_jumps_to_the_field_ends() {
let (cx, state, mut visual) = input_window("hello world", false);
focus(&mut visual, &state);
// Caret starts at the end; cmd-left brings it to the start.
visual.simulate_keystrokes("cmd-left");
visual.simulate_keystrokes("x");
assert_eq!(value(&cx, &state), "xhello world");
// cmd-shift-right selects from the caret to the end.
visual.simulate_keystrokes("cmd-shift-right");
visual.simulate_keystrokes("backspace");
assert_eq!(value(&cx, &state), "x");
// cmd-up / cmd-down mirror the jumps.
visual.simulate_keystrokes("cmd-down");
visual.simulate_keystrokes("x");
assert_eq!(value(&cx, &state), "xx");
visual.simulate_keystrokes("cmd-up");
visual.simulate_keystrokes("y");
assert_eq!(value(&cx, &state), "yxx");
}
#[cfg(target_os = "macos")]
#[test]
fn cmd_backspace_deletes_to_the_field_start() {
let (cx, state, mut visual) = input_window("hello", false);
focus(&mut visual, &state);
// The caret starts at the end; cmd-backspace clears the whole field.
visual.simulate_keystrokes("cmd-backspace");
assert_eq!(value(&cx, &state), "");
}
#[test]
fn undo_reverts_a_typing_burst_as_one_step() {
let (cx, state, mut visual) = input_window("", false);
focus(&mut visual, &state);
visual.simulate_keystrokes("h e l l o");
visual.simulate_keystrokes("cmd-z");
assert_eq!(value(&cx, &state), "");
visual.simulate_keystrokes("cmd-shift-z");
assert_eq!(value(&cx, &state), "hello");
}
#[test]
fn undo_steps_through_edits_outside_the_group_interval() {
let (cx, state, mut visual) = input_window("", false);
focus(&mut visual, &state);
visual.update(|_, cx| {
state.update(cx, |state, _| state.set_group_interval(Duration::ZERO));
});
visual.simulate_keystrokes("a b");
assert_eq!(value(&cx, &state), "ab");
visual.simulate_keystrokes("cmd-z");
assert_eq!(value(&cx, &state), "a");
visual.simulate_keystrokes("cmd-z");
assert_eq!(value(&cx, &state), "");
visual.simulate_keystrokes("cmd-shift-z");
assert_eq!(value(&cx, &state), "a");
visual.simulate_keystrokes("cmd-shift-z");
assert_eq!(value(&cx, &state), "ab");
// A deletion gets its own entry and undoes back to the typed text.
visual.simulate_keystrokes("backspace");
assert_eq!(value(&cx, &state), "a");
visual.simulate_keystrokes("cmd-z");
assert_eq!(value(&cx, &state), "ab");
}
#[test]
fn new_edits_clear_the_redo_stack() {
let (cx, state, mut visual) = input_window("", false);
focus(&mut visual, &state);
visual.simulate_keystrokes("a");
visual.simulate_keystrokes("cmd-z");
visual.simulate_keystrokes("b");
visual.simulate_keystrokes("cmd-shift-z");
assert_eq!(value(&cx, &state), "b");
visual.simulate_keystrokes("cmd-z");
assert_eq!(value(&cx, &state), "");
}
#[test]
fn set_value_clears_the_history() {
let (cx, state, mut visual) = input_window("", false);
focus(&mut visual, &state);
visual.simulate_keystrokes("typed");
visual.update(|window, cx| {
state.update(cx, |state, cx| state.set_value("preset", window, cx));
});
visual.simulate_keystrokes("cmd-z");
assert_eq!(value(&cx, &state), "preset");
}
#[test]
fn shift_arrow_selects_and_backspace_removes_the_selection() {
let (cx, state, mut visual) = input_window("ab", false);