Skip to content

Commit 6789afb

Browse files
committed
Vi normal mode fix
1 parent d9300a7 commit 6789afb

File tree

2 files changed

+38
-7
lines changed

2 files changed

+38
-7
lines changed

src/core_editor/editor.rs

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use super::{edit_stack::EditStack, Clipboard, ClipboardMode, LineBuffer};
22
#[cfg(feature = "system_clipboard")]
33
use crate::core_editor::get_system_clipboard;
44
use crate::enums::{EditType, UndoBehavior};
5+
use crate::prompt::{PromptEditMode, PromptViMode};
56
use crate::{core_editor::get_local_clipboard, EditCommand};
67
use std::ops::DerefMut;
78

@@ -17,6 +18,7 @@ pub struct Editor {
1718
edit_stack: EditStack<LineBuffer>,
1819
last_undo_behavior: UndoBehavior,
1920
selection_anchor: Option<usize>,
21+
edit_mode: PromptEditMode,
2022
}
2123

2224
impl Default for Editor {
@@ -29,6 +31,7 @@ impl Default for Editor {
2931
edit_stack: EditStack::new(),
3032
last_undo_behavior: UndoBehavior::CreateUndoPoint,
3133
selection_anchor: None,
34+
edit_mode: PromptEditMode::Default,
3235
}
3336
}
3437
}
@@ -212,6 +215,11 @@ impl Editor {
212215
None
213216
};
214217
}
218+
219+
/// Updates the current edit mode for mode-aware selection behavior
220+
pub fn set_edit_mode(&mut self, mode: PromptEditMode) {
221+
self.edit_mode = mode;
222+
}
215223
fn move_to_position(&mut self, position: usize, select: bool) {
216224
self.update_selection_anchor(select);
217225
self.line_buffer.set_insertion_point(position)
@@ -625,8 +633,10 @@ impl Editor {
625633

626634
/// If a selection is active returns the selected range, otherwise None.
627635
/// The range is guaranteed to be ascending.
636+
/// Automatically determines inclusive/exclusive based on current edit mode.
628637
pub fn get_selection(&self) -> Option<(usize, usize)> {
629-
self.get_selection_with_mode(true)
638+
let inclusive = matches!(self.edit_mode, PromptEditMode::Vi(PromptViMode::Normal));
639+
self.get_selection_with_mode(inclusive)
630640
}
631641

632642
/// If a selection is active returns the selected range, otherwise None.
@@ -638,19 +648,21 @@ impl Editor {
638648
let buffer_len = self.line_buffer.len();
639649
if self.insertion_point() > selection_anchor {
640650
let end_pos = if inclusive {
641-
// Only extend for Vi normal mode where cursor is on the character
642-
// For Emacs and Vi insert modes, use cursor position directly
643-
self.insertion_point().min(buffer_len)
651+
// Vi normal mode: extend selection to include character under cursor
652+
self.line_buffer.grapheme_right_index().min(buffer_len)
644653
} else {
654+
// Emacs/Vi insert mode: cursor is between characters
645655
self.insertion_point().min(buffer_len)
646656
};
647657
(selection_anchor, end_pos)
648658
} else {
649659
let end_pos = if inclusive {
650-
// Only extend for Vi normal mode where cursor is on the character
651-
// For Emacs and Vi insert modes, use anchor position directly
652-
selection_anchor.min(buffer_len)
660+
// Vi normal mode: extend selection to include character under cursor
661+
self.line_buffer
662+
.grapheme_right_index_from_pos(selection_anchor)
663+
.min(buffer_len)
653664
} else {
665+
// Emacs/Vi insert mode: cursor is between characters
654666
selection_anchor.min(buffer_len)
655667
};
656668
(self.insertion_point(), end_pos)
@@ -1175,6 +1187,22 @@ mod test {
11751187
assert_eq!(editor.get_selection(), Some((0, 3)));
11761188
}
11771189

1190+
#[test]
1191+
fn test_vi_normal_mode_inclusive_selection() {
1192+
let mut editor = editor_with("This is some test content");
1193+
editor.line_buffer.set_insertion_point(0);
1194+
editor.set_edit_mode(PromptEditMode::Vi(PromptViMode::Normal));
1195+
editor.update_selection_anchor(true);
1196+
1197+
for _ in 0..3 {
1198+
editor.run_edit_command(&EditCommand::MoveRight { select: true });
1199+
}
1200+
assert_eq!(editor.selection_anchor, Some(0));
1201+
assert_eq!(editor.insertion_point(), 3);
1202+
// In Vi normal mode, selection should be inclusive (include character at position 3)
1203+
assert_eq!(editor.get_selection(), Some((0, 4)));
1204+
}
1205+
11781206
#[cfg(feature = "system_clipboard")]
11791207
mod without_system_clipboard {
11801208
use super::*;

src/engine.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1481,6 +1481,9 @@ impl Reedline {
14811481
self.input_mode = InputMode::Regular;
14821482
}
14831483

1484+
// Update editor with current edit mode for mode-aware selection behavior
1485+
self.editor.set_edit_mode(self.edit_mode.edit_mode());
1486+
14841487
// Run the commands over the edit buffer
14851488
for command in commands {
14861489
self.editor.run_edit_command(command);

0 commit comments

Comments
 (0)