mirror of
https://github.com/openai/codex.git
synced 2026-10-28 18:58:46 +00:00
feat(tui): add vim change-to-line-end binding
This commit is contained in:
@@ -223,6 +223,8 @@ pub struct TuiVimNormalKeymap {
|
||||
pub delete_char: Option<KeybindingsSpec>,
|
||||
/// Delete from cursor to end of line (`D`).
|
||||
pub delete_to_line_end: Option<KeybindingsSpec>,
|
||||
/// Change from cursor to end of line and enter insert mode (`C`).
|
||||
pub change_to_line_end: Option<KeybindingsSpec>,
|
||||
/// Yank the entire line (`Y`).
|
||||
pub yank_line: Option<KeybindingsSpec>,
|
||||
/// Paste after cursor (`p`).
|
||||
|
||||
@@ -2824,6 +2824,7 @@
|
||||
"append_after_cursor": null,
|
||||
"append_line_end": null,
|
||||
"cancel_operator": null,
|
||||
"change_to_line_end": null,
|
||||
"delete_char": null,
|
||||
"delete_to_line_end": null,
|
||||
"enter_insert": null,
|
||||
@@ -3504,6 +3505,7 @@
|
||||
"append_after_cursor": null,
|
||||
"append_line_end": null,
|
||||
"cancel_operator": null,
|
||||
"change_to_line_end": null,
|
||||
"delete_char": null,
|
||||
"delete_to_line_end": null,
|
||||
"enter_insert": null,
|
||||
@@ -3790,6 +3792,14 @@
|
||||
],
|
||||
"description": "Cancel a pending operator and return to normal mode."
|
||||
},
|
||||
"change_to_line_end": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/KeybindingsSpec"
|
||||
}
|
||||
],
|
||||
"description": "Change from cursor to end of line and enter insert mode (`C`)."
|
||||
},
|
||||
"delete_char": {
|
||||
"allOf": [
|
||||
{
|
||||
|
||||
@@ -736,7 +736,12 @@ impl TextArea {
|
||||
return;
|
||||
}
|
||||
if self.vim_normal_keymap.delete_to_line_end.is_pressed(event) {
|
||||
self.kill_to_end_of_line();
|
||||
self.vim_kill_to_end_of_line();
|
||||
return;
|
||||
}
|
||||
if self.vim_normal_keymap.change_to_line_end.is_pressed(event) {
|
||||
self.vim_kill_to_end_of_line();
|
||||
self.vim_mode = VimMode::Insert;
|
||||
return;
|
||||
}
|
||||
if self.vim_normal_keymap.yank_line.is_pressed(event) {
|
||||
@@ -1035,6 +1040,13 @@ impl TextArea {
|
||||
}
|
||||
}
|
||||
|
||||
fn vim_kill_to_end_of_line(&mut self) {
|
||||
let eol = self.end_of_current_line();
|
||||
if self.cursor_pos < eol {
|
||||
self.kill_range(self.cursor_pos..eol);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn kill_to_beginning_of_line(&mut self) {
|
||||
let bol = self.beginning_of_current_line();
|
||||
let range = if self.cursor_pos == bol {
|
||||
@@ -2321,6 +2333,60 @@ mod tests {
|
||||
assert_eq!(t.cursor(), 11);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vim_shift_c_changes_to_line_end_and_enters_insert_mode() {
|
||||
let mut t = ta_with("hello world\nnext line");
|
||||
t.set_cursor(/*pos*/ 6);
|
||||
t.set_vim_enabled(/*enabled*/ true);
|
||||
|
||||
t.input(KeyEvent::new(KeyCode::Char('c'), KeyModifiers::SHIFT));
|
||||
|
||||
assert_eq!(t.text(), "hello \nnext line");
|
||||
assert_eq!(t.vim_mode_label(), Some("Insert"));
|
||||
assert_eq!(t.cursor(), 6);
|
||||
assert_eq!(t.kill_buffer, "world");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vim_uppercase_c_changes_to_line_end() {
|
||||
let mut t = ta_with("hello world\nnext line");
|
||||
t.set_cursor(/*pos*/ 6);
|
||||
t.set_vim_enabled(/*enabled*/ true);
|
||||
|
||||
t.input(KeyEvent::new(KeyCode::Char('C'), KeyModifiers::NONE));
|
||||
|
||||
assert_eq!(t.text(), "hello \nnext line");
|
||||
assert_eq!(t.vim_mode_label(), Some("Insert"));
|
||||
assert_eq!(t.cursor(), 6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vim_d_at_line_end_does_not_remove_newline() {
|
||||
let mut t = ta_with("hello\nworld");
|
||||
t.set_cursor(/*pos*/ "hello".len());
|
||||
t.set_vim_enabled(/*enabled*/ true);
|
||||
|
||||
t.input(KeyEvent::new(KeyCode::Char('D'), KeyModifiers::NONE));
|
||||
|
||||
assert_eq!(t.text(), "hello\nworld");
|
||||
assert_eq!(t.vim_mode_label(), Some("Normal"));
|
||||
assert_eq!(t.kill_buffer, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vim_c_at_line_end_enters_insert_without_removing_newline() {
|
||||
let mut t = ta_with("hello\nworld");
|
||||
t.set_cursor(/*pos*/ "hello".len());
|
||||
t.set_vim_enabled(/*enabled*/ true);
|
||||
|
||||
t.input(KeyEvent::new(KeyCode::Char('C'), KeyModifiers::NONE));
|
||||
|
||||
assert_eq!(t.text(), "hello\nworld");
|
||||
assert_eq!(t.vim_mode_label(), Some("Insert"));
|
||||
assert_eq!(t.cursor(), "hello".len());
|
||||
assert_eq!(t.kill_buffer, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vim_shift_o_opens_line_above_with_shift_only_binding() {
|
||||
let mut t = ta_with("hello\nworld");
|
||||
|
||||
@@ -157,6 +157,7 @@ pub(crate) struct VimNormalKeymap {
|
||||
pub(crate) move_line_end: Vec<KeyBinding>,
|
||||
pub(crate) delete_char: Vec<KeyBinding>,
|
||||
pub(crate) delete_to_line_end: Vec<KeyBinding>,
|
||||
pub(crate) change_to_line_end: Vec<KeyBinding>,
|
||||
pub(crate) yank_line: Vec<KeyBinding>,
|
||||
pub(crate) paste_after: Vec<KeyBinding>,
|
||||
pub(crate) start_delete_operator: Vec<KeyBinding>,
|
||||
@@ -487,6 +488,7 @@ impl RuntimeKeymap {
|
||||
move_line_end: resolve_local!(keymap, defaults, vim_normal, move_line_end),
|
||||
delete_char: resolve_local!(keymap, defaults, vim_normal, delete_char),
|
||||
delete_to_line_end: resolve_local!(keymap, defaults, vim_normal, delete_to_line_end),
|
||||
change_to_line_end: resolve_local!(keymap, defaults, vim_normal, change_to_line_end),
|
||||
yank_line: resolve_local!(keymap, defaults, vim_normal, yank_line),
|
||||
paste_after: resolve_local!(keymap, defaults, vim_normal, paste_after),
|
||||
start_delete_operator: resolve_local!(
|
||||
@@ -984,6 +986,10 @@ impl RuntimeKeymap {
|
||||
shift(KeyCode::Char('d')),
|
||||
plain(KeyCode::Char('D'))
|
||||
],
|
||||
change_to_line_end: default_bindings![
|
||||
shift(KeyCode::Char('c')),
|
||||
plain(KeyCode::Char('C'))
|
||||
],
|
||||
yank_line: default_bindings![shift(KeyCode::Char('y')), plain(KeyCode::Char('Y'))],
|
||||
paste_after: default_bindings![plain(KeyCode::Char('p'))],
|
||||
start_delete_operator: default_bindings![plain(KeyCode::Char('d'))],
|
||||
@@ -1404,6 +1410,10 @@ impl RuntimeKeymap {
|
||||
"delete_to_line_end",
|
||||
self.vim_normal.delete_to_line_end.as_slice(),
|
||||
),
|
||||
(
|
||||
"change_to_line_end",
|
||||
self.vim_normal.change_to_line_end.as_slice(),
|
||||
),
|
||||
("yank_line", self.vim_normal.yank_line.as_slice()),
|
||||
("paste_after", self.vim_normal.paste_after.as_slice()),
|
||||
(
|
||||
|
||||
@@ -135,6 +135,7 @@ pub(super) const KEYMAP_ACTIONS: &[KeymapActionDescriptor] = &[
|
||||
action("vim_normal", "Vim normal", "move_line_end", "Move to the end of the line."),
|
||||
action("vim_normal", "Vim normal", "delete_char", "Delete the character under the cursor."),
|
||||
action("vim_normal", "Vim normal", "delete_to_line_end", "Delete from cursor to end of line."),
|
||||
action("vim_normal", "Vim normal", "change_to_line_end", "Change from cursor to end of line and enter insert mode."),
|
||||
action("vim_normal", "Vim normal", "yank_line", "Yank the entire line."),
|
||||
action("vim_normal", "Vim normal", "paste_after", "Paste after the cursor."),
|
||||
action("vim_normal", "Vim normal", "start_delete_operator", "Begin a delete operator and wait for a motion."),
|
||||
@@ -275,6 +276,7 @@ pub(super) fn binding_slot<'a>(
|
||||
("vim_normal", "move_line_end") => Some(&mut keymap.vim_normal.move_line_end),
|
||||
("vim_normal", "delete_char") => Some(&mut keymap.vim_normal.delete_char),
|
||||
("vim_normal", "delete_to_line_end") => Some(&mut keymap.vim_normal.delete_to_line_end),
|
||||
("vim_normal", "change_to_line_end") => Some(&mut keymap.vim_normal.change_to_line_end),
|
||||
("vim_normal", "yank_line") => Some(&mut keymap.vim_normal.yank_line),
|
||||
("vim_normal", "paste_after") => Some(&mut keymap.vim_normal.paste_after),
|
||||
("vim_normal", "start_delete_operator") => Some(&mut keymap.vim_normal.start_delete_operator),
|
||||
@@ -397,6 +399,7 @@ pub(super) fn bindings_for_action<'a>(
|
||||
("vim_normal", "move_line_end") => Some(runtime_keymap.vim_normal.move_line_end.as_slice()),
|
||||
("vim_normal", "delete_char") => Some(runtime_keymap.vim_normal.delete_char.as_slice()),
|
||||
("vim_normal", "delete_to_line_end") => Some(runtime_keymap.vim_normal.delete_to_line_end.as_slice()),
|
||||
("vim_normal", "change_to_line_end") => Some(runtime_keymap.vim_normal.change_to_line_end.as_slice()),
|
||||
("vim_normal", "yank_line") => Some(runtime_keymap.vim_normal.yank_line.as_slice()),
|
||||
("vim_normal", "paste_after") => Some(runtime_keymap.vim_normal.paste_after.as_slice()),
|
||||
("vim_normal", "start_delete_operator") => Some(runtime_keymap.vim_normal.start_delete_operator.as_slice()),
|
||||
|
||||
Reference in New Issue
Block a user