Fix vim selection to include entire range
Co-Authored-By: Max Brunsfeld <maxbrunsfeld@gmail.com>
This commit is contained in:
parent
e6f3e0ab9c
commit
b53fb8633e
21 changed files with 489 additions and 335 deletions
|
@ -61,6 +61,9 @@ pub struct NeovimBackedTestContext<'a> {
|
|||
// bindings are exempted. If None, all bindings are ignored for that insertion text.
|
||||
exemptions: HashMap<String, Option<HashSet<String>>>,
|
||||
neovim: NeovimConnection,
|
||||
|
||||
last_set_state: Option<String>,
|
||||
recent_keystrokes: Vec<String>,
|
||||
}
|
||||
|
||||
impl<'a> NeovimBackedTestContext<'a> {
|
||||
|
@ -71,6 +74,9 @@ impl<'a> NeovimBackedTestContext<'a> {
|
|||
cx,
|
||||
exemptions: Default::default(),
|
||||
neovim: NeovimConnection::new(function_name).await,
|
||||
|
||||
last_set_state: None,
|
||||
recent_keystrokes: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,13 +108,21 @@ impl<'a> NeovimBackedTestContext<'a> {
|
|||
keystroke_texts: [&str; COUNT],
|
||||
) -> ContextHandle {
|
||||
for keystroke_text in keystroke_texts.into_iter() {
|
||||
self.recent_keystrokes.push(keystroke_text.to_string());
|
||||
self.neovim.send_keystroke(keystroke_text).await;
|
||||
}
|
||||
self.simulate_keystrokes(keystroke_texts)
|
||||
}
|
||||
|
||||
pub async fn set_shared_state(&mut self, marked_text: &str) -> ContextHandle {
|
||||
let context_handle = self.set_state(marked_text, Mode::Normal);
|
||||
let mode = if marked_text.contains("»") {
|
||||
Mode::Visual { line: false }
|
||||
} else {
|
||||
Mode::Normal
|
||||
};
|
||||
let context_handle = self.set_state(marked_text, mode);
|
||||
self.last_set_state = Some(marked_text.to_string());
|
||||
self.recent_keystrokes = Vec::new();
|
||||
self.neovim.set_state(marked_text).await;
|
||||
context_handle
|
||||
}
|
||||
|
@ -116,15 +130,25 @@ impl<'a> NeovimBackedTestContext<'a> {
|
|||
pub async fn assert_shared_state(&mut self, marked_text: &str) {
|
||||
let neovim = self.neovim_state().await;
|
||||
if neovim != marked_text {
|
||||
let initial_state = self
|
||||
.last_set_state
|
||||
.as_ref()
|
||||
.unwrap_or(&"N/A".to_string())
|
||||
.clone();
|
||||
panic!(
|
||||
indoc! {"Test is incorrect (currently expected != neovim state)
|
||||
|
||||
# initial state:
|
||||
{}
|
||||
# keystrokes:
|
||||
{}
|
||||
# currently expected:
|
||||
{}
|
||||
# neovim state:
|
||||
{}
|
||||
# zed state:
|
||||
{}"},
|
||||
initial_state,
|
||||
self.recent_keystrokes.join(" "),
|
||||
marked_text,
|
||||
neovim,
|
||||
self.editor_state(),
|
||||
|
@ -141,28 +165,40 @@ impl<'a> NeovimBackedTestContext<'a> {
|
|||
)
|
||||
}
|
||||
|
||||
pub async fn neovim_mode(&mut self) -> Mode {
|
||||
self.neovim.mode().await.unwrap()
|
||||
}
|
||||
|
||||
async fn neovim_selection(&mut self) -> Range<usize> {
|
||||
let mut neovim_selection = self.neovim.selection().await;
|
||||
// Zed selections adjust themselves to make the end point visually make sense
|
||||
if neovim_selection.start > neovim_selection.end {
|
||||
neovim_selection.start.column += 1;
|
||||
}
|
||||
let neovim_selection = self.neovim.selection().await;
|
||||
neovim_selection.to_offset(&self.buffer_snapshot())
|
||||
}
|
||||
|
||||
pub async fn assert_state_matches(&mut self) {
|
||||
assert_eq!(
|
||||
self.neovim.text().await,
|
||||
self.buffer_text(),
|
||||
"{}",
|
||||
self.assertion_context()
|
||||
);
|
||||
let neovim = self.neovim_state().await;
|
||||
let editor = self.editor_state();
|
||||
let initial_state = self
|
||||
.last_set_state
|
||||
.as_ref()
|
||||
.unwrap_or(&"N/A".to_string())
|
||||
.clone();
|
||||
|
||||
let selections = vec![self.neovim_selection().await];
|
||||
self.assert_editor_selections(selections);
|
||||
|
||||
if let Some(neovim_mode) = self.neovim.mode().await {
|
||||
assert_eq!(neovim_mode, self.mode(), "{}", self.assertion_context(),);
|
||||
if neovim != editor {
|
||||
panic!(
|
||||
indoc! {"Test failed (zed does not match nvim behaviour)
|
||||
# initial state:
|
||||
{}
|
||||
# keystrokes:
|
||||
{}
|
||||
# neovim state:
|
||||
{}
|
||||
# zed state:
|
||||
{}"},
|
||||
initial_state,
|
||||
self.recent_keystrokes.join(" "),
|
||||
neovim,
|
||||
editor,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -207,6 +243,29 @@ impl<'a> NeovimBackedTestContext<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn each_marked_position(&self, marked_positions: &str) -> Vec<String> {
|
||||
let (unmarked_text, cursor_offsets) = marked_text_offsets(marked_positions);
|
||||
let mut ret = Vec::with_capacity(cursor_offsets.len());
|
||||
|
||||
for cursor_offset in cursor_offsets.iter() {
|
||||
let mut marked_text = unmarked_text.clone();
|
||||
marked_text.insert(*cursor_offset, 'ˇ');
|
||||
ret.push(marked_text)
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
pub async fn assert_neovim_compatible<const COUNT: usize>(
|
||||
&mut self,
|
||||
marked_positions: &str,
|
||||
keystrokes: [&str; COUNT],
|
||||
) {
|
||||
self.set_shared_state(&marked_positions).await;
|
||||
self.simulate_shared_keystrokes(keystrokes).await;
|
||||
self.assert_state_matches().await;
|
||||
}
|
||||
|
||||
pub async fn assert_binding_matches_all_exempted<const COUNT: usize>(
|
||||
&mut self,
|
||||
keystrokes: [&str; COUNT],
|
||||
|
|
|
@ -213,6 +213,16 @@ impl NeovimConnection {
|
|||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "neovim")]
|
||||
async fn read_position(&mut self, cmd: &str) -> u32 {
|
||||
self.nvim
|
||||
.command_output(cmd)
|
||||
.await
|
||||
.unwrap()
|
||||
.parse::<u32>()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[cfg(feature = "neovim")]
|
||||
pub async fn state(&mut self) -> (Option<Mode>, String, Range<Point>) {
|
||||
let nvim_buffer = self
|
||||
|
@ -226,22 +236,11 @@ impl NeovimConnection {
|
|||
.expect("Could not get buffer text")
|
||||
.join("\n");
|
||||
|
||||
let cursor_row: u32 = self
|
||||
.nvim
|
||||
.command_output("echo line('.')")
|
||||
.await
|
||||
.unwrap()
|
||||
.parse::<u32>()
|
||||
.unwrap()
|
||||
- 1; // Neovim rows start at 1
|
||||
let cursor_col: u32 = self
|
||||
.nvim
|
||||
.command_output("echo col('.')")
|
||||
.await
|
||||
.unwrap()
|
||||
.parse::<u32>()
|
||||
.unwrap()
|
||||
- 1; // Neovim columns start at 1
|
||||
// nvim columns are 1-based, so -1.
|
||||
let cursor_row = self.read_position("echo line('.')").await - 1;
|
||||
let mut cursor_col = self.read_position("echo col('.')").await - 1;
|
||||
let selection_row = self.read_position("echo line('v')").await - 1;
|
||||
let mut selection_col = self.read_position("echo col('v')").await - 1;
|
||||
|
||||
let nvim_mode_text = self
|
||||
.nvim
|
||||
|
@ -266,46 +265,32 @@ impl NeovimConnection {
|
|||
_ => None,
|
||||
};
|
||||
|
||||
let (start, end) = if let Some(Mode::Visual { .. }) = mode {
|
||||
self.nvim
|
||||
.input("<escape>")
|
||||
.await
|
||||
.expect("Could not exit visual mode");
|
||||
let nvim_buffer = self
|
||||
.nvim
|
||||
.get_current_buf()
|
||||
.await
|
||||
.expect("Could not get neovim buffer");
|
||||
let (start_row, start_col) = nvim_buffer
|
||||
.get_mark("<")
|
||||
.await
|
||||
.expect("Could not get selection start");
|
||||
let (end_row, end_col) = nvim_buffer
|
||||
.get_mark(">")
|
||||
.await
|
||||
.expect("Could not get selection end");
|
||||
self.nvim
|
||||
.input("gv")
|
||||
.await
|
||||
.expect("Could not reselect visual selection");
|
||||
|
||||
if cursor_row == start_row as u32 - 1 && cursor_col == start_col as u32 {
|
||||
(
|
||||
Point::new(end_row as u32 - 1, end_col as u32),
|
||||
Point::new(start_row as u32 - 1, start_col as u32),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
Point::new(start_row as u32 - 1, start_col as u32),
|
||||
Point::new(end_row as u32 - 1, end_col as u32),
|
||||
)
|
||||
// Vim uses the index of the first and last character in the selection
|
||||
// Zed uses the index of the positions between the characters, so we need
|
||||
// to add one to the end in visual mode.
|
||||
match mode {
|
||||
Some(Mode::Visual { .. }) => {
|
||||
if selection_col > cursor_col {
|
||||
let selection_line_length =
|
||||
self.read_position("echo strlen(getline(line('v')))").await;
|
||||
if selection_line_length > 0 {
|
||||
selection_col += 1;
|
||||
}
|
||||
} else {
|
||||
let cursor_line_length =
|
||||
self.read_position("echo strlen(getline(line('.')))").await;
|
||||
if cursor_line_length > 0 {
|
||||
cursor_col += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
(
|
||||
Point::new(cursor_row, cursor_col),
|
||||
Point::new(cursor_row, cursor_col),
|
||||
)
|
||||
};
|
||||
Some(Mode::Insert) | Some(Mode::Normal) | None => {}
|
||||
}
|
||||
|
||||
let (start, end) = (
|
||||
Point::new(selection_row, selection_col),
|
||||
Point::new(cursor_row, cursor_col),
|
||||
);
|
||||
|
||||
let state = NeovimData::Get {
|
||||
mode,
|
||||
|
|
|
@ -86,12 +86,13 @@ impl<'a> VimTestContext<'a> {
|
|||
|
||||
pub fn set_state(&mut self, text: &str, mode: Mode) -> ContextHandle {
|
||||
let window_id = self.window_id;
|
||||
let context_handle = self.cx.set_state(text);
|
||||
self.update_window(window_id, |cx| {
|
||||
Vim::update(cx, |vim, cx| {
|
||||
vim.switch_mode(mode, false, cx);
|
||||
vim.switch_mode(mode, true, cx);
|
||||
})
|
||||
});
|
||||
self.cx.set_state(text)
|
||||
context_handle
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue