vim: Fix linewise copy of last line with no trailing newline (#2885)

Along the way, delete the VimBindingTestContext by updating the
visual tests to no-longer need it.


Release Notes:

- vim: Fix `y` when on the last line of a file with no trailing newline.
This commit is contained in:
Conrad Irwin 2023-08-24 10:45:46 -06:00 committed by GitHub
commit 3b6794fe36
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 118 additions and 187 deletions

View file

@ -1,7 +1,6 @@
mod neovim_backed_binding_test_context;
mod neovim_backed_test_context;
mod neovim_connection;
mod vim_binding_test_context;
mod vim_test_context;
use std::sync::Arc;
@ -10,7 +9,6 @@ use command_palette::CommandPalette;
use editor::DisplayPoint;
pub use neovim_backed_binding_test_context::*;
pub use neovim_backed_test_context::*;
pub use vim_binding_test_context::*;
pub use vim_test_context::*;
use indoc::indoc;

View file

@ -1,64 +0,0 @@
use std::ops::{Deref, DerefMut};
use crate::*;
use super::VimTestContext;
pub struct VimBindingTestContext<'a, const COUNT: usize> {
cx: VimTestContext<'a>,
keystrokes_under_test: [&'static str; COUNT],
mode_before: Mode,
mode_after: Mode,
}
impl<'a, const COUNT: usize> VimBindingTestContext<'a, COUNT> {
pub fn new(
keystrokes_under_test: [&'static str; COUNT],
mode_before: Mode,
mode_after: Mode,
cx: VimTestContext<'a>,
) -> Self {
Self {
cx,
keystrokes_under_test,
mode_before,
mode_after,
}
}
pub fn binding<const NEW_COUNT: usize>(
self,
keystrokes_under_test: [&'static str; NEW_COUNT],
) -> VimBindingTestContext<'a, NEW_COUNT> {
VimBindingTestContext {
keystrokes_under_test,
cx: self.cx,
mode_before: self.mode_before,
mode_after: self.mode_after,
}
}
pub fn assert(&mut self, initial_state: &str, state_after: &str) {
self.cx.assert_binding(
self.keystrokes_under_test,
initial_state,
self.mode_before,
state_after,
self.mode_after,
)
}
}
impl<'a, const COUNT: usize> Deref for VimBindingTestContext<'a, COUNT> {
type Target = VimTestContext<'a>;
fn deref(&self) -> &Self::Target {
&self.cx
}
}
impl<'a, const COUNT: usize> DerefMut for VimBindingTestContext<'a, COUNT> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.cx
}
}

View file

