@@ -182,6 +182,9 @@ impl Vim {
Some(Operator::ToggleComments) => {
self.toggle_comments_motion(motion, times, window, cx)
}
+ Some(Operator::ReplaceWithRegister) => {
+ self.replace_with_register_motion(motion, times, window, cx)
+ }
Some(operator) => {
// Can't do anything for text objects, Ignoring
error!("Unexpected normal mode motion operator: {:?}", operator)
@@ -228,6 +231,9 @@ impl Vim {
Some(Operator::ToggleComments) => {
self.toggle_comments_object(object, around, window, cx)
}
+ Some(Operator::ReplaceWithRegister) => {
+ self.replace_with_register_object(object, around, window, cx)
+ }
_ => {
// Can't do anything for namespace operators. Ignoring
}
@@ -6,6 +6,8 @@ use serde::Deserialize;
use std::cmp;
use crate::{
+ motion::Motion,
+ object::Object,
state::{Mode, Register},
Vim,
};
@@ -192,12 +194,85 @@ impl Vim {
});
self.switch_mode(Mode::Normal, true, window, cx);
}
+
+ pub fn replace_with_register_object(
+ &mut self,
+ object: Object,
+ around: bool,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ self.stop_recording(cx);
+ let selected_register = self.selected_register.take();
+ self.update_editor(window, cx, |_, editor, window, cx| {
+ editor.transact(window, cx, |editor, window, cx| {
+ editor.set_clip_at_line_ends(false, cx);
+ editor.change_selections(None, window, cx, |s| {
+ s.move_with(|map, selection| {
+ object.expand_selection(map, selection, around);
+ });
+ });
+
+ let Some(Register { text, .. }) = Vim::update_globals(cx, |globals, cx| {
+ globals.read_register(selected_register, Some(editor), cx)
+ })
+ .filter(|reg| !reg.text.is_empty()) else {
+ return;
+ };
+ editor.insert(&text, window, cx);
+ editor.set_clip_at_line_ends(true, cx);
+ editor.change_selections(None, window, cx, |s| {
+ s.move_with(|map, selection| {
+ selection.start = map.clip_point(selection.start, Bias::Left);
+ selection.end = selection.start
+ })
+ })
+ });
+ });
+ }
+
+ pub fn replace_with_register_motion(
+ &mut self,
+ motion: Motion,
+ times: Option<usize>,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ self.stop_recording(cx);
+ let selected_register = self.selected_register.take();
+ self.update_editor(window, cx, |_, editor, window, cx| {
+ let text_layout_details = editor.text_layout_details(window);
+ editor.transact(window, cx, |editor, window, cx| {
+ editor.set_clip_at_line_ends(false, cx);
+ editor.change_selections(None, window, cx, |s| {
+ s.move_with(|map, selection| {
+ motion.expand_selection(map, selection, times, false, &text_layout_details);
+ });
+ });
+
+ let Some(Register { text, .. }) = Vim::update_globals(cx, |globals, cx| {
+ globals.read_register(selected_register, Some(editor), cx)
+ })
+ .filter(|reg| !reg.text.is_empty()) else {
+ return;
+ };
+ editor.insert(&text, window, cx);
+ editor.set_clip_at_line_ends(true, cx);
+ editor.change_selections(None, window, cx, |s| {
+ s.move_with(|map, selection| {
+ selection.start = map.clip_point(selection.start, Bias::Left);
+ selection.end = selection.start
+ })
+ })
+ });
+ });
+ }
}
#[cfg(test)]
mod test {
use crate::{
- state::Mode,
+ state::{Mode, Register},
test::{NeovimBackedTestContext, VimTestContext},
UseSystemClipboard, VimSettings,
};
@@ -742,4 +817,37 @@ mod test {
Mode::Normal,
);
}
+
+ #[gpui::test]
+ async fn test_replace_with_register(cx: &mut gpui::TestAppContext) {
+ let mut cx = VimTestContext::new(cx, true).await;
+
+ cx.set_state(
+ indoc! {"
+ หfish one
+ two three
+ "},
+ Mode::Normal,
+ );
+ cx.simulate_keystrokes("y i w");
+ cx.simulate_keystrokes("w");
+ cx.simulate_keystrokes("g r i w");
+ cx.assert_state(
+ indoc! {"
+ fish fisหh
+ two three
+ "},
+ Mode::Normal,
+ );
+ cx.simulate_keystrokes("j b g r e");
+ cx.assert_state(
+ indoc! {"
+ fish fish
+ two fisหh
+ "},
+ Mode::Normal,
+ );
+ let clipboard: Register = cx.read_from_clipboard().unwrap().into();
+ assert_eq!(clipboard.text, "fish");
+ }
}
@@ -111,6 +111,7 @@ pub enum Operator {
RecordRegister,
ReplayRegister,
ToggleComments,
+ ReplaceWithRegister,
}
#[derive(Default, Clone, Debug)]
@@ -499,6 +500,7 @@ impl Operator {
Operator::AutoIndent => "eq",
Operator::ShellCommand => "sh",
Operator::Rewrap => "gq",
+ Operator::ReplaceWithRegister => "gr",
Operator::Outdent => "<",
Operator::Uppercase => "gU",
Operator::Lowercase => "gu",
@@ -551,6 +553,7 @@ impl Operator {
| Operator::ShellCommand
| Operator::Lowercase
| Operator::Uppercase
+ | Operator::ReplaceWithRegister
| Operator::Object { .. }
| Operator::ChangeSurrounds { target: None }
| Operator::OppositeCase