Detailed changes
@@ -965,8 +965,17 @@ impl Vim {
window: &mut Window,
cx: &mut Context<Self>,
) {
+ // We need to use `text.chars().count()` instead of `text.len()` here as
+ // `len()` counts bytes, not characters.
+ let char_count = text.chars().count();
+ let count = Vim::take_count(cx).unwrap_or(char_count);
let is_return_char = text == "\n".into() || text == "\r".into();
- let count = Vim::take_count(cx).unwrap_or(1);
+ let repeat_count = match (is_return_char, char_count) {
+ (true, _) => 0,
+ (_, 1) => count,
+ (_, _) => 1,
+ };
+
Vim::take_forced_motion(cx);
self.stop_recording(cx);
self.update_editor(cx, |_, editor, cx| {
@@ -989,7 +998,7 @@ impl Vim {
edits.push((
range.start.to_offset(&display_map, Bias::Left)
..range.end.to_offset(&display_map, Bias::Left),
- text.repeat(if is_return_char { 0 } else { count }),
+ text.repeat(repeat_count),
));
}
@@ -1,5 +1,5 @@
use crate::{
- Vim,
+ Operator, Vim,
motion::{self, Motion},
object::Object,
state::Mode,
@@ -8,7 +8,7 @@ use editor::{
Anchor, Bias, Editor, EditorSnapshot, SelectionEffects, ToOffset, ToPoint,
display_map::ToDisplayPoint,
};
-use gpui::{Context, Window, actions};
+use gpui::{ClipboardEntry, Context, Window, actions};
use language::{Point, SelectionGoal};
use std::ops::Range;
use std::sync::Arc;
@@ -278,10 +278,27 @@ impl Vim {
);
}
}
+
+ /// Pastes the clipboard contents, replacing the same number of characters
+ /// as the clipboard's contents.
+ pub fn paste_replace(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+ let clipboard_text =
+ cx.read_from_clipboard()
+ .and_then(|item| match item.entries().first() {
+ Some(ClipboardEntry::String(text)) => Some(text.text().to_string()),
+ _ => None,
+ });
+
+ if let Some(text) = clipboard_text {
+ self.push_operator(Operator::Replace, window, cx);
+ self.normal_replace(Arc::from(text), window, cx);
+ }
+ }
}
#[cfg(test)]
mod test {
+ use gpui::ClipboardItem;
use indoc::indoc;
use crate::{
@@ -521,4 +538,22 @@ mod test {
assert_eq!(0, highlights.len());
});
}
+
+ #[gpui::test]
+ async fn test_paste_replace(cx: &mut gpui::TestAppContext) {
+ let mut cx = VimTestContext::new(cx, true).await;
+
+ cx.set_state(indoc! {"ˇ123"}, Mode::Replace);
+ cx.write_to_clipboard(ClipboardItem::new_string("456".to_string()));
+ cx.dispatch_action(editor::actions::Paste);
+ cx.assert_state(indoc! {"45ˇ6"}, Mode::Replace);
+
+ // If the clipboard's contents length is greater than the remaining text
+ // length, nothing sould be replace and cursor should remain in the same
+ // position.
+ cx.set_state(indoc! {"ˇ123"}, Mode::Replace);
+ cx.write_to_clipboard(ClipboardItem::new_string("4567".to_string()));
+ cx.dispatch_action(editor::actions::Paste);
+ cx.assert_state(indoc! {"ˇ123"}, Mode::Replace);
+ }
}
@@ -23,6 +23,7 @@ use collections::HashMap;
use editor::{
Anchor, Bias, Editor, EditorEvent, EditorSettings, HideMouseCursorOrigin, SelectionEffects,
ToPoint,
+ actions::Paste,
movement::{self, FindRange},
};
use gpui::{
@@ -919,6 +920,17 @@ impl Vim {
);
});
+ Vim::action(
+ editor,
+ cx,
+ |vim, _: &editor::actions::Paste, window, cx| match vim.mode {
+ Mode::Replace => vim.paste_replace(window, cx),
+ _ => {
+ vim.update_editor(cx, |_, editor, cx| editor.paste(&Paste, window, cx));
+ }
+ },
+ );
+
normal::register(editor, cx);
insert::register(editor, cx);
helix::register(editor, cx);