@ -8,8 +8,6 @@ use search::{BufferSearchBar, ProjectSearchBar};
use crate::{state::Operator, *};
use super::VimBindingTestContext;
pub struct VimTestContext<'a> {
cx: EditorLspTestContext<'a>,
}
@ -126,14 +124,6 @@ impl<'a> VimTestContext<'a> {
assert_eq!(self.mode(), mode_after, "{}", self.assertion_context());
assert_eq!(self.active_operator(), None, "{}", self.assertion_context());
}
pub fn binding<const COUNT: usize>(
mut self,
keystrokes: [&'static str; COUNT],
) -> VimBindingTestContext<'a, COUNT> {
let mode = self.mode();
VimBindingTestContext::new(keystrokes, mode, mode, self)
}
}
impl<'a> Deref for VimTestContext<'a> {

View file

@ -1,5 +1,6 @@
use editor::{ClipboardSelection, Editor};
use gpui::{AppContext, ClipboardItem};
use language::Point;
pub fn copy_selections_content(editor: &mut Editor, linewise: bool, cx: &mut AppContext) {
let selections = editor.selections.all_adjusted(cx);
@ -9,7 +10,7 @@ pub fn copy_selections_content(editor: &mut Editor, linewise: bool, cx: &mut App
{
let mut is_first = true;
for selection in selections.iter() {
let start = selection.start;
let mut start = selection.start;
let end = selection.end;
if is_first {
is_first = false;
@ -17,9 +18,25 @@ pub fn copy_selections_content(editor: &mut Editor, linewise: bool, cx: &mut App
text.push_str("\n");
}
let initial_len = text.len();
// if the file does not end with \n, and our line-mode selection ends on
// that line, we will have expanded the start of the selection to ensure it
// contains a newline (so that delete works as expected). We undo that change
// here.
let is_last_line = linewise
&& end.row == buffer.max_buffer_row()
&& buffer.max_point().column > 0
&& start == Point::new(start.row, buffer.line_len(start.row));
if is_last_line {
start = Point::new(buffer.max_buffer_row(), 0);
}
for chunk in buffer.text_for_range(start..end) {
text.push_str(chunk);
}
if is_last_line {
text.push_str("\n");
}
clipboard_selections.push(ClipboardSelection {
len: text.len() - initial_len,
is_entire_line: linewise,

View file

@ -563,38 +563,41 @@ mod test {
#[gpui::test]
async fn test_visual_line_delete(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx)
.await
.binding(["shift-v", "x"]);
cx.assert(indoc! {"
let mut cx = NeovimBackedTestContext::new(cx).await;
cx.set_shared_state(indoc! {"
The quˇick brown
fox jumps over
the lazy dog"})
.await;
cx.simulate_shared_keystrokes(["shift-v", "x"]).await;
cx.assert_state_matches().await;
// Test pasting code copied on delete
cx.simulate_shared_keystroke("p").await;
cx.assert_state_matches().await;
cx.assert_all(indoc! {"
cx.set_shared_state(indoc! {"
The quick brown
fox juˇmps over
the laˇzy dog"})
.await;
let mut cx = cx.binding(["shift-v", "j", "x"]);
cx.assert(indoc! {"
The quˇick brown
fox jumps over
the lazy dog"})
.await;
// Test pasting code copied on delete
cx.simulate_shared_keystroke("p").await;
cx.assert_state_matches().await;
cx.assert_all(indoc! {"
The quick brown
fox juˇmps over
the laˇzy dog"})
.await;
cx.simulate_shared_keystrokes(["shift-v", "x"]).await;
cx.assert_state_matches().await;
cx.assert_shared_clipboard("the lazy dog\n").await;
for marked_text in cx.each_marked_position(indoc! {"
The quˇick brown
fox jumps over
the lazy dog"})
{
cx.set_shared_state(&marked_text).await;
cx.simulate_shared_keystrokes(["shift-v", "j", "x"]).await;
cx.assert_state_matches().await;
// Test pasting code copied on delete
cx.simulate_shared_keystroke("p").await;
cx.assert_state_matches().await;
}
cx.set_shared_state(indoc! {"
The ˇlong line
@ -608,86 +611,57 @@ mod test {
#[gpui::test]
async fn test_visual_yank(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["v", "w", "y"]);
cx.assert("The quick ˇbrown", "The quick ˇbrown");
cx.assert_clipboard_content(Some("brown"));
let mut cx = cx.binding(["v", "w", "j", "y"]);
cx.assert(
indoc! {"
let mut cx = NeovimBackedTestContext::new(cx).await;
cx.set_shared_state("The quick ˇbrown").await;
cx.simulate_shared_keystrokes(["v", "w", "y"]).await;
cx.assert_shared_state("The quick ˇbrown").await;
cx.assert_shared_clipboard("brown").await;
cx.set_shared_state(indoc! {"
The ˇquick brown
fox jumps over
the lazy dog"},
indoc! {"
The ˇquick brown
fox jumps over
the lazy dog"},
);
cx.assert_clipboard_content(Some(indoc! {"
quick brown
fox jumps o"}));
cx.assert(
indoc! {"
The quick brown
fox jumps over
the ˇlazy dog"},
indoc! {"
The quick brown
fox jumps over
the ˇlazy dog"},
);
cx.assert_clipboard_content(Some("lazy d"));
cx.assert(
indoc! {"
The quick brown
fox jumps ˇover
the lazy dog"},
indoc! {"
The quick brown
fox jumps ˇover
the lazy dog"},
);
cx.assert_clipboard_content(Some(indoc! {"
over
t"}));
the lazy dog"})
.await;
cx.simulate_shared_keystrokes(["v", "w", "j", "y"]).await;
cx.assert_shared_state(indoc! {"
The ˇquick brown
fox jumps over
the lazy dog"})
.await;
cx.assert_shared_clipboard(indoc! {"
quick brown
fox jumps o"})
.await;
cx.set_shared_state(indoc! {"
The quick brown
fox jumps over
the ˇlazy dog"})
.await;
cx.simulate_shared_keystrokes(["v", "w", "j", "y"]).await;
cx.assert_shared_state(indoc! {"
The quick brown
fox jumps over
the ˇlazy dog"})
.await;
cx.assert_shared_clipboard("lazy d").await;
cx.simulate_shared_keystrokes(["shift-v", "y"]).await;
cx.assert_shared_clipboard("the lazy dog\n").await;
let mut cx = cx.binding(["v", "b", "k", "y"]);
cx.assert(
indoc! {"
The ˇquick brown
fox jumps over
the lazy dog"},
indoc! {"
ˇThe quick brown
fox jumps over
the lazy dog"},
);
cx.set_shared_state(indoc! {"
The ˇquick brown
fox jumps over
the lazy dog"})
.await;
cx.simulate_shared_keystrokes(["v", "b", "k", "y"]).await;
cx.assert_shared_state(indoc! {"
ˇThe quick brown
fox jumps over
the lazy dog"})
.await;
cx.assert_clipboard_content(Some("The q"));
cx.assert(
indoc! {"
The quick brown
fox jumps over
the ˇlazy dog"},
indoc! {"
The quick brown
ˇfox jumps over
the lazy dog"},
);
cx.assert_clipboard_content(Some(indoc! {"
fox jumps over
the l"}));
cx.assert(
indoc! {"
The quick brown
fox jumps ˇover
the lazy dog"},
indoc! {"
The ˇquick brown
fox jumps over
the lazy dog"},
);
cx.assert_clipboard_content(Some(indoc! {"
quick brown
fox jumps o"}));
}
#[gpui::test]

View file

@ -4,14 +4,11 @@
{"Get":{"state":"fox juˇmps over\nthe lazy dog","mode":"Normal"}}
{"Key":"p"}
{"Get":{"state":"fox jumps over\nˇThe quick brown\nthe lazy dog","mode":"Normal"}}
{"Put":{"state":"The quick brown\nfox juˇmps over\nthe lazy dog"}}
{"Key":"shift-v"}
{"Key":"x"}
{"Get":{"state":"The quick brown\nthe laˇzy dog","mode":"Normal"}}
{"Put":{"state":"The quick brown\nfox jumps over\nthe laˇzy dog"}}
{"Key":"shift-v"}
{"Key":"x"}
{"Get":{"state":"The quick brown\nfox juˇmps over","mode":"Normal"}}
{"ReadRegister":{"name":"\"","value":"the lazy dog\n"}}
{"Put":{"state":"The quˇick brown\nfox jumps over\nthe lazy dog"}}
{"Key":"shift-v"}
{"Key":"j"}
@ -19,16 +16,6 @@
{"Get":{"state":"the laˇzy dog","mode":"Normal"}}
{"Key":"p"}
{"Get":{"state":"the lazy dog\nˇThe quick brown\nfox jumps over","mode":"Normal"}}
{"Put":{"state":"The quick brown\nfox juˇmps over\nthe lazy dog"}}
{"Key":"shift-v"}
{"Key":"j"}
{"Key":"x"}
{"Get":{"state":"The quˇick brown","mode":"Normal"}}
{"Put":{"state":"The quick brown\nfox jumps over\nthe laˇzy dog"}}
{"Key":"shift-v"}
{"Key":"j"}
{"Key":"x"}
{"Get":{"state":"The quick brown\nfox juˇmps over","mode":"Normal"}}
{"Put":{"state":"The ˇlong line\nshould not\ncrash\n"}}
{"Key":"shift-v"}
{"Key":"$"}

View file

@ -0,0 +1,29 @@
{"Put":{"state":"The quick ˇbrown"}}
{"Key":"v"}
{"Key":"w"}
{"Key":"y"}
{"Get":{"state":"The quick ˇbrown","mode":"Normal"}}
{"ReadRegister":{"name":"\"","value":"brown"}}
{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog"}}
{"Key":"v"}
{"Key":"w"}
{"Key":"j"}
{"Key":"y"}
{"Get":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog","mode":"Normal"}}
{"ReadRegister":{"name":"\"","value":"quick brown\nfox jumps o"}}
{"Put":{"state":"The quick brown\nfox jumps over\nthe ˇlazy dog"}}
{"Key":"v"}
{"Key":"w"}
{"Key":"j"}
{"Key":"y"}
{"Get":{"state":"The quick brown\nfox jumps over\nthe ˇlazy dog","mode":"Normal"}}
{"ReadRegister":{"name":"\"","value":"lazy d"}}
{"Key":"shift-v"}
{"Key":"y"}
{"ReadRegister":{"name":"\"","value":"the lazy dog\n"}}
{"Put":{"state":"The ˇquick brown\nfox jumps over\nthe lazy dog"}}
{"Key":"v"}
{"Key":"b"}
{"Key":"k"}
{"Key":"y"}
{"Get":{"state":"ˇThe quick brown\nfox jumps over\nthe lazy dog","mode":"Normal"}}