vim: Improve pasting while in replace mode (#41549)
- Update `vim::normal::Vim.normal_replace` to work with more than one character - Add `vim::replace::Vim.paste_replace` to handle pasting the clipboard's contents while in replace mode - Update vim's handling of the `editor::actions::Paste` action so that the `paste_replace` method is called when vim is in replace mode, otherwise it'll just call the regular `editor::Editor.paste` method Closes #41378 Release Notes: - Improved pasting while in Vim's Replace mode, ensuring that the Zed replaces the same number of characters as the length of the contents being pasted
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user