diff --git a/codex-rs/core/config.schema.json b/codex-rs/core/config.schema.json index 9b82a00411..a7998ee422 100644 --- a/codex-rs/core/config.schema.json +++ b/codex-rs/core/config.schema.json @@ -3155,116 +3155,286 @@ }, "TuiVimNormalKeymap": { "additionalProperties": false, - "description": "Vim normal-mode keybindings for modal editing inside text areas.", + "description": "Vim normal-mode keybindings for modal editing inside text areas.\n\nActions that use uppercase letters (like `A` for append-line-end) should be specified as `shift-a` in config; the runtime matcher handles cross-terminal shift-reporting differences automatically.", "properties": { "append_after_cursor": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Enter insert mode after cursor (`a`)." }, "append_line_end": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Enter insert mode at end of line (`A`)." }, "cancel_operator": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Cancel a pending operator and return to normal mode." }, "delete_char": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Delete character under cursor (`x`)." }, "delete_to_line_end": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Delete from cursor to end of line (`D`)." }, "enter_insert": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Enter insert mode at cursor (`i`)." }, "insert_line_start": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Enter insert mode at first non-blank of line (`I`)." }, "move_down": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Move cursor down (`j`)." }, "move_left": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Move cursor left (`h`)." }, "move_line_end": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Move cursor to end of line (`$`)." }, "move_line_start": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Move cursor to start of line (`0`)." }, "move_right": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Move cursor right (`l`)." }, "move_up": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Move cursor up (`k`)." }, "move_word_backward": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Move cursor to start of previous word (`b`)." }, "move_word_end": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Move cursor to end of current/next word (`e`)." }, "move_word_forward": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Move cursor to start of next word (`w`)." }, "open_line_above": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Open a new line above and enter insert mode (`O`)." }, "open_line_below": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Open a new line below and enter insert mode (`o`)." }, "paste_after": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Paste after cursor (`p`)." }, "start_delete_operator": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Begin delete operator; next key selects motion (`d`)." }, "start_yank_operator": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Begin yank operator; next key selects motion (`y`)." }, "yank_line": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Yank the entire line (`Y`)." } }, "type": "object" }, "TuiVimOperatorKeymap": { "additionalProperties": false, - "description": "Vim operator-pending keybindings for modal editing inside text areas.", + "description": "Vim operator-pending keybindings for modal editing inside text areas.\n\nThis context is active only while waiting for a motion after `d` or `y`. Repeating the operator key (`dd`, `yy`) targets the entire line. Pressing `Esc` cancels the pending operator and returns to normal mode without modifying text.", "properties": { "cancel": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Cancel the pending operator and return to normal mode." }, "delete_line": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Repeat delete operator to delete the whole line (`dd`)." }, "motion_down": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Motion: down one line (`j`)." }, "motion_left": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Motion: left (`h`)." }, "motion_line_end": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Motion: to end of line (`$`)." }, "motion_line_start": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Motion: to start of line (`0`)." }, "motion_right": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Motion: right (`l`)." }, "motion_up": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Motion: up one line (`k`)." }, "motion_word_backward": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Motion: to start of previous word (`b`)." }, "motion_word_end": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Motion: to end of current/next word (`e`)." }, "motion_word_forward": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Motion: to start of next word (`w`)." }, "yank_line": { - "$ref": "#/definitions/KeybindingsSpec" + "allOf": [ + { + "$ref": "#/definitions/KeybindingsSpec" + } + ], + "description": "Repeat yank operator to yank the whole line (`yy`)." } }, "type": "object" diff --git a/codex-rs/tui/src/bottom_pane/textarea.rs b/codex-rs/tui/src/bottom_pane/textarea.rs index 068c04488b..30fad3e24a 100644 --- a/codex-rs/tui/src/bottom_pane/textarea.rs +++ b/codex-rs/tui/src/bottom_pane/textarea.rs @@ -1988,7 +1988,7 @@ mod tests { #[test] fn vim_insert_and_escape() { let mut t = TextArea::new(); - t.set_vim_enabled(true); + t.set_vim_enabled(/*enabled*/ true); t.input(KeyEvent::new(KeyCode::Char('i'), KeyModifiers::NONE)); t.input(KeyEvent::new(KeyCode::Char('h'), KeyModifiers::NONE)); @@ -2002,7 +2002,7 @@ mod tests { #[test] fn vim_escape_from_insert_at_start_does_not_underflow() { let mut t = TextArea::new(); - t.set_vim_enabled(true); + t.set_vim_enabled(/*enabled*/ true); t.input(KeyEvent::new(KeyCode::Char('i'), KeyModifiers::NONE)); t.input(KeyEvent::new(KeyCode::Esc, KeyModifiers::NONE)); @@ -2015,7 +2015,7 @@ mod tests { fn vim_escape_moves_by_grapheme_boundary() { let mut t = ta_with("👍👍"); t.set_cursor(t.text().len()); - t.set_vim_enabled(true); + t.set_vim_enabled(/*enabled*/ true); t.input(KeyEvent::new(KeyCode::Char('i'), KeyModifiers::NONE)); t.input(KeyEvent::new(KeyCode::Esc, KeyModifiers::NONE)); @@ -2029,7 +2029,7 @@ mod tests { let mut t = TextArea::new(); t.insert_str("a"); t.insert_element(""); - t.set_vim_enabled(true); + t.set_vim_enabled(/*enabled*/ true); t.input(KeyEvent::new(KeyCode::Char('i'), KeyModifiers::NONE)); t.input(KeyEvent::new(KeyCode::Esc, KeyModifiers::NONE)); @@ -2042,8 +2042,8 @@ mod tests { fn vim_shift_i_enters_insert_at_line_start_with_shift_only_binding() { let mut t = ta_with("hello\nworld"); t.vim_normal_keymap.insert_line_start = vec![key_hint::shift(KeyCode::Char('i'))]; - t.set_cursor(9); - t.set_vim_enabled(true); + t.set_cursor(/*pos*/ 9); + t.set_vim_enabled(/*enabled*/ true); t.input(KeyEvent::new(KeyCode::Char('I'), KeyModifiers::NONE)); @@ -2055,8 +2055,8 @@ mod tests { fn vim_shift_a_enters_insert_at_line_end_with_shift_only_binding() { let mut t = ta_with("hello\nworld"); t.vim_normal_keymap.append_line_end = vec![key_hint::shift(KeyCode::Char('a'))]; - t.set_cursor(8); - t.set_vim_enabled(true); + t.set_cursor(/*pos*/ 8); + t.set_vim_enabled(/*enabled*/ true); t.input(KeyEvent::new(KeyCode::Char('A'), KeyModifiers::NONE)); @@ -2068,8 +2068,8 @@ mod tests { fn vim_shift_o_opens_line_above_with_shift_only_binding() { let mut t = ta_with("hello\nworld"); t.vim_normal_keymap.open_line_above = vec![key_hint::shift(KeyCode::Char('o'))]; - t.set_cursor(8); - t.set_vim_enabled(true); + t.set_cursor(/*pos*/ 8); + t.set_vim_enabled(/*enabled*/ true); t.input(KeyEvent::new(KeyCode::Char('O'), KeyModifiers::NONE)); @@ -2081,8 +2081,8 @@ mod tests { #[test] fn vim_delete_word() { let mut t = ta_with("hello world"); - t.set_cursor(0); - t.set_vim_enabled(true); + t.set_cursor(/*pos*/ 0); + t.set_vim_enabled(/*enabled*/ true); t.input(KeyEvent::new(KeyCode::Char('d'), KeyModifiers::NONE)); t.input(KeyEvent::new(KeyCode::Char('w'), KeyModifiers::NONE)); diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index ce230ffbdc..8dbcf47fda 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -11298,7 +11298,7 @@ impl ChatWidget { } else { "Vim mode disabled." }; - self.add_info_message(message.to_string(), None); + self.add_info_message(message.to_string(), /*hint*/ None); } pub(crate) fn submit_user_message_with_mode( diff --git a/codex-rs/tui/src/custom_terminal.rs b/codex-rs/tui/src/custom_terminal.rs index 09220ff611..d216e0a37a 100644 --- a/codex-rs/tui/src/custom_terminal.rs +++ b/codex-rs/tui/src/custom_terminal.rs @@ -886,7 +886,9 @@ mod tests { #[test] fn terminal_draw_applies_requested_cursor_style() { let mut output = Vec::new(); - let mut terminal = Terminal::with_options(CaptureBackend::new(2, 1)).expect("terminal"); + let mut terminal = + Terminal::with_options(CaptureBackend::new(/*width*/ 2, /*height*/ 1)) + .expect("terminal"); terminal.set_viewport_area(Rect::new(0, 0, 2, 1)); terminal @@ -909,7 +911,9 @@ mod tests { #[test] fn reset_cursor_style_emits_default_user_shape() { let mut output = Vec::new(); - let mut terminal = Terminal::with_options(CaptureBackend::new(2, 1)).expect("terminal"); + let mut terminal = + Terminal::with_options(CaptureBackend::new(/*width*/ 2, /*height*/ 1)) + .expect("terminal"); terminal.reset_cursor_style().expect("reset cursor style"); ratatui::backend::Backend::flush(terminal.backend_mut()).expect("flush backend");